summaryrefslogtreecommitdiff
path: root/chromium/cc
diff options
context:
space:
mode:
authorAndras Becsi <andras.becsi@digia.com>2013-12-11 21:33:03 +0100
committerAndras Becsi <andras.becsi@digia.com>2013-12-13 12:34:07 +0100
commitf2a33ff9cbc6d19943f1c7fbddd1f23d23975577 (patch)
tree0586a32aa390ade8557dfd6b4897f43a07449578 /chromium/cc
parent5362912cdb5eea702b68ebe23702468d17c3017a (diff)
downloadqtwebengine-chromium-f2a33ff9cbc6d19943f1c7fbddd1f23d23975577.tar.gz
Update Chromium to branch 1650 (31.0.1650.63)
Change-Id: I57d8c832eaec1eb2364e0a8e7352a6dd354db99f Reviewed-by: Jocelyn Turcotte <jocelyn.turcotte@digia.com>
Diffstat (limited to 'chromium/cc')
-rw-r--r--chromium/cc/DEPS3
-rw-r--r--chromium/cc/OWNERS4
-rw-r--r--chromium/cc/PRESUBMIT.py16
-rw-r--r--chromium/cc/animation/animation.cc6
-rw-r--r--chromium/cc/animation/animation_curve.h10
-rw-r--r--chromium/cc/animation/animation_unittest.cc35
-rw-r--r--chromium/cc/animation/keyframed_animation_curve.cc23
-rw-r--r--chromium/cc/animation/keyframed_animation_curve.h2
-rw-r--r--chromium/cc/animation/keyframed_animation_curve_unittest.cc25
-rw-r--r--chromium/cc/animation/layer_animation_controller.cc36
-rw-r--r--chromium/cc/animation/layer_animation_controller.h7
-rw-r--r--chromium/cc/animation/layer_animation_controller_unittest.cc73
-rw-r--r--chromium/cc/animation/scrollbar_animation_controller.h5
-rw-r--r--chromium/cc/animation/scrollbar_animation_controller_linear_fade.cc46
-rw-r--r--chromium/cc/animation/scrollbar_animation_controller_linear_fade.h5
-rw-r--r--chromium/cc/animation/scrollbar_animation_controller_linear_fade_unittest.cc105
-rw-r--r--chromium/cc/animation/scrollbar_animation_controller_thinning.cc132
-rw-r--r--chromium/cc/animation/scrollbar_animation_controller_thinning.h65
-rw-r--r--chromium/cc/animation/scrollbar_animation_controller_thinning_unittest.cc185
-rw-r--r--chromium/cc/animation/timing_function.cc53
-rw-r--r--chromium/cc/animation/timing_function.h6
-rw-r--r--chromium/cc/animation/timing_function_unittest.cc70
-rw-r--r--chromium/cc/animation/transform_operation.cc240
-rw-r--r--chromium/cc/animation/transform_operation.h25
-rw-r--r--chromium/cc/animation/transform_operations.cc59
-rw-r--r--chromium/cc/animation/transform_operations.h27
-rw-r--r--chromium/cc/animation/transform_operations_unittest.cc338
-rw-r--r--chromium/cc/base/float_quad_unittest.cc25
-rw-r--r--chromium/cc/base/math_util.cc44
-rw-r--r--chromium/cc/base/math_util.h13
-rw-r--r--chromium/cc/base/scoped_ptr_hash_map.h157
-rw-r--r--chromium/cc/base/switches.cc42
-rw-r--r--chromium/cc/base/switches.h8
-rw-r--r--chromium/cc/cc.gyp38
-rw-r--r--chromium/cc/cc_tests.gyp14
-rw-r--r--chromium/cc/debug/devtools_instrumentation.h17
-rw-r--r--chromium/cc/debug/fake_web_graphics_context_3d.h3
-rw-r--r--chromium/cc/debug/frame_rate_counter.cc24
-rw-r--r--chromium/cc/debug/frame_rate_counter.h2
-rw-r--r--chromium/cc/debug/overdraw_metrics.cc14
-rw-r--r--chromium/cc/debug/rendering_stats.cc233
-rw-r--r--chromium/cc/debug/rendering_stats.h106
-rw-r--r--chromium/cc/debug/rendering_stats_instrumentation.cc85
-rw-r--r--chromium/cc/debug/rendering_stats_instrumentation.h35
-rw-r--r--chromium/cc/debug/test_context_provider.cc240
-rw-r--r--chromium/cc/debug/test_context_provider.h90
-rw-r--r--chromium/cc/debug/test_web_graphics_context_3d.cc632
-rw-r--r--chromium/cc/debug/test_web_graphics_context_3d.h292
-rw-r--r--chromium/cc/debug/traced_picture.cc30
-rw-r--r--chromium/cc/debug/traced_picture.h7
-rw-r--r--chromium/cc/input/input_handler.h1
-rw-r--r--chromium/cc/input/page_scale_animation.cc18
-rw-r--r--chromium/cc/input/page_scale_animation.h7
-rw-r--r--chromium/cc/input/scrollbar.h1
-rw-r--r--chromium/cc/layers/compositing_reasons.h3
-rw-r--r--chromium/cc/layers/content_layer.cc26
-rw-r--r--chromium/cc/layers/content_layer.h2
-rw-r--r--chromium/cc/layers/delegated_renderer_layer.cc112
-rw-r--r--chromium/cc/layers/delegated_renderer_layer.h25
-rw-r--r--chromium/cc/layers/delegated_renderer_layer_impl.cc136
-rw-r--r--chromium/cc/layers/delegated_renderer_layer_impl.h21
-rw-r--r--chromium/cc/layers/delegated_renderer_layer_impl_unittest.cc128
-rw-r--r--chromium/cc/layers/draw_properties.h5
-rw-r--r--chromium/cc/layers/heads_up_display_layer.cc4
-rw-r--r--chromium/cc/layers/heads_up_display_layer.h4
-rw-r--r--chromium/cc/layers/heads_up_display_layer_impl.cc5
-rw-r--r--chromium/cc/layers/image_layer.cc5
-rw-r--r--chromium/cc/layers/io_surface_layer_impl.cc16
-rw-r--r--chromium/cc/layers/layer.cc211
-rw-r--r--chromium/cc/layers/layer.h85
-rw-r--r--chromium/cc/layers/layer_client.h24
-rw-r--r--chromium/cc/layers/layer_impl.cc312
-rw-r--r--chromium/cc/layers/layer_impl.h71
-rw-r--r--chromium/cc/layers/layer_impl_unittest.cc6
-rw-r--r--chromium/cc/layers/layer_unittest.cc3
-rw-r--r--chromium/cc/layers/nine_patch_layer.cc168
-rw-r--r--chromium/cc/layers/nine_patch_layer.h43
-rw-r--r--chromium/cc/layers/nine_patch_layer_impl.cc307
-rw-r--r--chromium/cc/layers/nine_patch_layer_impl.h42
-rw-r--r--chromium/cc/layers/nine_patch_layer_impl_unittest.cc199
-rw-r--r--chromium/cc/layers/nine_patch_layer_unittest.cc102
-rw-r--r--chromium/cc/layers/painted_scrollbar_layer.cc244
-rw-r--r--chromium/cc/layers/painted_scrollbar_layer.h (renamed from chromium/cc/layers/scrollbar_layer.h)75
-rw-r--r--chromium/cc/layers/painted_scrollbar_layer_impl.cc181
-rw-r--r--chromium/cc/layers/painted_scrollbar_layer_impl.h82
-rw-r--r--chromium/cc/layers/picture_layer.cc47
-rw-r--r--chromium/cc/layers/picture_layer.h1
-rw-r--r--chromium/cc/layers/picture_layer_impl.cc214
-rw-r--r--chromium/cc/layers/picture_layer_impl.h14
-rw-r--r--chromium/cc/layers/picture_layer_impl_unittest.cc179
-rw-r--r--chromium/cc/layers/picture_layer_unittest.cc69
-rw-r--r--chromium/cc/layers/scrollbar_layer.cc353
-rw-r--r--chromium/cc/layers/scrollbar_layer_impl.cc328
-rw-r--r--chromium/cc/layers/scrollbar_layer_impl.h103
-rw-r--r--chromium/cc/layers/scrollbar_layer_impl_base.cc170
-rw-r--r--chromium/cc/layers/scrollbar_layer_impl_base.h91
-rw-r--r--chromium/cc/layers/scrollbar_layer_interface.h30
-rw-r--r--chromium/cc/layers/scrollbar_layer_unittest.cc389
-rw-r--r--chromium/cc/layers/solid_color_scrollbar_layer.cc68
-rw-r--r--chromium/cc/layers/solid_color_scrollbar_layer.h54
-rw-r--r--chromium/cc/layers/solid_color_scrollbar_layer_impl.cc88
-rw-r--r--chromium/cc/layers/solid_color_scrollbar_layer_impl.h52
-rw-r--r--chromium/cc/layers/texture_layer.cc100
-rw-r--r--chromium/cc/layers/texture_layer.h116
-rw-r--r--chromium/cc/layers/texture_layer_client.h8
-rw-r--r--chromium/cc/layers/texture_layer_impl.cc24
-rw-r--r--chromium/cc/layers/texture_layer_impl.h5
-rw-r--r--chromium/cc/layers/texture_layer_unittest.cc1120
-rw-r--r--chromium/cc/layers/tiled_layer.cc10
-rw-r--r--chromium/cc/layers/tiled_layer.h8
-rw-r--r--chromium/cc/layers/tiled_layer_unittest.cc48
-rw-r--r--chromium/cc/layers/video_layer_impl.cc17
-rw-r--r--chromium/cc/layers/video_layer_impl.h4
-rw-r--r--chromium/cc/output/begin_frame_args.cc11
-rw-r--r--chromium/cc/output/begin_frame_args.h4
-rw-r--r--chromium/cc/output/compositor_frame_ack.h4
-rw-r--r--chromium/cc/output/compositor_frame_metadata.h2
-rw-r--r--chromium/cc/output/context_provider.cc30
-rw-r--r--chromium/cc/output/context_provider.h40
-rw-r--r--chromium/cc/output/copy_output_request.cc13
-rw-r--r--chromium/cc/output/copy_output_request.h4
-rw-r--r--chromium/cc/output/copy_output_result.cc26
-rw-r--r--chromium/cc/output/copy_output_result.h19
-rw-r--r--chromium/cc/output/delegating_renderer.cc100
-rw-r--r--chromium/cc/output/delegating_renderer.h7
-rw-r--r--chromium/cc/output/delegating_renderer_unittest.cc14
-rw-r--r--chromium/cc/output/direct_renderer.cc108
-rw-r--r--chromium/cc/output/direct_renderer.h26
-rw-r--r--chromium/cc/output/filter_operation.cc10
-rw-r--r--chromium/cc/output/filter_operations.cc29
-rw-r--r--chromium/cc/output/gl_renderer.cc300
-rw-r--r--chromium/cc/output/gl_renderer.h61
-rw-r--r--chromium/cc/output/gl_renderer_unittest.cc911
-rw-r--r--chromium/cc/output/output_surface.cc267
-rw-r--r--chromium/cc/output/output_surface.h54
-rw-r--r--chromium/cc/output/output_surface_client.h8
-rw-r--r--chromium/cc/output/output_surface_unittest.cc311
-rw-r--r--chromium/cc/output/render_surface_filters.cc6
-rw-r--r--chromium/cc/output/renderer.h29
-rw-r--r--chromium/cc/output/renderer_pixeltest.cc170
-rw-r--r--chromium/cc/output/shader.cc2
-rw-r--r--chromium/cc/output/software_output_device.cc4
-rw-r--r--chromium/cc/output/software_output_device.h31
-rw-r--r--chromium/cc/output/software_renderer.cc106
-rw-r--r--chromium/cc/output/software_renderer.h17
-rw-r--r--chromium/cc/output/software_renderer_unittest.cc204
-rw-r--r--chromium/cc/quads/draw_quad_unittest.cc14
-rw-r--r--chromium/cc/quads/picture_draw_quad.cc31
-rw-r--r--chromium/cc/quads/picture_draw_quad.h5
-rw-r--r--chromium/cc/quads/render_pass.h3
-rw-r--r--chromium/cc/quads/render_pass_draw_quad.cc1
-rw-r--r--chromium/cc/resources/bitmap_content_layer_updater.cc12
-rw-r--r--chromium/cc/resources/bitmap_skpicture_content_layer_updater.cc4
-rw-r--r--chromium/cc/resources/content_layer_updater.cc27
-rw-r--r--chromium/cc/resources/content_layer_updater.h9
-rw-r--r--chromium/cc/resources/image_raster_worker_pool.cc21
-rw-r--r--chromium/cc/resources/image_raster_worker_pool.h1
-rw-r--r--chromium/cc/resources/layer_tiling_data.h4
-rw-r--r--chromium/cc/resources/managed_tile_state.cc90
-rw-r--r--chromium/cc/resources/managed_tile_state.h38
-rw-r--r--chromium/cc/resources/picture.cc31
-rw-r--r--chromium/cc/resources/picture.h2
-rw-r--r--chromium/cc/resources/picture_layer_tiling_perftest.cc172
-rw-r--r--chromium/cc/resources/picture_layer_tiling_set.cc9
-rw-r--r--chromium/cc/resources/picture_layer_tiling_set.h1
-rw-r--r--chromium/cc/resources/picture_layer_tiling_set_unittest.cc19
-rw-r--r--chromium/cc/resources/picture_pile_base.cc10
-rw-r--r--chromium/cc/resources/picture_pile_impl.cc11
-rw-r--r--chromium/cc/resources/picture_unittest.cc53
-rw-r--r--chromium/cc/resources/pixel_buffer_raster_worker_pool.cc78
-rw-r--r--chromium/cc/resources/pixel_buffer_raster_worker_pool.h17
-rw-r--r--chromium/cc/resources/platform_color.h29
-rw-r--r--chromium/cc/resources/prioritized_resource.cc13
-rw-r--r--chromium/cc/resources/prioritized_resource.h20
-rw-r--r--chromium/cc/resources/prioritized_resource_manager.cc7
-rw-r--r--chromium/cc/resources/prioritized_resource_manager.h9
-rw-r--r--chromium/cc/resources/prioritized_resource_unittest.cc12
-rw-r--r--chromium/cc/resources/prioritized_tile_set.cc63
-rw-r--r--chromium/cc/resources/prioritized_tile_set.h25
-rw-r--r--chromium/cc/resources/prioritized_tile_set_unittest.cc385
-rw-r--r--chromium/cc/resources/raster_mode.cc1
-rw-r--r--chromium/cc/resources/raster_worker_pool.cc80
-rw-r--r--chromium/cc/resources/raster_worker_pool.h18
-rw-r--r--chromium/cc/resources/raster_worker_pool_perftest.cc77
-rw-r--r--chromium/cc/resources/raster_worker_pool_unittest.cc32
-rw-r--r--chromium/cc/resources/release_callback.h17
-rw-r--r--chromium/cc/resources/resource.cc23
-rw-r--r--chromium/cc/resources/resource.h12
-rw-r--r--chromium/cc/resources/resource_format.cc25
-rw-r--r--chromium/cc/resources/resource_format.h26
-rw-r--r--chromium/cc/resources/resource_pool.cc26
-rw-r--r--chromium/cc/resources/resource_pool.h11
-rw-r--r--chromium/cc/resources/resource_provider.cc966
-rw-r--r--chromium/cc/resources/resource_provider.h122
-rw-r--r--chromium/cc/resources/resource_provider_unittest.cc1339
-rw-r--r--chromium/cc/resources/resource_update_controller.cc8
-rw-r--r--chromium/cc/resources/resource_update_controller_unittest.cc30
-rw-r--r--chromium/cc/resources/return_callback.h17
-rw-r--r--chromium/cc/resources/returned_resource.h27
-rw-r--r--chromium/cc/resources/scoped_resource.cc7
-rw-r--r--chromium/cc/resources/scoped_resource.h4
-rw-r--r--chromium/cc/resources/scoped_resource_unittest.cc52
-rw-r--r--chromium/cc/resources/scoped_ui_resource.cc11
-rw-r--r--chromium/cc/resources/scoped_ui_resource.h24
-rw-r--r--chromium/cc/resources/single_release_callback.cc29
-rw-r--r--chromium/cc/resources/single_release_callback.h33
-rw-r--r--chromium/cc/resources/skpicture_content_layer_updater.cc9
-rw-r--r--chromium/cc/resources/skpicture_content_layer_updater.h7
-rw-r--r--chromium/cc/resources/texture_mailbox.cc70
-rw-r--r--chromium/cc/resources/texture_mailbox.h21
-rw-r--r--chromium/cc/resources/texture_mailbox_deleter.cc92
-rw-r--r--chromium/cc/resources/texture_mailbox_deleter.h46
-rw-r--r--chromium/cc/resources/texture_mailbox_deleter_unittest.cc44
-rw-r--r--chromium/cc/resources/tile.cc20
-rw-r--r--chromium/cc/resources/tile.h18
-rw-r--r--chromium/cc/resources/tile_manager.cc336
-rw-r--r--chromium/cc/resources/tile_manager.h51
-rw-r--r--chromium/cc/resources/tile_manager_perftest.cc145
-rw-r--r--chromium/cc/resources/tile_manager_unittest.cc69
-rw-r--r--chromium/cc/resources/tile_priority.cc14
-rw-r--r--chromium/cc/resources/tile_priority.h3
-rw-r--r--chromium/cc/resources/transferable_resource.cc20
-rw-r--r--chromium/cc/resources/transferable_resource.h14
-rw-r--r--chromium/cc/resources/ui_resource_bitmap.cc49
-rw-r--r--chromium/cc/resources/ui_resource_bitmap.h53
-rw-r--r--chromium/cc/resources/ui_resource_client.h4
-rw-r--r--chromium/cc/resources/video_resource_updater.cc72
-rw-r--r--chromium/cc/resources/video_resource_updater.h16
-rw-r--r--chromium/cc/resources/video_resource_updater_unittest.cc17
-rw-r--r--chromium/cc/resources/worker_pool.h4
-rw-r--r--chromium/cc/resources/worker_pool_perftest.cc88
-rw-r--r--chromium/cc/scheduler/delay_based_time_source.cc100
-rw-r--r--chromium/cc/scheduler/delay_based_time_source.h11
-rw-r--r--chromium/cc/scheduler/delay_based_time_source_unittest.cc150
-rw-r--r--chromium/cc/scheduler/frame_rate_controller.cc22
-rw-r--r--chromium/cc/scheduler/frame_rate_controller.h11
-rw-r--r--chromium/cc/scheduler/scheduler.cc252
-rw-r--r--chromium/cc/scheduler/scheduler.h64
-rw-r--r--chromium/cc/scheduler/scheduler_settings.cc4
-rw-r--r--chromium/cc/scheduler/scheduler_settings.h2
-rw-r--r--chromium/cc/scheduler/scheduler_state_machine.cc1121
-rw-r--r--chromium/cc/scheduler/scheduler_state_machine.h195
-rw-r--r--chromium/cc/scheduler/scheduler_state_machine_unittest.cc1307
-rw-r--r--chromium/cc/scheduler/scheduler_unittest.cc691
-rw-r--r--chromium/cc/scheduler/texture_uploader.cc82
-rw-r--r--chromium/cc/scheduler/texture_uploader.h8
-rw-r--r--chromium/cc/scheduler/texture_uploader_unittest.cc28
-rw-r--r--chromium/cc/scheduler/time_source.h7
-rw-r--r--chromium/cc/trees/blocking_task_runner.cc105
-rw-r--r--chromium/cc/trees/blocking_task_runner.h94
-rw-r--r--chromium/cc/trees/damage_tracker_unittest.cc12
-rw-r--r--chromium/cc/trees/layer_tree_host.cc178
-rw-r--r--chromium/cc/trees/layer_tree_host.h70
-rw-r--r--chromium/cc/trees/layer_tree_host_common.cc562
-rw-r--r--chromium/cc/trees/layer_tree_host_common_unittest.cc1086
-rw-r--r--chromium/cc/trees/layer_tree_host_impl.cc505
-rw-r--r--chromium/cc/trees/layer_tree_host_impl.h144
-rw-r--r--chromium/cc/trees/layer_tree_host_impl_unittest.cc826
-rw-r--r--chromium/cc/trees/layer_tree_host_perftest.cc76
-rw-r--r--chromium/cc/trees/layer_tree_host_pixeltest_filters.cc53
-rw-r--r--chromium/cc/trees/layer_tree_host_pixeltest_readback.cc164
-rw-r--r--chromium/cc/trees/layer_tree_host_unittest.cc714
-rw-r--r--chromium/cc/trees/layer_tree_host_unittest_animation.cc130
-rw-r--r--chromium/cc/trees/layer_tree_host_unittest_context.cc605
-rw-r--r--chromium/cc/trees/layer_tree_host_unittest_damage.cc448
-rw-r--r--chromium/cc/trees/layer_tree_host_unittest_delegated.cc712
-rw-r--r--chromium/cc/trees/layer_tree_host_unittest_scroll.cc125
-rw-r--r--chromium/cc/trees/layer_tree_impl.cc118
-rw-r--r--chromium/cc/trees/layer_tree_impl.h22
-rw-r--r--chromium/cc/trees/layer_tree_settings.cc9
-rw-r--r--chromium/cc/trees/layer_tree_settings.h15
-rw-r--r--chromium/cc/trees/proxy.cc4
-rw-r--r--chromium/cc/trees/proxy.h3
-rw-r--r--chromium/cc/trees/single_thread_proxy.cc94
-rw-r--r--chromium/cc/trees/single_thread_proxy.h8
-rw-r--r--chromium/cc/trees/thread_proxy.cc357
-rw-r--r--chromium/cc/trees/thread_proxy.h41
-rw-r--r--chromium/cc/trees/tree_synchronizer.cc27
-rw-r--r--chromium/cc/trees/tree_synchronizer.h1
-rw-r--r--chromium/cc/trees/tree_synchronizer_unittest.cc213
280 files changed, 22157 insertions, 8008 deletions
diff --git a/chromium/cc/DEPS b/chromium/cc/DEPS
index ad58ebab32d..91ebb2067c9 100644
--- a/chromium/cc/DEPS
+++ b/chromium/cc/DEPS
@@ -6,11 +6,10 @@ include_rules = [
"+third_party/skia/include",
"+third_party/khronos/GLES2/gl2.h",
"+third_party/khronos/GLES2/gl2ext.h",
- "+ui/base",
+ "+ui/events/latency_info.h",
"+ui/gfx",
"+ui/gl",
# DO NOT ADD ANY NEW WEBKIT HEADERS TO THIS LIST.
# TODO(danakj): Drop dependencies on WebKit Platform API from cc.
"+third_party/WebKit/public/platform/WebGraphicsContext3D.h",
- "+third_party/WebKit/public/platform/WebGraphicsMemoryAllocation.h",
]
diff --git a/chromium/cc/OWNERS b/chromium/cc/OWNERS
index e90c2f30dd9..57a0e846d3e 100644
--- a/chromium/cc/OWNERS
+++ b/chromium/cc/OWNERS
@@ -17,7 +17,7 @@ danakj@chromium.org
piman@chromium.org
danakj@chromium.org
-# resource management
+# resource management / mac-specific
# unofficial: epenner@chromium.org
ccameron@chromium.org
@@ -36,9 +36,11 @@ vmpstr@chromium.org
# math / geometry / layer_tree_host_common
shawnsingh@chromium.org
enne@chromium.org
+vollick@chromium.org
# animation
vollick@chromium.org
+ajuma@chromium.org
per-file *.isolate=csharp@chromium.org
per-file *.isolate=maruel@chromium.org
diff --git a/chromium/cc/PRESUBMIT.py b/chromium/cc/PRESUBMIT.py
index bcf71ef0d52..9e84e3f6609 100644
--- a/chromium/cc/PRESUBMIT.py
+++ b/chromium/cc/PRESUBMIT.py
@@ -74,11 +74,17 @@ def CheckStdAbs(input_api, output_api,
using_std_abs_files.append(f.LocalPath())
if re.search(r"\bfabsf?\(", contents):
found_fabs_files.append(f.LocalPath());
- # The following regular expression in words says:
- # "if there is no 'std::' behind an 'abs(' or 'absf(',
- # or if there is no 'std::' behind a 'fabs(' or 'fabsf(',
- # then it's a match."
- if re.search(r"((?<!std::)(\babsf?\()|(?<!std::)(\bfabsf?\())", contents):
+
+ no_std_prefix = r"(?<!std::)"
+ # Matches occurrences of abs/absf/fabs/fabsf without a "std::" prefix.
+ abs_without_prefix = r"%s(\babsf?\()" % no_std_prefix
+ fabs_without_prefix = r"%s(\bfabsf?\()" % no_std_prefix
+ # Skips matching any lines that have "// NOLINT".
+ no_nolint = r"(?![^\n]*//\s+NOLINT)"
+
+ expression = re.compile("(%s|%s)%s" %
+ (abs_without_prefix, fabs_without_prefix, no_nolint))
+ if expression.search(contents):
missing_std_prefix_files.append(f.LocalPath())
result = []
diff --git a/chromium/cc/animation/animation.cc b/chromium/cc/animation/animation.cc
index 214b8bfc47f..b509cfd994f 100644
--- a/chromium/cc/animation/animation.cc
+++ b/chromium/cc/animation/animation.cc
@@ -167,6 +167,12 @@ double Animation::TrimTimeToCurrentIteration(double monotonic_time) const {
// subtract all time spent paused.
trimmed -= start_time_ + total_paused_time_;
+ // If we're just starting or we're waiting on receiving a start time,
+ // time is 'stuck' at the initial state.
+ if ((run_state_ == Starting && !has_set_start_time()) ||
+ needs_synchronized_start_time())
+ trimmed = time_offset_;
+
// Zero is always the start of the animation.
if (trimmed <= 0)
return 0;
diff --git a/chromium/cc/animation/animation_curve.h b/chromium/cc/animation/animation_curve.h
index 3860234ab13..f4cdb10560c 100644
--- a/chromium/cc/animation/animation_curve.h
+++ b/chromium/cc/animation/animation_curve.h
@@ -10,6 +10,10 @@
#include "cc/output/filter_operations.h"
#include "ui/gfx/transform.h"
+namespace gfx {
+class BoxF;
+}
+
namespace cc {
class FilterAnimationCurve;
@@ -49,6 +53,12 @@ class CC_EXPORT TransformAnimationCurve : public AnimationCurve {
virtual gfx::Transform GetValue(double t) const = 0;
+ // Sets |bounds| to be the bounding box for the region within which |box|
+ // will move during this animation. If this region cannot be computed,
+ // returns false.
+ virtual bool AnimatedBoundsForBox(const gfx::BoxF& box,
+ gfx::BoxF* bounds) const = 0;
+
// Partial Animation implementation.
virtual CurveType Type() const OVERRIDE;
};
diff --git a/chromium/cc/animation/animation_unittest.cc b/chromium/cc/animation/animation_unittest.cc
index 337d3eedb35..23d41697679 100644
--- a/chromium/cc/animation/animation_unittest.cc
+++ b/chromium/cc/animation/animation_unittest.cc
@@ -110,6 +110,41 @@ TEST(AnimationTest, TrimTimeZeroDuration) {
EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(1.0));
}
+TEST(AnimationTest, TrimTimeStarting) {
+ scoped_ptr<Animation> anim(CreateAnimation(1, 5.0));
+ anim->SetRunState(Animation::Starting, 0.0);
+ EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(-1.0));
+ EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(0.0));
+ EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(1.0));
+ anim->set_time_offset(2.0);
+ EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(-1.0));
+ EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(0.0));
+ EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(1.0));
+ anim->set_start_time(1.0);
+ EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(-1.0));
+ EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(0.0));
+ EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(1.0));
+ EXPECT_EQ(3.0, anim->TrimTimeToCurrentIteration(2.0));
+}
+
+TEST(AnimationTest, TrimTimeNeedsSynchronizedStartTime) {
+ scoped_ptr<Animation> anim(CreateAnimation(1, 5.0));
+ anim->SetRunState(Animation::Running, 0.0);
+ anim->set_needs_synchronized_start_time(true);
+ EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(-1.0));
+ EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(0.0));
+ EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(1.0));
+ anim->set_time_offset(2.0);
+ EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(-1.0));
+ EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(0.0));
+ EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(1.0));
+ anim->set_start_time(1.0);
+ anim->set_needs_synchronized_start_time(false);
+ EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(0.0));
+ EXPECT_EQ(2.0, anim->TrimTimeToCurrentIteration(1.0));
+ EXPECT_EQ(3.0, anim->TrimTimeToCurrentIteration(2.0));
+}
+
TEST(AnimationTest, IsFinishedAtZeroIterations) {
scoped_ptr<Animation> anim(CreateAnimation(0));
anim->SetRunState(Animation::Running, 0.0);
diff --git a/chromium/cc/animation/keyframed_animation_curve.cc b/chromium/cc/animation/keyframed_animation_curve.cc
index c016c91335b..14dfd9c8446 100644
--- a/chromium/cc/animation/keyframed_animation_curve.cc
+++ b/chromium/cc/animation/keyframed_animation_curve.cc
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "cc/animation/keyframed_animation_curve.h"
+#include "ui/gfx/box_f.h"
namespace cc {
@@ -231,6 +232,28 @@ gfx::Transform KeyframedTransformAnimationCurve::GetValue(double t) const {
return GetCurveValue<gfx::Transform, TransformKeyframe>(&keyframes_, t);
}
+bool KeyframedTransformAnimationCurve::AnimatedBoundsForBox(
+ const gfx::BoxF& box,
+ gfx::BoxF* bounds) const {
+ DCHECK_GE(keyframes_.size(), 2ul);
+ *bounds = gfx::BoxF();
+ for (size_t i = 0; i < keyframes_.size() - 1; ++i) {
+ gfx::BoxF bounds_for_step;
+ float min_progress = 0.0;
+ float max_progress = 1.0;
+ if (keyframes_[i]->timing_function())
+ keyframes_[i]->timing_function()->Range(&min_progress, &max_progress);
+ if (!keyframes_[i+1]->Value().BlendedBoundsForBox(box,
+ keyframes_[i]->Value(),
+ min_progress,
+ max_progress,
+ &bounds_for_step))
+ return false;
+ bounds->Union(bounds_for_step);
+ }
+ return true;
+}
+
scoped_ptr<KeyframedFilterAnimationCurve> KeyframedFilterAnimationCurve::
Create() {
return make_scoped_ptr(new KeyframedFilterAnimationCurve);
diff --git a/chromium/cc/animation/keyframed_animation_curve.h b/chromium/cc/animation/keyframed_animation_curve.h
index 9dda773caec..5892dc70e58 100644
--- a/chromium/cc/animation/keyframed_animation_curve.h
+++ b/chromium/cc/animation/keyframed_animation_curve.h
@@ -135,6 +135,8 @@ class CC_EXPORT KeyframedTransformAnimationCurve
// TransformAnimationCurve implementation
virtual gfx::Transform GetValue(double t) const OVERRIDE;
+ virtual bool AnimatedBoundsForBox(const gfx::BoxF& box,
+ gfx::BoxF* bounds) const OVERRIDE;
private:
KeyframedTransformAnimationCurve();
diff --git a/chromium/cc/animation/keyframed_animation_curve_unittest.cc b/chromium/cc/animation/keyframed_animation_curve_unittest.cc
index 32efc147636..48f511c0317 100644
--- a/chromium/cc/animation/keyframed_animation_curve_unittest.cc
+++ b/chromium/cc/animation/keyframed_animation_curve_unittest.cc
@@ -7,6 +7,7 @@
#include "cc/animation/transform_operations.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/box_f.h"
namespace cc {
namespace {
@@ -332,5 +333,29 @@ TEST(KeyframedAnimationCurveTest, CubicBezierTimingFunction) {
EXPECT_FLOAT_EQ(1.f, curve->GetValue(1.f));
}
+// Tests that animated bounds are computed as expected.
+TEST(KeyframedAnimationCurveTest, AnimatedBounds) {
+ scoped_ptr<KeyframedTransformAnimationCurve> curve(
+ KeyframedTransformAnimationCurve::Create());
+
+ TransformOperations operations1;
+ curve->AddKeyframe(TransformKeyframe::Create(
+ 0.0, operations1, scoped_ptr<TimingFunction>()));
+ operations1.AppendTranslate(2.0, 3.0, -1.0);
+ curve->AddKeyframe(TransformKeyframe::Create(
+ 0.5, operations1, scoped_ptr<TimingFunction>()));
+ TransformOperations operations2;
+ operations2.AppendTranslate(4.0, 1.0, 2.0);
+ curve->AddKeyframe(TransformKeyframe::Create(
+ 1.0, operations2, EaseTimingFunction::Create()));
+
+ gfx::BoxF box(2.f, 3.f, 4.f, 1.f, 3.f, 2.f);
+ gfx::BoxF bounds;
+
+ EXPECT_TRUE(curve->AnimatedBoundsForBox(box, &bounds));
+ EXPECT_EQ(gfx::BoxF(2.f, 3.f, 3.f, 5.f, 6.f, 5.f).ToString(),
+ bounds.ToString());
+}
+
} // namespace
} // namespace cc
diff --git a/chromium/cc/animation/layer_animation_controller.cc b/chromium/cc/animation/layer_animation_controller.cc
index 5823b7919d2..04920acc6ef 100644
--- a/chromium/cc/animation/layer_animation_controller.cc
+++ b/chromium/cc/animation/layer_animation_controller.cc
@@ -12,6 +12,7 @@
#include "cc/animation/keyframed_animation_curve.h"
#include "cc/animation/layer_animation_value_observer.h"
#include "cc/base/scoped_ptr_algorithm.h"
+#include "ui/gfx/box_f.h"
#include "ui/gfx/transform.h"
namespace cc {
@@ -343,6 +344,31 @@ void LayerAnimationController::RemoveEventObserver(
event_observers_.RemoveObserver(observer);
}
+bool LayerAnimationController::AnimatedBoundsForBox(const gfx::BoxF& box,
+ gfx::BoxF* bounds) {
+ // Compute bounds based on animations for which is_finished() is false.
+ // Do nothing if there are no such animations; in this case, it is assumed
+ // that callers will take care of computing bounds based on the owning layer's
+ // actual transform.
+ *bounds = gfx::BoxF();
+ for (size_t i = 0; i < active_animations_.size(); ++i) {
+ if (active_animations_[i]->is_finished() ||
+ active_animations_[i]->target_property() != Animation::Transform)
+ continue;
+
+ const TransformAnimationCurve* transform_animation_curve =
+ active_animations_[i]->curve()->ToTransformAnimationCurve();
+ gfx::BoxF animation_bounds;
+ bool success =
+ transform_animation_curve->AnimatedBoundsForBox(box, &animation_bounds);
+ if (!success)
+ return false;
+ bounds->Union(animation_bounds);
+ }
+
+ return true;
+}
+
void LayerAnimationController::PushNewAnimationsToImplThread(
LayerAnimationController* controller_impl) const {
// Any new animations owned by the main thread's controller are cloned and
@@ -645,16 +671,6 @@ void LayerAnimationController::TickAnimations(double monotonic_time) {
double trimmed =
active_animations_[i]->TrimTimeToCurrentIteration(monotonic_time);
- // Animation assumes its initial value until it gets the synchronized
- // start time from the impl thread and can start ticking.
- if (active_animations_[i]->needs_synchronized_start_time())
- trimmed = 0;
-
- // A just-started animation assumes its initial value.
- if (active_animations_[i]->run_state() == Animation::Starting &&
- !active_animations_[i]->has_set_start_time())
- trimmed = 0;
-
switch (active_animations_[i]->target_property()) {
case Animation::Transform: {
const TransformAnimationCurve* transform_animation_curve =
diff --git a/chromium/cc/animation/layer_animation_controller.h b/chromium/cc/animation/layer_animation_controller.h
index e450f1534d5..4138704af66 100644
--- a/chromium/cc/animation/layer_animation_controller.h
+++ b/chromium/cc/animation/layer_animation_controller.h
@@ -17,7 +17,10 @@
#include "cc/base/scoped_ptr_vector.h"
#include "ui/gfx/transform.h"
-namespace gfx { class Transform; }
+namespace gfx {
+class BoxF;
+class Transform;
+}
namespace cc {
@@ -100,6 +103,8 @@ class CC_EXPORT LayerAnimationController
layer_animation_delegate_ = delegate;
}
+ bool AnimatedBoundsForBox(const gfx::BoxF& box, gfx::BoxF* bounds);
+
protected:
friend class base::RefCounted<LayerAnimationController>;
diff --git a/chromium/cc/animation/layer_animation_controller_unittest.cc b/chromium/cc/animation/layer_animation_controller_unittest.cc
index 2e06f5cc829..e55c53c07f3 100644
--- a/chromium/cc/animation/layer_animation_controller_unittest.cc
+++ b/chromium/cc/animation/layer_animation_controller_unittest.cc
@@ -12,14 +12,12 @@
#include "cc/test/animation_test_common.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/box_f.h"
#include "ui/gfx/transform.h"
namespace cc {
namespace {
-void ExpectTranslateX(double translate_x, const gfx::Transform& matrix) {
- EXPECT_FLOAT_EQ(translate_x, matrix.matrix().getDouble(0, 3)); }
-
scoped_ptr<Animation> CreateAnimation(scoped_ptr<AnimationCurve> curve,
int id,
Animation::TargetProperty property) {
@@ -1117,5 +1115,74 @@ TEST(LayerAnimationControllerTest, InactiveObserverGetsTicked) {
EXPECT_NE(0.5f, dummy.opacity());
}
+TEST(LayerAnimationControllerTest, AnimatedBounds) {
+ scoped_refptr<LayerAnimationController> controller_impl(
+ LayerAnimationController::Create(0));
+
+ scoped_ptr<KeyframedTransformAnimationCurve> curve1(
+ KeyframedTransformAnimationCurve::Create());
+
+ TransformOperations operations1;
+ curve1->AddKeyframe(TransformKeyframe::Create(
+ 0.0, operations1, scoped_ptr<TimingFunction>()));
+ operations1.AppendTranslate(10.0, 15.0, 0.0);
+ curve1->AddKeyframe(TransformKeyframe::Create(
+ 1.0, operations1, scoped_ptr<TimingFunction>()));
+
+ scoped_ptr<Animation> animation(Animation::Create(
+ curve1.PassAs<AnimationCurve>(), 1, 1, Animation::Transform));
+ controller_impl->AddAnimation(animation.Pass());
+
+ scoped_ptr<KeyframedTransformAnimationCurve> curve2(
+ KeyframedTransformAnimationCurve::Create());
+
+ TransformOperations operations2;
+ curve2->AddKeyframe(TransformKeyframe::Create(
+ 0.0, operations2, scoped_ptr<TimingFunction>()));
+ operations2.AppendScale(2.0, 3.0, 4.0);
+ curve2->AddKeyframe(TransformKeyframe::Create(
+ 1.0, operations2, scoped_ptr<TimingFunction>()));
+
+ animation = Animation::Create(
+ curve2.PassAs<AnimationCurve>(), 2, 2, Animation::Transform);
+ controller_impl->AddAnimation(animation.Pass());
+
+ gfx::BoxF box(1.f, 2.f, -1.f, 3.f, 4.f, 5.f);
+ gfx::BoxF bounds;
+
+ EXPECT_TRUE(controller_impl->AnimatedBoundsForBox(box, &bounds));
+ EXPECT_EQ(gfx::BoxF(1.f, 2.f, -4.f, 13.f, 19.f, 20.f).ToString(),
+ bounds.ToString());
+
+ controller_impl->GetAnimation(1, Animation::Transform)->SetRunState(
+ cc::Animation::Finished, 0.0);
+
+ // Only the unfinished animation should affect the animated bounds.
+ EXPECT_TRUE(controller_impl->AnimatedBoundsForBox(box, &bounds));
+ EXPECT_EQ(gfx::BoxF(1.f, 2.f, -4.f, 7.f, 16.f, 20.f).ToString(),
+ bounds.ToString());
+
+ controller_impl->GetAnimation(2, Animation::Transform)->SetRunState(
+ cc::Animation::Finished, 0.0);
+
+ // There are no longer any running animations.
+ EXPECT_TRUE(controller_impl->AnimatedBoundsForBox(box, &bounds));
+ EXPECT_EQ(gfx::BoxF().ToString(), bounds.ToString());
+
+ // Add an animation whose bounds we don't yet support computing.
+ scoped_ptr<KeyframedTransformAnimationCurve> curve3(
+ KeyframedTransformAnimationCurve::Create());
+ TransformOperations operations3;
+ curve3->AddKeyframe(TransformKeyframe::Create(
+ 0.0, operations3, scoped_ptr<TimingFunction>()));
+ operations3.AppendSkew(1.0, 2.0);
+ curve3->AddKeyframe(TransformKeyframe::Create(
+ 1.0, operations3, scoped_ptr<TimingFunction>()));
+ animation = Animation::Create(
+ curve3.PassAs<AnimationCurve>(), 3, 3, Animation::Transform);
+ controller_impl->AddAnimation(animation.Pass());
+ EXPECT_FALSE(controller_impl->AnimatedBoundsForBox(box, &bounds));
+}
+
} // namespace
} // namespace cc
diff --git a/chromium/cc/animation/scrollbar_animation_controller.h b/chromium/cc/animation/scrollbar_animation_controller.h
index 55d29457689..d3667f10bca 100644
--- a/chromium/cc/animation/scrollbar_animation_controller.h
+++ b/chromium/cc/animation/scrollbar_animation_controller.h
@@ -18,14 +18,15 @@ class CC_EXPORT ScrollbarAnimationController {
public:
virtual ~ScrollbarAnimationController() {}
- virtual bool IsScrollGestureInProgress() const = 0;
virtual bool IsAnimating() const = 0;
virtual base::TimeDelta DelayBeforeStart(base::TimeTicks now) const = 0;
virtual bool Animate(base::TimeTicks now) = 0;
virtual void DidScrollGestureBegin() = 0;
virtual void DidScrollGestureEnd(base::TimeTicks now) = 0;
- virtual void DidProgrammaticallyUpdateScroll(base::TimeTicks now) = 0;
+
+ // Returns true if we should start an animation.
+ virtual bool DidScrollUpdate(base::TimeTicks now) = 0;
};
} // namespace cc
diff --git a/chromium/cc/animation/scrollbar_animation_controller_linear_fade.cc b/chromium/cc/animation/scrollbar_animation_controller_linear_fade.cc
index 53827120a5c..eafcca08d51 100644
--- a/chromium/cc/animation/scrollbar_animation_controller_linear_fade.cc
+++ b/chromium/cc/animation/scrollbar_animation_controller_linear_fade.cc
@@ -6,6 +6,7 @@
#include "base/time/time.h"
#include "cc/layers/layer_impl.h"
+#include "cc/layers/scrollbar_layer_impl_base.h"
namespace cc {
@@ -24,16 +25,13 @@ ScrollbarAnimationControllerLinearFade::ScrollbarAnimationControllerLinearFade(
: ScrollbarAnimationController(),
scroll_layer_(scroll_layer),
scroll_gesture_in_progress_(false),
+ scroll_gesture_has_scrolled_(false),
fadeout_delay_(fadeout_delay),
fadeout_length_(fadeout_length) {}
ScrollbarAnimationControllerLinearFade::
~ScrollbarAnimationControllerLinearFade() {}
-bool ScrollbarAnimationControllerLinearFade::IsScrollGestureInProgress() const {
- return scroll_gesture_in_progress_;
-}
-
bool ScrollbarAnimationControllerLinearFade::IsAnimating() const {
return !last_awaken_time_.is_null();
}
@@ -47,35 +45,44 @@ base::TimeDelta ScrollbarAnimationControllerLinearFade::DelayBeforeStart(
bool ScrollbarAnimationControllerLinearFade::Animate(base::TimeTicks now) {
float opacity = OpacityAtTime(now);
- scroll_layer_->SetScrollbarOpacity(opacity);
+ ApplyOpacityToScrollbars(opacity);
if (!opacity)
last_awaken_time_ = base::TimeTicks();
return IsAnimating() && DelayBeforeStart(now) == base::TimeDelta();
}
void ScrollbarAnimationControllerLinearFade::DidScrollGestureBegin() {
- scroll_layer_->SetScrollbarOpacity(1.0f);
scroll_gesture_in_progress_ = true;
- last_awaken_time_ = base::TimeTicks();
+ scroll_gesture_has_scrolled_ = false;
}
void ScrollbarAnimationControllerLinearFade::DidScrollGestureEnd(
base::TimeTicks now) {
+ // The animation should not be triggered if no scrolling has occurred.
+ if (scroll_gesture_has_scrolled_)
+ last_awaken_time_ = now;
+ scroll_gesture_has_scrolled_ = false;
scroll_gesture_in_progress_ = false;
- last_awaken_time_ = now;
}
-void ScrollbarAnimationControllerLinearFade::DidProgrammaticallyUpdateScroll(
+bool ScrollbarAnimationControllerLinearFade::DidScrollUpdate(
base::TimeTicks now) {
- // Don't set scroll_gesture_in_progress_ as this scroll is not from a gesture
- // and we won't receive ScrollEnd.
- scroll_layer_->SetScrollbarOpacity(1.0f);
+ ApplyOpacityToScrollbars(1.0f);
+ // The animation should only be activated if the scroll updated occurred
+ // programatically, outside the scope of a scroll gesture.
+ if (scroll_gesture_in_progress_) {
+ last_awaken_time_ = base::TimeTicks();
+ scroll_gesture_has_scrolled_ = true;
+ return false;
+ }
+
last_awaken_time_ = now;
+ return true;
}
float ScrollbarAnimationControllerLinearFade::OpacityAtTime(
base::TimeTicks now) {
- if (scroll_gesture_in_progress_)
+ if (scroll_gesture_has_scrolled_)
return 1.0f;
if (last_awaken_time_.is_null())
@@ -92,4 +99,17 @@ float ScrollbarAnimationControllerLinearFade::OpacityAtTime(
return 0.0f;
}
+void ScrollbarAnimationControllerLinearFade::ApplyOpacityToScrollbars(
+ float opacity) {
+ ScrollbarLayerImplBase* horizontal_scrollbar =
+ scroll_layer_->horizontal_scrollbar_layer();
+ if (horizontal_scrollbar)
+ horizontal_scrollbar->SetOpacity(opacity);
+
+ ScrollbarLayerImplBase* vertical_scrollbar =
+ scroll_layer_->vertical_scrollbar_layer();
+ if (vertical_scrollbar)
+ vertical_scrollbar->SetOpacity(opacity);
+}
+
} // namespace cc
diff --git a/chromium/cc/animation/scrollbar_animation_controller_linear_fade.h b/chromium/cc/animation/scrollbar_animation_controller_linear_fade.h
index 9ecb3c1617c..eca4c4f86f0 100644
--- a/chromium/cc/animation/scrollbar_animation_controller_linear_fade.h
+++ b/chromium/cc/animation/scrollbar_animation_controller_linear_fade.h
@@ -23,14 +23,13 @@ class CC_EXPORT ScrollbarAnimationControllerLinearFade
virtual ~ScrollbarAnimationControllerLinearFade();
// ScrollbarAnimationController overrides.
- virtual bool IsScrollGestureInProgress() const OVERRIDE;
virtual bool IsAnimating() const OVERRIDE;
virtual base::TimeDelta DelayBeforeStart(base::TimeTicks now) const OVERRIDE;
virtual bool Animate(base::TimeTicks now) OVERRIDE;
virtual void DidScrollGestureBegin() OVERRIDE;
virtual void DidScrollGestureEnd(base::TimeTicks now) OVERRIDE;
- virtual void DidProgrammaticallyUpdateScroll(base::TimeTicks now) OVERRIDE;
+ virtual bool DidScrollUpdate(base::TimeTicks now) OVERRIDE;
protected:
ScrollbarAnimationControllerLinearFade(LayerImpl* scroll_layer,
@@ -39,11 +38,13 @@ class CC_EXPORT ScrollbarAnimationControllerLinearFade
private:
float OpacityAtTime(base::TimeTicks now);
+ void ApplyOpacityToScrollbars(float opacity);
LayerImpl* scroll_layer_;
base::TimeTicks last_awaken_time_;
bool scroll_gesture_in_progress_;
+ bool scroll_gesture_has_scrolled_;
base::TimeDelta fadeout_delay_;
base::TimeDelta fadeout_length_;
diff --git a/chromium/cc/animation/scrollbar_animation_controller_linear_fade_unittest.cc b/chromium/cc/animation/scrollbar_animation_controller_linear_fade_unittest.cc
index a8365e8b4de..b4c1160b081 100644
--- a/chromium/cc/animation/scrollbar_animation_controller_linear_fade_unittest.cc
+++ b/chromium/cc/animation/scrollbar_animation_controller_linear_fade_unittest.cc
@@ -4,7 +4,7 @@
#include "cc/animation/scrollbar_animation_controller_linear_fade.h"
-#include "cc/layers/scrollbar_layer_impl.h"
+#include "cc/layers/painted_scrollbar_layer_impl.h"
#include "cc/test/fake_impl_proxy.h"
#include "cc/test/fake_layer_tree_host_impl.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -19,7 +19,7 @@ class ScrollbarAnimationControllerLinearFadeTest : public testing::Test {
protected:
virtual void SetUp() {
scroll_layer_ = LayerImpl::Create(host_impl_.active_tree(), 1);
- scrollbar_layer_ = ScrollbarLayerImpl::Create(
+ scrollbar_layer_ = PaintedScrollbarLayerImpl::Create(
host_impl_.active_tree(), 2, HORIZONTAL);
scroll_layer_->SetMaxScrollOffset(gfx::Vector2d(50, 50));
@@ -36,7 +36,7 @@ class ScrollbarAnimationControllerLinearFadeTest : public testing::Test {
FakeLayerTreeHostImpl host_impl_;
scoped_ptr<ScrollbarAnimationControllerLinearFade> scrollbar_controller_;
scoped_ptr<LayerImpl> scroll_layer_;
- scoped_ptr<ScrollbarLayerImpl> scrollbar_layer_;
+ scoped_ptr<PaintedScrollbarLayerImpl> scrollbar_layer_;
};
TEST_F(ScrollbarAnimationControllerLinearFadeTest, HiddenInBegin) {
@@ -44,20 +44,43 @@ TEST_F(ScrollbarAnimationControllerLinearFadeTest, HiddenInBegin) {
EXPECT_FLOAT_EQ(0.0f, scrollbar_layer_->opacity());
}
-TEST_F(ScrollbarAnimationControllerLinearFadeTest, AwakenByScrollGesture) {
+TEST_F(ScrollbarAnimationControllerLinearFadeTest,
+ HiddenAfterNonScrollingGesture) {
+ scrollbar_controller_->DidScrollGestureBegin();
+ EXPECT_FALSE(scrollbar_controller_->IsAnimating());
+ EXPECT_FALSE(scrollbar_controller_->Animate(base::TimeTicks()));
+ EXPECT_FLOAT_EQ(0.0f, scrollbar_layer_->opacity());
+
+ base::TimeTicks time;
+ time += base::TimeDelta::FromSeconds(100);
+ EXPECT_FALSE(scrollbar_controller_->Animate(time));
+ EXPECT_FLOAT_EQ(0.0f, scrollbar_layer_->opacity());
+ scrollbar_controller_->DidScrollGestureEnd(time);
+
+ time += base::TimeDelta::FromSeconds(100);
+ EXPECT_FALSE(scrollbar_controller_->IsAnimating());
+ EXPECT_FALSE(scrollbar_controller_->Animate(time));
+ EXPECT_FLOAT_EQ(0.0f, scrollbar_layer_->opacity());
+}
+
+TEST_F(ScrollbarAnimationControllerLinearFadeTest, AwakenByScrollingGesture) {
base::TimeTicks time;
time += base::TimeDelta::FromSeconds(1);
scrollbar_controller_->DidScrollGestureBegin();
- EXPECT_TRUE(scrollbar_controller_->IsScrollGestureInProgress());
+ scrollbar_controller_->Animate(time);
+ EXPECT_FALSE(scrollbar_controller_->IsAnimating());
+ EXPECT_FLOAT_EQ(0.0f, scrollbar_layer_->opacity());
+
+ EXPECT_FALSE(scrollbar_controller_->DidScrollUpdate(time));
EXPECT_FALSE(scrollbar_controller_->IsAnimating());
EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->opacity());
time += base::TimeDelta::FromSeconds(100);
scrollbar_controller_->Animate(time);
+ EXPECT_FALSE(scrollbar_controller_->IsAnimating());
EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->opacity());
scrollbar_controller_->DidScrollGestureEnd(time);
- EXPECT_FALSE(scrollbar_controller_->IsScrollGestureInProgress());
EXPECT_TRUE(scrollbar_controller_->IsAnimating());
EXPECT_EQ(2, scrollbar_controller_->DelayBeforeStart(time).InSeconds());
@@ -80,6 +103,7 @@ TEST_F(ScrollbarAnimationControllerLinearFadeTest, AwakenByScrollGesture) {
time += base::TimeDelta::FromSeconds(1);
scrollbar_controller_->DidScrollGestureBegin();
+ EXPECT_FALSE(scrollbar_controller_->DidScrollUpdate(time));
scrollbar_controller_->DidScrollGestureEnd(time);
time += base::TimeDelta::FromSeconds(1);
@@ -106,8 +130,7 @@ TEST_F(ScrollbarAnimationControllerLinearFadeTest, AwakenByScrollGesture) {
TEST_F(ScrollbarAnimationControllerLinearFadeTest, AwakenByProgrammaticScroll) {
base::TimeTicks time;
time += base::TimeDelta::FromSeconds(1);
- scrollbar_controller_->DidProgrammaticallyUpdateScroll(time);
- EXPECT_FALSE(scrollbar_controller_->IsScrollGestureInProgress());
+ EXPECT_TRUE(scrollbar_controller_->DidScrollUpdate(time));
EXPECT_TRUE(scrollbar_controller_->IsAnimating());
EXPECT_EQ(2, scrollbar_controller_->DelayBeforeStart(time).InSeconds());
scrollbar_controller_->Animate(time);
@@ -116,7 +139,7 @@ TEST_F(ScrollbarAnimationControllerLinearFadeTest, AwakenByProgrammaticScroll) {
time += base::TimeDelta::FromSeconds(1);
scrollbar_controller_->Animate(time);
EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->opacity());
- scrollbar_controller_->DidProgrammaticallyUpdateScroll(time);
+ EXPECT_TRUE(scrollbar_controller_->DidScrollUpdate(time));
time += base::TimeDelta::FromSeconds(1);
scrollbar_controller_->Animate(time);
@@ -135,7 +158,7 @@ TEST_F(ScrollbarAnimationControllerLinearFadeTest, AwakenByProgrammaticScroll) {
EXPECT_FLOAT_EQ(1.0f / 3.0f, scrollbar_layer_->opacity());
time += base::TimeDelta::FromSeconds(1);
- scrollbar_controller_->DidProgrammaticallyUpdateScroll(time);
+ EXPECT_TRUE(scrollbar_controller_->DidScrollUpdate(time));
time += base::TimeDelta::FromSeconds(1);
scrollbar_controller_->Animate(time);
EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->opacity());
@@ -157,5 +180,67 @@ TEST_F(ScrollbarAnimationControllerLinearFadeTest, AwakenByProgrammaticScroll) {
EXPECT_FLOAT_EQ(0.0f, scrollbar_layer_->opacity());
}
+TEST_F(ScrollbarAnimationControllerLinearFadeTest,
+ AnimationPreservedByNonScrollingGesture) {
+ base::TimeTicks time;
+ time += base::TimeDelta::FromSeconds(1);
+ EXPECT_TRUE(scrollbar_controller_->DidScrollUpdate(time));
+ EXPECT_TRUE(scrollbar_controller_->IsAnimating());
+ scrollbar_controller_->Animate(time);
+ EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->opacity());
+
+ time += base::TimeDelta::FromSeconds(3);
+ scrollbar_controller_->Animate(time);
+ EXPECT_FLOAT_EQ(2.0f / 3.0f, scrollbar_layer_->opacity());
+
+ scrollbar_controller_->DidScrollGestureBegin();
+ EXPECT_TRUE(scrollbar_controller_->IsAnimating());
+ EXPECT_FLOAT_EQ(2.0f / 3.0f, scrollbar_layer_->opacity());
+
+ time += base::TimeDelta::FromSeconds(1);
+ scrollbar_controller_->Animate(time);
+ EXPECT_FLOAT_EQ(1.0f / 3.0f, scrollbar_layer_->opacity());
+
+ scrollbar_controller_->DidScrollGestureEnd(time);
+ EXPECT_TRUE(scrollbar_controller_->IsAnimating());
+ EXPECT_FLOAT_EQ(1.0f / 3.0f, scrollbar_layer_->opacity());
+
+ time += base::TimeDelta::FromSeconds(1);
+ EXPECT_FALSE(scrollbar_controller_->Animate(time));
+ EXPECT_FLOAT_EQ(0.0f, scrollbar_layer_->opacity());
+}
+
+TEST_F(ScrollbarAnimationControllerLinearFadeTest,
+ AnimationOverriddenByScrollingGesture) {
+ base::TimeTicks time;
+ time += base::TimeDelta::FromSeconds(1);
+ EXPECT_TRUE(scrollbar_controller_->DidScrollUpdate(time));
+ EXPECT_TRUE(scrollbar_controller_->IsAnimating());
+ scrollbar_controller_->Animate(time);
+ EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->opacity());
+
+ time += base::TimeDelta::FromSeconds(3);
+ scrollbar_controller_->Animate(time);
+ EXPECT_FLOAT_EQ(2.0f / 3.0f, scrollbar_layer_->opacity());
+
+ scrollbar_controller_->DidScrollGestureBegin();
+ EXPECT_TRUE(scrollbar_controller_->IsAnimating());
+ EXPECT_FLOAT_EQ(2.0f / 3.0f, scrollbar_layer_->opacity());
+
+ time += base::TimeDelta::FromSeconds(1);
+ scrollbar_controller_->Animate(time);
+ EXPECT_FLOAT_EQ(1.0f / 3.0f, scrollbar_layer_->opacity());
+
+ time += base::TimeDelta::FromSeconds(1);
+ EXPECT_FALSE(scrollbar_controller_->DidScrollUpdate(time));
+ EXPECT_FALSE(scrollbar_controller_->IsAnimating());
+ EXPECT_FLOAT_EQ(1, scrollbar_layer_->opacity());
+
+ time += base::TimeDelta::FromSeconds(1);
+ scrollbar_controller_->DidScrollGestureEnd(time);
+ EXPECT_TRUE(scrollbar_controller_->IsAnimating());
+ EXPECT_FLOAT_EQ(1, scrollbar_layer_->opacity());
+}
+
} // namespace
} // namespace cc
diff --git a/chromium/cc/animation/scrollbar_animation_controller_thinning.cc b/chromium/cc/animation/scrollbar_animation_controller_thinning.cc
new file mode 100644
index 00000000000..fcacfaab4a3
--- /dev/null
+++ b/chromium/cc/animation/scrollbar_animation_controller_thinning.cc
@@ -0,0 +1,132 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/animation/scrollbar_animation_controller_thinning.h"
+
+#include <algorithm>
+
+#include "base/time/time.h"
+#include "cc/layers/layer_impl.h"
+#include "cc/layers/scrollbar_layer_impl_base.h"
+
+namespace cc {
+
+scoped_ptr<ScrollbarAnimationControllerThinning>
+ScrollbarAnimationControllerThinning::Create(LayerImpl* scroll_layer) {
+ return make_scoped_ptr(new ScrollbarAnimationControllerThinning(
+ scroll_layer,
+ base::TimeDelta::FromMilliseconds(500),
+ base::TimeDelta::FromMilliseconds(300)));
+}
+
+scoped_ptr<ScrollbarAnimationControllerThinning>
+ScrollbarAnimationControllerThinning::CreateForTest(LayerImpl* scroll_layer,
+ base::TimeDelta animation_delay, base::TimeDelta animation_duration) {
+ return make_scoped_ptr(new ScrollbarAnimationControllerThinning(
+ scroll_layer, animation_delay, animation_duration));
+}
+
+ScrollbarAnimationControllerThinning::ScrollbarAnimationControllerThinning(
+ LayerImpl* scroll_layer,
+ base::TimeDelta animation_delay,
+ base::TimeDelta animation_duration)
+ : ScrollbarAnimationController(),
+ scroll_layer_(scroll_layer),
+ scroll_gesture_in_progress_(false),
+ animation_delay_(animation_delay),
+ animation_duration_(animation_duration) {}
+
+ScrollbarAnimationControllerThinning::~ScrollbarAnimationControllerThinning() {
+}
+
+bool ScrollbarAnimationControllerThinning::IsAnimating() const {
+ return !last_awaken_time_.is_null();
+}
+
+base::TimeDelta ScrollbarAnimationControllerThinning::DelayBeforeStart(
+ base::TimeTicks now) const {
+ if (now > last_awaken_time_ + animation_delay_)
+ return base::TimeDelta();
+ return animation_delay_ - (now - last_awaken_time_);
+}
+
+bool ScrollbarAnimationControllerThinning::Animate(base::TimeTicks now) {
+ float progress = AnimationProgressAtTime(now);
+ float opacity = OpacityAtAnimationProgress(progress);
+ float thumb_thickness_scale = ThumbThicknessScaleAtAnimationProgress(
+ progress);
+ ApplyOpacityAndThumbThicknessScale(opacity, thumb_thickness_scale);
+ if (progress == 1.f)
+ last_awaken_time_ = base::TimeTicks();
+ return IsAnimating() && DelayBeforeStart(now) == base::TimeDelta();
+}
+
+void ScrollbarAnimationControllerThinning::DidScrollGestureBegin() {
+ ApplyOpacityAndThumbThicknessScale(1, 1);
+ last_awaken_time_ = base::TimeTicks();
+ scroll_gesture_in_progress_ = true;
+}
+
+void ScrollbarAnimationControllerThinning::DidScrollGestureEnd(
+ base::TimeTicks now) {
+ last_awaken_time_ = now;
+ scroll_gesture_in_progress_ = false;
+}
+
+bool ScrollbarAnimationControllerThinning::DidScrollUpdate(
+ base::TimeTicks now) {
+ ApplyOpacityAndThumbThicknessScale(1, 1);
+
+ last_awaken_time_ = now;
+ return true;
+}
+
+float ScrollbarAnimationControllerThinning::AnimationProgressAtTime(
+ base::TimeTicks now) {
+ if (scroll_gesture_in_progress_)
+ return 0;
+
+ if (last_awaken_time_.is_null())
+ return 1;
+
+ base::TimeDelta delta = now - last_awaken_time_;
+ float progress = (delta - animation_delay_).InSecondsF() /
+ animation_duration_.InSecondsF();
+ return std::max(std::min(progress, 1.f), 0.f);
+}
+
+float ScrollbarAnimationControllerThinning::OpacityAtAnimationProgress(
+ float progress) {
+ const float kIdleOpacity = 0.7f;
+
+ return ((1 - kIdleOpacity) * (1.f - progress)) + kIdleOpacity;
+}
+
+float
+ScrollbarAnimationControllerThinning::ThumbThicknessScaleAtAnimationProgress(
+ float progress) {
+ const float kIdleThicknessScale = 0.4f;
+
+ return ((1 - kIdleThicknessScale) * (1.f - progress)) + kIdleThicknessScale;
+}
+
+void ScrollbarAnimationControllerThinning::ApplyOpacityAndThumbThicknessScale(
+ float opacity, float thumb_thickness_scale) {
+ ScrollbarLayerImplBase* horizontal_scrollbar =
+ scroll_layer_->horizontal_scrollbar_layer();
+ if (horizontal_scrollbar) {
+ horizontal_scrollbar->SetOpacity(opacity);
+ horizontal_scrollbar->set_thumb_thickness_scale_factor(
+ thumb_thickness_scale);
+ }
+
+ ScrollbarLayerImplBase* vertical_scrollbar =
+ scroll_layer_->vertical_scrollbar_layer();
+ if (vertical_scrollbar) {
+ vertical_scrollbar->SetOpacity(opacity);
+ vertical_scrollbar->set_thumb_thickness_scale_factor(thumb_thickness_scale);
+ }
+}
+
+} // namespace cc
diff --git a/chromium/cc/animation/scrollbar_animation_controller_thinning.h b/chromium/cc/animation/scrollbar_animation_controller_thinning.h
new file mode 100644
index 00000000000..0211479ef9a
--- /dev/null
+++ b/chromium/cc/animation/scrollbar_animation_controller_thinning.h
@@ -0,0 +1,65 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_ANIMATION_SCROLLBAR_ANIMATION_CONTROLLER_THINNING_H_
+#define CC_ANIMATION_SCROLLBAR_ANIMATION_CONTROLLER_THINNING_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "cc/animation/scrollbar_animation_controller.h"
+#include "cc/base/cc_export.h"
+
+namespace cc {
+class LayerImpl;
+
+// Scrollbar animation that partially fades and thins after an idle delay.
+class CC_EXPORT ScrollbarAnimationControllerThinning
+ : public ScrollbarAnimationController {
+ public:
+ static scoped_ptr<ScrollbarAnimationControllerThinning> Create(
+ LayerImpl* scroll_layer);
+
+ static scoped_ptr<ScrollbarAnimationControllerThinning> CreateForTest(
+ LayerImpl* scroll_layer,
+ base::TimeDelta animation_delay,
+ base::TimeDelta animation_duration);
+
+ virtual ~ScrollbarAnimationControllerThinning();
+
+ // ScrollbarAnimationController overrides.
+ virtual bool IsAnimating() const OVERRIDE;
+ virtual base::TimeDelta DelayBeforeStart(base::TimeTicks now) const OVERRIDE;
+
+ virtual bool Animate(base::TimeTicks now) OVERRIDE;
+ virtual void DidScrollGestureBegin() OVERRIDE;
+ virtual void DidScrollGestureEnd(base::TimeTicks now) OVERRIDE;
+ virtual bool DidScrollUpdate(base::TimeTicks now) OVERRIDE;
+
+ protected:
+ ScrollbarAnimationControllerThinning(LayerImpl* scroll_layer,
+ base::TimeDelta animation_delay,
+ base::TimeDelta animation_duration);
+
+ private:
+ // Returns how far through the animation we are as a progress value from
+ // 0 to 1.
+ float AnimationProgressAtTime(base::TimeTicks now);
+ float OpacityAtAnimationProgress(float progress);
+ float ThumbThicknessScaleAtAnimationProgress(float progress);
+ void ApplyOpacityAndThumbThicknessScale(float opacity,
+ float thumb_thickness_scale);
+
+ LayerImpl* scroll_layer_;
+
+ base::TimeTicks last_awaken_time_;
+ bool scroll_gesture_in_progress_;
+
+ base::TimeDelta animation_delay_;
+ base::TimeDelta animation_duration_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScrollbarAnimationControllerThinning);
+};
+
+} // namespace cc
+
+#endif // CC_ANIMATION_SCROLLBAR_ANIMATION_CONTROLLER_THINNING_H_
diff --git a/chromium/cc/animation/scrollbar_animation_controller_thinning_unittest.cc b/chromium/cc/animation/scrollbar_animation_controller_thinning_unittest.cc
new file mode 100644
index 00000000000..c83bdc81b39
--- /dev/null
+++ b/chromium/cc/animation/scrollbar_animation_controller_thinning_unittest.cc
@@ -0,0 +1,185 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/animation/scrollbar_animation_controller_thinning.h"
+
+#include "cc/layers/solid_color_scrollbar_layer_impl.h"
+#include "cc/test/fake_impl_proxy.h"
+#include "cc/test/fake_layer_tree_host_impl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cc {
+namespace {
+
+class ScrollbarAnimationControllerThinningTest : public testing::Test {
+ public:
+ ScrollbarAnimationControllerThinningTest() : host_impl_(&proxy_) {}
+
+ protected:
+ virtual void SetUp() {
+ scroll_layer_ = LayerImpl::Create(host_impl_.active_tree(), 1);
+ const int kId = 2;
+ const int kThumbThickness = 10;
+ const bool kIsLeftSideVerticalScrollbar = false;
+ scrollbar_layer_ = SolidColorScrollbarLayerImpl::Create(
+ host_impl_.active_tree(), kId, HORIZONTAL, kThumbThickness,
+ kIsLeftSideVerticalScrollbar);
+
+ scroll_layer_->SetMaxScrollOffset(gfx::Vector2d(50, 50));
+ scroll_layer_->SetBounds(gfx::Size(50, 50));
+ scroll_layer_->SetHorizontalScrollbarLayer(scrollbar_layer_.get());
+
+ scrollbar_controller_ = ScrollbarAnimationControllerThinning::CreateForTest(
+ scroll_layer_.get(),
+ base::TimeDelta::FromSeconds(2),
+ base::TimeDelta::FromSeconds(3));
+ }
+
+ FakeImplProxy proxy_;
+ FakeLayerTreeHostImpl host_impl_;
+ scoped_ptr<ScrollbarAnimationControllerThinning> scrollbar_controller_;
+ scoped_ptr<LayerImpl> scroll_layer_;
+ scoped_ptr<SolidColorScrollbarLayerImpl> scrollbar_layer_;
+};
+
+TEST_F(ScrollbarAnimationControllerThinningTest, Idle) {
+ scrollbar_controller_->Animate(base::TimeTicks());
+ EXPECT_FLOAT_EQ(0.7f, scrollbar_layer_->opacity());
+ EXPECT_FLOAT_EQ(0.4f, scrollbar_layer_->thumb_thickness_scale_factor());
+}
+
+TEST_F(ScrollbarAnimationControllerThinningTest, AwakenByScrollGesture) {
+ base::TimeTicks time;
+ time += base::TimeDelta::FromSeconds(1);
+ scrollbar_controller_->DidScrollGestureBegin();
+ EXPECT_FALSE(scrollbar_controller_->IsAnimating());
+ EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->opacity());
+ EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor());
+
+ time += base::TimeDelta::FromSeconds(100);
+ scrollbar_controller_->Animate(time);
+ EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->opacity());
+ EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor());
+ scrollbar_controller_->DidScrollGestureEnd(time);
+
+ EXPECT_TRUE(scrollbar_controller_->IsAnimating());
+ EXPECT_EQ(2, scrollbar_controller_->DelayBeforeStart(time).InSeconds());
+
+ time += base::TimeDelta::FromSeconds(1);
+ scrollbar_controller_->Animate(time);
+ EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->opacity());
+ EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor());
+
+ time += base::TimeDelta::FromSeconds(1);
+ scrollbar_controller_->Animate(time);
+ EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->opacity());
+ EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor());
+
+ time += base::TimeDelta::FromSeconds(1);
+ scrollbar_controller_->Animate(time);
+ EXPECT_FLOAT_EQ(0.9f, scrollbar_layer_->opacity());
+ EXPECT_FLOAT_EQ(0.8f, scrollbar_layer_->thumb_thickness_scale_factor());
+
+ time += base::TimeDelta::FromSeconds(1);
+ scrollbar_controller_->Animate(time);
+ EXPECT_FLOAT_EQ(0.8f, scrollbar_layer_->opacity());
+ EXPECT_FLOAT_EQ(0.6f, scrollbar_layer_->thumb_thickness_scale_factor());
+
+ time += base::TimeDelta::FromSeconds(1);
+
+ scrollbar_controller_->DidScrollGestureBegin();
+ scrollbar_controller_->DidScrollGestureEnd(time);
+
+ time += base::TimeDelta::FromSeconds(1);
+ scrollbar_controller_->Animate(time);
+ EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->opacity());
+ EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor());
+
+ time += base::TimeDelta::FromSeconds(1);
+ scrollbar_controller_->Animate(time);
+ EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->opacity());
+ EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor());
+
+ time += base::TimeDelta::FromSeconds(1);
+ scrollbar_controller_->Animate(time);
+ EXPECT_FLOAT_EQ(0.9f, scrollbar_layer_->opacity());
+ EXPECT_FLOAT_EQ(0.8f, scrollbar_layer_->thumb_thickness_scale_factor());
+
+ time += base::TimeDelta::FromSeconds(1);
+ scrollbar_controller_->Animate(time);
+ EXPECT_FLOAT_EQ(0.8f, scrollbar_layer_->opacity());
+ EXPECT_FLOAT_EQ(0.6f, scrollbar_layer_->thumb_thickness_scale_factor());
+
+ time += base::TimeDelta::FromSeconds(1);
+ scrollbar_controller_->Animate(time);
+ EXPECT_FLOAT_EQ(0.7f, scrollbar_layer_->opacity());
+ EXPECT_FLOAT_EQ(0.4f, scrollbar_layer_->thumb_thickness_scale_factor());
+}
+
+TEST_F(ScrollbarAnimationControllerThinningTest, AwakenByProgrammaticScroll) {
+ base::TimeTicks time;
+ time += base::TimeDelta::FromSeconds(1);
+ EXPECT_TRUE(scrollbar_controller_->DidScrollUpdate(time));
+ EXPECT_TRUE(scrollbar_controller_->IsAnimating());
+ EXPECT_EQ(2, scrollbar_controller_->DelayBeforeStart(time).InSeconds());
+ scrollbar_controller_->Animate(time);
+ EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->opacity());
+ EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor());
+
+ time += base::TimeDelta::FromSeconds(1);
+ scrollbar_controller_->Animate(time);
+ EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->opacity());
+ EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor());
+ EXPECT_TRUE(scrollbar_controller_->DidScrollUpdate(time));
+
+ time += base::TimeDelta::FromSeconds(1);
+ scrollbar_controller_->Animate(time);
+ EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->opacity());
+ EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor());
+
+ time += base::TimeDelta::FromSeconds(1);
+ scrollbar_controller_->Animate(time);
+ EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->opacity());
+ EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor());
+
+ time += base::TimeDelta::FromSeconds(1);
+ scrollbar_controller_->Animate(time);
+ EXPECT_FLOAT_EQ(0.9f, scrollbar_layer_->opacity());
+ EXPECT_FLOAT_EQ(0.8f, scrollbar_layer_->thumb_thickness_scale_factor());
+
+ time += base::TimeDelta::FromSeconds(1);
+ scrollbar_controller_->Animate(time);
+ EXPECT_FLOAT_EQ(0.8f, scrollbar_layer_->opacity());
+ EXPECT_FLOAT_EQ(0.6f, scrollbar_layer_->thumb_thickness_scale_factor());
+
+ time += base::TimeDelta::FromSeconds(1);
+ EXPECT_TRUE(scrollbar_controller_->DidScrollUpdate(time));
+ time += base::TimeDelta::FromSeconds(1);
+ scrollbar_controller_->Animate(time);
+ EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->opacity());
+ EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor());
+
+ time += base::TimeDelta::FromSeconds(1);
+ scrollbar_controller_->Animate(time);
+ EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->opacity());
+ EXPECT_FLOAT_EQ(1.0f, scrollbar_layer_->thumb_thickness_scale_factor());
+
+ time += base::TimeDelta::FromSeconds(1);
+ scrollbar_controller_->Animate(time);
+ EXPECT_FLOAT_EQ(0.9f, scrollbar_layer_->opacity());
+ EXPECT_FLOAT_EQ(0.8f, scrollbar_layer_->thumb_thickness_scale_factor());
+
+ time += base::TimeDelta::FromSeconds(1);
+ scrollbar_controller_->Animate(time);
+ EXPECT_FLOAT_EQ(0.8f, scrollbar_layer_->opacity());
+ EXPECT_FLOAT_EQ(0.6f, scrollbar_layer_->thumb_thickness_scale_factor());
+
+ time += base::TimeDelta::FromSeconds(1);
+ scrollbar_controller_->Animate(time);
+ EXPECT_FLOAT_EQ(0.7f, scrollbar_layer_->opacity());
+ EXPECT_FLOAT_EQ(0.4f, scrollbar_layer_->thumb_thickness_scale_factor());
+}
+
+} // namespace
+} // namespace cc
diff --git a/chromium/cc/animation/timing_function.cc b/chromium/cc/animation/timing_function.cc
index 65d607fd573..7fdb37fed96 100644
--- a/chromium/cc/animation/timing_function.cc
+++ b/chromium/cc/animation/timing_function.cc
@@ -12,7 +12,7 @@ namespace cc {
namespace {
-static const double BEZIER_EPSILON = 1e-7;
+static const double kBezierEpsilon = 1e-7;
static const int MAX_STEPS = 30;
static double eval_bezier(double x1, double x2, double t) {
@@ -48,14 +48,14 @@ static double bezier_interp(double x1,
double step = 1.0;
for (int i = 0; i < MAX_STEPS; ++i, step *= 0.5) {
const double error = eval_bezier(x1, x2, t) - x;
- if (std::abs(error) < BEZIER_EPSILON)
+ if (std::abs(error) < kBezierEpsilon)
break;
t += error > 0.0 ? -step : step;
}
// We should have terminated the above loop because we got close to x, not
// because we exceeded MAX_STEPS. Do a DCHECK here to confirm.
- DCHECK_GT(BEZIER_EPSILON, std::abs(eval_bezier(x1, x2, t) - x));
+ DCHECK_GT(kBezierEpsilon, std::abs(eval_bezier(x1, x2, t) - x));
// Step 2. Return the interpolated y values at the t we computed above.
return eval_bezier(y1, y2, t);
@@ -93,6 +93,53 @@ scoped_ptr<AnimationCurve> CubicBezierTimingFunction::Clone() const {
new CubicBezierTimingFunction(*this)).PassAs<AnimationCurve>();
}
+void CubicBezierTimingFunction::Range(float* min, float* max) const {
+ *min = 0.f;
+ *max = 1.f;
+ if (0.f <= y1_ && y1_ < 1.f && 0.f <= y2_ && y2_ <= 1.f)
+ return;
+
+ // Represent the function's derivative in the form at^2 + bt + c.
+ float a = 3.f * (y1_ - y2_) + 1.f;
+ float b = 2.f * (y2_ - 2.f * y1_);
+ float c = y1_;
+
+ // Check if the derivative is constant.
+ if (std::abs(a) < kBezierEpsilon &&
+ std::abs(b) < kBezierEpsilon)
+ return;
+
+ // Zeros of the function's derivative.
+ float t_1 = 0.f;
+ float t_2 = 0.f;
+
+ if (std::abs(a) < kBezierEpsilon) {
+ // The function's derivative is linear.
+ t_1 = -c / b;
+ } else {
+ // The function's derivative is a quadratic. We find the zeros of this
+ // quadratic using the quadratic formula.
+ float discriminant = b * b - 4 * a * c;
+ if (discriminant < 0.f)
+ return;
+ float discriminant_sqrt = sqrt(discriminant);
+ t_1 = (-b + discriminant_sqrt) / (2.f * a);
+ t_2 = (-b - discriminant_sqrt) / (2.f * a);
+ }
+
+ float sol_1 = 0.f;
+ float sol_2 = 0.f;
+
+ if (0.f < t_1 && t_1 < 1.f)
+ sol_1 = eval_bezier(y1_, y2_, t_1);
+
+ if (0.f < t_2 && t_2 < 1.f)
+ sol_2 = eval_bezier(y1_, y2_, t_2);
+
+ *min = std::min(std::min(*min, sol_1), sol_2);
+ *max = std::max(std::max(*max, sol_1), sol_2);
+}
+
// These numbers come from
// http://www.w3.org/TR/css3-transitions/#transition-timing-function_tag.
scoped_ptr<TimingFunction> EaseTimingFunction::Create() {
diff --git a/chromium/cc/animation/timing_function.h b/chromium/cc/animation/timing_function.h
index 3aa2f253d81..056ad414193 100644
--- a/chromium/cc/animation/timing_function.h
+++ b/chromium/cc/animation/timing_function.h
@@ -19,6 +19,10 @@ class CC_EXPORT TimingFunction : public FloatAnimationCurve {
// Partial implementation of FloatAnimationCurve.
virtual double Duration() const OVERRIDE;
+ // The smallest and largest values returned by GetValue for inputs in
+ // [0, 1].
+ virtual void Range(float* min, float* max) const = 0;
+
protected:
TimingFunction();
@@ -36,6 +40,8 @@ class CC_EXPORT CubicBezierTimingFunction : public TimingFunction {
virtual float GetValue(double time) const OVERRIDE;
virtual scoped_ptr<AnimationCurve> Clone() const OVERRIDE;
+ virtual void Range(float* min, float* max) const OVERRIDE;
+
protected:
CubicBezierTimingFunction(double x1, double y1, double x2, double y2);
diff --git a/chromium/cc/animation/timing_function_unittest.cc b/chromium/cc/animation/timing_function_unittest.cc
index 2caa12d0772..4be225fd6b9 100644
--- a/chromium/cc/animation/timing_function_unittest.cc
+++ b/chromium/cc/animation/timing_function_unittest.cc
@@ -67,5 +67,75 @@ TEST(TimingFunctionTest, CubicBezierTimingFunctionUnclampedYValues) {
EXPECT_NEAR(function->GetValue(1.0), 1.0, epsilon);
}
+TEST(TimingFunctionTest, CubicBezierTimingFunctionRange) {
+ double epsilon = 0.00015;
+ float min, max;
+
+ // Derivative is a constant.
+ scoped_ptr<CubicBezierTimingFunction> function =
+ CubicBezierTimingFunction::Create(0.25, (1.0 / 3.0), 0.75, (2.0 / 3.0));
+ function->Range(&min, &max);
+ EXPECT_EQ(0.f, min);
+ EXPECT_EQ(1.f, max);
+
+ // Derivative is linear.
+ function = CubicBezierTimingFunction::Create(0.25, -0.5, 0.75, (-1.0 / 6.0));
+ function->Range(&min, &max);
+ EXPECT_NEAR(min, -0.225, epsilon);
+ EXPECT_EQ(1.f, max);
+
+ // Derivative has no real roots.
+ function = CubicBezierTimingFunction::Create(0.25, 0.25, 0.75, 0.5);
+ function->Range(&min, &max);
+ EXPECT_EQ(0.f, min);
+ EXPECT_EQ(1.f, max);
+
+ // Derivative has exactly one real root.
+ function = CubicBezierTimingFunction::Create(0.0, 1.0, 1.0, 0.0);
+ function->Range(&min, &max);
+ EXPECT_EQ(0.f, min);
+ EXPECT_EQ(1.f, max);
+
+ // Derivative has one root < 0 and one root > 1.
+ function = CubicBezierTimingFunction::Create(0.25, 0.1, 0.75, 0.9);
+ function->Range(&min, &max);
+ EXPECT_EQ(0.f, min);
+ EXPECT_EQ(1.f, max);
+
+ // Derivative has two roots in [0,1].
+ function = CubicBezierTimingFunction::Create(0.25, 2.5, 0.75, 0.5);
+ function->Range(&min, &max);
+ EXPECT_EQ(0.f, min);
+ EXPECT_NEAR(max, 1.28818, epsilon);
+ function = CubicBezierTimingFunction::Create(0.25, 0.5, 0.75, -1.5);
+ function->Range(&min, &max);
+ EXPECT_NEAR(min, -0.28818, epsilon);
+ EXPECT_EQ(1.f, max);
+
+ // Derivative has one root < 0 and one root in [0,1].
+ function = CubicBezierTimingFunction::Create(0.25, 0.1, 0.75, 1.5);
+ function->Range(&min, &max);
+ EXPECT_EQ(0.f, min);
+ EXPECT_NEAR(max, 1.10755, epsilon);
+
+ // Derivative has one root in [0,1] and one root > 1.
+ function = CubicBezierTimingFunction::Create(0.25, -0.5, 0.75, 0.9);
+ function->Range(&min, &max);
+ EXPECT_NEAR(min, -0.10755, epsilon);
+ EXPECT_EQ(1.f, max);
+
+ // Derivative has two roots < 0.
+ function = CubicBezierTimingFunction::Create(0.25, 0.3, 0.75, 0.633);
+ function->Range(&min, &max);
+ EXPECT_EQ(0.f, min);
+ EXPECT_EQ(1.f, max);
+
+ // Derivative has two roots > 1.
+ function = CubicBezierTimingFunction::Create(0.25, 0.367, 0.75, 0.7);
+ function->Range(&min, &max);
+ EXPECT_EQ(0.f, min);
+ EXPECT_EQ(1.f, max);
+}
+
} // namespace
} // namespace cc
diff --git a/chromium/cc/animation/transform_operation.cc b/chromium/cc/animation/transform_operation.cc
index dacea06d797..93f40f37f1a 100644
--- a/chromium/cc/animation/transform_operation.cc
+++ b/chromium/cc/animation/transform_operation.cc
@@ -2,14 +2,17 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <algorithm>
#include <cmath>
#include <limits>
+#include "base/logging.h"
#include "cc/animation/transform_operation.h"
+#include "ui/gfx/box_f.h"
#include "ui/gfx/vector3d_f.h"
namespace {
-const double kAngleEpsilon = 1e-4;
+const SkMScalar kAngleEpsilon = 1e-4;
}
namespace cc {
@@ -24,10 +27,10 @@ static bool IsOperationIdentity(const TransformOperation* operation) {
static bool ShareSameAxis(const TransformOperation* from,
const TransformOperation* to,
- double* axis_x,
- double* axis_y,
- double* axis_z,
- double* angle_from) {
+ SkMScalar* axis_x,
+ SkMScalar* axis_y,
+ SkMScalar* axis_z,
+ SkMScalar* angle_from) {
if (IsOperationIdentity(from) && IsOperationIdentity(to))
return false;
@@ -47,20 +50,21 @@ static bool ShareSameAxis(const TransformOperation* from,
return true;
}
- double length_2 = from->rotate.axis.x * from->rotate.axis.x +
- from->rotate.axis.y * from->rotate.axis.y +
- from->rotate.axis.z * from->rotate.axis.z;
- double other_length_2 = to->rotate.axis.x * to->rotate.axis.x +
- to->rotate.axis.y * to->rotate.axis.y +
- to->rotate.axis.z * to->rotate.axis.z;
+ SkMScalar length_2 = from->rotate.axis.x * from->rotate.axis.x +
+ from->rotate.axis.y * from->rotate.axis.y +
+ from->rotate.axis.z * from->rotate.axis.z;
+ SkMScalar other_length_2 = to->rotate.axis.x * to->rotate.axis.x +
+ to->rotate.axis.y * to->rotate.axis.y +
+ to->rotate.axis.z * to->rotate.axis.z;
if (length_2 <= kAngleEpsilon || other_length_2 <= kAngleEpsilon)
return false;
- double dot = to->rotate.axis.x * from->rotate.axis.x +
- to->rotate.axis.y * from->rotate.axis.y +
- to->rotate.axis.z * from->rotate.axis.z;
- double error = std::abs(1.0 - (dot * dot) / (length_2 * other_length_2));
+ SkMScalar dot = to->rotate.axis.x * from->rotate.axis.x +
+ to->rotate.axis.y * from->rotate.axis.y +
+ to->rotate.axis.z * from->rotate.axis.z;
+ SkMScalar error =
+ std::abs(SK_MScalar1 - (dot * dot) / (length_2 * other_length_2));
bool result = error < kAngleEpsilon;
if (result) {
*axis_x = to->rotate.axis.x;
@@ -73,14 +77,16 @@ static bool ShareSameAxis(const TransformOperation* from,
return result;
}
-static double BlendDoubles(double from, double to, double progress) {
+static SkMScalar BlendSkMScalars(SkMScalar from,
+ SkMScalar to,
+ SkMScalar progress) {
return from * (1 - progress) + to * progress;
}
bool TransformOperation::BlendTransformOperations(
const TransformOperation* from,
const TransformOperation* to,
- double progress,
+ SkMScalar progress,
gfx::Transform* result) {
if (IsOperationIdentity(from) && IsOperationIdentity(to))
return true;
@@ -94,26 +100,26 @@ bool TransformOperation::BlendTransformOperations(
switch (interpolation_type) {
case TransformOperation::TransformOperationTranslate: {
- double from_x = IsOperationIdentity(from) ? 0 : from->translate.x;
- double from_y = IsOperationIdentity(from) ? 0 : from->translate.y;
- double from_z = IsOperationIdentity(from) ? 0 : from->translate.z;
- double to_x = IsOperationIdentity(to) ? 0 : to->translate.x;
- double to_y = IsOperationIdentity(to) ? 0 : to->translate.y;
- double to_z = IsOperationIdentity(to) ? 0 : to->translate.z;
- result->Translate3d(BlendDoubles(from_x, to_x, progress),
- BlendDoubles(from_y, to_y, progress),
- BlendDoubles(from_z, to_z, progress));
+ SkMScalar from_x = IsOperationIdentity(from) ? 0 : from->translate.x;
+ SkMScalar from_y = IsOperationIdentity(from) ? 0 : from->translate.y;
+ SkMScalar from_z = IsOperationIdentity(from) ? 0 : from->translate.z;
+ SkMScalar to_x = IsOperationIdentity(to) ? 0 : to->translate.x;
+ SkMScalar to_y = IsOperationIdentity(to) ? 0 : to->translate.y;
+ SkMScalar to_z = IsOperationIdentity(to) ? 0 : to->translate.z;
+ result->Translate3d(BlendSkMScalars(from_x, to_x, progress),
+ BlendSkMScalars(from_y, to_y, progress),
+ BlendSkMScalars(from_z, to_z, progress));
break;
}
case TransformOperation::TransformOperationRotate: {
- double axis_x = 0;
- double axis_y = 0;
- double axis_z = 1;
- double from_angle = 0;
- double to_angle = IsOperationIdentity(to) ? 0 : to->rotate.angle;
+ SkMScalar axis_x = 0;
+ SkMScalar axis_y = 0;
+ SkMScalar axis_z = 1;
+ SkMScalar from_angle = 0;
+ SkMScalar to_angle = IsOperationIdentity(to) ? 0 : to->rotate.angle;
if (ShareSameAxis(from, to, &axis_x, &axis_y, &axis_z, &from_angle)) {
result->RotateAbout(gfx::Vector3dF(axis_x, axis_y, axis_z),
- BlendDoubles(from_angle, to_angle, progress));
+ BlendSkMScalars(from_angle, to_angle, progress));
} else {
gfx::Transform to_matrix;
if (!IsOperationIdentity(to))
@@ -128,33 +134,35 @@ bool TransformOperation::BlendTransformOperations(
break;
}
case TransformOperation::TransformOperationScale: {
- double from_x = IsOperationIdentity(from) ? 1 : from->scale.x;
- double from_y = IsOperationIdentity(from) ? 1 : from->scale.y;
- double from_z = IsOperationIdentity(from) ? 1 : from->scale.z;
- double to_x = IsOperationIdentity(to) ? 1 : to->scale.x;
- double to_y = IsOperationIdentity(to) ? 1 : to->scale.y;
- double to_z = IsOperationIdentity(to) ? 1 : to->scale.z;
- result->Scale3d(BlendDoubles(from_x, to_x, progress),
- BlendDoubles(from_y, to_y, progress),
- BlendDoubles(from_z, to_z, progress));
+ SkMScalar from_x = IsOperationIdentity(from) ? 1 : from->scale.x;
+ SkMScalar from_y = IsOperationIdentity(from) ? 1 : from->scale.y;
+ SkMScalar from_z = IsOperationIdentity(from) ? 1 : from->scale.z;
+ SkMScalar to_x = IsOperationIdentity(to) ? 1 : to->scale.x;
+ SkMScalar to_y = IsOperationIdentity(to) ? 1 : to->scale.y;
+ SkMScalar to_z = IsOperationIdentity(to) ? 1 : to->scale.z;
+ result->Scale3d(BlendSkMScalars(from_x, to_x, progress),
+ BlendSkMScalars(from_y, to_y, progress),
+ BlendSkMScalars(from_z, to_z, progress));
break;
}
case TransformOperation::TransformOperationSkew: {
- double from_x = IsOperationIdentity(from) ? 0 : from->skew.x;
- double from_y = IsOperationIdentity(from) ? 0 : from->skew.y;
- double to_x = IsOperationIdentity(to) ? 0 : to->skew.x;
- double to_y = IsOperationIdentity(to) ? 0 : to->skew.y;
- result->SkewX(BlendDoubles(from_x, to_x, progress));
- result->SkewY(BlendDoubles(from_y, to_y, progress));
+ SkMScalar from_x = IsOperationIdentity(from) ? 0 : from->skew.x;
+ SkMScalar from_y = IsOperationIdentity(from) ? 0 : from->skew.y;
+ SkMScalar to_x = IsOperationIdentity(to) ? 0 : to->skew.x;
+ SkMScalar to_y = IsOperationIdentity(to) ? 0 : to->skew.y;
+ result->SkewX(BlendSkMScalars(from_x, to_x, progress));
+ result->SkewY(BlendSkMScalars(from_y, to_y, progress));
break;
}
case TransformOperation::TransformOperationPerspective: {
- double from_perspective_depth = IsOperationIdentity(from) ?
- std::numeric_limits<double>::max() : from->perspective_depth;
- double to_perspective_depth = IsOperationIdentity(to) ?
- std::numeric_limits<double>::max() : to->perspective_depth;
- result->ApplyPerspectiveDepth(
- BlendDoubles(from_perspective_depth, to_perspective_depth, progress));
+ SkMScalar from_perspective_depth =
+ IsOperationIdentity(from) ? std::numeric_limits<SkMScalar>::max()
+ : from->perspective_depth;
+ SkMScalar to_perspective_depth = IsOperationIdentity(to)
+ ? std::numeric_limits<SkMScalar>::max()
+ : to->perspective_depth;
+ result->ApplyPerspectiveDepth(BlendSkMScalars(
+ from_perspective_depth, to_perspective_depth, progress));
break;
}
case TransformOperation::TransformOperationMatrix: {
@@ -177,4 +185,130 @@ bool TransformOperation::BlendTransformOperations(
return true;
}
+static void ApplyScaleToBox(float x_scale,
+ float y_scale,
+ float z_scale,
+ gfx::BoxF* box) {
+ if (x_scale < 0)
+ box->set_x(-box->right());
+ if (y_scale < 0)
+ box->set_y(-box->bottom());
+ if (z_scale < 0)
+ box->set_z(-box->front());
+ box->Scale(std::abs(x_scale), std::abs(y_scale), std::abs(z_scale));
+}
+
+static void UnionBoxWithZeroScale(gfx::BoxF* box) {
+ float min_x = std::min(box->x(), 0.f);
+ float min_y = std::min(box->y(), 0.f);
+ float min_z = std::min(box->z(), 0.f);
+ float max_x = std::max(box->right(), 0.f);
+ float max_y = std::max(box->bottom(), 0.f);
+ float max_z = std::max(box->front(), 0.f);
+ *box = gfx::BoxF(
+ min_x, min_y, min_z, max_x - min_x, max_y - min_y, max_z - min_z);
+}
+
+bool TransformOperation::BlendedBoundsForBox(const gfx::BoxF& box,
+ const TransformOperation* from,
+ const TransformOperation* to,
+ SkMScalar min_progress,
+ SkMScalar max_progress,
+ gfx::BoxF* bounds) {
+ bool is_identity_from = IsOperationIdentity(from);
+ bool is_identity_to = IsOperationIdentity(to);
+ if (is_identity_from && is_identity_to) {
+ *bounds = box;
+ return true;
+ }
+
+ TransformOperation::Type interpolation_type =
+ TransformOperation::TransformOperationIdentity;
+ if (is_identity_to)
+ interpolation_type = from->type;
+ else
+ interpolation_type = to->type;
+
+ switch (interpolation_type) {
+ case TransformOperation::TransformOperationTranslate: {
+ SkMScalar from_x, from_y, from_z;
+ if (is_identity_from) {
+ from_x = from_y = from_z = 0.0;
+ } else {
+ from_x = from->translate.x;
+ from_y = from->translate.y;
+ from_z = from->translate.z;
+ }
+ SkMScalar to_x, to_y, to_z;
+ if (is_identity_to) {
+ to_x = to_y = to_z = 0.0;
+ } else {
+ to_x = to->translate.x;
+ to_y = to->translate.y;
+ to_z = to->translate.z;
+ }
+ *bounds = box;
+ *bounds += gfx::Vector3dF(BlendSkMScalars(from_x, to_x, min_progress),
+ BlendSkMScalars(from_y, to_y, min_progress),
+ BlendSkMScalars(from_z, to_z, min_progress));
+ gfx::BoxF bounds_max = box;
+ bounds_max += gfx::Vector3dF(BlendSkMScalars(from_x, to_x, max_progress),
+ BlendSkMScalars(from_y, to_y, max_progress),
+ BlendSkMScalars(from_z, to_z, max_progress));
+ bounds->Union(bounds_max);
+ return true;
+ }
+ case TransformOperation::TransformOperationScale: {
+ SkMScalar from_x, from_y, from_z;
+ if (is_identity_from) {
+ from_x = from_y = from_z = 1.0;
+ } else {
+ from_x = from->scale.x;
+ from_y = from->scale.y;
+ from_z = from->scale.z;
+ }
+ SkMScalar to_x, to_y, to_z;
+ if (is_identity_to) {
+ to_x = to_y = to_z = 1.0;
+ } else {
+ to_x = to->scale.x;
+ to_y = to->scale.y;
+ to_z = to->scale.z;
+ }
+ *bounds = box;
+ ApplyScaleToBox(
+ SkMScalarToFloat(BlendSkMScalars(from_x, to_x, min_progress)),
+ SkMScalarToFloat(BlendSkMScalars(from_y, to_y, min_progress)),
+ SkMScalarToFloat(BlendSkMScalars(from_z, to_z, min_progress)),
+ bounds);
+ gfx::BoxF bounds_max = box;
+ ApplyScaleToBox(
+ SkMScalarToFloat(BlendSkMScalars(from_x, to_x, max_progress)),
+ SkMScalarToFloat(BlendSkMScalars(from_y, to_y, max_progress)),
+ SkMScalarToFloat(BlendSkMScalars(from_z, to_z, max_progress)),
+ &bounds_max);
+ if (!bounds->IsEmpty() && !bounds_max.IsEmpty()) {
+ bounds->Union(bounds_max);
+ } else if (!bounds->IsEmpty()) {
+ UnionBoxWithZeroScale(bounds);
+ } else if (!bounds_max.IsEmpty()) {
+ UnionBoxWithZeroScale(&bounds_max);
+ *bounds = bounds_max;
+ }
+
+ return true;
+ }
+ case TransformOperation::TransformOperationIdentity:
+ *bounds = box;
+ return true;
+ case TransformOperation::TransformOperationRotate:
+ case TransformOperation::TransformOperationSkew:
+ case TransformOperation::TransformOperationPerspective:
+ case TransformOperation::TransformOperationMatrix:
+ return false;
+ }
+ NOTREACHED();
+ return false;
+}
+
} // namespace cc
diff --git a/chromium/cc/animation/transform_operation.h b/chromium/cc/animation/transform_operation.h
index 74673ab48cb..345ff295e39 100644
--- a/chromium/cc/animation/transform_operation.h
+++ b/chromium/cc/animation/transform_operation.h
@@ -7,6 +7,10 @@
#include "ui/gfx/transform.h"
+namespace gfx {
+class BoxF;
+}
+
namespace cc {
struct TransformOperation {
@@ -28,34 +32,41 @@ struct TransformOperation {
gfx::Transform matrix;
union {
- double perspective_depth;
+ SkMScalar perspective_depth;
struct {
- double x, y;
+ SkMScalar x, y;
} skew;
struct {
- double x, y, z;
+ SkMScalar x, y, z;
} scale;
struct {
- double x, y, z;
+ SkMScalar x, y, z;
} translate;
struct {
struct {
- double x, y, z;
+ SkMScalar x, y, z;
} axis;
- double angle;
+ SkMScalar angle;
} rotate;
};
bool IsIdentity() const;
static bool BlendTransformOperations(const TransformOperation* from,
const TransformOperation* to,
- double progress,
+ SkMScalar progress,
gfx::Transform* result);
+
+ static bool BlendedBoundsForBox(const gfx::BoxF& box,
+ const TransformOperation* from,
+ const TransformOperation* to,
+ SkMScalar min_progress,
+ SkMScalar max_progress,
+ gfx::BoxF* bounds);
};
} // namespace cc
diff --git a/chromium/cc/animation/transform_operations.cc b/chromium/cc/animation/transform_operations.cc
index d74f1a5343a..f8abe441fbf 100644
--- a/chromium/cc/animation/transform_operations.cc
+++ b/chromium/cc/animation/transform_operations.cc
@@ -6,6 +6,7 @@
#include <algorithm>
+#include "ui/gfx/box_f.h"
#include "ui/gfx/transform_util.h"
#include "ui/gfx/vector3d_f.h"
@@ -34,13 +35,49 @@ gfx::Transform TransformOperations::Apply() const {
return to_return;
}
-gfx::Transform TransformOperations::Blend(
- const TransformOperations& from, double progress) const {
+gfx::Transform TransformOperations::Blend(const TransformOperations& from,
+ SkMScalar progress) const {
gfx::Transform to_return;
BlendInternal(from, progress, &to_return);
return to_return;
}
+bool TransformOperations::BlendedBoundsForBox(const gfx::BoxF& box,
+ const TransformOperations& from,
+ SkMScalar min_progress,
+ SkMScalar max_progress,
+ gfx::BoxF* bounds) const {
+ *bounds = box;
+
+ bool from_identity = from.IsIdentity();
+ bool to_identity = IsIdentity();
+ if (from_identity && to_identity)
+ return true;
+
+ if (!MatchesTypes(from))
+ return false;
+
+ size_t num_operations =
+ std::max(from_identity ? 0 : from.operations_.size(),
+ to_identity ? 0 : operations_.size());
+ for (size_t i = 0; i < num_operations; ++i) {
+ gfx::BoxF bounds_for_operation;
+ const TransformOperation* from_op =
+ from_identity ? NULL : &from.operations_[i];
+ const TransformOperation* to_op = to_identity ? NULL : &operations_[i];
+ if (!TransformOperation::BlendedBoundsForBox(*bounds,
+ from_op,
+ to_op,
+ min_progress,
+ max_progress,
+ &bounds_for_operation))
+ return false;
+ *bounds = bounds_for_operation;
+ }
+
+ return true;
+}
+
bool TransformOperations::MatchesTypes(const TransformOperations& other) const {
if (IsIdentity() || other.IsIdentity())
return true;
@@ -64,7 +101,9 @@ bool TransformOperations::CanBlendWith(
return BlendInternal(other, 0.5, &dummy);
}
-void TransformOperations::AppendTranslate(double x, double y, double z) {
+void TransformOperations::AppendTranslate(SkMScalar x,
+ SkMScalar y,
+ SkMScalar z) {
TransformOperation to_add;
to_add.matrix.Translate3d(x, y, z);
to_add.type = TransformOperation::TransformOperationTranslate;
@@ -75,8 +114,10 @@ void TransformOperations::AppendTranslate(double x, double y, double z) {
decomposed_transform_dirty_ = true;
}
-void TransformOperations::AppendRotate(double x, double y, double z,
- double degrees) {
+void TransformOperations::AppendRotate(SkMScalar x,
+ SkMScalar y,
+ SkMScalar z,
+ SkMScalar degrees) {
TransformOperation to_add;
to_add.matrix.RotateAbout(gfx::Vector3dF(x, y, z), degrees);
to_add.type = TransformOperation::TransformOperationRotate;
@@ -88,7 +129,7 @@ void TransformOperations::AppendRotate(double x, double y, double z,
decomposed_transform_dirty_ = true;
}
-void TransformOperations::AppendScale(double x, double y, double z) {
+void TransformOperations::AppendScale(SkMScalar x, SkMScalar y, SkMScalar z) {
TransformOperation to_add;
to_add.matrix.Scale3d(x, y, z);
to_add.type = TransformOperation::TransformOperationScale;
@@ -99,7 +140,7 @@ void TransformOperations::AppendScale(double x, double y, double z) {
decomposed_transform_dirty_ = true;
}
-void TransformOperations::AppendSkew(double x, double y) {
+void TransformOperations::AppendSkew(SkMScalar x, SkMScalar y) {
TransformOperation to_add;
to_add.matrix.SkewX(x);
to_add.matrix.SkewY(y);
@@ -110,7 +151,7 @@ void TransformOperations::AppendSkew(double x, double y) {
decomposed_transform_dirty_ = true;
}
-void TransformOperations::AppendPerspective(double depth) {
+void TransformOperations::AppendPerspective(SkMScalar depth) {
TransformOperation to_add;
to_add.matrix.ApplyPerspectiveDepth(depth);
to_add.type = TransformOperation::TransformOperationPerspective;
@@ -140,7 +181,7 @@ bool TransformOperations::IsIdentity() const {
}
bool TransformOperations::BlendInternal(const TransformOperations& from,
- double progress,
+ SkMScalar progress,
gfx::Transform* result) const {
bool from_identity = from.IsIdentity();
bool to_identity = IsIdentity();
diff --git a/chromium/cc/animation/transform_operations.h b/chromium/cc/animation/transform_operations.h
index b5f960fa688..7c509349092 100644
--- a/chromium/cc/animation/transform_operations.h
+++ b/chromium/cc/animation/transform_operations.h
@@ -13,6 +13,7 @@
#include "ui/gfx/transform.h"
namespace gfx {
+class BoxF;
struct DecomposedTransform;
}
@@ -43,7 +44,18 @@ class CC_EXPORT TransformOperations {
// transforms are baked to matrices (using apply), and the matrices are
// then decomposed and interpolated. For more information, see
// http://www.w3.org/TR/2011/WD-css3-2d-transforms-20111215/#matrix-decomposition.
- gfx::Transform Blend(const TransformOperations& from, double progress) const;
+ gfx::Transform Blend(const TransformOperations& from,
+ SkMScalar progress) const;
+
+ // Sets |bounds| be the bounding box for the region within which |box| will
+ // exist when it is transformed by the result of calling Blend on |from| and
+ // with progress in the range [min_progress, max_progress]. If this region
+ // cannot be computed, returns false.
+ bool BlendedBoundsForBox(const gfx::BoxF& box,
+ const TransformOperations& from,
+ SkMScalar min_progress,
+ SkMScalar max_progress,
+ gfx::BoxF* bounds) const;
// Returns true if this operation and its descendants have the same types
// as other and its descendants.
@@ -54,17 +66,18 @@ class CC_EXPORT TransformOperations {
// fails (this can happen if either matrix cannot be decomposed).
bool CanBlendWith(const TransformOperations& other) const;
- void AppendTranslate(double x, double y, double z);
- void AppendRotate(double x, double y, double z, double degrees);
- void AppendScale(double x, double y, double z);
- void AppendSkew(double x, double y);
- void AppendPerspective(double depth);
+ void AppendTranslate(SkMScalar x, SkMScalar y, SkMScalar z);
+ void AppendRotate(SkMScalar x, SkMScalar y, SkMScalar z, SkMScalar degrees);
+ void AppendScale(SkMScalar x, SkMScalar y, SkMScalar z);
+ void AppendSkew(SkMScalar x, SkMScalar y);
+ void AppendPerspective(SkMScalar depth);
void AppendMatrix(const gfx::Transform& matrix);
void AppendIdentity();
bool IsIdentity() const;
private:
- bool BlendInternal(const TransformOperations& from, double progress,
+ bool BlendInternal(const TransformOperations& from,
+ SkMScalar progress,
gfx::Transform* result) const;
std::vector<TransformOperation> operations_;
diff --git a/chromium/cc/animation/transform_operations_unittest.cc b/chromium/cc/animation/transform_operations_unittest.cc
index 8fc9b949d74..a7e4c510a52 100644
--- a/chromium/cc/animation/transform_operations_unittest.cc
+++ b/chromium/cc/animation/transform_operations_unittest.cc
@@ -8,6 +8,7 @@
#include "cc/animation/transform_operations.h"
#include "cc/test/geometry_test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/box_f.h"
#include "ui/gfx/vector3d_f.h"
namespace cc {
@@ -146,9 +147,9 @@ TEST(TransformOperationTest, IdentityAlwaysMatches) {
}
TEST(TransformOperationTest, ApplyTranslate) {
- double x = 1;
- double y = 2;
- double z = 3;
+ SkMScalar x = 1;
+ SkMScalar y = 2;
+ SkMScalar z = 3;
TransformOperations operations;
operations.AppendTranslate(x, y, z);
gfx::Transform expected;
@@ -157,10 +158,10 @@ TEST(TransformOperationTest, ApplyTranslate) {
}
TEST(TransformOperationTest, ApplyRotate) {
- double x = 1;
- double y = 2;
- double z = 3;
- double degrees = 80;
+ SkMScalar x = 1;
+ SkMScalar y = 2;
+ SkMScalar z = 3;
+ SkMScalar degrees = 80;
TransformOperations operations;
operations.AppendRotate(x, y, z, degrees);
gfx::Transform expected;
@@ -169,9 +170,9 @@ TEST(TransformOperationTest, ApplyRotate) {
}
TEST(TransformOperationTest, ApplyScale) {
- double x = 1;
- double y = 2;
- double z = 3;
+ SkMScalar x = 1;
+ SkMScalar y = 2;
+ SkMScalar z = 3;
TransformOperations operations;
operations.AppendScale(x, y, z);
gfx::Transform expected;
@@ -180,8 +181,8 @@ TEST(TransformOperationTest, ApplyScale) {
}
TEST(TransformOperationTest, ApplySkew) {
- double x = 1;
- double y = 2;
+ SkMScalar x = 1;
+ SkMScalar y = 2;
TransformOperations operations;
operations.AppendSkew(x, y);
gfx::Transform expected;
@@ -191,7 +192,7 @@ TEST(TransformOperationTest, ApplySkew) {
}
TEST(TransformOperationTest, ApplyPerspective) {
- double depth = 800;
+ SkMScalar depth = 800;
TransformOperations operations;
operations.AppendPerspective(depth);
gfx::Transform expected;
@@ -200,9 +201,9 @@ TEST(TransformOperationTest, ApplyPerspective) {
}
TEST(TransformOperationTest, ApplyMatrix) {
- double dx = 1;
- double dy = 2;
- double dz = 3;
+ SkMScalar dx = 1;
+ SkMScalar dy = 2;
+ SkMScalar dz = 3;
gfx::Transform expected_matrix;
expected_matrix.Translate3d(dx, dy, dz);
TransformOperations matrix_transform;
@@ -211,13 +212,13 @@ TEST(TransformOperationTest, ApplyMatrix) {
}
TEST(TransformOperationTest, ApplyOrder) {
- double sx = 2;
- double sy = 4;
- double sz = 8;
+ SkMScalar sx = 2;
+ SkMScalar sy = 4;
+ SkMScalar sz = 8;
- double dx = 1;
- double dy = 2;
- double dz = 3;
+ SkMScalar dx = 1;
+ SkMScalar dy = 2;
+ SkMScalar dz = 3;
TransformOperations operations;
operations.AppendScale(sx, sy, sz);
@@ -236,21 +237,21 @@ TEST(TransformOperationTest, ApplyOrder) {
}
TEST(TransformOperationTest, BlendOrder) {
- double sx1 = 2;
- double sy1 = 4;
- double sz1 = 8;
+ SkMScalar sx1 = 2;
+ SkMScalar sy1 = 4;
+ SkMScalar sz1 = 8;
- double dx1 = 1;
- double dy1 = 2;
- double dz1 = 3;
+ SkMScalar dx1 = 1;
+ SkMScalar dy1 = 2;
+ SkMScalar dz1 = 3;
- double sx2 = 4;
- double sy2 = 8;
- double sz2 = 16;
+ SkMScalar sx2 = 4;
+ SkMScalar sy2 = 8;
+ SkMScalar sz2 = 16;
- double dx2 = 10;
- double dy2 = 20;
- double dz2 = 30;
+ SkMScalar dx2 = 10;
+ SkMScalar dy2 = 20;
+ SkMScalar dz2 = 30;
TransformOperations operations_from;
operations_from.AppendScale(sx1, sy1, sz1);
@@ -270,7 +271,7 @@ TEST(TransformOperationTest, BlendOrder) {
gfx::Transform translate_to;
translate_to.Translate3d(dx2, dy2, dz2);
- double progress = 0.25;
+ SkMScalar progress = 0.25f;
gfx::Transform blended_scale = scale_to;
blended_scale.Blend(scale_from, progress);
@@ -285,11 +286,11 @@ TEST(TransformOperationTest, BlendOrder) {
expected, operations_to.Blend(operations_from, progress));
}
-static void CheckProgress(double progress,
- const gfx::Transform& from_matrix,
- const gfx::Transform& to_matrix,
- const TransformOperations& from_transform,
- const TransformOperations& to_transform) {
+static void CheckProgress(SkMScalar progress,
+ const gfx::Transform& from_matrix,
+ const gfx::Transform& to_matrix,
+ const TransformOperations& from_transform,
+ const TransformOperations& to_transform) {
gfx::Transform expected_matrix = to_matrix;
expected_matrix.Blend(from_matrix, progress);
EXPECT_TRANSFORMATION_MATRIX_EQ(
@@ -297,9 +298,9 @@ static void CheckProgress(double progress,
}
TEST(TransformOperationTest, BlendProgress) {
- double sx = 2;
- double sy = 4;
- double sz = 8;
+ SkMScalar sx = 2;
+ SkMScalar sy = 4;
+ SkMScalar sz = 8;
TransformOperations operations_from;
operations_from.AppendScale(sx, sy, sz);
@@ -317,28 +318,28 @@ TEST(TransformOperationTest, BlendProgress) {
CheckProgress(-1, matrix_from, matrix_to, operations_from, operations_to);
CheckProgress(0, matrix_from, matrix_to, operations_from, operations_to);
- CheckProgress(0.25, matrix_from, matrix_to, operations_from, operations_to);
- CheckProgress(0.5, matrix_from, matrix_to, operations_from, operations_to);
+ CheckProgress(0.25f, matrix_from, matrix_to, operations_from, operations_to);
+ CheckProgress(0.5f, matrix_from, matrix_to, operations_from, operations_to);
CheckProgress(1, matrix_from, matrix_to, operations_from, operations_to);
CheckProgress(2, matrix_from, matrix_to, operations_from, operations_to);
}
TEST(TransformOperationTest, BlendWhenTypesDoNotMatch) {
- double sx1 = 2;
- double sy1 = 4;
- double sz1 = 8;
+ SkMScalar sx1 = 2;
+ SkMScalar sy1 = 4;
+ SkMScalar sz1 = 8;
- double dx1 = 1;
- double dy1 = 2;
- double dz1 = 3;
+ SkMScalar dx1 = 1;
+ SkMScalar dy1 = 2;
+ SkMScalar dz1 = 3;
- double sx2 = 4;
- double sy2 = 8;
- double sz2 = 16;
+ SkMScalar sx2 = 4;
+ SkMScalar sy2 = 8;
+ SkMScalar sz2 = 16;
- double dx2 = 10;
- double dy2 = 20;
- double dz2 = 30;
+ SkMScalar dx2 = 10;
+ SkMScalar dy2 = 20;
+ SkMScalar dz2 = 30;
TransformOperations operations_from;
operations_from.AppendScale(sx1, sy1, sz1);
@@ -356,7 +357,7 @@ TEST(TransformOperationTest, BlendWhenTypesDoNotMatch) {
to.Translate3d(dx2, dy2, dz2);
to.Scale3d(sx2, sy2, sz2);
- double progress = 0.25;
+ SkMScalar progress = 0.25f;
gfx::Transform expected = to;
expected.Blend(from, progress);
@@ -372,7 +373,7 @@ TEST(TransformOperationTest, LargeRotationsWithSameAxis) {
TransformOperations operations_to;
operations_to.AppendRotate(0, 0, 2, 360);
- double progress = 0.5;
+ SkMScalar progress = 0.5f;
gfx::Transform expected;
expected.RotateAbout(gfx::Vector3dF(0, 0, 1), 180);
@@ -388,7 +389,7 @@ TEST(TransformOperationTest, LargeRotationsWithSameAxisInDifferentDirection) {
TransformOperations operations_to;
operations_to.AppendRotate(0, 0, -1, 180);
- double progress = 0.5;
+ SkMScalar progress = 0.5f;
gfx::Transform expected;
@@ -403,7 +404,7 @@ TEST(TransformOperationTest, LargeRotationsWithDifferentAxes) {
TransformOperations operations_to;
operations_to.AppendRotate(0, 1, 0, 175);
- double progress = 0.5;
+ SkMScalar progress = 0.5f;
gfx::Transform matrix_from;
matrix_from.RotateAbout(gfx::Vector3dF(0, 0, 1), 175);
@@ -425,7 +426,7 @@ TEST(TransformOperationTest, BlendRotationFromIdentity) {
TransformOperations operations;
operations.AppendRotate(0, 0, 1, 360);
- double progress = 0.5;
+ SkMScalar progress = 0.5f;
gfx::Transform expected;
expected.RotateAbout(gfx::Vector3dF(0, 0, 1), 180);
@@ -433,7 +434,7 @@ TEST(TransformOperationTest, BlendRotationFromIdentity) {
EXPECT_TRANSFORMATION_MATRIX_EQ(
expected, operations.Blend(*identity_operations[i], progress));
- progress = -0.5;
+ progress = -0.5f;
expected.MakeIdentity();
expected.RotateAbout(gfx::Vector3dF(0, 0, 1), -180);
@@ -441,7 +442,7 @@ TEST(TransformOperationTest, BlendRotationFromIdentity) {
EXPECT_TRANSFORMATION_MATRIX_EQ(
expected, operations.Blend(*identity_operations[i], progress));
- progress = 1.5;
+ progress = 1.5f;
expected.MakeIdentity();
expected.RotateAbout(gfx::Vector3dF(0, 0, 1), 540);
@@ -459,7 +460,7 @@ TEST(TransformOperationTest, BlendTranslationFromIdentity) {
TransformOperations operations;
operations.AppendTranslate(2, 2, 2);
- double progress = 0.5;
+ SkMScalar progress = 0.5f;
gfx::Transform expected;
expected.Translate3d(1, 1, 1);
@@ -467,7 +468,7 @@ TEST(TransformOperationTest, BlendTranslationFromIdentity) {
EXPECT_TRANSFORMATION_MATRIX_EQ(
expected, operations.Blend(*identity_operations[i], progress));
- progress = -0.5;
+ progress = -0.5f;
expected.MakeIdentity();
expected.Translate3d(-1, -1, -1);
@@ -475,7 +476,7 @@ TEST(TransformOperationTest, BlendTranslationFromIdentity) {
EXPECT_TRANSFORMATION_MATRIX_EQ(
expected, operations.Blend(*identity_operations[i], progress));
- progress = 1.5;
+ progress = 1.5f;
expected.MakeIdentity();
expected.Translate3d(3, 3, 3);
@@ -493,7 +494,7 @@ TEST(TransformOperationTest, BlendScaleFromIdentity) {
TransformOperations operations;
operations.AppendScale(3, 3, 3);
- double progress = 0.5;
+ SkMScalar progress = 0.5f;
gfx::Transform expected;
expected.Scale3d(2, 2, 2);
@@ -501,7 +502,7 @@ TEST(TransformOperationTest, BlendScaleFromIdentity) {
EXPECT_TRANSFORMATION_MATRIX_EQ(
expected, operations.Blend(*identity_operations[i], progress));
- progress = -0.5;
+ progress = -0.5f;
expected.MakeIdentity();
expected.Scale3d(0, 0, 0);
@@ -509,7 +510,7 @@ TEST(TransformOperationTest, BlendScaleFromIdentity) {
EXPECT_TRANSFORMATION_MATRIX_EQ(
expected, operations.Blend(*identity_operations[i], progress));
- progress = 1.5;
+ progress = 1.5f;
expected.MakeIdentity();
expected.Scale3d(4, 4, 4);
@@ -527,7 +528,7 @@ TEST(TransformOperationTest, BlendSkewFromIdentity) {
TransformOperations operations;
operations.AppendSkew(2, 2);
- double progress = 0.5;
+ SkMScalar progress = 0.5f;
gfx::Transform expected;
expected.SkewX(1);
@@ -536,7 +537,7 @@ TEST(TransformOperationTest, BlendSkewFromIdentity) {
EXPECT_TRANSFORMATION_MATRIX_EQ(
expected, operations.Blend(*identity_operations[i], progress));
- progress = -0.5;
+ progress = -0.5f;
expected.MakeIdentity();
expected.SkewX(-1);
@@ -545,7 +546,7 @@ TEST(TransformOperationTest, BlendSkewFromIdentity) {
EXPECT_TRANSFORMATION_MATRIX_EQ(
expected, operations.Blend(*identity_operations[i], progress));
- progress = 1.5;
+ progress = 1.5f;
expected.MakeIdentity();
expected.SkewX(3);
@@ -564,11 +565,11 @@ TEST(TransformOperationTest, BlendPerspectiveFromIdentity) {
TransformOperations operations;
operations.AppendPerspective(1000);
- double progress = 0.5;
+ SkMScalar progress = 0.5f;
gfx::Transform expected;
- expected.ApplyPerspectiveDepth(
- 500 + 0.5 * std::numeric_limits<double>::max());
+ expected.ApplyPerspectiveDepth(500 +
+ 0.5 * std::numeric_limits<SkMScalar>::max());
EXPECT_TRANSFORMATION_MATRIX_EQ(
expected, operations.Blend(*identity_operations[i], progress));
@@ -583,7 +584,7 @@ TEST(TransformOperationTest, BlendRotationToIdentity) {
TransformOperations operations;
operations.AppendRotate(0, 0, 1, 360);
- double progress = 0.5;
+ SkMScalar progress = 0.5f;
gfx::Transform expected;
expected.RotateAbout(gfx::Vector3dF(0, 0, 1), 180);
@@ -601,7 +602,7 @@ TEST(TransformOperationTest, BlendTranslationToIdentity) {
TransformOperations operations;
operations.AppendTranslate(2, 2, 2);
- double progress = 0.5;
+ SkMScalar progress = 0.5f;
gfx::Transform expected;
expected.Translate3d(1, 1, 1);
@@ -619,7 +620,7 @@ TEST(TransformOperationTest, BlendScaleToIdentity) {
TransformOperations operations;
operations.AppendScale(3, 3, 3);
- double progress = 0.5;
+ SkMScalar progress = 0.5f;
gfx::Transform expected;
expected.Scale3d(2, 2, 2);
@@ -637,7 +638,7 @@ TEST(TransformOperationTest, BlendSkewToIdentity) {
TransformOperations operations;
operations.AppendSkew(2, 2);
- double progress = 0.5;
+ SkMScalar progress = 0.5f;
gfx::Transform expected;
expected.SkewX(1);
@@ -656,11 +657,11 @@ TEST(TransformOperationTest, BlendPerspectiveToIdentity) {
TransformOperations operations;
operations.AppendPerspective(1000);
- double progress = 0.5;
+ SkMScalar progress = 0.5f;
gfx::Transform expected;
- expected.ApplyPerspectiveDepth(
- 500 + 0.5 * std::numeric_limits<double>::max());
+ expected.ApplyPerspectiveDepth(500 +
+ 0.5 * std::numeric_limits<SkMScalar>::max());
EXPECT_TRANSFORMATION_MATRIX_EQ(
expected, identity_operations[i]->Blend(operations, progress));
@@ -707,5 +708,176 @@ TEST(TransformOperationTest, ExtrapolateMatrixBlending) {
expected, operations1.Blend(operations2, -0.5));
}
+TEST(TransformOperationTest, BlendedBoundsWhenTypesDoNotMatch) {
+ TransformOperations operations_from;
+ operations_from.AppendScale(2.0, 4.0, 8.0);
+ operations_from.AppendTranslate(1.0, 2.0, 3.0);
+
+ TransformOperations operations_to;
+ operations_to.AppendTranslate(10.0, 20.0, 30.0);
+ operations_to.AppendScale(4.0, 8.0, 16.0);
+
+ gfx::BoxF box(1.f, 1.f, 1.f);
+ gfx::BoxF bounds;
+
+ SkMScalar min_progress = 0.f;
+ SkMScalar max_progress = 1.f;
+
+ EXPECT_FALSE(operations_to.BlendedBoundsForBox(
+ box, operations_from, min_progress, max_progress, &bounds));
+}
+
+TEST(TransformOperationTest, BlendedBoundsForIdentity) {
+ TransformOperations operations_from;
+ operations_from.AppendIdentity();
+ TransformOperations operations_to;
+ operations_to.AppendIdentity();
+
+ gfx::BoxF box(1.f, 2.f, 3.f);
+ gfx::BoxF bounds;
+
+ SkMScalar min_progress = 0.f;
+ SkMScalar max_progress = 1.f;
+
+ EXPECT_TRUE(operations_to.BlendedBoundsForBox(
+ box, operations_from, min_progress, max_progress, &bounds));
+ EXPECT_EQ(box.ToString(), bounds.ToString());
+}
+
+TEST(TransformOperationTest, BlendedBoundsForTranslate) {
+ TransformOperations operations_from;
+ operations_from.AppendTranslate(3.0, -4.0, 2.0);
+ TransformOperations operations_to;
+ operations_to.AppendTranslate(7.0, 4.0, -2.0);
+
+ gfx::BoxF box(1.f, 2.f, 3.f, 4.f, 4.f, 4.f);
+ gfx::BoxF bounds;
+
+ SkMScalar min_progress = -0.5f;
+ SkMScalar max_progress = 1.5f;
+ EXPECT_TRUE(operations_to.BlendedBoundsForBox(
+ box, operations_from, min_progress, max_progress, &bounds));
+ EXPECT_EQ(gfx::BoxF(2.f, -6.f, -1.f, 12.f, 20.f, 12.f).ToString(),
+ bounds.ToString());
+
+ min_progress = 0.f;
+ max_progress = 1.f;
+ EXPECT_TRUE(operations_to.BlendedBoundsForBox(
+ box, operations_from, min_progress, max_progress, &bounds));
+ EXPECT_EQ(gfx::BoxF(4.f, -2.f, 1.f, 8.f, 12.f, 8.f).ToString(),
+ bounds.ToString());
+
+ TransformOperations identity;
+ EXPECT_TRUE(operations_to.BlendedBoundsForBox(
+ box, identity, min_progress, max_progress, &bounds));
+ EXPECT_EQ(gfx::BoxF(1.f, 2.f, 1.f, 11.f, 8.f, 6.f).ToString(),
+ bounds.ToString());
+
+ EXPECT_TRUE(identity.BlendedBoundsForBox(
+ box, operations_from, min_progress, max_progress, &bounds));
+ EXPECT_EQ(gfx::BoxF(1.f, -2.f, 3.f, 7.f, 8.f, 6.f).ToString(),
+ bounds.ToString());
+}
+
+TEST(TransformOperationTest, BlendedBoundsForScale) {
+ TransformOperations operations_from;
+ operations_from.AppendScale(3.0, 0.5, 2.0);
+ TransformOperations operations_to;
+ operations_to.AppendScale(7.0, 4.0, -2.0);
+
+ gfx::BoxF box(1.f, 2.f, 3.f, 4.f, 4.f, 4.f);
+ gfx::BoxF bounds;
+
+ SkMScalar min_progress = -0.5f;
+ SkMScalar max_progress = 1.5f;
+ EXPECT_TRUE(operations_to.BlendedBoundsForBox(
+ box, operations_from, min_progress, max_progress, &bounds));
+ EXPECT_EQ(gfx::BoxF(1.f, -7.5f, -28.f, 44.f, 42.f, 56.f).ToString(),
+ bounds.ToString());
+
+ min_progress = 0.f;
+ max_progress = 1.f;
+ EXPECT_TRUE(operations_to.BlendedBoundsForBox(
+ box, operations_from, min_progress, max_progress, &bounds));
+ EXPECT_EQ(gfx::BoxF(3.f, 1.f, -14.f, 32.f, 23.f, 28.f).ToString(),
+ bounds.ToString());
+
+ TransformOperations identity;
+ EXPECT_TRUE(operations_to.BlendedBoundsForBox(
+ box, identity, min_progress, max_progress, &bounds));
+ EXPECT_EQ(gfx::BoxF(1.f, 2.f, -14.f, 34.f, 22.f, 21.f).ToString(),
+ bounds.ToString());
+
+ EXPECT_TRUE(identity.BlendedBoundsForBox(
+ box, operations_from, min_progress, max_progress, &bounds));
+ EXPECT_EQ(gfx::BoxF(1.f, 1.f, 3.f, 14.f, 5.f, 11.f).ToString(),
+ bounds.ToString());
+}
+
+TEST(TransformOperationTest, BlendedBoundsWithZeroScale) {
+ TransformOperations zero_scale;
+ zero_scale.AppendScale(0.0, 0.0, 0.0);
+ TransformOperations non_zero_scale;
+ non_zero_scale.AppendScale(2.0, -4.0, 5.0);
+
+ gfx::BoxF box(1.f, 2.f, 3.f, 4.f, 4.f, 4.f);
+ gfx::BoxF bounds;
+
+ SkMScalar min_progress = 0.f;
+ SkMScalar max_progress = 1.f;
+ EXPECT_TRUE(zero_scale.BlendedBoundsForBox(
+ box, non_zero_scale, min_progress, max_progress, &bounds));
+ EXPECT_EQ(gfx::BoxF(0.f, -24.f, 0.f, 10.f, 24.f, 35.f).ToString(),
+ bounds.ToString());
+
+ EXPECT_TRUE(non_zero_scale.BlendedBoundsForBox(
+ box, zero_scale, min_progress, max_progress, &bounds));
+ EXPECT_EQ(gfx::BoxF(0.f, -24.f, 0.f, 10.f, 24.f, 35.f).ToString(),
+ bounds.ToString());
+
+ EXPECT_TRUE(zero_scale.BlendedBoundsForBox(
+ box, zero_scale, min_progress, max_progress, &bounds));
+ EXPECT_EQ(gfx::BoxF().ToString(), bounds.ToString());
+}
+
+TEST(TransformOperationTest, BlendedBoundsForSequence) {
+ TransformOperations operations_from;
+ operations_from.AppendTranslate(2.0, 4.0, -1.0);
+ operations_from.AppendScale(-1.0, 2.0, 3.0);
+ operations_from.AppendTranslate(1.0, -5.0, 1.0);
+ TransformOperations operations_to;
+ operations_to.AppendTranslate(6.0, -2.0, 3.0);
+ operations_to.AppendScale(-3.0, -2.0, 5.0);
+ operations_to.AppendTranslate(13.0, -1.0, 5.0);
+
+ gfx::BoxF box(1.f, 2.f, 3.f, 4.f, 4.f, 4.f);
+ gfx::BoxF bounds;
+
+ SkMScalar min_progress = -0.5f;
+ SkMScalar max_progress = 1.5f;
+ EXPECT_TRUE(operations_to.BlendedBoundsForBox(
+ box, operations_from, min_progress, max_progress, &bounds));
+ EXPECT_EQ(gfx::BoxF(-57.f, -59.f, -1.f, 76.f, 112.f, 80.f).ToString(),
+ bounds.ToString());
+
+ min_progress = 0.f;
+ max_progress = 1.f;
+ EXPECT_TRUE(operations_to.BlendedBoundsForBox(
+ box, operations_from, min_progress, max_progress, &bounds));
+ EXPECT_EQ(gfx::BoxF(-32.f, -25.f, 7.f, 42.f, 44.f, 48.f).ToString(),
+ bounds.ToString());
+
+ TransformOperations identity;
+ EXPECT_TRUE(operations_to.BlendedBoundsForBox(
+ box, identity, min_progress, max_progress, &bounds));
+ EXPECT_EQ(gfx::BoxF(-33.f, -13.f, 3.f, 57.f, 19.f, 52.f).ToString(),
+ bounds.ToString());
+
+ EXPECT_TRUE(identity.BlendedBoundsForBox(
+ box, operations_from, min_progress, max_progress, &bounds));
+ EXPECT_EQ(gfx::BoxF(-7.f, -3.f, 2.f, 15.f, 23.f, 20.f).ToString(),
+ bounds.ToString());
+}
+
} // namespace
} // namespace cc
diff --git a/chromium/cc/base/float_quad_unittest.cc b/chromium/cc/base/float_quad_unittest.cc
index bb3446c0d69..186624eec34 100644
--- a/chromium/cc/base/float_quad_unittest.cc
+++ b/chromium/cc/base/float_quad_unittest.cc
@@ -25,15 +25,15 @@ TEST(FloatQuadTest, IsRectilinearTest) {
rectilinear_trans[7].Scale(100000, 100000);
rectilinear_trans[7].Rotate(180.0);
+ gfx::QuadF original(
+ gfx::RectF(0.01010101f, 0.01010101f, 100.01010101f, 100.01010101f));
+
for (int i = 0; i < kNumRectilinear; ++i) {
bool clipped = false;
- gfx::QuadF quad = MathUtil::MapQuad(
- rectilinear_trans[i],
- gfx::QuadF(
- gfx::RectF(0.01010101f, 0.01010101f, 100.01010101f, 100.01010101f)),
- &clipped);
- ASSERT_TRUE(!clipped);
- EXPECT_TRUE(quad.IsRectilinear());
+ gfx::QuadF quad =
+ MathUtil::MapQuad(rectilinear_trans[i], original, &clipped);
+ ASSERT_TRUE(!clipped) << "case " << i;
+ EXPECT_TRUE(quad.IsRectilinear()) << "case " << i;
}
const int kNumNonRectilinear = 10;
@@ -51,13 +51,10 @@ TEST(FloatQuadTest, IsRectilinearTest) {
for (int i = 0; i < kNumNonRectilinear; ++i) {
bool clipped = false;
- gfx::QuadF quad = MathUtil::MapQuad(
- non_rectilinear_trans[i],
- gfx::QuadF(
- gfx::RectF(0.01010101f, 0.01010101f, 100.01010101f, 100.01010101f)),
- &clipped);
- ASSERT_TRUE(!clipped);
- EXPECT_FALSE(quad.IsRectilinear());
+ gfx::QuadF quad =
+ MathUtil::MapQuad(non_rectilinear_trans[i], original, &clipped);
+ ASSERT_TRUE(!clipped) << "case " << i;
+ EXPECT_FALSE(quad.IsRectilinear()) << "case " << i;
}
}
diff --git a/chromium/cc/base/math_util.cc b/chromium/cc/base/math_util.cc
index fa7b21a8c29..40b8d11c68c 100644
--- a/chromium/cc/base/math_util.cc
+++ b/chromium/cc/base/math_util.cc
@@ -108,15 +108,15 @@ gfx::Rect MathUtil::MapClippedRect(const gfx::Transform& transform,
gfx::RectF MathUtil::MapClippedRect(const gfx::Transform& transform,
const gfx::RectF& src_rect) {
- if (transform.IsIdentityOrTranslation())
+ if (transform.IsIdentityOrTranslation()) {
return src_rect +
- gfx::Vector2dF(
- static_cast<float>(transform.matrix().getDouble(0, 3)),
- static_cast<float>(transform.matrix().getDouble(1, 3)));
+ gfx::Vector2dF(SkMScalarToFloat(transform.matrix().get(0, 3)),
+ SkMScalarToFloat(transform.matrix().get(1, 3)));
+ }
// Apply the transform, but retain the result in homogeneous coordinates.
- double quad[4 * 2]; // input: 4 x 2D points
+ SkMScalar quad[4 * 2]; // input: 4 x 2D points
quad[0] = src_rect.x();
quad[1] = src_rect.y();
quad[2] = src_rect.right();
@@ -126,7 +126,7 @@ gfx::RectF MathUtil::MapClippedRect(const gfx::Transform& transform,
quad[6] = src_rect.x();
quad[7] = src_rect.bottom();
- double result[4 * 4]; // output: 4 x 4D homogeneous points
+ SkMScalar result[4 * 4]; // output: 4 x 4D homogeneous points
transform.matrix().map2(quad, 4, result);
HomogeneousCoordinate hc0(result[0], result[1], result[2], result[3]);
@@ -140,9 +140,8 @@ gfx::RectF MathUtil::ProjectClippedRect(const gfx::Transform& transform,
const gfx::RectF& src_rect) {
if (transform.IsIdentityOrTranslation()) {
return src_rect +
- gfx::Vector2dF(
- static_cast<float>(transform.matrix().getDouble(0, 3)),
- static_cast<float>(transform.matrix().getDouble(1, 3)));
+ gfx::Vector2dF(SkMScalarToFloat(transform.matrix().get(0, 3)),
+ SkMScalarToFloat(transform.matrix().get(1, 3)));
}
// Perform the projection, but retain the result in homogeneous coordinates.
@@ -330,8 +329,8 @@ gfx::QuadF MathUtil::MapQuad(const gfx::Transform& transform,
if (transform.IsIdentityOrTranslation()) {
gfx::QuadF mapped_quad(q);
mapped_quad +=
- gfx::Vector2dF(static_cast<float>(transform.matrix().getDouble(0, 3)),
- static_cast<float>(transform.matrix().getDouble(1, 3)));
+ gfx::Vector2dF(SkMScalarToFloat(transform.matrix().get(0, 3)),
+ SkMScalarToFloat(transform.matrix().get(1, 3)));
*clipped = false;
return mapped_quad;
}
@@ -446,8 +445,29 @@ gfx::PointF MathUtil::ProjectPoint(const gfx::Transform& transform,
return h.CartesianPoint2d();
}
+gfx::RectF MathUtil::ScaleRectProportional(const gfx::RectF& input_outer_rect,
+ const gfx::RectF& scale_outer_rect,
+ const gfx::RectF& scale_inner_rect) {
+ gfx::RectF output_inner_rect = input_outer_rect;
+ float scale_rect_to_input_scale_x =
+ scale_outer_rect.width() / input_outer_rect.width();
+ float scale_rect_to_input_scale_y =
+ scale_outer_rect.height() / input_outer_rect.height();
+
+ gfx::Vector2dF top_left_diff =
+ scale_inner_rect.origin() - scale_outer_rect.origin();
+ gfx::Vector2dF bottom_right_diff =
+ scale_inner_rect.bottom_right() - scale_outer_rect.bottom_right();
+ output_inner_rect.Inset(top_left_diff.x() / scale_rect_to_input_scale_x,
+ top_left_diff.y() / scale_rect_to_input_scale_y,
+ -bottom_right_diff.x() / scale_rect_to_input_scale_x,
+ -bottom_right_diff.y() / scale_rect_to_input_scale_y);
+ return output_inner_rect;
+}
+
static inline float ScaleOnAxis(double a, double b, double c) {
- return std::sqrt(a * a + b * b + c * c);
+ // Do the sqrt as a double to not lose precision.
+ return static_cast<float>(std::sqrt(a * a + b * b + c * c));
}
gfx::Vector2dF MathUtil::ComputeTransform2dScaleComponents(
diff --git a/chromium/cc/base/math_util.h b/chromium/cc/base/math_util.h
index 37f0c07602f..7912f8a8a25 100644
--- a/chromium/cc/base/math_util.h
+++ b/chromium/cc/base/math_util.h
@@ -45,7 +45,7 @@ struct HomogeneousCoordinate {
// For now, because this code is used privately only by MathUtil, it should
// never be called when w == 0, and we do not yet need to handle that case.
DCHECK(w());
- double inv_w = 1.0 / w();
+ SkMScalar inv_w = 1.0 / w();
return gfx::PointF(x() * inv_w, y() * inv_w);
}
@@ -56,7 +56,7 @@ struct HomogeneousCoordinate {
// For now, because this code is used privately only by MathUtil, it should
// never be called when w == 0, and we do not yet need to handle that case.
DCHECK(w());
- double inv_w = 1.0 / w();
+ SkMScalar inv_w = 1.0 / w();
return gfx::Point3F(x() * inv_w, y() * inv_w, z() * inv_w);
}
@@ -144,6 +144,15 @@ class CC_EXPORT MathUtil {
static gfx::Vector2dF ComputeTransform2dScaleComponents(const gfx::Transform&,
float fallbackValue);
+ // Makes a rect that has the same relationship to input_outer_rect as
+ // scale_inner_rect has to scale_outer_rect. scale_inner_rect should be
+ // contained within scale_outer_rect, and likewise the rectangle that is
+ // returned will be within input_outer_rect at a similar relative, scaled
+ // position.
+ static gfx::RectF ScaleRectProportional(const gfx::RectF& input_outer_rect,
+ const gfx::RectF& scale_outer_rect,
+ const gfx::RectF& scale_inner_rect);
+
// Returns the smallest angle between the given two vectors in degrees.
// Neither vector is assumed to be normalized.
static float SmallestAngleBetweenVectors(gfx::Vector2dF v1,
diff --git a/chromium/cc/base/scoped_ptr_hash_map.h b/chromium/cc/base/scoped_ptr_hash_map.h
deleted file mode 100644
index 47135822d2d..00000000000
--- a/chromium/cc/base/scoped_ptr_hash_map.h
+++ /dev/null
@@ -1,157 +0,0 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CC_BASE_SCOPED_PTR_HASH_MAP_H_
-#define CC_BASE_SCOPED_PTR_HASH_MAP_H_
-
-#include <algorithm>
-#include <utility>
-
-#include "base/basictypes.h"
-#include "base/containers/hash_tables.h"
-#include "base/logging.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/stl_util.h"
-
-namespace cc {
-
-// This type acts like a hash_map<K, scoped_ptr<V> >, based on top of
-// base::hash_map. The ScopedPtrHashMap has ownership of all values in the data
-// structure.
-template <typename Key, typename Value>
-class ScopedPtrHashMap {
- typedef base::hash_map<Key, Value*> Container;
-
- public:
- typedef typename Container::iterator iterator;
- typedef typename Container::const_iterator const_iterator;
-
- ScopedPtrHashMap() {}
-
- ~ScopedPtrHashMap() { clear(); }
-
- void swap(ScopedPtrHashMap<Key, Value>& other) {
- data_.swap(other.data_);
- }
-
- std::pair<iterator, bool> insert(
- std::pair<Key, const scoped_ptr<Value> > pair) {
- return data_.insert(
- std::pair<Key, Value*>(pair.first, pair.second.release()));
- }
-
- // Replaces value but not key if key is already present.
- std::pair<iterator, bool> set(Key key, scoped_ptr<Value> data) {
- iterator it = find(key);
- if (it != end())
- erase(it);
- Value* raw_ptr = data.release();
- return data_.insert(std::pair<Key, Value*>(key, raw_ptr));
- }
-
- // Does nothing if key is already present
- std::pair<iterator, bool> add(Key key, scoped_ptr<Value> data) {
- Value* raw_ptr = data.release();
- return data_.insert(std::pair<Key, Value*>(key, raw_ptr));
- }
-
- void erase(iterator it) {
- if (it->second)
- delete it->second;
- data_.erase(it);
- }
-
- size_t erase(const Key& k) {
- iterator it = data_.find(k);
- if (it == data_.end())
- return 0;
- erase(it);
- return 1;
- }
-
- scoped_ptr<Value> take(iterator it) {
- DCHECK(it != data_.end());
- if (it == data_.end())
- return scoped_ptr<Value>();
-
- Key key = it->first;
- scoped_ptr<Value> ret(it->second);
- data_.erase(it);
- data_.insert(std::pair<Key, Value*>(key, static_cast<Value*>(NULL)));
- return ret.Pass();
- }
-
- scoped_ptr<Value> take(const Key& k) {
- iterator it = find(k);
- if (it == data_.end())
- return scoped_ptr<Value>();
-
- return take(it);
- }
-
- scoped_ptr<Value> take_and_erase(iterator it) {
- DCHECK(it != data_.end());
- if (it == data_.end())
- return scoped_ptr<Value>();
-
- scoped_ptr<Value> ret(it->second);
- data_.erase(it);
- return ret.Pass();
- }
-
- scoped_ptr<Value> take_and_erase(const Key& k) {
- iterator it = find(k);
- if (it == data_.end())
- return scoped_ptr<Value>();
-
- return take_and_erase(it);
- }
-
- // Returns the first element in the hash_map that matches the given key.
- // If no such element exists it returns NULL.
- Value* get(const Key& k) const {
- const_iterator it = find(k);
- if (it == end())
- return 0;
- return it->second;
- }
-
- inline bool contains(const Key& k) const { return data_.count(k) > 0; }
-
- inline void clear() { STLDeleteValues(&data_); }
-
- inline const_iterator find(const Key& k) const { return data_.find(k); }
- inline iterator find(const Key& k) { return data_.find(k); }
-
- inline size_t count(const Key& k) const { return data_.count(k); }
- inline std::pair<const_iterator, const_iterator> equal_range(
- const Key& k) const {
- return data_.equal_range(k);
- }
- inline std::pair<iterator, iterator> equal_range(const Key& k) {
- return data_.equal_range(k);
- }
-
- inline size_t size() const { return data_.size(); }
- inline size_t max_size() const { return data_.max_size(); }
-
- inline bool empty() const { return data_.empty(); }
-
- inline size_t bucket_count() const { return data_.bucket_count(); }
- inline void resize(size_t size) const { return data_.resize(size); }
-
- inline iterator begin() { return data_.begin(); }
- inline const_iterator begin() const { return data_.begin(); }
- inline iterator end() { return data_.end(); }
- inline const_iterator end() const { return data_.end(); }
-
- private:
- Container data_;
-
- DISALLOW_COPY_AND_ASSIGN(ScopedPtrHashMap);
-};
-
-} // namespace cc
-
-#endif // CC_BASE_SCOPED_PTR_HASH_MAP_H_
diff --git a/chromium/cc/base/switches.cc b/chromium/cc/base/switches.cc
index 2dac58b6911..87e10ce0089 100644
--- a/chromium/cc/base/switches.cc
+++ b/chromium/cc/base/switches.cc
@@ -14,6 +14,9 @@ namespace switches {
const char kBackgroundColorInsteadOfCheckerboard[] =
"background-color-instead-of-checkerboard";
+// Disables LCD text.
+const char kDisableLCDText[] = "disable-lcd-text";
+
const char kDisableThreadedAnimation[] = "disable-threaded-animation";
// Disables layer-edge anti-aliasing in the compositor.
@@ -24,6 +27,9 @@ const char kDisableCompositedAntialiasing[] =
// Overrides the kEnableImplSidePainting flag.
const char kDisableImplSidePainting[] = "disable-impl-side-painting";
+// Enables LCD text.
+const char kEnableLCDText[] = "enable-lcd-text";
+
// Paint content on the compositor thread instead of the main thread.
const char kEnableImplSidePainting[] = "enable-impl-side-painting";
@@ -125,12 +131,33 @@ const char kUIShowOccludingRects[] = "ui-show-occluding-rects";
const char kShowNonOccludingRects[] = "show-nonoccluding-rects";
const char kUIShowNonOccludingRects[] = "ui-show-nonoccluding-rects";
-// Enable the codepath that uses images within TileManager.
-const char kUseMapImage[] = "use-map-image";
+// Enable rasterizer that writes directly to GPU memory.
+const char kEnableMapImage[] = "enable-map-image";
+
+// Disable rasterizer that writes directly to GPU memory.
+// Overrides the kEnableMapImage flag.
+const char kDisableMapImage[] = "disable-map-image";
// Prevents the layer tree unit tests from timing out.
const char kCCLayerTreeTestNoTimeout[] = "cc-layer-tree-test-no-timeout";
+// Disable textures using RGBA_4444 layout.
+const char kDisable4444Textures[] = "disable-4444-textures";
+
+bool IsLCDTextEnabled() {
+ const CommandLine* command_line = CommandLine::ForCurrentProcess();
+ if (command_line->HasSwitch(cc::switches::kDisableLCDText))
+ return false;
+ else if (command_line->HasSwitch(cc::switches::kEnableLCDText))
+ return true;
+
+#if defined(OS_ANDROID)
+ return false;
+#else
+ return true;
+#endif
+}
+
bool IsImplSidePaintingEnabled() {
const CommandLine& command_line = *CommandLine::ForCurrentProcess();
@@ -146,5 +173,16 @@ bool IsImplSidePaintingEnabled() {
#endif
}
+bool IsMapImageEnabled() {
+ const CommandLine& command_line = *CommandLine::ForCurrentProcess();
+
+ if (command_line.HasSwitch(cc::switches::kDisableMapImage))
+ return false;
+ else if (command_line.HasSwitch(cc::switches::kEnableMapImage))
+ return true;
+
+ return false;
+}
+
} // namespace switches
} // namespace cc
diff --git a/chromium/cc/base/switches.h b/chromium/cc/base/switches.h
index f87eb53ee16..97fe833390c 100644
--- a/chromium/cc/base/switches.h
+++ b/chromium/cc/base/switches.h
@@ -17,9 +17,11 @@ namespace switches {
// Switches for the renderer compositor only.
CC_EXPORT extern const char kBackgroundColorInsteadOfCheckerboard[];
+CC_EXPORT extern const char kDisableLCDText[];
CC_EXPORT extern const char kDisableImplSidePainting[];
CC_EXPORT extern const char kDisableThreadedAnimation[];
CC_EXPORT extern const char kDisableCompositedAntialiasing[];
+CC_EXPORT extern const char kEnableLCDText[];
CC_EXPORT extern const char kEnableImplSidePainting[];
CC_EXPORT extern const char kEnableTopControlsPositionCalculation[];
CC_EXPORT extern const char kForceDirectLayerDrawing[];
@@ -37,7 +39,9 @@ CC_EXPORT extern const char kMaxUnusedResourceMemoryUsagePercentage[];
CC_EXPORT extern const char kEnablePinchVirtualViewport[];
CC_EXPORT extern const char kEnablePartialSwap[];
CC_EXPORT extern const char kStrictLayerPropertyChangeChecking[];
-CC_EXPORT extern const char kUseMapImage[];
+CC_EXPORT extern const char kEnableMapImage[];
+CC_EXPORT extern const char kDisableMapImage[];
+CC_EXPORT extern const char kDisable4444Textures[];
// Switches for both the renderer and ui compositors.
CC_EXPORT extern const char kUIDisablePartialSwap[];
@@ -65,7 +69,9 @@ CC_EXPORT extern const char kUIShowNonOccludingRects[];
// Unit test related.
CC_EXPORT extern const char kCCLayerTreeTestNoTimeout[];
+CC_EXPORT bool IsLCDTextEnabled();
CC_EXPORT bool IsImplSidePaintingEnabled();
+CC_EXPORT bool IsMapImageEnabled();
} // namespace switches
} // namespace cc
diff --git a/chromium/cc/cc.gyp b/chromium/cc/cc.gyp
index 78060a4175c..ffc86153f29 100644
--- a/chromium/cc/cc.gyp
+++ b/chromium/cc/cc.gyp
@@ -14,11 +14,11 @@
'<(DEPTH)/base/base.gyp:base',
'<(DEPTH)/base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
'<(DEPTH)/gpu/gpu.gyp:gpu',
- '<(DEPTH)/skia/skia.gyp:skia',
'<(DEPTH)/media/media.gyp:media',
+ '<(DEPTH)/skia/skia.gyp:skia',
+ '<(DEPTH)/third_party/WebKit/public/blink.gyp:blink_minimal',
'<(DEPTH)/ui/gl/gl.gyp:gl',
'<(DEPTH)/ui/ui.gyp:ui',
- '<(DEPTH)/third_party/WebKit/public/blink.gyp:blink_minimal',
],
'defines': [
'CC_IMPLEMENTATION=1',
@@ -44,6 +44,8 @@
'animation/scrollbar_animation_controller.h',
'animation/scrollbar_animation_controller_linear_fade.cc',
'animation/scrollbar_animation_controller_linear_fade.h',
+ 'animation/scrollbar_animation_controller_thinning.cc',
+ 'animation/scrollbar_animation_controller_thinning.h',
'animation/timing_function.cc',
'animation/timing_function.h',
'animation/transform_operation.cc',
@@ -59,7 +61,6 @@
'base/region.h',
'base/scoped_ptr_algorithm.h',
'base/scoped_ptr_deque.h',
- 'base/scoped_ptr_hash_map.h',
'base/scoped_ptr_vector.h',
'base/switches.cc',
'base/switches.h',
@@ -86,6 +87,10 @@
'debug/rendering_stats_instrumentation.cc',
'debug/rendering_stats_instrumentation.h',
'debug/ring_buffer.h',
+ 'debug/test_context_provider.cc',
+ 'debug/test_context_provider.h',
+ 'debug/test_web_graphics_context_3d.cc',
+ 'debug/test_web_graphics_context_3d.h',
'debug/traced_picture.cc',
'debug/traced_picture.h',
'debug/traced_value.cc',
@@ -121,6 +126,7 @@
'layers/io_surface_layer_impl.h',
'layers/layer.cc',
'layers/layer.h',
+ 'layers/layer_client.h',
'layers/layer_impl.cc',
'layers/layer_impl.h',
'layers/layer_iterator.cc',
@@ -134,6 +140,10 @@
'layers/nine_patch_layer_impl.cc',
'layers/nine_patch_layer_impl.h',
'layers/paint_properties.h',
+ 'layers/painted_scrollbar_layer.cc',
+ 'layers/painted_scrollbar_layer.h',
+ 'layers/painted_scrollbar_layer_impl.cc',
+ 'layers/painted_scrollbar_layer_impl.h',
'layers/picture_image_layer.cc',
'layers/picture_image_layer.h',
'layers/picture_image_layer_impl.cc',
@@ -148,14 +158,17 @@
'layers/render_surface.h',
'layers/render_surface_impl.cc',
'layers/render_surface_impl.h',
- 'layers/scrollbar_layer.cc',
- 'layers/scrollbar_layer.h',
- 'layers/scrollbar_layer_impl.cc',
- 'layers/scrollbar_layer_impl.h',
+ 'layers/scrollbar_layer_impl_base.cc',
+ 'layers/scrollbar_layer_impl_base.h',
+ 'layers/scrollbar_layer_interface.h',
'layers/solid_color_layer.cc',
'layers/solid_color_layer.h',
'layers/solid_color_layer_impl.cc',
'layers/solid_color_layer_impl.h',
+ 'layers/solid_color_scrollbar_layer.cc',
+ 'layers/solid_color_scrollbar_layer.h',
+ 'layers/solid_color_scrollbar_layer_impl.cc',
+ 'layers/solid_color_scrollbar_layer_impl.h',
'layers/texture_layer.cc',
'layers/texture_layer.h',
'layers/texture_layer_client.h',
@@ -180,6 +193,7 @@
'output/compositor_frame_ack.h',
'output/compositor_frame_metadata.cc',
'output/compositor_frame_metadata.h',
+ 'output/context_provider.cc',
'output/context_provider.h',
'output/copy_output_request.cc',
'output/copy_output_request.h',
@@ -300,8 +314,11 @@
'resources/raster_mode.h',
'resources/raster_worker_pool.cc',
'resources/raster_worker_pool.h',
+ 'resources/release_callback.h',
'resources/resource.cc',
'resources/resource.h',
+ 'resources/resource_format.h',
+ 'resources/resource_format.cc',
'resources/resource_pool.cc',
'resources/resource_pool.h',
'resources/resource_provider.cc',
@@ -312,16 +329,21 @@
'resources/resource_update_controller.h',
'resources/resource_update_queue.cc',
'resources/resource_update_queue.h',
+ 'resources/returned_resource.h',
'resources/scoped_resource.cc',
'resources/scoped_resource.h',
'resources/scoped_ui_resource.cc',
'resources/scoped_ui_resource.h',
+ 'resources/single_release_callback.cc',
+ 'resources/single_release_callback.h',
'resources/skpicture_content_layer_updater.cc',
'resources/skpicture_content_layer_updater.h',
'resources/sync_point_helper.cc',
'resources/sync_point_helper.h',
'resources/texture_mailbox.cc',
'resources/texture_mailbox.h',
+ 'resources/texture_mailbox_deleter.cc',
+ 'resources/texture_mailbox_deleter.h',
'resources/tile.cc',
'resources/tile.h',
'resources/tile_manager.cc',
@@ -354,6 +376,8 @@
'scheduler/texture_uploader.cc',
'scheduler/texture_uploader.h',
'scheduler/time_source.h',
+ 'trees/blocking_task_runner.cc',
+ 'trees/blocking_task_runner.h',
'trees/damage_tracker.cc',
'trees/damage_tracker.h',
'trees/layer_sorter.cc',
diff --git a/chromium/cc/cc_tests.gyp b/chromium/cc/cc_tests.gyp
index 7e2ff87f574..f271a7074be 100644
--- a/chromium/cc/cc_tests.gyp
+++ b/chromium/cc/cc_tests.gyp
@@ -10,6 +10,7 @@
'animation/keyframed_animation_curve_unittest.cc',
'animation/layer_animation_controller_unittest.cc',
'animation/scrollbar_animation_controller_linear_fade_unittest.cc',
+ 'animation/scrollbar_animation_controller_thinning_unittest.cc',
'animation/timing_function_unittest.cc',
'animation/transform_operations_unittest.cc',
'base/float_quad_unittest.cc',
@@ -31,6 +32,7 @@
'layers/nine_patch_layer_unittest.cc',
'layers/picture_image_layer_impl_unittest.cc',
'layers/picture_layer_impl_unittest.cc',
+ 'layers/picture_layer_unittest.cc',
'layers/render_surface_unittest.cc',
'layers/scrollbar_layer_unittest.cc',
'layers/solid_color_layer_impl_unittest.cc',
@@ -59,6 +61,7 @@
'resources/resource_provider_unittest.cc',
'resources/resource_update_controller_unittest.cc',
'resources/scoped_resource_unittest.cc',
+ 'resources/texture_mailbox_deleter_unittest.cc',
'resources/tile_manager_unittest.cc',
'resources/tile_priority_unittest.cc',
'resources/video_resource_updater_unittest.cc',
@@ -100,8 +103,6 @@
'test/fake_content_layer_client.h',
'test/fake_content_layer_impl.cc',
'test/fake_content_layer_impl.h',
- 'test/fake_context_provider.cc',
- 'test/fake_context_provider.h',
'test/fake_delegated_renderer_layer.cc',
'test/fake_delegated_renderer_layer.h',
'test/fake_delegated_renderer_layer_impl.cc',
@@ -113,6 +114,8 @@
'test/fake_layer_tree_host_impl_client.cc',
'test/fake_layer_tree_host_impl_client.h',
'test/fake_layer_tree_host_impl.h',
+ 'test/fake_painted_scrollbar_layer.cc',
+ 'test/fake_painted_scrollbar_layer.h',
'test/fake_picture_layer.cc',
'test/fake_picture_layer.h',
'test/fake_picture_layer_impl.cc',
@@ -126,8 +129,6 @@
'test/fake_rendering_stats_instrumentation.h',
'test/fake_scrollbar.cc',
'test/fake_scrollbar.h',
- 'test/fake_scrollbar_layer.cc',
- 'test/fake_scrollbar_layer.h',
'test/fake_tile_manager.cc',
'test/fake_tile_manager.h',
'test/fake_tile_manager_client.h',
@@ -174,8 +175,6 @@
'test/skia_common.h',
'test/test_tile_priorities.cc',
'test/test_tile_priorities.h',
- 'test/test_web_graphics_context_3d.cc',
- 'test/test_web_graphics_context_3d.h',
'test/tiled_layer_test_common.cc',
'test/tiled_layer_test_common.h',
],
@@ -240,15 +239,18 @@
'../skia/skia.gyp:skia',
'../testing/gmock.gyp:gmock',
'../testing/gtest.gyp:gtest',
+ '../testing/perf/perf_test.gyp:*',
'../ui/ui.gyp:ui',
'cc.gyp:cc',
'cc_test_support',
],
'sources': [
+ 'resources/picture_layer_tiling_perftest.cc',
'resources/raster_worker_pool_perftest.cc',
'resources/tile_manager_perftest.cc',
'resources/worker_pool_perftest.cc',
'test/cc_test_suite.cc',
+ 'test/lap_timer.cc',
'test/run_all_unittests.cc',
'trees/layer_tree_host_perftest.cc',
],
diff --git a/chromium/cc/debug/devtools_instrumentation.h b/chromium/cc/debug/devtools_instrumentation.h
index 7a77d404454..eea9e62f02a 100644
--- a/chromium/cc/debug/devtools_instrumentation.h
+++ b/chromium/cc/debug/devtools_instrumentation.h
@@ -14,11 +14,13 @@ namespace internal {
const char kCategory[] = "cc,devtools";
const char kLayerId[] = "layerId";
const char kLayerTreeId[] = "layerTreeId";
+const char kPixelRefId[] = "pixelRefId";
+
+const char kImageDecodeTask[] = "ImageDecodeTask";
}
const char kPaintLayer[] = "PaintLayer";
const char kRasterTask[] = "RasterTask";
-const char kImageDecodeTask[] = "ImageDecodeTask";
const char kPaintSetup[] = "PaintSetup";
const char kUpdateLayer[] = "UpdateLayer";
@@ -38,6 +40,19 @@ class ScopedLayerTask {
DISALLOW_COPY_AND_ASSIGN(ScopedLayerTask);
};
+class ScopedImageDecodeTask {
+ public:
+ explicit ScopedImageDecodeTask(void* pixelRef) {
+ TRACE_EVENT_BEGIN1(internal::kCategory, internal::kImageDecodeTask,
+ internal::kPixelRefId, reinterpret_cast<uint64>(pixelRef));
+ }
+ ~ScopedImageDecodeTask() {
+ TRACE_EVENT_END0(internal::kCategory, internal::kImageDecodeTask);
+ }
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ScopedImageDecodeTask);
+};
+
class ScopedLayerTreeTask {
public:
ScopedLayerTreeTask(const char* event_name,
diff --git a/chromium/cc/debug/fake_web_graphics_context_3d.h b/chromium/cc/debug/fake_web_graphics_context_3d.h
index 3c8a6f53b76..04c983de006 100644
--- a/chromium/cc/debug/fake_web_graphics_context_3d.h
+++ b/chromium/cc/debug/fake_web_graphics_context_3d.h
@@ -67,9 +67,6 @@ class CC_EXPORT FakeWebGraphicsContext3D
WebKit::WGC3Dsizei num_attachments,
const WebKit::WGC3Denum* attachments) {}
- virtual void setMemoryAllocationChangedCallbackCHROMIUM(
- WebGraphicsMemoryAllocationChangedCallbackCHROMIUM* callback) {}
-
virtual WebKit::WebString getRequestableExtensionsCHROMIUM();
virtual void requestExtensionCHROMIUM(const char*) {}
diff --git a/chromium/cc/debug/frame_rate_counter.cc b/chromium/cc/debug/frame_rate_counter.cc
index 8a829ff711a..2f3b291fea9 100644
--- a/chromium/cc/debug/frame_rate_counter.cc
+++ b/chromium/cc/debug/frame_rate_counter.cc
@@ -44,7 +44,7 @@ base::TimeDelta FrameRateCounter::RecentFrameInterval(size_t n) const {
FrameRateCounter::FrameRateCounter(bool has_impl_thread)
: has_impl_thread_(has_impl_thread), dropped_frame_count_(0) {}
-void FrameRateCounter::SaveTimeStamp(base::TimeTicks timestamp) {
+void FrameRateCounter::SaveTimeStamp(base::TimeTicks timestamp, bool software) {
ring_buffer_.SaveToBuffer(timestamp);
// Check if frame interval can be computed.
@@ -55,16 +55,26 @@ void FrameRateCounter::SaveTimeStamp(base::TimeTicks timestamp) {
RecentFrameInterval(ring_buffer_.BufferSize() - 1);
if (has_impl_thread_ && ring_buffer_.CurrentIndex() > 0) {
- UMA_HISTOGRAM_CUSTOM_COUNTS("Renderer4.CompositorThreadImplDrawDelay",
- frame_interval_seconds.InMilliseconds(),
- 1,
- 120,
- 60);
+ if (software) {
+ UMA_HISTOGRAM_CUSTOM_COUNTS(
+ "Renderer4.SoftwareCompositorThreadImplDrawDelay",
+ frame_interval_seconds.InMilliseconds(),
+ 1,
+ 120,
+ 60);
+ } else {
+ UMA_HISTOGRAM_CUSTOM_COUNTS("Renderer4.CompositorThreadImplDrawDelay",
+ frame_interval_seconds.InMilliseconds(),
+ 1,
+ 120,
+ 60);
+ }
}
if (!IsBadFrameInterval(frame_interval_seconds) &&
frame_interval_seconds.InSecondsF() > kDroppedFrameTime)
- ++dropped_frame_count_;
+ dropped_frame_count_ +=
+ frame_interval_seconds.InSecondsF() / kDroppedFrameTime;
}
bool FrameRateCounter::IsBadFrameInterval(
diff --git a/chromium/cc/debug/frame_rate_counter.h b/chromium/cc/debug/frame_rate_counter.h
index 32b4c2c24f1..0b69b29aea5 100644
--- a/chromium/cc/debug/frame_rate_counter.h
+++ b/chromium/cc/debug/frame_rate_counter.h
@@ -22,7 +22,7 @@ class FrameRateCounter {
int dropped_frame_count() const { return dropped_frame_count_; }
size_t time_stamp_history_size() const { return ring_buffer_.BufferSize(); }
- void SaveTimeStamp(base::TimeTicks timestamp);
+ void SaveTimeStamp(base::TimeTicks timestamp, bool software);
// n = 0 returns the oldest frame interval retained in the history, while n =
// time_stamp_history_size() - 1 returns the most recent frame interval.
diff --git a/chromium/cc/debug/overdraw_metrics.cc b/chromium/cc/debug/overdraw_metrics.cc
index 5b19370fe2c..a14284d800d 100644
--- a/chromium/cc/debug/overdraw_metrics.cc
+++ b/chromium/cc/debug/overdraw_metrics.cc
@@ -147,11 +147,11 @@ void OverdrawMetrics::RecordMetrics(
}
}
-static gfx::Size DeviceViewportSize(const LayerTreeHost* host) {
+static gfx::Size DrawViewportSize(const LayerTreeHost* host) {
return host->device_viewport_size();
}
-static gfx::Size DeviceViewportSize(const LayerTreeHostImpl* host_impl) {
- return host_impl->device_viewport_size();
+static gfx::Size DrawViewportSize(const LayerTreeHostImpl* host_impl) {
+ return host_impl->DrawViewportSize();
}
template <typename LayerTreeHostType>
@@ -160,13 +160,13 @@ void OverdrawMetrics::RecordMetricsInternal(
const LayerTreeHostType* layer_tree_host) const {
// This gives approximately 10x the percentage of pixels to fill the viewport
// once.
- float normalization = 1000.f / (DeviceViewportSize(layer_tree_host).width() *
- DeviceViewportSize(layer_tree_host).height());
+ float normalization = 1000.f / (DrawViewportSize(layer_tree_host).width() *
+ DrawViewportSize(layer_tree_host).height());
// This gives approximately 100x the percentage of tiles to fill the viewport
// once, if all tiles were 256x256.
float tile_normalization =
- 10000.f / (DeviceViewportSize(layer_tree_host).width() / 256.f *
- DeviceViewportSize(layer_tree_host).height() / 256.f);
+ 10000.f / (DrawViewportSize(layer_tree_host).width() / 256.f *
+ DrawViewportSize(layer_tree_host).height() / 256.f);
// This gives approximately 10x the percentage of bytes to fill the viewport
// once, assuming 4 bytes per pixel.
float byte_normalization = normalization / 4;
diff --git a/chromium/cc/debug/rendering_stats.cc b/chromium/cc/debug/rendering_stats.cc
index 12c54530c71..64cf69e0fe3 100644
--- a/chromium/cc/debug/rendering_stats.cc
+++ b/chromium/cc/debug/rendering_stats.cc
@@ -2,98 +2,195 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/values.h"
#include "cc/debug/rendering_stats.h"
namespace cc {
-RenderingStats::RenderingStats()
- : animation_frame_count(0),
- screen_frame_count(0),
+MainThreadRenderingStats::MainThreadRenderingStats()
+ : animation_frame_count(0),
+ screen_frame_count(0),
+ commit_count(0),
+ painted_pixel_count(0),
+ recorded_pixel_count(0),
+ image_gathering_count(0) {}
+
+ImplThreadRenderingStats::ImplThreadRenderingStats()
+ : screen_frame_count(0),
dropped_frame_count(0),
- total_commit_count(0),
- total_pixels_painted(0),
- total_pixels_recorded(0),
- total_pixels_rasterized(0),
- num_impl_thread_scrolls(0),
- num_main_thread_scrolls(0),
- num_layers_drawn(0),
- num_missing_tiles(0),
- total_deferred_image_decode_count(0),
- total_deferred_image_cache_hit_count(0),
- total_image_gathering_count(0),
- total_tiles_analyzed(0),
- solid_color_tiles_analyzed(0) {}
+ rasterized_pixel_count(0),
+ impl_thread_scroll_count(0),
+ main_thread_scroll_count(0),
+ drawn_layer_count(0),
+ missing_tile_count(0),
+ deferred_image_decode_count(0),
+ deferred_image_cache_hit_count(0),
+ tile_analysis_count(0),
+ solid_color_tile_analysis_count(0) {}
void RenderingStats::EnumerateFields(Enumerator* enumerator) const {
- enumerator->AddInt64("numAnimationFrames", animation_frame_count);
- enumerator->AddInt64("numFramesSentToScreen", screen_frame_count);
- enumerator->AddInt64("droppedFrameCount", dropped_frame_count);
+ enumerator->AddInt64("numAnimationFrames",
+ main_stats.animation_frame_count);
+ enumerator->AddInt64("numFramesSentToScreen", main_stats.screen_frame_count +
+ impl_stats.screen_frame_count);
enumerator->AddDouble("totalPaintTimeInSeconds",
- total_paint_time.InSecondsF());
+ main_stats.paint_time.InSecondsF());
enumerator->AddDouble("totalRecordTimeInSeconds",
- total_record_time.InSecondsF());
+ main_stats.record_time.InSecondsF());
+ enumerator->AddDouble("totalCommitTimeInSeconds",
+ main_stats.commit_time.InSecondsF());
+ enumerator->AddInt64("totalCommitCount", main_stats.commit_count);
+ enumerator->AddInt64("totalPixelsPainted", main_stats.painted_pixel_count);
+ enumerator->AddInt64("totalPixelsRecorded", main_stats.recorded_pixel_count);
+ enumerator->AddInt64("totalImageGatheringCount",
+ main_stats.image_gathering_count);
+ enumerator->AddDouble("totalImageGatheringTimeInSeconds",
+ main_stats.image_gathering_time.InSecondsF());
+ enumerator->AddInt64("droppedFrameCount", impl_stats.dropped_frame_count);
enumerator->AddDouble("totalRasterizeTimeInSeconds",
- total_rasterize_time.InSecondsF());
+ impl_stats.rasterize_time.InSecondsF());
enumerator->AddDouble(
"totalRasterizeTimeForNowBinsOnPendingTree",
- total_rasterize_time_for_now_bins_on_pending_tree.InSecondsF());
- enumerator->AddDouble("totalCommitTimeInSeconds",
- total_commit_time.InSecondsF());
+ impl_stats.rasterize_time_for_now_bins_on_pending_tree.InSecondsF());
enumerator->AddDouble("bestRasterizeTimeInSeconds",
- best_rasterize_time.InSecondsF());
- enumerator->AddInt64("totalCommitCount", total_commit_count);
- enumerator->AddInt64("totalPixelsPainted", total_pixels_painted);
- enumerator->AddInt64("totalPixelsRecorded", total_pixels_recorded);
- enumerator->AddInt64("totalPixelsRasterized", total_pixels_rasterized);
- enumerator->AddInt64("numImplThreadScrolls", num_impl_thread_scrolls);
- enumerator->AddInt64("numMainThreadScrolls", num_main_thread_scrolls);
- enumerator->AddInt64("numLayersDrawn", num_layers_drawn);
- enumerator->AddInt64("numMissingTiles", num_missing_tiles);
+ impl_stats.best_rasterize_time.InSecondsF());
+ enumerator->AddInt64("totalPixelsRasterized",
+ impl_stats.rasterized_pixel_count);
+ enumerator->AddInt64("numImplThreadScrolls",
+ impl_stats.impl_thread_scroll_count);
+ enumerator->AddInt64("numMainThreadScrolls",
+ impl_stats.main_thread_scroll_count);
+ enumerator->AddInt64("numLayersDrawn", impl_stats.drawn_layer_count);
+ enumerator->AddInt64("numMissingTiles", impl_stats.missing_tile_count);
enumerator->AddInt64("totalDeferredImageDecodeCount",
- total_deferred_image_decode_count);
- enumerator->AddInt64("totalTilesAnalyzed", total_tiles_analyzed);
+ impl_stats.deferred_image_decode_count);
+ enumerator->AddInt64("totalTilesAnalyzed", impl_stats.tile_analysis_count);
enumerator->AddInt64("solidColorTilesAnalyzed",
- solid_color_tiles_analyzed);
+ impl_stats.solid_color_tile_analysis_count);
enumerator->AddInt64("totalDeferredImageCacheHitCount",
- total_deferred_image_cache_hit_count);
- enumerator->AddInt64("totalImageGatheringCount",
- total_image_gathering_count);
+ impl_stats.deferred_image_cache_hit_count);
enumerator->AddDouble("totalDeferredImageDecodeTimeInSeconds",
- total_deferred_image_decode_time.InSecondsF());
- enumerator->AddDouble("totalImageGatheringTimeInSeconds",
- total_image_gathering_time.InSecondsF());
+ impl_stats.deferred_image_decode_time.InSecondsF());
enumerator->AddDouble("totalTileAnalysisTimeInSeconds",
- total_tile_analysis_time.InSecondsF());
+ impl_stats.tile_analysis_time.InSecondsF());
}
-void RenderingStats::Add(const RenderingStats& other) {
+void MainThreadRenderingStats::IssueTraceEvent() const {
+ TRACE_EVENT_INSTANT1("benchmark",
+ "MainThreadRenderingStats::IssueTraceEvent",
+ TRACE_EVENT_SCOPE_THREAD,
+ "data", AsTraceableData());
+}
+
+scoped_ptr<base::debug::ConvertableToTraceFormat>
+MainThreadRenderingStats::AsTraceableData() const {
+ scoped_ptr<base::DictionaryValue> record_data(new base::DictionaryValue());
+ record_data->SetInteger("animation_frame_count",
+ animation_frame_count);
+ record_data->SetInteger("screen_frame_count",
+ screen_frame_count);
+ record_data->SetDouble("paint_time",
+ paint_time.InSecondsF());
+ record_data->SetDouble("record_time",
+ record_time.InSecondsF());
+ record_data->SetDouble("commit_time",
+ commit_time.InSecondsF());
+ record_data->SetInteger("commit_count",
+ commit_count);
+ record_data->SetInteger("painted_pixel_count",
+ painted_pixel_count);
+ record_data->SetInteger("recorded_pixel_count",
+ recorded_pixel_count);
+ record_data->SetInteger("image_gathering_count",
+ image_gathering_count);
+ record_data->SetDouble("image_gathering_time",
+ image_gathering_time.InSecondsF());
+ return TracedValue::FromValue(record_data.release());
+}
+
+void ImplThreadRenderingStats::IssueTraceEvent() const {
+ TRACE_EVENT_INSTANT1("benchmark",
+ "ImplThreadRenderingStats::IssueTraceEvent",
+ TRACE_EVENT_SCOPE_THREAD,
+ "data", AsTraceableData());
+}
+
+scoped_ptr<base::debug::ConvertableToTraceFormat>
+ImplThreadRenderingStats::AsTraceableData() const {
+ scoped_ptr<base::DictionaryValue> record_data(new base::DictionaryValue());
+ record_data->SetInteger("screen_frame_count",
+ screen_frame_count);
+ record_data->SetInteger("dropped_frame_count",
+ dropped_frame_count);
+ record_data->SetDouble("rasterize_time",
+ rasterize_time.InSecondsF());
+ record_data->SetDouble(
+ "rasterize_time_for_now_bins_on_pending_tree",
+ rasterize_time_for_now_bins_on_pending_tree.InSecondsF());
+ record_data->SetDouble("best_rasterize_time",
+ best_rasterize_time.InSecondsF());
+ record_data->SetInteger("rasterized_pixel_count",
+ rasterized_pixel_count);
+ record_data->SetInteger("impl_thread_scroll_count",
+ impl_thread_scroll_count);
+ record_data->SetInteger("main_thread_scroll_count",
+ main_thread_scroll_count);
+ record_data->SetInteger("drawn_layer_count",
+ drawn_layer_count);
+ record_data->SetInteger("missing_tile_count",
+ missing_tile_count);
+ record_data->SetInteger("deferred_image_decode_count",
+ deferred_image_decode_count);
+ record_data->SetInteger("deferred_image_cache_hit_count",
+ deferred_image_cache_hit_count);
+ record_data->SetInteger("tile_analysis_count",
+ tile_analysis_count);
+ record_data->SetInteger("solid_color_tile_analysis_count",
+ solid_color_tile_analysis_count);
+ record_data->SetDouble("deferred_image_decode_time",
+ deferred_image_decode_time.InSecondsF());
+ record_data->SetDouble("tile_analysis_time",
+ tile_analysis_time.InSecondsF());
+ return TracedValue::FromValue(record_data.release());
+}
+
+
+void MainThreadRenderingStats::Add(const MainThreadRenderingStats& other) {
animation_frame_count += other.animation_frame_count;
screen_frame_count += other.screen_frame_count;
+ paint_time += other.paint_time;
+ record_time += other.record_time;
+ commit_time += other.commit_time;
+ commit_count += other.commit_count;
+ painted_pixel_count += other.painted_pixel_count;
+ recorded_pixel_count += other.recorded_pixel_count;
+ image_gathering_count += other.image_gathering_count;
+ image_gathering_time += other.image_gathering_time;
+}
+
+void ImplThreadRenderingStats::Add(const ImplThreadRenderingStats& other) {
+ screen_frame_count += other.screen_frame_count;
dropped_frame_count += other.dropped_frame_count;
- total_paint_time += other.total_paint_time;
- total_record_time += other.total_record_time;
- total_rasterize_time += other.total_rasterize_time;
- total_rasterize_time_for_now_bins_on_pending_tree +=
- other.total_rasterize_time_for_now_bins_on_pending_tree;
- total_commit_time += other.total_commit_time;
+ rasterize_time += other.rasterize_time;
+ rasterize_time_for_now_bins_on_pending_tree +=
+ other.rasterize_time_for_now_bins_on_pending_tree;
best_rasterize_time += other.best_rasterize_time;
- total_commit_count += other.total_commit_count;
- total_pixels_painted += other.total_pixels_painted;
- total_pixels_recorded += other.total_pixels_recorded;
- total_pixels_rasterized += other.total_pixels_rasterized;
- num_impl_thread_scrolls += other.num_impl_thread_scrolls;
- num_main_thread_scrolls += other.num_main_thread_scrolls;
- num_layers_drawn += other.num_layers_drawn;
- num_missing_tiles += other.num_missing_tiles;
- total_deferred_image_decode_count += other.total_deferred_image_decode_count;
- total_deferred_image_cache_hit_count +=
- other.total_deferred_image_cache_hit_count;
- total_image_gathering_count += other.total_image_gathering_count;
- total_deferred_image_decode_time += other.total_deferred_image_decode_time;
- total_image_gathering_time += other.total_image_gathering_time;
- total_tiles_analyzed += other.total_tiles_analyzed;
- solid_color_tiles_analyzed += other.solid_color_tiles_analyzed;
- total_tile_analysis_time += other.total_tile_analysis_time;
+ rasterized_pixel_count += other.rasterized_pixel_count;
+ impl_thread_scroll_count += other.impl_thread_scroll_count;
+ main_thread_scroll_count += other.main_thread_scroll_count;
+ drawn_layer_count += other.drawn_layer_count;
+ missing_tile_count += other.missing_tile_count;
+ deferred_image_decode_count += other.deferred_image_decode_count;
+ deferred_image_cache_hit_count += other.deferred_image_cache_hit_count;
+ deferred_image_decode_time += other.deferred_image_decode_time;
+ tile_analysis_count += other.tile_analysis_count;
+ solid_color_tile_analysis_count += other.solid_color_tile_analysis_count;
+ tile_analysis_time += other.tile_analysis_time;
+}
+
+void RenderingStats::Add(const RenderingStats& other) {
+ main_stats.Add(other.main_stats);
+ impl_stats.Add(other.impl_stats);
}
} // namespace cc
diff --git a/chromium/cc/debug/rendering_stats.h b/chromium/cc/debug/rendering_stats.h
index eb01a571b11..ee13706034a 100644
--- a/chromium/cc/debug/rendering_stats.h
+++ b/chromium/cc/debug/rendering_stats.h
@@ -8,55 +8,79 @@
#include "base/basictypes.h"
#include "base/time/time.h"
#include "cc/base/cc_export.h"
+#include "cc/debug/traced_value.h"
namespace cc {
-struct CC_EXPORT RenderingStats {
+// In conjunction with EnumerateFields, this allows the embedder to
+// enumerate the values in this structure without
+// having to embed references to its specific member variables. This
+// simplifies the addition of new fields to this type.
+class RenderingStatsEnumerator {
+ public:
+ virtual void AddInt64(const char* name, int64 value) = 0;
+ virtual void AddDouble(const char* name, double value) = 0;
+ virtual void AddInt(const char* name, int value) = 0;
+ virtual void AddTimeDeltaInSecondsF(const char* name,
+ const base::TimeDelta& value) = 0;
+
+ protected:
+ virtual ~RenderingStatsEnumerator() {}
+};
+
+struct CC_EXPORT MainThreadRenderingStats {
+ // Note: when adding new members, please remember to update EnumerateFields
+ // and Add in rendering_stats.cc.
+
int64 animation_frame_count;
int64 screen_frame_count;
- int64 dropped_frame_count;
- base::TimeDelta total_paint_time;
- base::TimeDelta total_record_time;
- base::TimeDelta total_rasterize_time;
- base::TimeDelta total_rasterize_time_for_now_bins_on_pending_tree;
- base::TimeDelta total_commit_time;
- base::TimeDelta best_rasterize_time;
- int64 total_commit_count;
- int64 total_pixels_painted;
- int64 total_pixels_recorded;
- int64 total_pixels_rasterized;
- int64 num_impl_thread_scrolls;
- int64 num_main_thread_scrolls;
- int64 num_layers_drawn;
- int64 num_missing_tiles;
- int64 total_deferred_image_decode_count;
- int64 total_deferred_image_cache_hit_count;
- int64 total_image_gathering_count;
- int64 total_tiles_analyzed;
- int64 solid_color_tiles_analyzed;
- base::TimeDelta total_deferred_image_decode_time;
- base::TimeDelta total_image_gathering_time;
- base::TimeDelta total_tile_analysis_time;
+ base::TimeDelta paint_time;
+ base::TimeDelta record_time;
+ base::TimeDelta commit_time;
+ int64 commit_count;
+ int64 painted_pixel_count;
+ int64 recorded_pixel_count;
+ int64 image_gathering_count;
+ base::TimeDelta image_gathering_time;
+
+ MainThreadRenderingStats();
+ void IssueTraceEvent() const;
+ scoped_ptr<base::debug::ConvertableToTraceFormat> AsTraceableData() const;
+ void Add(const MainThreadRenderingStats& other);
+};
+
+struct CC_EXPORT ImplThreadRenderingStats {
// Note: when adding new members, please remember to update EnumerateFields
// and Add in rendering_stats.cc.
- RenderingStats();
-
- // In conjunction with EnumerateFields, this allows the embedder to
- // enumerate the values in this structure without
- // having to embed references to its specific member variables. This
- // simplifies the addition of new fields to this type.
- class Enumerator {
- public:
- virtual void AddInt64(const char* name, int64 value) = 0;
- virtual void AddDouble(const char* name, double value) = 0;
- virtual void AddInt(const char* name, int value) = 0;
- virtual void AddTimeDeltaInSecondsF(const char* name,
- const base::TimeDelta& value) = 0;
-
- protected:
- virtual ~Enumerator() {}
- };
+ int64 screen_frame_count;
+ int64 dropped_frame_count;
+ base::TimeDelta rasterize_time;
+ base::TimeDelta rasterize_time_for_now_bins_on_pending_tree;
+ base::TimeDelta best_rasterize_time;
+ int64 rasterized_pixel_count;
+ int64 impl_thread_scroll_count;
+ int64 main_thread_scroll_count;
+ int64 drawn_layer_count;
+ int64 missing_tile_count;
+ int64 deferred_image_decode_count;
+ int64 deferred_image_cache_hit_count;
+ int64 tile_analysis_count;
+ int64 solid_color_tile_analysis_count;
+ base::TimeDelta deferred_image_decode_time;
+ base::TimeDelta tile_analysis_time;
+
+ ImplThreadRenderingStats();
+ void IssueTraceEvent() const;
+ scoped_ptr<base::debug::ConvertableToTraceFormat> AsTraceableData() const;
+ void Add(const ImplThreadRenderingStats& other);
+};
+
+struct CC_EXPORT RenderingStats {
+ typedef RenderingStatsEnumerator Enumerator;
+
+ MainThreadRenderingStats main_stats;
+ ImplThreadRenderingStats impl_stats;
// Outputs the fields in this structure to the provided enumerator.
void EnumerateFields(Enumerator* enumerator) const;
diff --git a/chromium/cc/debug/rendering_stats_instrumentation.cc b/chromium/cc/debug/rendering_stats_instrumentation.cc
index b3886af51c0..3c2c96b8b00 100644
--- a/chromium/cc/debug/rendering_stats_instrumentation.cc
+++ b/chromium/cc/debug/rendering_stats_instrumentation.cc
@@ -20,19 +20,40 @@ RenderingStatsInstrumentation::~RenderingStatsInstrumentation() {}
RenderingStats RenderingStatsInstrumentation::GetRenderingStats() {
base::AutoLock scoped_lock(lock_);
- return rendering_stats_;
+ RenderingStats rendering_stats;
+ rendering_stats.main_stats = main_stats_accu_;
+ rendering_stats.main_stats.Add(main_stats_);
+ rendering_stats.impl_stats = impl_stats_accu_;
+ rendering_stats.impl_stats.Add(impl_stats_);
+ return rendering_stats;
+}
+
+void RenderingStatsInstrumentation::AccumulateAndClearMainThreadStats() {
+ main_stats_accu_.Add(main_stats_);
+ main_stats_ = MainThreadRenderingStats();
+}
+
+void RenderingStatsInstrumentation::AccumulateAndClearImplThreadStats() {
+ impl_stats_accu_.Add(impl_stats_);
+ impl_stats_ = ImplThreadRenderingStats();
}
base::TimeTicks RenderingStatsInstrumentation::StartRecording() const {
- if (record_rendering_stats_)
+ if (record_rendering_stats_) {
+ if (base::TimeTicks::IsThreadNowSupported())
+ return base::TimeTicks::ThreadNow();
return base::TimeTicks::HighResNow();
+ }
return base::TimeTicks();
}
base::TimeDelta RenderingStatsInstrumentation::EndRecording(
base::TimeTicks start_time) const {
- if (!start_time.is_null())
+ if (!start_time.is_null()) {
+ if (base::TimeTicks::IsThreadNowSupported())
+ return base::TimeTicks::ThreadNow() - start_time;
return base::TimeTicks::HighResNow() - start_time;
+ }
return base::TimeDelta();
}
@@ -41,23 +62,27 @@ void RenderingStatsInstrumentation::IncrementAnimationFrameCount() {
return;
base::AutoLock scoped_lock(lock_);
- rendering_stats_.animation_frame_count++;
+ main_stats_.animation_frame_count++;
}
-void RenderingStatsInstrumentation::SetScreenFrameCount(int64 count) {
+void RenderingStatsInstrumentation::IncrementScreenFrameCount(
+ int64 count, bool main_thread) {
if (!record_rendering_stats_)
return;
base::AutoLock scoped_lock(lock_);
- rendering_stats_.screen_frame_count = count;
+ if (main_thread)
+ main_stats_.screen_frame_count += count;
+ else
+ impl_stats_.screen_frame_count += count;
}
-void RenderingStatsInstrumentation::SetDroppedFrameCount(int64 count) {
+void RenderingStatsInstrumentation::IncrementDroppedFrameCount(int64 count) {
if (!record_rendering_stats_)
return;
base::AutoLock scoped_lock(lock_);
- rendering_stats_.dropped_frame_count = count;
+ impl_stats_.dropped_frame_count += count;
}
void RenderingStatsInstrumentation::AddCommit(base::TimeDelta duration) {
@@ -65,8 +90,8 @@ void RenderingStatsInstrumentation::AddCommit(base::TimeDelta duration) {
return;
base::AutoLock scoped_lock(lock_);
- rendering_stats_.total_commit_time += duration;
- rendering_stats_.total_commit_count++;
+ main_stats_.commit_time += duration;
+ main_stats_.commit_count++;
}
void RenderingStatsInstrumentation::AddPaint(base::TimeDelta duration,
@@ -75,8 +100,8 @@ void RenderingStatsInstrumentation::AddPaint(base::TimeDelta duration,
return;
base::AutoLock scoped_lock(lock_);
- rendering_stats_.total_paint_time += duration;
- rendering_stats_.total_pixels_painted += pixels;
+ main_stats_.paint_time += duration;
+ main_stats_.painted_pixel_count += pixels;
}
void RenderingStatsInstrumentation::AddRecord(base::TimeDelta duration,
@@ -85,8 +110,8 @@ void RenderingStatsInstrumentation::AddRecord(base::TimeDelta duration,
return;
base::AutoLock scoped_lock(lock_);
- rendering_stats_.total_record_time += duration;
- rendering_stats_.total_pixels_recorded += pixels;
+ main_stats_.record_time += duration;
+ main_stats_.recorded_pixel_count += pixels;
}
void RenderingStatsInstrumentation::AddRaster(base::TimeDelta total_duration,
@@ -97,12 +122,12 @@ void RenderingStatsInstrumentation::AddRaster(base::TimeDelta total_duration,
return;
base::AutoLock scoped_lock(lock_);
- rendering_stats_.total_rasterize_time += total_duration;
- rendering_stats_.best_rasterize_time += best_duration;
- rendering_stats_.total_pixels_rasterized += pixels;
+ impl_stats_.rasterize_time += total_duration;
+ impl_stats_.best_rasterize_time += best_duration;
+ impl_stats_.rasterized_pixel_count += pixels;
if (is_in_pending_tree_now_bin) {
- rendering_stats_.total_rasterize_time_for_now_bins_on_pending_tree +=
+ impl_stats_.rasterize_time_for_now_bins_on_pending_tree +=
total_duration;
}
}
@@ -112,7 +137,7 @@ void RenderingStatsInstrumentation::IncrementImplThreadScrolls() {
return;
base::AutoLock scoped_lock(lock_);
- rendering_stats_.num_impl_thread_scrolls++;
+ impl_stats_.impl_thread_scroll_count++;
}
void RenderingStatsInstrumentation::IncrementMainThreadScrolls() {
@@ -120,7 +145,7 @@ void RenderingStatsInstrumentation::IncrementMainThreadScrolls() {
return;
base::AutoLock scoped_lock(lock_);
- rendering_stats_.num_main_thread_scrolls++;
+ impl_stats_.main_thread_scroll_count++;
}
void RenderingStatsInstrumentation::AddLayersDrawn(int64 amount) {
@@ -128,7 +153,7 @@ void RenderingStatsInstrumentation::AddLayersDrawn(int64 amount) {
return;
base::AutoLock scoped_lock(lock_);
- rendering_stats_.num_layers_drawn += amount;
+ impl_stats_.drawn_layer_count += amount;
}
void RenderingStatsInstrumentation::AddMissingTiles(int64 amount) {
@@ -136,7 +161,7 @@ void RenderingStatsInstrumentation::AddMissingTiles(int64 amount) {
return;
base::AutoLock scoped_lock(lock_);
- rendering_stats_.num_missing_tiles += amount;
+ impl_stats_.missing_tile_count += amount;
}
void RenderingStatsInstrumentation::AddDeferredImageDecode(
@@ -145,8 +170,8 @@ void RenderingStatsInstrumentation::AddDeferredImageDecode(
return;
base::AutoLock scoped_lock(lock_);
- rendering_stats_.total_deferred_image_decode_time += duration;
- rendering_stats_.total_deferred_image_decode_count++;
+ impl_stats_.deferred_image_decode_time += duration;
+ impl_stats_.deferred_image_decode_count++;
}
void RenderingStatsInstrumentation::AddImageGathering(
@@ -155,8 +180,8 @@ void RenderingStatsInstrumentation::AddImageGathering(
return;
base::AutoLock scoped_lock(lock_);
- rendering_stats_.total_image_gathering_time += duration;
- rendering_stats_.total_image_gathering_count++;
+ main_stats_.image_gathering_time += duration;
+ main_stats_.image_gathering_count++;
}
void RenderingStatsInstrumentation::IncrementDeferredImageCacheHitCount() {
@@ -164,7 +189,7 @@ void RenderingStatsInstrumentation::IncrementDeferredImageCacheHitCount() {
return;
base::AutoLock scoped_lock(lock_);
- rendering_stats_.total_deferred_image_cache_hit_count++;
+ impl_stats_.deferred_image_cache_hit_count++;
}
void RenderingStatsInstrumentation::AddAnalysisResult(
@@ -174,10 +199,10 @@ void RenderingStatsInstrumentation::AddAnalysisResult(
return;
base::AutoLock scoped_lock(lock_);
- rendering_stats_.total_tiles_analyzed++;
- rendering_stats_.total_tile_analysis_time += duration;
+ impl_stats_.tile_analysis_count++;
+ impl_stats_.tile_analysis_time += duration;
if (is_solid_color)
- rendering_stats_.solid_color_tiles_analyzed++;
+ impl_stats_.solid_color_tile_analysis_count++;
}
} // namespace cc
diff --git a/chromium/cc/debug/rendering_stats_instrumentation.h b/chromium/cc/debug/rendering_stats_instrumentation.h
index 6f7515a9418..5f74f9e6132 100644
--- a/chromium/cc/debug/rendering_stats_instrumentation.h
+++ b/chromium/cc/debug/rendering_stats_instrumentation.h
@@ -18,8 +18,33 @@ class CC_EXPORT RenderingStatsInstrumentation {
static scoped_ptr<RenderingStatsInstrumentation> Create();
virtual ~RenderingStatsInstrumentation();
+ // Return current main thread rendering stats.
+ MainThreadRenderingStats GetMainThreadRenderingStats() {
+ return main_stats_;
+ }
+ // Return current impl thread rendering stats.
+ ImplThreadRenderingStats GetImplThreadRenderingStats() {
+ return impl_stats_;
+ }
+ // Return the accumulated, combined rendering stats.
RenderingStats GetRenderingStats();
+ // Add current main thread rendering stats to accumulator and
+ // clear current stats.
+ void AccumulateAndClearMainThreadStats();
+ // Add current impl thread rendering stats to accumulator and
+ // clear current stats.
+ void AccumulateAndClearImplThreadStats();
+
+ // Issue trace event for current main thread rendering stats.
+ void IssueTraceEventForMainThreadStats() {
+ main_stats_.IssueTraceEvent();
+ }
+ // Issue trace event for current impl thread rendering stats.
+ void IssueTraceEventForImplThreadStats() {
+ impl_stats_.IssueTraceEvent();
+ }
+
// Read and write access to the record_rendering_stats_ flag is not locked to
// improve performance. The flag is commonly turned off and hardly changes
// it's value during runtime.
@@ -33,8 +58,8 @@ class CC_EXPORT RenderingStatsInstrumentation {
base::TimeDelta EndRecording(base::TimeTicks start_time) const;
void IncrementAnimationFrameCount();
- void SetScreenFrameCount(int64 count);
- void SetDroppedFrameCount(int64 count);
+ void IncrementScreenFrameCount(int64 count, bool main_thread);
+ void IncrementDroppedFrameCount(int64 count);
void AddCommit(base::TimeDelta duration);
void AddPaint(base::TimeDelta duration, int64 pixels);
@@ -61,7 +86,11 @@ class CC_EXPORT RenderingStatsInstrumentation {
RenderingStatsInstrumentation();
private:
- RenderingStats rendering_stats_;
+ MainThreadRenderingStats main_stats_;
+ MainThreadRenderingStats main_stats_accu_;
+ ImplThreadRenderingStats impl_stats_;
+ ImplThreadRenderingStats impl_stats_accu_;
+
bool record_rendering_stats_;
base::Lock lock_;
diff --git a/chromium/cc/debug/test_context_provider.cc b/chromium/cc/debug/test_context_provider.cc
new file mode 100644
index 00000000000..d09ecd86ed9
--- /dev/null
+++ b/chromium/cc/debug/test_context_provider.cc
@@ -0,0 +1,240 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/debug/test_context_provider.h"
+
+#include <set>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/logging.h"
+#include "base/strings/string_split.h"
+#include "cc/debug/test_web_graphics_context_3d.h"
+
+namespace cc {
+
+class TestContextProvider::LostContextCallbackProxy
+ : public WebKit::WebGraphicsContext3D::WebGraphicsContextLostCallback {
+ public:
+ explicit LostContextCallbackProxy(TestContextProvider* provider)
+ : provider_(provider) {
+ provider_->context3d_->setContextLostCallback(this);
+ }
+
+ virtual ~LostContextCallbackProxy() {
+ provider_->context3d_->setContextLostCallback(NULL);
+ }
+
+ virtual void onContextLost() {
+ provider_->OnLostContext();
+ }
+
+ private:
+ TestContextProvider* provider_;
+};
+
+class TestContextProvider::SwapBuffersCompleteCallbackProxy
+ : public WebKit::WebGraphicsContext3D::
+ WebGraphicsSwapBuffersCompleteCallbackCHROMIUM {
+ public:
+ explicit SwapBuffersCompleteCallbackProxy(TestContextProvider* provider)
+ : provider_(provider) {
+ provider_->context3d_->setSwapBuffersCompleteCallbackCHROMIUM(this);
+ }
+
+ virtual ~SwapBuffersCompleteCallbackProxy() {
+ provider_->context3d_->setSwapBuffersCompleteCallbackCHROMIUM(NULL);
+ }
+
+ virtual void onSwapBuffersComplete() {
+ provider_->OnSwapBuffersComplete();
+ }
+
+ private:
+ TestContextProvider* provider_;
+};
+
+// static
+scoped_refptr<TestContextProvider> TestContextProvider::Create() {
+ return Create(TestWebGraphicsContext3D::Create().Pass());
+}
+
+// static
+scoped_refptr<TestContextProvider> TestContextProvider::Create(
+ const CreateCallback& create_callback) {
+ scoped_refptr<TestContextProvider> provider = new TestContextProvider;
+ if (!provider->InitializeOnMainThread(create_callback))
+ return NULL;
+ return provider;
+}
+
+scoped_ptr<TestWebGraphicsContext3D> ReturnScopedContext(
+ scoped_ptr<TestWebGraphicsContext3D> context) {
+ return context.Pass();
+}
+
+// static
+scoped_refptr<TestContextProvider> TestContextProvider::Create(
+ scoped_ptr<TestWebGraphicsContext3D> context) {
+ return Create(base::Bind(&ReturnScopedContext, base::Passed(&context)));
+}
+
+TestContextProvider::TestContextProvider()
+ : bound_(false),
+ destroyed_(false) {
+ DCHECK(main_thread_checker_.CalledOnValidThread());
+ context_thread_checker_.DetachFromThread();
+}
+
+TestContextProvider::~TestContextProvider() {
+ DCHECK(main_thread_checker_.CalledOnValidThread() ||
+ context_thread_checker_.CalledOnValidThread());
+}
+
+bool TestContextProvider::InitializeOnMainThread(
+ const CreateCallback& create_callback) {
+ DCHECK(main_thread_checker_.CalledOnValidThread());
+
+ DCHECK(!context3d_);
+ DCHECK(!create_callback.is_null());
+ context3d_ = create_callback.Run();
+ return context3d_;
+}
+
+bool TestContextProvider::BindToCurrentThread() {
+ DCHECK(context3d_);
+
+ // This is called on the thread the context will be used.
+ DCHECK(context_thread_checker_.CalledOnValidThread());
+
+ if (bound_)
+ return true;
+
+ bound_ = true;
+ if (!context3d_->makeContextCurrent()) {
+ base::AutoLock lock(destroyed_lock_);
+ destroyed_ = true;
+ return false;
+ }
+
+ lost_context_callback_proxy_.reset(new LostContextCallbackProxy(this));
+ swap_buffers_complete_callback_proxy_.reset(
+ new SwapBuffersCompleteCallbackProxy(this));
+
+ return true;
+}
+
+ContextProvider::Capabilities TestContextProvider::ContextCapabilities() {
+ DCHECK(context3d_);
+ DCHECK(bound_);
+ DCHECK(context_thread_checker_.CalledOnValidThread());
+
+ return context3d_->test_capabilities();
+}
+
+WebKit::WebGraphicsContext3D* TestContextProvider::Context3d() {
+ DCHECK(context3d_);
+ DCHECK(bound_);
+ DCHECK(context_thread_checker_.CalledOnValidThread());
+
+ return context3d_.get();
+}
+
+class GrContext* TestContextProvider::GrContext() {
+ DCHECK(context3d_);
+ DCHECK(bound_);
+ DCHECK(context_thread_checker_.CalledOnValidThread());
+
+ // TODO(danakj): Make a test GrContext that works with a test Context3d.
+ return NULL;
+}
+
+void TestContextProvider::VerifyContexts() {
+ DCHECK(context3d_);
+ DCHECK(bound_);
+ DCHECK(context_thread_checker_.CalledOnValidThread());
+
+ if (context3d_->isContextLost()) {
+ base::AutoLock lock(destroyed_lock_);
+ destroyed_ = true;
+ }
+}
+
+bool TestContextProvider::DestroyedOnMainThread() {
+ DCHECK(main_thread_checker_.CalledOnValidThread());
+
+ base::AutoLock lock(destroyed_lock_);
+ return destroyed_;
+}
+
+void TestContextProvider::OnLostContext() {
+ DCHECK(context_thread_checker_.CalledOnValidThread());
+ {
+ base::AutoLock lock(destroyed_lock_);
+ if (destroyed_)
+ return;
+ destroyed_ = true;
+ }
+ if (!lost_context_callback_.is_null())
+ base::ResetAndReturn(&lost_context_callback_).Run();
+}
+
+void TestContextProvider::OnSwapBuffersComplete() {
+ DCHECK(context_thread_checker_.CalledOnValidThread());
+ if (!swap_buffers_complete_callback_.is_null())
+ swap_buffers_complete_callback_.Run();
+}
+
+TestWebGraphicsContext3D* TestContextProvider::TestContext3d() {
+ DCHECK(context3d_);
+ DCHECK(bound_);
+ DCHECK(context_thread_checker_.CalledOnValidThread());
+
+ return context3d_.get();
+}
+
+TestWebGraphicsContext3D* TestContextProvider::UnboundTestContext3d() {
+ DCHECK(context3d_);
+ DCHECK(context_thread_checker_.CalledOnValidThread());
+
+ return context3d_.get();
+}
+
+void TestContextProvider::SetMemoryAllocation(
+ const ManagedMemoryPolicy& policy,
+ bool discard_backbuffer_when_not_visible) {
+ if (memory_policy_changed_callback_.is_null())
+ return;
+ memory_policy_changed_callback_.Run(
+ policy, discard_backbuffer_when_not_visible);
+}
+
+void TestContextProvider::SetLostContextCallback(
+ const LostContextCallback& cb) {
+ DCHECK(context_thread_checker_.CalledOnValidThread());
+ DCHECK(lost_context_callback_.is_null() || cb.is_null());
+ lost_context_callback_ = cb;
+}
+
+void TestContextProvider::SetSwapBuffersCompleteCallback(
+ const SwapBuffersCompleteCallback& cb) {
+ DCHECK(context_thread_checker_.CalledOnValidThread());
+ DCHECK(swap_buffers_complete_callback_.is_null() || cb.is_null());
+ swap_buffers_complete_callback_ = cb;
+}
+
+void TestContextProvider::SetMemoryPolicyChangedCallback(
+ const MemoryPolicyChangedCallback& cb) {
+ DCHECK(context_thread_checker_.CalledOnValidThread());
+ DCHECK(memory_policy_changed_callback_.is_null() || cb.is_null());
+ memory_policy_changed_callback_ = cb;
+}
+
+void TestContextProvider::SetMaxTransferBufferUsageBytes(
+ size_t max_transfer_buffer_usage_bytes) {
+ context3d_->SetMaxTransferBufferUsageBytes(max_transfer_buffer_usage_bytes);
+}
+
+} // namespace cc
diff --git a/chromium/cc/debug/test_context_provider.h b/chromium/cc/debug/test_context_provider.h
new file mode 100644
index 00000000000..0401fcf1ba8
--- /dev/null
+++ b/chromium/cc/debug/test_context_provider.h
@@ -0,0 +1,90 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_DEBUG_TEST_CONTEXT_PROVIDER_H_
+#define CC_DEBUG_TEST_CONTEXT_PROVIDER_H_
+
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/thread_checker.h"
+#include "cc/base/cc_export.h"
+#include "cc/output/context_provider.h"
+
+namespace WebKit { class WebGraphicsContext3D; }
+
+namespace cc {
+class TestWebGraphicsContext3D;
+
+class CC_EXPORT TestContextProvider
+ : public NON_EXPORTED_BASE(cc::ContextProvider) {
+ public:
+ typedef base::Callback<scoped_ptr<TestWebGraphicsContext3D>(void)>
+ CreateCallback;
+
+ static scoped_refptr<TestContextProvider> Create();
+ static scoped_refptr<TestContextProvider> Create(
+ const CreateCallback& create_callback);
+ static scoped_refptr<TestContextProvider> Create(
+ scoped_ptr<TestWebGraphicsContext3D> context);
+
+ virtual bool BindToCurrentThread() OVERRIDE;
+ virtual Capabilities ContextCapabilities() OVERRIDE;
+ virtual WebKit::WebGraphicsContext3D* Context3d() OVERRIDE;
+ virtual class GrContext* GrContext() OVERRIDE;
+ virtual void VerifyContexts() OVERRIDE;
+ virtual bool DestroyedOnMainThread() OVERRIDE;
+ virtual void SetLostContextCallback(const LostContextCallback& cb) OVERRIDE;
+ virtual void SetSwapBuffersCompleteCallback(
+ const SwapBuffersCompleteCallback& cb) OVERRIDE;
+ virtual void SetMemoryPolicyChangedCallback(
+ const MemoryPolicyChangedCallback& cb) OVERRIDE;
+
+ TestWebGraphicsContext3D* TestContext3d();
+
+ // This returns the TestWebGraphicsContext3D but is valid to call
+ // before the context is bound to a thread. This is needed to set up
+ // state on the test context before binding. Don't call
+ // makeContextCurrent on the context returned from this method.
+ TestWebGraphicsContext3D* UnboundTestContext3d();
+
+ void SetMemoryAllocation(const ManagedMemoryPolicy& policy,
+ bool discard_backbuffer_when_not_visible);
+
+ void SetMaxTransferBufferUsageBytes(size_t max_transfer_buffer_usage_bytes);
+
+ protected:
+ TestContextProvider();
+ virtual ~TestContextProvider();
+
+ bool InitializeOnMainThread(const CreateCallback& create_callback);
+
+ void OnLostContext();
+ void OnSwapBuffersComplete();
+
+ scoped_ptr<TestWebGraphicsContext3D> context3d_;
+ bool bound_;
+
+ base::ThreadChecker main_thread_checker_;
+ base::ThreadChecker context_thread_checker_;
+
+ base::Lock destroyed_lock_;
+ bool destroyed_;
+
+ LostContextCallback lost_context_callback_;
+ SwapBuffersCompleteCallback swap_buffers_complete_callback_;
+ MemoryPolicyChangedCallback memory_policy_changed_callback_;
+
+ class LostContextCallbackProxy;
+ scoped_ptr<LostContextCallbackProxy> lost_context_callback_proxy_;
+
+ class SwapBuffersCompleteCallbackProxy;
+ scoped_ptr<SwapBuffersCompleteCallbackProxy>
+ swap_buffers_complete_callback_proxy_;
+};
+
+} // namespace cc
+
+#endif // CC_DEBUG_TEST_CONTEXT_PROVIDER_H_
+
diff --git a/chromium/cc/debug/test_web_graphics_context_3d.cc b/chromium/cc/debug/test_web_graphics_context_3d.cc
new file mode 100644
index 00000000000..634778fc4e6
--- /dev/null
+++ b/chromium/cc/debug/test_web_graphics_context_3d.cc
@@ -0,0 +1,632 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/debug/test_web_graphics_context_3d.h"
+
+#include <algorithm>
+#include <string>
+
+#include "base/bind.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "gpu/GLES2/gl2extchromium.h"
+#include "third_party/khronos/GLES2/gl2ext.h"
+
+using WebKit::WGC3Dboolean;
+using WebKit::WGC3Dchar;
+using WebKit::WGC3Denum;
+using WebKit::WGC3Dint;
+using WebKit::WGC3Dsizei;
+using WebKit::WGC3Dsizeiptr;
+using WebKit::WGC3Duint;
+using WebKit::WebGLId;
+using WebKit::WebGraphicsContext3D;
+
+namespace cc {
+
+static const WebGLId kFramebufferId = 1;
+static const WebGLId kProgramId = 2;
+static const WebGLId kRenderbufferId = 3;
+static const WebGLId kShaderId = 4;
+
+static unsigned s_context_id = 1;
+
+const WebGLId TestWebGraphicsContext3D::kExternalTextureId = 1337;
+
+static base::LazyInstance<base::Lock>::Leaky
+ g_shared_namespace_lock = LAZY_INSTANCE_INITIALIZER;
+
+TestWebGraphicsContext3D::Namespace*
+ TestWebGraphicsContext3D::shared_namespace_ = NULL;
+
+TestWebGraphicsContext3D::Namespace::Namespace()
+ : next_buffer_id(1),
+ next_image_id(1),
+ next_texture_id(1) {
+}
+
+TestWebGraphicsContext3D::Namespace::~Namespace() {
+ g_shared_namespace_lock.Get().AssertAcquired();
+ if (shared_namespace_ == this)
+ shared_namespace_ = NULL;
+}
+
+// static
+scoped_ptr<TestWebGraphicsContext3D> TestWebGraphicsContext3D::Create() {
+ return make_scoped_ptr(new TestWebGraphicsContext3D());
+}
+
+TestWebGraphicsContext3D::TestWebGraphicsContext3D()
+ : FakeWebGraphicsContext3D(),
+ context_id_(s_context_id++),
+ times_make_current_succeeds_(-1),
+ times_bind_texture_succeeds_(-1),
+ times_end_query_succeeds_(-1),
+ times_gen_mailbox_succeeds_(-1),
+ context_lost_(false),
+ times_map_image_chromium_succeeds_(-1),
+ times_map_buffer_chromium_succeeds_(-1),
+ context_lost_callback_(NULL),
+ swap_buffers_callback_(NULL),
+ max_texture_size_(2048),
+ width_(0),
+ height_(0),
+ bound_buffer_(0),
+ weak_ptr_factory_(this) {
+ CreateNamespace();
+ test_capabilities_.swapbuffers_complete_callback = true;
+}
+
+TestWebGraphicsContext3D::TestWebGraphicsContext3D(
+ const WebGraphicsContext3D::Attributes& attributes)
+ : FakeWebGraphicsContext3D(),
+ context_id_(s_context_id++),
+ attributes_(attributes),
+ times_make_current_succeeds_(-1),
+ times_bind_texture_succeeds_(-1),
+ times_end_query_succeeds_(-1),
+ times_gen_mailbox_succeeds_(-1),
+ context_lost_(false),
+ times_map_image_chromium_succeeds_(-1),
+ times_map_buffer_chromium_succeeds_(-1),
+ context_lost_callback_(NULL),
+ swap_buffers_callback_(NULL),
+ max_texture_size_(2048),
+ width_(0),
+ height_(0),
+ bound_buffer_(0),
+ weak_ptr_factory_(this) {
+ CreateNamespace();
+ test_capabilities_.swapbuffers_complete_callback = true;
+}
+
+void TestWebGraphicsContext3D::CreateNamespace() {
+ if (attributes_.shareResources) {
+ base::AutoLock lock(g_shared_namespace_lock.Get());
+ if (shared_namespace_) {
+ namespace_ = shared_namespace_;
+ } else {
+ namespace_ = new Namespace;
+ shared_namespace_ = namespace_.get();
+ }
+ } else {
+ namespace_ = new Namespace;
+ }
+}
+
+TestWebGraphicsContext3D::~TestWebGraphicsContext3D() {
+ for (size_t i = 0; i < sync_point_callbacks_.size(); ++i) {
+ if (sync_point_callbacks_[i] != NULL)
+ delete sync_point_callbacks_[i];
+ }
+ base::AutoLock lock(g_shared_namespace_lock.Get());
+ namespace_ = NULL;
+}
+
+bool TestWebGraphicsContext3D::makeContextCurrent() {
+ if (times_make_current_succeeds_ >= 0) {
+ if (!times_make_current_succeeds_) {
+ loseContextCHROMIUM(GL_GUILTY_CONTEXT_RESET_ARB,
+ GL_INNOCENT_CONTEXT_RESET_ARB);
+ }
+ --times_make_current_succeeds_;
+ }
+ return !context_lost_;
+}
+
+int TestWebGraphicsContext3D::width() {
+ return width_;
+}
+
+int TestWebGraphicsContext3D::height() {
+ return height_;
+}
+
+void TestWebGraphicsContext3D::reshapeWithScaleFactor(
+ int width, int height, float scale_factor) {
+ width_ = width;
+ height_ = height;
+}
+
+bool TestWebGraphicsContext3D::isContextLost() {
+ return context_lost_;
+}
+
+WGC3Denum TestWebGraphicsContext3D::getGraphicsResetStatusARB() {
+ return context_lost_ ? GL_UNKNOWN_CONTEXT_RESET_ARB : GL_NO_ERROR;
+}
+
+WGC3Denum TestWebGraphicsContext3D::checkFramebufferStatus(
+ WGC3Denum target) {
+ if (context_lost_)
+ return GL_FRAMEBUFFER_UNDEFINED_OES;
+ return GL_FRAMEBUFFER_COMPLETE;
+}
+
+WebGraphicsContext3D::Attributes
+ TestWebGraphicsContext3D::getContextAttributes() {
+ return attributes_;
+}
+
+WebKit::WebString TestWebGraphicsContext3D::getString(WGC3Denum name) {
+ return WebKit::WebString();
+}
+
+WGC3Dint TestWebGraphicsContext3D::getUniformLocation(
+ WebGLId program,
+ const WGC3Dchar* name) {
+ return 0;
+}
+
+WGC3Dsizeiptr TestWebGraphicsContext3D::getVertexAttribOffset(
+ WGC3Duint index,
+ WGC3Denum pname) {
+ return 0;
+}
+
+WGC3Dboolean TestWebGraphicsContext3D::isBuffer(
+ WebGLId buffer) {
+ return false;
+}
+
+WGC3Dboolean TestWebGraphicsContext3D::isEnabled(
+ WGC3Denum cap) {
+ return false;
+}
+
+WGC3Dboolean TestWebGraphicsContext3D::isFramebuffer(
+ WebGLId framebuffer) {
+ return false;
+}
+
+WGC3Dboolean TestWebGraphicsContext3D::isProgram(
+ WebGLId program) {
+ return false;
+}
+
+WGC3Dboolean TestWebGraphicsContext3D::isRenderbuffer(
+ WebGLId renderbuffer) {
+ return false;
+}
+
+WGC3Dboolean TestWebGraphicsContext3D::isShader(
+ WebGLId shader) {
+ return false;
+}
+
+WGC3Dboolean TestWebGraphicsContext3D::isTexture(
+ WebGLId texture) {
+ return false;
+}
+
+WebGLId TestWebGraphicsContext3D::createBuffer() {
+ return NextBufferId();
+}
+
+void TestWebGraphicsContext3D::deleteBuffer(WebGLId id) {
+ base::AutoLock lock(namespace_->lock);
+ unsigned context_id = id >> 17;
+ unsigned buffer_id = id & 0x1ffff;
+ DCHECK(buffer_id && buffer_id < namespace_->next_buffer_id);
+ DCHECK_EQ(context_id, context_id_);
+}
+
+WebGLId TestWebGraphicsContext3D::createFramebuffer() {
+ return kFramebufferId | context_id_ << 16;
+}
+
+void TestWebGraphicsContext3D::deleteFramebuffer(WebGLId id) {
+ DCHECK_EQ(kFramebufferId | context_id_ << 16, id);
+}
+
+WebGLId TestWebGraphicsContext3D::createProgram() {
+ return kProgramId | context_id_ << 16;
+}
+
+void TestWebGraphicsContext3D::deleteProgram(WebGLId id) {
+ DCHECK_EQ(kProgramId | context_id_ << 16, id);
+}
+
+WebGLId TestWebGraphicsContext3D::createRenderbuffer() {
+ return kRenderbufferId | context_id_ << 16;
+}
+
+void TestWebGraphicsContext3D::deleteRenderbuffer(WebGLId id) {
+ DCHECK_EQ(kRenderbufferId | context_id_ << 16, id);
+}
+
+WebGLId TestWebGraphicsContext3D::createShader(WGC3Denum) {
+ return kShaderId | context_id_ << 16;
+}
+
+void TestWebGraphicsContext3D::deleteShader(WebGLId id) {
+ DCHECK_EQ(kShaderId | context_id_ << 16, id);
+}
+
+WebGLId TestWebGraphicsContext3D::createTexture() {
+ WebGLId texture_id = NextTextureId();
+ DCHECK_NE(texture_id, kExternalTextureId);
+ base::AutoLock lock(namespace_->lock);
+ namespace_->textures.push_back(texture_id);
+ return texture_id;
+}
+
+void TestWebGraphicsContext3D::deleteTexture(WebGLId texture_id) {
+ base::AutoLock lock(namespace_->lock);
+ std::vector<WebKit::WebGLId>& textures = namespace_->textures;
+ DCHECK(std::find(textures.begin(), textures.end(), texture_id) !=
+ textures.end());
+ textures.erase(std::find(textures.begin(), textures.end(), texture_id));
+}
+
+void TestWebGraphicsContext3D::attachShader(WebGLId program, WebGLId shader) {
+ DCHECK_EQ(kProgramId | context_id_ << 16, program);
+ DCHECK_EQ(kShaderId | context_id_ << 16, shader);
+}
+
+void TestWebGraphicsContext3D::useProgram(WebGLId program) {
+ if (!program)
+ return;
+ DCHECK_EQ(kProgramId | context_id_ << 16, program);
+}
+
+void TestWebGraphicsContext3D::bindFramebuffer(
+ WGC3Denum target, WebGLId framebuffer) {
+ if (!framebuffer)
+ return;
+ DCHECK_EQ(kFramebufferId | context_id_ << 16, framebuffer);
+}
+
+void TestWebGraphicsContext3D::bindRenderbuffer(
+ WGC3Denum target, WebGLId renderbuffer) {
+ if (!renderbuffer)
+ return;
+ DCHECK_EQ(kRenderbufferId | context_id_ << 16, renderbuffer);
+}
+
+void TestWebGraphicsContext3D::bindTexture(
+ WGC3Denum target, WebGLId texture_id) {
+ if (times_bind_texture_succeeds_ >= 0) {
+ if (!times_bind_texture_succeeds_) {
+ loseContextCHROMIUM(GL_GUILTY_CONTEXT_RESET_ARB,
+ GL_INNOCENT_CONTEXT_RESET_ARB);
+ }
+ --times_bind_texture_succeeds_;
+ }
+
+ if (!texture_id)
+ return;
+ if (texture_id == kExternalTextureId)
+ return;
+ base::AutoLock lock(namespace_->lock);
+ std::vector<WebKit::WebGLId>& textures = namespace_->textures;
+ DCHECK(std::find(textures.begin(), textures.end(), texture_id) !=
+ textures.end());
+ used_textures_.insert(texture_id);
+}
+
+void TestWebGraphicsContext3D::endQueryEXT(WGC3Denum target) {
+ if (times_end_query_succeeds_ >= 0) {
+ if (!times_end_query_succeeds_) {
+ loseContextCHROMIUM(GL_GUILTY_CONTEXT_RESET_ARB,
+ GL_INNOCENT_CONTEXT_RESET_ARB);
+ }
+ --times_end_query_succeeds_;
+ }
+}
+
+void TestWebGraphicsContext3D::getQueryObjectuivEXT(
+ WebGLId query,
+ WGC3Denum pname,
+ WGC3Duint* params) {
+ // If the context is lost, behave as if result is available.
+ if (pname == GL_QUERY_RESULT_AVAILABLE_EXT)
+ *params = 1;
+}
+
+void TestWebGraphicsContext3D::getIntegerv(
+ WGC3Denum pname,
+ WebKit::WGC3Dint* value) {
+ if (pname == GL_MAX_TEXTURE_SIZE)
+ *value = max_texture_size_;
+ else if (pname == GL_ACTIVE_TEXTURE)
+ *value = GL_TEXTURE0;
+}
+
+void TestWebGraphicsContext3D::genMailboxCHROMIUM(WebKit::WGC3Dbyte* mailbox) {
+ if (times_gen_mailbox_succeeds_ >= 0) {
+ if (!times_gen_mailbox_succeeds_) {
+ loseContextCHROMIUM(GL_GUILTY_CONTEXT_RESET_ARB,
+ GL_INNOCENT_CONTEXT_RESET_ARB);
+ }
+ --times_gen_mailbox_succeeds_;
+ }
+ if (context_lost_) {
+ memset(mailbox, 0, 64);
+ return;
+ }
+
+ static char mailbox_name1 = '1';
+ static char mailbox_name2 = '1';
+ mailbox[0] = mailbox_name1;
+ mailbox[1] = mailbox_name2;
+ mailbox[2] = '\0';
+ if (++mailbox_name1 == 0) {
+ mailbox_name1 = '1';
+ ++mailbox_name2;
+ }
+}
+
+void TestWebGraphicsContext3D::setContextLostCallback(
+ WebGraphicsContextLostCallback* callback) {
+ context_lost_callback_ = callback;
+}
+
+void TestWebGraphicsContext3D::loseContextCHROMIUM(WGC3Denum current,
+ WGC3Denum other) {
+ if (context_lost_)
+ return;
+ context_lost_ = true;
+ if (context_lost_callback_)
+ context_lost_callback_->onContextLost();
+
+ for (size_t i = 0; i < shared_contexts_.size(); ++i)
+ shared_contexts_[i]->loseContextCHROMIUM(current, other);
+ shared_contexts_.clear();
+}
+
+void TestWebGraphicsContext3D::signalSyncPoint(
+ unsigned sync_point,
+ WebGraphicsSyncPointCallback* callback) {
+ sync_point_callbacks_.push_back(callback);
+}
+
+void TestWebGraphicsContext3D::signalQuery(
+ WebKit::WebGLId query,
+ WebGraphicsSyncPointCallback* callback) {
+ sync_point_callbacks_.push_back(callback);
+}
+
+void TestWebGraphicsContext3D::setSwapBuffersCompleteCallbackCHROMIUM(
+ WebGraphicsSwapBuffersCompleteCallbackCHROMIUM* callback) {
+ if (test_capabilities_.swapbuffers_complete_callback)
+ swap_buffers_callback_ = callback;
+}
+
+void TestWebGraphicsContext3D::prepareTexture() {
+ if (swap_buffers_callback_) {
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&TestWebGraphicsContext3D::SwapBuffersComplete,
+ weak_ptr_factory_.GetWeakPtr()));
+ }
+ CallAllSyncPointCallbacks();
+}
+
+void TestWebGraphicsContext3D::finish() {
+ CallAllSyncPointCallbacks();
+}
+
+void TestWebGraphicsContext3D::flush() {
+ CallAllSyncPointCallbacks();
+}
+
+static void CallAndDestroy(
+ WebKit::WebGraphicsContext3D::WebGraphicsSyncPointCallback* callback) {
+ if (!callback)
+ return;
+ callback->onSyncPointReached();
+ delete callback;
+}
+
+void TestWebGraphicsContext3D::CallAllSyncPointCallbacks() {
+ for (size_t i = 0; i < sync_point_callbacks_.size(); ++i) {
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&CallAndDestroy,
+ sync_point_callbacks_[i]));
+ }
+ sync_point_callbacks_.clear();
+}
+
+void TestWebGraphicsContext3D::SwapBuffersComplete() {
+ if (swap_buffers_callback_)
+ swap_buffers_callback_->onSwapBuffersComplete();
+}
+
+void TestWebGraphicsContext3D::bindBuffer(WebKit::WGC3Denum target,
+ WebKit::WebGLId buffer) {
+ bound_buffer_ = buffer;
+ if (!bound_buffer_)
+ return;
+ unsigned context_id = buffer >> 17;
+ unsigned buffer_id = buffer & 0x1ffff;
+ base::AutoLock lock(namespace_->lock);
+ DCHECK(buffer_id && buffer_id < namespace_->next_buffer_id);
+ DCHECK_EQ(context_id, context_id_);
+
+ base::ScopedPtrHashMap<unsigned, Buffer>& buffers = namespace_->buffers;
+ if (buffers.count(bound_buffer_) == 0)
+ buffers.set(bound_buffer_, make_scoped_ptr(new Buffer).Pass());
+
+ buffers.get(bound_buffer_)->target = target;
+}
+
+void TestWebGraphicsContext3D::bufferData(WebKit::WGC3Denum target,
+ WebKit::WGC3Dsizeiptr size,
+ const void* data,
+ WebKit::WGC3Denum usage) {
+ base::AutoLock lock(namespace_->lock);
+ base::ScopedPtrHashMap<unsigned, Buffer>& buffers = namespace_->buffers;
+ DCHECK_GT(buffers.count(bound_buffer_), 0u);
+ DCHECK_EQ(target, buffers.get(bound_buffer_)->target);
+ Buffer* buffer = buffers.get(bound_buffer_);
+ if (context_lost_) {
+ buffer->pixels.reset();
+ return;
+ }
+
+ buffer->pixels.reset(new uint8[size]);
+ buffer->size = size;
+ if (data != NULL)
+ memcpy(buffer->pixels.get(), data, size);
+}
+
+void* TestWebGraphicsContext3D::mapBufferCHROMIUM(WebKit::WGC3Denum target,
+ WebKit::WGC3Denum access) {
+ base::AutoLock lock(namespace_->lock);
+ base::ScopedPtrHashMap<unsigned, Buffer>& buffers = namespace_->buffers;
+ DCHECK_GT(buffers.count(bound_buffer_), 0u);
+ DCHECK_EQ(target, buffers.get(bound_buffer_)->target);
+ if (times_map_buffer_chromium_succeeds_ >= 0) {
+ if (!times_map_buffer_chromium_succeeds_) {
+ return NULL;
+ }
+ --times_map_buffer_chromium_succeeds_;
+ }
+ return buffers.get(bound_buffer_)->pixels.get();
+}
+
+WebKit::WGC3Dboolean TestWebGraphicsContext3D::unmapBufferCHROMIUM(
+ WebKit::WGC3Denum target) {
+ base::AutoLock lock(namespace_->lock);
+ base::ScopedPtrHashMap<unsigned, Buffer>& buffers = namespace_->buffers;
+ DCHECK_GT(buffers.count(bound_buffer_), 0u);
+ DCHECK_EQ(target, buffers.get(bound_buffer_)->target);
+ buffers.get(bound_buffer_)->pixels.reset();
+ return true;
+}
+
+WebKit::WGC3Duint TestWebGraphicsContext3D::createImageCHROMIUM(
+ WebKit::WGC3Dsizei width, WebKit::WGC3Dsizei height,
+ WebKit::WGC3Denum internalformat) {
+ DCHECK_EQ(GL_RGBA8_OES, static_cast<int>(internalformat));
+ WebKit::WGC3Duint image_id = NextImageId();
+ base::AutoLock lock(namespace_->lock);
+ base::ScopedPtrHashMap<unsigned, Image>& images = namespace_->images;
+ images.set(image_id, make_scoped_ptr(new Image).Pass());
+ images.get(image_id)->pixels.reset(new uint8[width * height * 4]);
+ return image_id;
+}
+
+void TestWebGraphicsContext3D::destroyImageCHROMIUM(
+ WebKit::WGC3Duint id) {
+ base::AutoLock lock(namespace_->lock);
+ unsigned context_id = id >> 17;
+ unsigned image_id = id & 0x1ffff;
+ DCHECK(image_id && image_id < namespace_->next_image_id);
+ DCHECK_EQ(context_id, context_id_);
+}
+
+void TestWebGraphicsContext3D::getImageParameterivCHROMIUM(
+ WebKit::WGC3Duint image_id,
+ WebKit::WGC3Denum pname,
+ WebKit::WGC3Dint* params) {
+ base::AutoLock lock(namespace_->lock);
+ DCHECK_GT(namespace_->images.count(image_id), 0u);
+ DCHECK_EQ(GL_IMAGE_ROWBYTES_CHROMIUM, static_cast<int>(pname));
+ *params = 0;
+}
+
+void* TestWebGraphicsContext3D::mapImageCHROMIUM(WebKit::WGC3Duint image_id,
+ WebKit::WGC3Denum access) {
+ base::AutoLock lock(namespace_->lock);
+ base::ScopedPtrHashMap<unsigned, Image>& images = namespace_->images;
+ DCHECK_GT(images.count(image_id), 0u);
+ if (times_map_image_chromium_succeeds_ >= 0) {
+ if (!times_map_image_chromium_succeeds_) {
+ return NULL;
+ }
+ --times_map_image_chromium_succeeds_;
+ }
+ return images.get(image_id)->pixels.get();
+}
+
+void TestWebGraphicsContext3D::unmapImageCHROMIUM(
+ WebKit::WGC3Duint image_id) {
+ base::AutoLock lock(namespace_->lock);
+ DCHECK_GT(namespace_->images.count(image_id), 0u);
+}
+
+size_t TestWebGraphicsContext3D::NumTextures() const {
+ base::AutoLock lock(namespace_->lock);
+ return namespace_->textures.size();
+}
+
+WebKit::WebGLId TestWebGraphicsContext3D::TextureAt(int i) const {
+ base::AutoLock lock(namespace_->lock);
+ return namespace_->textures[i];
+}
+
+WebGLId TestWebGraphicsContext3D::NextTextureId() {
+ base::AutoLock lock(namespace_->lock);
+ WebGLId texture_id = namespace_->next_texture_id++;
+ DCHECK(texture_id < (1 << 16));
+ texture_id |= context_id_ << 16;
+ return texture_id;
+}
+
+WebGLId TestWebGraphicsContext3D::NextBufferId() {
+ base::AutoLock lock(namespace_->lock);
+ WebGLId buffer_id = namespace_->next_buffer_id++;
+ DCHECK(buffer_id < (1 << 17));
+ buffer_id |= context_id_ << 17;
+ return buffer_id;
+}
+
+WebKit::WGC3Duint TestWebGraphicsContext3D::NextImageId() {
+ base::AutoLock lock(namespace_->lock);
+ WGC3Duint image_id = namespace_->next_image_id++;
+ DCHECK(image_id < (1 << 17));
+ image_id |= context_id_ << 17;
+ return image_id;
+}
+
+size_t TestWebGraphicsContext3D::GetTransferBufferMemoryUsedBytes() const {
+ size_t total_bytes = 0;
+ base::ScopedPtrHashMap<unsigned, Buffer>& buffers = namespace_->buffers;
+ base::ScopedPtrHashMap<unsigned, Buffer>::iterator it = buffers.begin();
+ for (; it != buffers.end(); ++it) {
+ Buffer* buffer = it->second;
+ if (buffer->target == GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM)
+ total_bytes += buffer->size;
+ }
+ return total_bytes;
+}
+
+void TestWebGraphicsContext3D::SetMaxTransferBufferUsageBytes(
+ size_t max_transfer_buffer_usage_bytes) {
+ test_capabilities_.max_transfer_buffer_usage_bytes =
+ max_transfer_buffer_usage_bytes;
+}
+
+TestWebGraphicsContext3D::Buffer::Buffer() : target(0), size(0) {}
+
+TestWebGraphicsContext3D::Buffer::~Buffer() {}
+
+TestWebGraphicsContext3D::Image::Image() {}
+
+TestWebGraphicsContext3D::Image::~Image() {}
+
+} // namespace cc
diff --git a/chromium/cc/debug/test_web_graphics_context_3d.h b/chromium/cc/debug/test_web_graphics_context_3d.h
new file mode 100644
index 00000000000..4db270c4dd6
--- /dev/null
+++ b/chromium/cc/debug/test_web_graphics_context_3d.h
@@ -0,0 +1,292 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_DEBUG_TEST_WEB_GRAPHICS_CONTEXT_3D_H_
+#define CC_DEBUG_TEST_WEB_GRAPHICS_CONTEXT_3D_H_
+
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/containers/hash_tables.h"
+#include "base/containers/scoped_ptr_hash_map.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/stl_util.h"
+#include "base/synchronization/lock.h"
+#include "cc/base/cc_export.h"
+#include "cc/debug/fake_web_graphics_context_3d.h"
+#include "cc/output/context_provider.h"
+#include "third_party/khronos/GLES2/gl2.h"
+
+namespace cc {
+
+class CC_EXPORT TestWebGraphicsContext3D : public FakeWebGraphicsContext3D {
+ public:
+ static scoped_ptr<TestWebGraphicsContext3D> Create();
+
+ virtual ~TestWebGraphicsContext3D();
+
+ virtual bool makeContextCurrent();
+
+ virtual int width();
+ virtual int height();
+
+ virtual void reshapeWithScaleFactor(
+ int width, int height, float scale_factor);
+
+ virtual bool isContextLost();
+ virtual WebKit::WGC3Denum getGraphicsResetStatusARB();
+
+ virtual void attachShader(WebKit::WebGLId program, WebKit::WebGLId shader);
+ virtual void bindFramebuffer(
+ WebKit::WGC3Denum target, WebKit::WebGLId framebuffer);
+ virtual void bindRenderbuffer(
+ WebKit::WGC3Denum target, WebKit::WebGLId renderbuffer);
+ virtual void bindTexture(
+ WebKit::WGC3Denum target,
+ WebKit::WebGLId texture_id);
+
+ virtual WebKit::WGC3Denum checkFramebufferStatus(WebKit::WGC3Denum target);
+
+ virtual Attributes getContextAttributes();
+
+ virtual WebKit::WebString getString(WebKit::WGC3Denum name);
+ virtual WebKit::WGC3Dint getUniformLocation(
+ WebKit::WebGLId program,
+ const WebKit::WGC3Dchar* name);
+ virtual WebKit::WGC3Dsizeiptr getVertexAttribOffset(
+ WebKit::WGC3Duint index,
+ WebKit::WGC3Denum pname);
+
+ virtual WebKit::WGC3Dboolean isBuffer(WebKit::WebGLId buffer);
+ virtual WebKit::WGC3Dboolean isEnabled(WebKit::WGC3Denum cap);
+ virtual WebKit::WGC3Dboolean isFramebuffer(WebKit::WebGLId framebuffer);
+ virtual WebKit::WGC3Dboolean isProgram(WebKit::WebGLId program);
+ virtual WebKit::WGC3Dboolean isRenderbuffer(WebKit::WebGLId renderbuffer);
+ virtual WebKit::WGC3Dboolean isShader(WebKit::WebGLId shader);
+ virtual WebKit::WGC3Dboolean isTexture(WebKit::WebGLId texture);
+
+ virtual void useProgram(WebKit::WebGLId program);
+
+ virtual WebKit::WebGLId createBuffer();
+ virtual WebKit::WebGLId createFramebuffer();
+ virtual WebKit::WebGLId createProgram();
+ virtual WebKit::WebGLId createRenderbuffer();
+ virtual WebKit::WebGLId createShader(WebKit::WGC3Denum);
+ virtual WebKit::WebGLId createTexture();
+
+ virtual void deleteBuffer(WebKit::WebGLId id);
+ virtual void deleteFramebuffer(WebKit::WebGLId id);
+ virtual void deleteProgram(WebKit::WebGLId id);
+ virtual void deleteRenderbuffer(WebKit::WebGLId id);
+ virtual void deleteShader(WebKit::WebGLId id);
+ virtual void deleteTexture(WebKit::WebGLId texture_id);
+
+ virtual void endQueryEXT(WebKit::WGC3Denum target);
+ virtual void getQueryObjectuivEXT(
+ WebKit::WebGLId query,
+ WebKit::WGC3Denum pname,
+ WebKit::WGC3Duint* params);
+
+ virtual void getIntegerv(
+ WebKit::WGC3Denum pname,
+ WebKit::WGC3Dint* value);
+
+ virtual void genMailboxCHROMIUM(WebKit::WGC3Dbyte* mailbox);
+ virtual void produceTextureCHROMIUM(WebKit::WGC3Denum target,
+ const WebKit::WGC3Dbyte* mailbox) { }
+ virtual void consumeTextureCHROMIUM(WebKit::WGC3Denum target,
+ const WebKit::WGC3Dbyte* mailbox) { }
+
+ virtual void setContextLostCallback(
+ WebGraphicsContextLostCallback* callback);
+
+ virtual void loseContextCHROMIUM(WebKit::WGC3Denum current,
+ WebKit::WGC3Denum other);
+
+ // Takes ownership of the |callback|.
+ virtual void signalSyncPoint(unsigned sync_point,
+ WebGraphicsSyncPointCallback* callback);
+ virtual void signalQuery(WebKit::WebGLId query,
+ WebGraphicsSyncPointCallback* callback);
+
+ virtual void setSwapBuffersCompleteCallbackCHROMIUM(
+ WebGraphicsSwapBuffersCompleteCallbackCHROMIUM* callback);
+
+ virtual void prepareTexture();
+ virtual void finish();
+ virtual void flush();
+
+ virtual void bindBuffer(WebKit::WGC3Denum target, WebKit::WebGLId buffer);
+ virtual void bufferData(WebKit::WGC3Denum target,
+ WebKit::WGC3Dsizeiptr size,
+ const void* data,
+ WebKit::WGC3Denum usage);
+ virtual void* mapBufferCHROMIUM(WebKit::WGC3Denum target,
+ WebKit::WGC3Denum access);
+ virtual WebKit::WGC3Dboolean unmapBufferCHROMIUM(WebKit::WGC3Denum target);
+
+ virtual WebKit::WGC3Duint createImageCHROMIUM(
+ WebKit::WGC3Dsizei width,
+ WebKit::WGC3Dsizei height,
+ WebKit::WGC3Denum internalformat);
+ virtual void destroyImageCHROMIUM(WebKit::WGC3Duint image_id);
+ virtual void getImageParameterivCHROMIUM(
+ WebKit::WGC3Duint image_id,
+ WebKit::WGC3Denum pname,
+ WebKit::WGC3Dint* params);
+ virtual void* mapImageCHROMIUM(
+ WebKit::WGC3Duint image_id,
+ WebKit::WGC3Denum access);
+ virtual void unmapImageCHROMIUM(WebKit::WGC3Duint image_id);
+
+ const ContextProvider::Capabilities& test_capabilities() const {
+ return test_capabilities_;
+ }
+
+ // When set, MakeCurrent() will fail after this many times.
+ void set_times_make_current_succeeds(int times) {
+ times_make_current_succeeds_ = times;
+ }
+ void set_times_bind_texture_succeeds(int times) {
+ times_bind_texture_succeeds_ = times;
+ }
+ void set_times_end_query_succeeds(int times) {
+ times_end_query_succeeds_ = times;
+ }
+ void set_times_gen_mailbox_succeeds(int times) {
+ times_gen_mailbox_succeeds_ = times;
+ }
+
+ // When set, mapImageCHROMIUM and mapBufferCHROMIUM will return NULL after
+ // this many times.
+ void set_times_map_image_chromium_succeeds(int times) {
+ times_map_image_chromium_succeeds_ = times;
+ }
+ void set_times_map_buffer_chromium_succeeds(int times) {
+ times_map_buffer_chromium_succeeds_ = times;
+ }
+
+ size_t NumTextures() const;
+ WebKit::WebGLId TextureAt(int i) const;
+
+ size_t NumUsedTextures() const { return used_textures_.size(); }
+ bool UsedTexture(int texture) const {
+ return ContainsKey(used_textures_, texture);
+ }
+ void ResetUsedTextures() { used_textures_.clear(); }
+
+ void set_support_swapbuffers_complete_callback(bool support) {
+ test_capabilities_.swapbuffers_complete_callback = support;
+ }
+ void set_have_extension_io_surface(bool have) {
+ test_capabilities_.iosurface = have;
+ }
+ void set_have_extension_egl_image(bool have) {
+ test_capabilities_.egl_image_external = have;
+ }
+ void set_have_post_sub_buffer(bool have) {
+ test_capabilities_.post_sub_buffer = have;
+ }
+ void set_have_discard_framebuffer(bool have) {
+ test_capabilities_.discard_framebuffer = have;
+ }
+
+ // When this context is lost, all contexts in its share group are also lost.
+ void add_share_group_context(WebKit::WebGraphicsContext3D* context3d) {
+ shared_contexts_.push_back(context3d);
+ }
+
+ void set_max_texture_size(int size) { max_texture_size_ = size; }
+
+ static const WebKit::WebGLId kExternalTextureId;
+ virtual WebKit::WebGLId NextTextureId();
+
+ virtual WebKit::WebGLId NextBufferId();
+
+ virtual WebKit::WebGLId NextImageId();
+
+ size_t GetTransferBufferMemoryUsedBytes() const;
+ void SetMaxTransferBufferUsageBytes(size_t max_transfer_buffer_usage_bytes);
+
+ protected:
+ struct Buffer {
+ Buffer();
+ ~Buffer();
+
+ WebKit::WGC3Denum target;
+ scoped_ptr<uint8[]> pixels;
+ size_t size;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Buffer);
+ };
+
+ struct Image {
+ Image();
+ ~Image();
+
+ scoped_ptr<uint8[]> pixels;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Image);
+ };
+
+ struct Namespace : public base::RefCountedThreadSafe<Namespace> {
+ Namespace();
+
+ // Protects all fields.
+ base::Lock lock;
+ unsigned next_buffer_id;
+ unsigned next_image_id;
+ unsigned next_texture_id;
+ std::vector<WebKit::WebGLId> textures;
+ base::ScopedPtrHashMap<unsigned, Buffer> buffers;
+ base::ScopedPtrHashMap<unsigned, Image> images;
+
+ private:
+ friend class base::RefCountedThreadSafe<Namespace>;
+ ~Namespace();
+ DISALLOW_COPY_AND_ASSIGN(Namespace);
+ };
+
+ TestWebGraphicsContext3D();
+ TestWebGraphicsContext3D(
+ const WebKit::WebGraphicsContext3D::Attributes& attributes);
+
+ void CallAllSyncPointCallbacks();
+ void SwapBuffersComplete();
+ void CreateNamespace();
+
+ unsigned context_id_;
+ Attributes attributes_;
+ ContextProvider::Capabilities test_capabilities_;
+ int times_make_current_succeeds_;
+ int times_bind_texture_succeeds_;
+ int times_end_query_succeeds_;
+ int times_gen_mailbox_succeeds_;
+ bool context_lost_;
+ int times_map_image_chromium_succeeds_;
+ int times_map_buffer_chromium_succeeds_;
+ WebGraphicsContextLostCallback* context_lost_callback_;
+ WebGraphicsSwapBuffersCompleteCallbackCHROMIUM* swap_buffers_callback_;
+ std::vector<WebGraphicsSyncPointCallback*> sync_point_callbacks_;
+ base::hash_set<WebKit::WebGLId> used_textures_;
+ std::vector<WebKit::WebGraphicsContext3D*> shared_contexts_;
+ int max_texture_size_;
+ int width_;
+ int height_;
+
+ unsigned bound_buffer_;
+
+ scoped_refptr<Namespace> namespace_;
+ static Namespace* shared_namespace_;
+
+ base::WeakPtrFactory<TestWebGraphicsContext3D> weak_ptr_factory_;
+};
+
+} // namespace cc
+
+#endif // CC_DEBUG_TEST_WEB_GRAPHICS_CONTEXT_3D_H_
diff --git a/chromium/cc/debug/traced_picture.cc b/chromium/cc/debug/traced_picture.cc
index 7f04d0d783f..0cc2148f5f0 100644
--- a/chromium/cc/debug/traced_picture.cc
+++ b/chromium/cc/debug/traced_picture.cc
@@ -12,7 +12,8 @@
namespace cc {
TracedPicture::TracedPicture(scoped_refptr<Picture> picture)
- : picture_(picture) {
+ : picture_(picture),
+ is_alias_(false) {
}
TracedPicture::~TracedPicture() {
@@ -25,7 +26,34 @@ scoped_ptr<base::debug::ConvertableToTraceFormat>
return result.PassAs<base::debug::ConvertableToTraceFormat>();
}
+scoped_ptr<base::debug::ConvertableToTraceFormat>
+ TracedPicture::AsTraceablePictureAlias(Picture* original) {
+ TracedPicture* ptr = new TracedPicture(original);
+ ptr->is_alias_ = true;
+ scoped_ptr<TracedPicture> result(ptr);
+ return result.PassAs<base::debug::ConvertableToTraceFormat>();
+}
+
void TracedPicture::AppendAsTraceFormat(std::string* out) const {
+ if (is_alias_)
+ AppendPictureAlias(out);
+ else
+ AppendPicture(out);
+}
+
+void TracedPicture::AppendPictureAlias(std::string* out) const {
+ scoped_ptr<base::DictionaryValue> alias(new base::DictionaryValue());
+ alias->SetString("id_ref", base::StringPrintf("%p", picture_.get()));
+
+ scoped_ptr<base::DictionaryValue> res(new base::DictionaryValue());
+ res->Set("alias", alias.release());
+
+ std::string tmp;
+ base::JSONWriter::Write(res.get(), &tmp);
+ out->append(tmp);
+}
+
+void TracedPicture::AppendPicture(std::string* out) const {
scoped_ptr<base::Value> value = picture_->AsValue();
std::string tmp;
base::JSONWriter::Write(value.get(), &tmp);
diff --git a/chromium/cc/debug/traced_picture.h b/chromium/cc/debug/traced_picture.h
index 9137d8f830e..50511f2034a 100644
--- a/chromium/cc/debug/traced_picture.h
+++ b/chromium/cc/debug/traced_picture.h
@@ -22,10 +22,17 @@ class TracedPicture : public base::debug::ConvertableToTraceFormat {
static scoped_ptr<base::debug::ConvertableToTraceFormat>
AsTraceablePicture(Picture* picture);
+ static scoped_ptr<base::debug::ConvertableToTraceFormat>
+ AsTraceablePictureAlias(Picture* original);
+
virtual void AppendAsTraceFormat(std::string* out) const OVERRIDE;
private:
+ void AppendPicture(std::string* out) const;
+ void AppendPictureAlias(std::string* out) const;
+
scoped_refptr<Picture> picture_;
+ bool is_alias_;
DISALLOW_COPY_AND_ASSIGN(TracedPicture);
};
diff --git a/chromium/cc/input/input_handler.h b/chromium/cc/input/input_handler.h
index db265617fe8..d28935f0a39 100644
--- a/chromium/cc/input/input_handler.h
+++ b/chromium/cc/input/input_handler.h
@@ -117,7 +117,6 @@ class CC_EXPORT InputHandler {
virtual void StartPageScaleAnimation(gfx::Vector2d target_offset,
bool anchor_point,
float page_scale,
- base::TimeTicks start_time,
base::TimeDelta duration) = 0;
// Request another callback to InputHandlerClient::Animate().
diff --git a/chromium/cc/input/page_scale_animation.cc b/chromium/cc/input/page_scale_animation.cc
index a535c87ad97..2b7d84c1f54 100644
--- a/chromium/cc/input/page_scale_animation.cc
+++ b/chromium/cc/input/page_scale_animation.cc
@@ -45,13 +45,11 @@ scoped_ptr<PageScaleAnimation> PageScaleAnimation::Create(
float start_page_scale_factor,
gfx::SizeF viewport_size,
gfx::SizeF root_layer_size,
- double start_time,
scoped_ptr<TimingFunction> timing_function) {
return make_scoped_ptr(new PageScaleAnimation(start_scroll_offset,
start_page_scale_factor,
viewport_size,
root_layer_size,
- start_time,
timing_function.Pass()));
}
@@ -60,7 +58,6 @@ PageScaleAnimation::PageScaleAnimation(
float start_page_scale_factor,
gfx::SizeF viewport_size,
gfx::SizeF root_layer_size,
- double start_time,
scoped_ptr<TimingFunction> timing_function)
: start_page_scale_factor_(start_page_scale_factor),
target_page_scale_factor_(0.f),
@@ -69,7 +66,7 @@ PageScaleAnimation::PageScaleAnimation(
target_anchor_(),
viewport_size_(viewport_size),
root_layer_size_(root_layer_size),
- start_time_(start_time),
+ start_time_(-1.0),
duration_(0.0),
timing_function_(timing_function.Pass()) {}
@@ -165,19 +162,32 @@ gfx::SizeF PageScaleAnimation::ViewportSizeAt(float interp) const {
return gfx::ScaleSize(viewport_size_, 1.f / PageScaleFactorAt(interp));
}
+bool PageScaleAnimation::IsAnimationStarted() const {
+ return start_time_ >= 0;
+}
+
+void PageScaleAnimation::StartAnimation(double time) {
+ DCHECK_GT(0, start_time_);
+ start_time_ = time;
+}
+
gfx::Vector2dF PageScaleAnimation::ScrollOffsetAtTime(double time) const {
+ DCHECK_GE(start_time_, 0);
return ScrollOffsetAt(InterpAtTime(time));
}
float PageScaleAnimation::PageScaleFactorAtTime(double time) const {
+ DCHECK_GE(start_time_, 0);
return PageScaleFactorAt(InterpAtTime(time));
}
bool PageScaleAnimation::IsAnimationCompleteAtTime(double time) const {
+ DCHECK_GE(start_time_, 0);
return time >= end_time();
}
float PageScaleAnimation::InterpAtTime(double time) const {
+ DCHECK_GE(start_time_, 0);
DCHECK_GE(time, start_time_);
if (IsAnimationCompleteAtTime(time))
return 1.f;
diff --git a/chromium/cc/input/page_scale_animation.h b/chromium/cc/input/page_scale_animation.h
index b6d35104153..4c30cdcbc52 100644
--- a/chromium/cc/input/page_scale_animation.h
+++ b/chromium/cc/input/page_scale_animation.h
@@ -29,7 +29,6 @@ class PageScaleAnimation {
float start_page_scale_factor,
gfx::SizeF viewport_size,
gfx::SizeF root_layer_size,
- double start_time,
scoped_ptr<TimingFunction> timing_function);
~PageScaleAnimation();
@@ -50,6 +49,11 @@ class PageScaleAnimation {
float target_page_scale_factor,
double duration);
+ // These should be called before the first frame of animation to initialize
+ // the start time. StartAnimation should only be called once after creation.
+ bool IsAnimationStarted() const;
+ void StartAnimation(double time);
+
// Call these functions while the animation is in progress to output the
// current state.
gfx::Vector2dF ScrollOffsetAtTime(double time) const;
@@ -69,7 +73,6 @@ class PageScaleAnimation {
float start_page_scale_factor,
gfx::SizeF viewport_size,
gfx::SizeF root_layer_size,
- double start_time,
scoped_ptr<TimingFunction> timing_function);
private:
diff --git a/chromium/cc/input/scrollbar.h b/chromium/cc/input/scrollbar.h
index 4c7e33837f6..bfefd430213 100644
--- a/chromium/cc/input/scrollbar.h
+++ b/chromium/cc/input/scrollbar.h
@@ -24,6 +24,7 @@ class Scrollbar {
virtual ~Scrollbar() {}
virtual ScrollbarOrientation Orientation() const = 0;
+ virtual bool IsLeftSideVerticalScrollbar() const = 0;
virtual gfx::Point Location() const = 0;
virtual bool IsOverlay() const = 0;
virtual bool HasThumb() const = 0;
diff --git a/chromium/cc/layers/compositing_reasons.h b/chromium/cc/layers/compositing_reasons.h
index 28c2f7b4dc6..02354a03283 100644
--- a/chromium/cc/layers/compositing_reasons.h
+++ b/chromium/cc/layers/compositing_reasons.h
@@ -22,7 +22,6 @@ const uint64 kCompositingReasonFilters = GG_UINT64_C(1) << 7;
const uint64 kCompositingReasonPositionFixed = GG_UINT64_C(1) << 8;
const uint64 kCompositingReasonPositionSticky = GG_UINT64_C(1) << 9;
const uint64 kCompositingReasonOverflowScrollingTouch = GG_UINT64_C(1) << 10;
-const uint64 kCompositingReasonBlending = GG_UINT64_C(1) << 11;
const uint64 kCompositingReasonAssumedOverlap = GG_UINT64_C(1) << 12;
const uint64 kCompositingReasonOverlap = GG_UINT64_C(1) << 13;
const uint64 kCompositingReasonNegativeZIndexChildren = GG_UINT64_C(1) << 14;
@@ -52,6 +51,8 @@ const uint64 kCompositingReasonLayerForScrollingContainer =
const uint64 kCompositingReasonLayerForForeground = GG_UINT64_C(1) << 29;
const uint64 kCompositingReasonLayerForBackground = GG_UINT64_C(1) << 30;
const uint64 kCompositingReasonLayerForMask = GG_UINT64_C(1) << 31;
+const uint64 kCompositingReasonOverflowScrollingParent = GG_UINT64_C(1) << 32;
+const uint64 kCompositingReasonOutOfFlowClipping = GG_UINT64_C(1) << 33;
typedef uint64 CompositingReasons;
diff --git a/chromium/cc/layers/content_layer.cc b/chromium/cc/layers/content_layer.cc
index 2053ecbeac1..3d611eeef28 100644
--- a/chromium/cc/layers/content_layer.cc
+++ b/chromium/cc/layers/content_layer.cc
@@ -29,6 +29,12 @@ void ContentLayerPainter::Paint(SkCanvas* canvas,
base::TimeTicks paint_start = base::TimeTicks::HighResNow();
client_->PaintContents(canvas, content_rect, opaque);
base::TimeTicks paint_end = base::TimeTicks::HighResNow();
+ // The start and end times might be the same if the paint was very fast or if
+ // our timer granularity is poor. Treat this as a very short time duration
+ // instead of none to avoid dividing by zero.
+ if (paint_end == paint_start)
+ paint_end += base::TimeDelta::FromMicroseconds(1);
+
double pixels_per_sec = (content_rect.width() * content_rect.height()) /
(paint_end - paint_start).InSecondsF();
UMA_HISTOGRAM_CUSTOM_COUNTS("Renderer4.AccelContentPaintDurationMS",
@@ -121,9 +127,8 @@ void ContentLayer::CreateUpdaterIfNeeded() {
}
updater_->SetOpaque(contents_opaque());
- unsigned texture_format =
- layer_tree_host()->GetRendererCapabilities().best_texture_format;
- SetTextureFormat(texture_format);
+ SetTextureFormat(
+ layer_tree_host()->GetRendererCapabilities().best_texture_format);
}
void ContentLayer::SetContentsOpaque(bool opaque) {
@@ -145,4 +150,19 @@ bool ContentLayer::SupportsLCDText() const {
return true;
}
+skia::RefPtr<SkPicture> ContentLayer::GetPicture() const {
+ if (!DrawsContent())
+ return skia::RefPtr<SkPicture>();
+
+ int width = bounds().width();
+ int height = bounds().height();
+ gfx::RectF opaque;
+
+ skia::RefPtr<SkPicture> picture = skia::AdoptRef(new SkPicture);
+ SkCanvas* canvas = picture->beginRecording(width, height);
+ client_->PaintContents(canvas, gfx::Rect(width, height), &opaque);
+ picture->endRecording();
+ return picture;
+}
+
} // namespace cc
diff --git a/chromium/cc/layers/content_layer.h b/chromium/cc/layers/content_layer.h
index f49215b6b40..a076c31dcac 100644
--- a/chromium/cc/layers/content_layer.h
+++ b/chromium/cc/layers/content_layer.h
@@ -52,6 +52,8 @@ class CC_EXPORT ContentLayer : public TiledLayer {
virtual bool SupportsLCDText() const OVERRIDE;
+ virtual skia::RefPtr<SkPicture> GetPicture() const OVERRIDE;
+
protected:
explicit ContentLayer(ContentLayerClient* client);
virtual ~ContentLayer();
diff --git a/chromium/cc/layers/delegated_renderer_layer.cc b/chromium/cc/layers/delegated_renderer_layer.cc
index 66064d9e849..cc49c753e25 100644
--- a/chromium/cc/layers/delegated_renderer_layer.cc
+++ b/chromium/cc/layers/delegated_renderer_layer.cc
@@ -7,6 +7,9 @@
#include "cc/layers/delegated_renderer_layer_client.h"
#include "cc/layers/delegated_renderer_layer_impl.h"
#include "cc/output/delegated_frame_data.h"
+#include "cc/quads/render_pass_draw_quad.h"
+#include "cc/trees/blocking_task_runner.h"
+#include "cc/trees/layer_tree_host.h"
namespace cc {
@@ -19,7 +22,10 @@ scoped_refptr<DelegatedRendererLayer> DelegatedRendererLayer::Create(
DelegatedRendererLayer::DelegatedRendererLayer(
DelegatedRendererLayerClient* client)
: Layer(),
- client_(client) {}
+ client_(client),
+ needs_filter_context_(false),
+ main_thread_runner_(BlockingTaskRunner::current()),
+ weak_ptrs_(this) {}
DelegatedRendererLayer::~DelegatedRendererLayer() {}
@@ -29,6 +35,26 @@ scoped_ptr<LayerImpl> DelegatedRendererLayer::CreateLayerImpl(
tree_impl, layer_id_).PassAs<LayerImpl>();
}
+void DelegatedRendererLayer::SetLayerTreeHost(LayerTreeHost* host) {
+ if (layer_tree_host() == host) {
+ Layer::SetLayerTreeHost(host);
+ return;
+ }
+
+ if (!host) {
+ // The active frame needs to be removed from the active tree and resources
+ // returned before the commit is called complete.
+ // TODO(danakj): Don't need to do this if the last frame commited was empty
+ // or we never commited a frame with resources.
+ SetNextCommitWaitsForActivation();
+ } else {
+ if (needs_filter_context_)
+ host->set_needs_filter_context();
+ }
+
+ Layer::SetLayerTreeHost(host);
+}
+
bool DelegatedRendererLayer::DrawsContent() const {
return Layer::DrawsContent() && !frame_size_.IsEmpty();
}
@@ -41,19 +67,23 @@ void DelegatedRendererLayer::PushPropertiesTo(LayerImpl* impl) {
delegated_impl->SetDisplaySize(display_size_);
+ delegated_impl->CreateChildIdIfNeeded(
+ base::Bind(&DelegatedRendererLayer::ReceiveUnusedResourcesOnImplThread,
+ main_thread_runner_,
+ weak_ptrs_.GetWeakPtr()));
+
if (frame_data_)
delegated_impl->SetFrameData(frame_data_.Pass(), damage_in_frame_);
frame_data_.reset();
damage_in_frame_ = gfx::RectF();
- delegated_impl->CollectUnusedResources(
- &unused_resources_for_child_compositor_);
-
+ // The ResourceProvider will have the new frame as soon as we push it to the
+ // pending tree. So unused resources will be returned as well.
if (client_)
client_->DidCommitFrameData();
- // TODO(danakj): TakeUnusedResourcesForChildCompositor requires a push
- // properties to happen in order to collect unused resources returned
+ // TODO(danakj): The DidCommitFrameData() notification requires a push
+ // properties to happen in order to notify about unused resources returned
// from the parent compositor. crbug.com/259090
needs_push_properties_ = true;
}
@@ -67,48 +97,74 @@ void DelegatedRendererLayer::SetDisplaySize(gfx::Size size) {
void DelegatedRendererLayer::SetFrameData(
scoped_ptr<DelegatedFrameData> new_frame_data) {
+ DCHECK(new_frame_data);
+
if (frame_data_) {
- // Copy the resources from the last provided frame into the new frame, as
- // it may use resources that were transferred in the last frame.
- new_frame_data->resource_list.insert(new_frame_data->resource_list.end(),
- frame_data_->resource_list.begin(),
- frame_data_->resource_list.end());
+ // Copy the resources from the last provided frame into the unused resources
+ // list, as the new frame will provide its own resources.
+ TransferableResource::ReturnResources(
+ frame_data_->resource_list,
+ &unused_resources_for_child_compositor_);
}
frame_data_ = new_frame_data.Pass();
if (!frame_data_->render_pass_list.empty()) {
RenderPass* root_pass = frame_data_->render_pass_list.back();
damage_in_frame_.Union(root_pass->damage_rect);
frame_size_ = root_pass->output_rect.size();
-
- // TODO(danakj): This could be optimized to only add resources to the
- // frame_data_ if they are actually used in the frame. For now, it will
- // cause the parent (this layer) to hold onto some resources it doesn't
- // need to for an extra frame.
- for (size_t i = 0; i < unused_resources_for_child_compositor_.size(); ++i) {
- frame_data_->resource_list.push_back(
- unused_resources_for_child_compositor_[i]);
- }
- unused_resources_for_child_compositor_.clear();
} else {
frame_size_ = gfx::Size();
}
+
+ // If any RenderPassDrawQuad has a filter operation, then we need a filter
+ // context to draw this layer's content.
+ for (size_t i = 0;
+ !needs_filter_context_ && i < frame_data_->render_pass_list.size();
+ ++i) {
+ const QuadList& quad_list = frame_data_->render_pass_list[i]->quad_list;
+ for (size_t j = 0; !needs_filter_context_ && j < quad_list.size(); ++j) {
+ if (quad_list[j]->material != DrawQuad::RENDER_PASS)
+ continue;
+ const RenderPassDrawQuad* render_pass_quad =
+ RenderPassDrawQuad::MaterialCast(quad_list[j]);
+ if (!render_pass_quad->filters.IsEmpty() ||
+ !render_pass_quad->background_filters.IsEmpty())
+ needs_filter_context_ = true;
+ }
+ }
+ if (needs_filter_context_ && layer_tree_host())
+ layer_tree_host()->set_needs_filter_context();
+
SetNeedsCommit();
+ // The active frame needs to be replaced and resources returned before the
+ // commit is called complete.
+ SetNextCommitWaitsForActivation();
}
void DelegatedRendererLayer::TakeUnusedResourcesForChildCompositor(
- TransferableResourceArray* array) {
+ ReturnedResourceArray* array) {
DCHECK(array->empty());
array->clear();
array->swap(unused_resources_for_child_compositor_);
}
-bool DelegatedRendererLayer::BlocksPendingCommit() const {
- // The active frame needs to be replaced and resources returned before the
- // commit is called complete. This is true even whenever there may be
- // resources to return, regardless of if the layer will draw in its new
- // state.
- return true;
+void DelegatedRendererLayer::ReceiveUnusedResources(
+ const ReturnedResourceArray& unused) {
+ unused_resources_for_child_compositor_.insert(
+ unused_resources_for_child_compositor_.end(),
+ unused.begin(),
+ unused.end());
+}
+
+// static
+void DelegatedRendererLayer::ReceiveUnusedResourcesOnImplThread(
+ scoped_refptr<BlockingTaskRunner> task_runner,
+ base::WeakPtr<DelegatedRendererLayer> self,
+ const ReturnedResourceArray& unused) {
+ task_runner->PostTask(
+ FROM_HERE,
+ base::Bind(
+ &DelegatedRendererLayer::ReceiveUnusedResources, self, unused));
}
} // namespace cc
diff --git a/chromium/cc/layers/delegated_renderer_layer.h b/chromium/cc/layers/delegated_renderer_layer.h
index 3f5280eec5f..60e9c3c05d9 100644
--- a/chromium/cc/layers/delegated_renderer_layer.h
+++ b/chromium/cc/layers/delegated_renderer_layer.h
@@ -5,12 +5,15 @@
#ifndef CC_LAYERS_DELEGATED_RENDERER_LAYER_H_
#define CC_LAYERS_DELEGATED_RENDERER_LAYER_H_
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/synchronization/lock.h"
#include "cc/base/cc_export.h"
#include "cc/layers/layer.h"
-#include "cc/resources/transferable_resource.h"
+#include "cc/resources/returned_resource.h"
namespace cc {
-
+class BlockingTaskRunner;
class DelegatedFrameData;
class DelegatedRendererLayerClient;
@@ -21,6 +24,7 @@ class CC_EXPORT DelegatedRendererLayer : public Layer {
virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl)
OVERRIDE;
+ virtual void SetLayerTreeHost(LayerTreeHost* host) OVERRIDE;
virtual void PushPropertiesTo(LayerImpl* impl) OVERRIDE;
virtual bool DrawsContent() const OVERRIDE;
@@ -34,25 +38,34 @@ class CC_EXPORT DelegatedRendererLayer : public Layer {
// Passes ownership of any unused resources that had been given by the child
// compositor to the given array, so they can be given back to the child.
- void TakeUnusedResourcesForChildCompositor(TransferableResourceArray* array);
-
- virtual bool BlocksPendingCommit() const OVERRIDE;
+ void TakeUnusedResourcesForChildCompositor(ReturnedResourceArray* array);
protected:
explicit DelegatedRendererLayer(DelegatedRendererLayerClient* client);
virtual ~DelegatedRendererLayer();
private:
+ void ReceiveUnusedResources(const ReturnedResourceArray& unused);
+ static void ReceiveUnusedResourcesOnImplThread(
+ scoped_refptr<BlockingTaskRunner> task_runner,
+ base::WeakPtr<DelegatedRendererLayer> self,
+ const ReturnedResourceArray& unused);
+
scoped_ptr<DelegatedFrameData> frame_data_;
gfx::RectF damage_in_frame_;
gfx::Size frame_size_;
gfx::Size display_size_;
- TransferableResourceArray unused_resources_for_child_compositor_;
DelegatedRendererLayerClient* client_;
+ bool needs_filter_context_;
+
+ ReturnedResourceArray unused_resources_for_child_compositor_;
+ scoped_refptr<BlockingTaskRunner> main_thread_runner_;
+ base::WeakPtrFactory<DelegatedRendererLayer> weak_ptrs_;
DISALLOW_COPY_AND_ASSIGN(DelegatedRendererLayer);
};
} // namespace cc
+
#endif // CC_LAYERS_DELEGATED_RENDERER_LAYER_H_
diff --git a/chromium/cc/layers/delegated_renderer_layer_impl.cc b/chromium/cc/layers/delegated_renderer_layer_impl.cc
index 2dfdac6eb2d..34a1cd86755 100644
--- a/chromium/cc/layers/delegated_renderer_layer_impl.cc
+++ b/chromium/cc/layers/delegated_renderer_layer_impl.cc
@@ -47,7 +47,7 @@ bool DelegatedRendererLayerImpl::HasContributingDelegatedRenderPasses() const {
static ResourceProvider::ResourceId ResourceRemapHelper(
bool* invalid_frame,
const ResourceProvider::ResourceIdMap& child_to_parent_map,
- ResourceProvider::ResourceIdSet *remapped_resources,
+ ResourceProvider::ResourceIdArray* resources_in_frame,
ResourceProvider::ResourceId id) {
ResourceProvider::ResourceIdMap::const_iterator it =
@@ -59,7 +59,7 @@ static ResourceProvider::ResourceId ResourceRemapHelper(
DCHECK_EQ(it->first, id);
ResourceProvider::ResourceId remapped_id = it->second;
- remapped_resources->insert(remapped_id);
+ resources_in_frame->push_back(id);
return remapped_id;
}
@@ -85,23 +85,31 @@ void DelegatedRendererLayerImpl::PushPropertiesTo(LayerImpl* layer) {
have_render_passes_to_push_ = false;
}
- // This is just a copy for testing since we keep the data on the pending layer
- // for returning resources to the child for now.
+ // This is just a copy for testing, since resources are added to the
+ // ResourceProvider in the pending tree.
delegated_layer->resources_ = resources_;
}
+void DelegatedRendererLayerImpl::CreateChildIdIfNeeded(
+ const ReturnCallback& return_callback) {
+ if (child_id_)
+ return;
+
+ ResourceProvider* resource_provider = layer_tree_impl()->resource_provider();
+ child_id_ = resource_provider->CreateChild(return_callback);
+ own_child_id_ = true;
+}
+
void DelegatedRendererLayerImpl::SetFrameData(
scoped_ptr<DelegatedFrameData> frame_data,
gfx::RectF damage_in_frame) {
DCHECK(frame_data);
+ DCHECK(child_id_) << "CreateChildIdIfNeeded must be called first.";
// A frame with an empty root render pass is invalid.
DCHECK(frame_data->render_pass_list.empty() ||
!frame_data->render_pass_list.back()->output_rect.IsEmpty());
- CreateChildIdIfNeeded();
- DCHECK(child_id_);
-
ResourceProvider* resource_provider = layer_tree_impl()->resource_provider();
const ResourceProvider::ResourceIdMap& resource_map =
resource_provider->GetChildToParentMap(child_id_);
@@ -109,12 +117,12 @@ void DelegatedRendererLayerImpl::SetFrameData(
resource_provider->ReceiveFromChild(child_id_, frame_data->resource_list);
bool invalid_frame = false;
- ResourceProvider::ResourceIdSet used_resources;
+ ResourceProvider::ResourceIdArray resources_in_frame;
DrawQuad::ResourceIteratorCallback remap_resources_to_parent_callback =
base::Bind(&ResourceRemapHelper,
&invalid_frame,
resource_map,
- &used_resources);
+ &resources_in_frame);
for (size_t i = 0; i < frame_data->render_pass_list.size(); ++i) {
RenderPass* pass = frame_data->render_pass_list[i];
for (size_t j = 0; j < pass->quad_list.size(); ++j) {
@@ -123,8 +131,15 @@ void DelegatedRendererLayerImpl::SetFrameData(
}
}
- if (invalid_frame)
+ if (invalid_frame) {
+ // Declare we are still using the last frame's resources.
+ resource_provider->DeclareUsedResourcesFromChild(child_id_, resources_);
return;
+ }
+
+ // Declare we are using the new frame's resources.
+ resources_.swap(resources_in_frame);
+ resource_provider->DeclareUsedResourcesFromChild(child_id_, resources_);
// Display size is already set so we can compute what the damage rect
// will be in layer space.
@@ -140,33 +155,9 @@ void DelegatedRendererLayerImpl::SetFrameData(
// Save the remapped quads on the layer. This steals the quads and render
// passes from the frame_data.
SetRenderPasses(&frame_data->render_pass_list);
- resources_.swap(used_resources);
have_render_passes_to_push_ = true;
}
-void DelegatedRendererLayerImpl::CollectUnusedResources(
- TransferableResourceArray* resources_for_ack) {
- CreateChildIdIfNeeded();
- DCHECK(child_id_);
-
- ResourceProvider* resource_provider = layer_tree_impl()->resource_provider();
- const ResourceProvider::ResourceIdMap& resource_map =
- resource_provider->GetChildToParentMap(child_id_);
-
- ResourceProvider::ResourceIdArray unused_resources;
- for (ResourceProvider::ResourceIdMap::const_iterator it =
- resource_map.begin();
- it != resource_map.end();
- ++it) {
- bool resource_is_in_current_frame = resources_.count(it->second) > 0;
- bool resource_is_in_use = resource_provider->InUseByConsumer(it->second);
- if (!resource_is_in_current_frame && !resource_is_in_use)
- unused_resources.push_back(it->second);
- }
- resource_provider->PrepareSendToChild(
- child_id_, unused_resources, resources_for_ack);
-}
-
void DelegatedRendererLayerImpl::SetDisplaySize(gfx::Size size) {
if (display_size_ == size)
return;
@@ -176,10 +167,6 @@ void DelegatedRendererLayerImpl::SetDisplaySize(gfx::Size size) {
void DelegatedRendererLayerImpl::SetRenderPasses(
ScopedPtrVector<RenderPass>* render_passes_in_draw_order) {
- gfx::RectF old_root_damage;
- if (!render_passes_in_draw_order_.empty())
- old_root_damage = render_passes_in_draw_order_.back()->damage_rect;
-
ClearRenderPasses();
for (size_t i = 0; i < render_passes_in_draw_order->size(); ++i) {
@@ -192,9 +179,6 @@ void DelegatedRendererLayerImpl::SetRenderPasses(
render_passes_in_draw_order_.push_back(taken_render_pass.Pass());
}
- if (!render_passes_in_draw_order_.empty())
- render_passes_in_draw_order_.back()->damage_rect.Union(old_root_damage);
-
// Give back an empty array instead of nulls.
render_passes_in_draw_order->clear();
}
@@ -240,13 +224,19 @@ RenderPass::Id DelegatedRendererLayerImpl::NextContributingRenderPassId(
return RenderPass::Id(previous.layer_id, previous.index + 1);
}
-RenderPass::Id DelegatedRendererLayerImpl::ConvertDelegatedRenderPassId(
- RenderPass::Id delegated_render_pass_id) const {
+bool DelegatedRendererLayerImpl::ConvertDelegatedRenderPassId(
+ RenderPass::Id delegated_render_pass_id,
+ RenderPass::Id* output_render_pass_id) const {
base::hash_map<RenderPass::Id, int>::const_iterator found =
render_passes_index_by_id_.find(delegated_render_pass_id);
- DCHECK(found != render_passes_index_by_id_.end());
+ if (found == render_passes_index_by_id_.end()) {
+ // Be robust against a RenderPass id that isn't part of the frame.
+ return false;
+ }
unsigned delegated_render_pass_index = found->second;
- return RenderPass::Id(id(), IndexToId(delegated_render_pass_index));
+ *output_render_pass_id =
+ RenderPass::Id(id(), IndexToId(delegated_render_pass_index));
+ return true;
}
void DelegatedRendererLayerImpl::AppendContributingRenderPasses(
@@ -254,10 +244,14 @@ void DelegatedRendererLayerImpl::AppendContributingRenderPasses(
DCHECK(HasContributingDelegatedRenderPasses());
for (size_t i = 0; i < render_passes_in_draw_order_.size() - 1; ++i) {
- RenderPass::Id output_render_pass_id =
- ConvertDelegatedRenderPassId(render_passes_in_draw_order_[i]->id);
+ RenderPass::Id output_render_pass_id(-1, -1);
+ bool present =
+ ConvertDelegatedRenderPassId(render_passes_in_draw_order_[i]->id,
+ &output_render_pass_id);
// Don't clash with the RenderPass we generate if we own a RenderSurface.
+ DCHECK(present) << render_passes_in_draw_order_[i]->id.layer_id << ", "
+ << render_passes_in_draw_order_[i]->id.index;
DCHECK_GT(output_render_pass_id.index, 0);
render_pass_sink->AppendRenderPass(
@@ -288,13 +282,13 @@ void DelegatedRendererLayerImpl::AppendQuads(
DCHECK(root_delegated_render_pass->output_rect.origin().IsOrigin());
gfx::Size frame_size = root_delegated_render_pass->output_rect.size();
- // If the index of the EenderPassId is 0, then it is a RenderPass generated
- // for a layer in this compositor, not the delegated renderer. Then we want to
- // merge our root RenderPass with the target RenderPass. Otherwise, it is some
- // RenderPass which we added from the delegated renderer.
+ // If the index of the RenderPassId is 0, then it is a RenderPass generated
+ // for a layer in this compositor, not the delegating renderer. Then we want
+ // to merge our root RenderPass with the target RenderPass. Otherwise, it is
+ // some RenderPass which we added from the delegating renderer.
bool should_merge_root_render_pass_with_target = !target_render_pass_id.index;
if (should_merge_root_render_pass_with_target) {
- // Verify that the RenderPass we are appending to is created our
+ // Verify that the RenderPass we are appending to is created by our
// render_target.
DCHECK(target_render_pass_id.layer_id == render_target()->id());
@@ -421,6 +415,7 @@ void DelegatedRendererLayerImpl::AppendRenderPassQuads(
if (render_target() == this) {
DCHECK(!is_clipped());
DCHECK(render_surface());
+ DCHECK_EQ(0, num_unclipped_descendants());
output_shared_quad_state->clip_rect = MathUtil::MapClippedRect(
delegated_frame_to_target_transform,
output_shared_quad_state->clip_rect);
@@ -446,18 +441,26 @@ void DelegatedRendererLayerImpl::AppendRenderPassQuads(
} else {
RenderPass::Id delegated_contributing_render_pass_id =
RenderPassDrawQuad::MaterialCast(delegated_quad)->render_pass_id;
- RenderPass::Id output_contributing_render_pass_id =
- ConvertDelegatedRenderPassId(delegated_contributing_render_pass_id);
- DCHECK(output_contributing_render_pass_id !=
- append_quads_data->render_pass_id);
-
- output_quad = RenderPassDrawQuad::MaterialCast(delegated_quad)->Copy(
- output_shared_quad_state,
- output_contributing_render_pass_id).PassAs<DrawQuad>();
+ RenderPass::Id output_contributing_render_pass_id(-1, -1);
+
+ bool present =
+ ConvertDelegatedRenderPassId(delegated_contributing_render_pass_id,
+ &output_contributing_render_pass_id);
+
+ // The frame may have a RenderPassDrawQuad that points to a RenderPass not
+ // part of the frame. Just ignore these quads.
+ if (present) {
+ DCHECK(output_contributing_render_pass_id !=
+ append_quads_data->render_pass_id);
+
+ output_quad = RenderPassDrawQuad::MaterialCast(delegated_quad)->Copy(
+ output_shared_quad_state,
+ output_contributing_render_pass_id).PassAs<DrawQuad>();
+ }
}
- DCHECK(output_quad.get());
- quad_sink->Append(output_quad.Pass(), append_quads_data);
+ if (output_quad)
+ quad_sink->Append(output_quad.Pass(), append_quads_data);
}
}
@@ -465,15 +468,6 @@ const char* DelegatedRendererLayerImpl::LayerTypeAsString() const {
return "cc::DelegatedRendererLayerImpl";
}
-void DelegatedRendererLayerImpl::CreateChildIdIfNeeded() {
- if (child_id_)
- return;
-
- ResourceProvider* resource_provider = layer_tree_impl()->resource_provider();
- child_id_ = resource_provider->CreateChild();
- own_child_id_ = true;
-}
-
void DelegatedRendererLayerImpl::ClearChildId() {
if (!child_id_)
return;
diff --git a/chromium/cc/layers/delegated_renderer_layer_impl.h b/chromium/cc/layers/delegated_renderer_layer_impl.h
index 9cfdcb6a351..7d774f1c357 100644
--- a/chromium/cc/layers/delegated_renderer_layer_impl.h
+++ b/chromium/cc/layers/delegated_renderer_layer_impl.h
@@ -40,11 +40,14 @@ class CC_EXPORT DelegatedRendererLayerImpl : public LayerImpl {
void AppendContributingRenderPasses(RenderPassSink* render_pass_sink);
+ // Creates an ID with the resource provider for the child renderer
+ // that will be sending quads to the layer. Registers the callback to
+ // inform when resources are no longer in use.
+ void CreateChildIdIfNeeded(const ReturnCallback& return_callback);
+
void SetFrameData(scoped_ptr<DelegatedFrameData> frame_data,
gfx::RectF damage_in_frame);
- void CollectUnusedResources(TransferableResourceArray* resources_for_ack);
-
void SetDisplaySize(gfx::Size size);
protected:
@@ -54,14 +57,11 @@ class CC_EXPORT DelegatedRendererLayerImpl : public LayerImpl {
const ScopedPtrVector<RenderPass>& RenderPassesInDrawOrderForTesting() const {
return render_passes_in_draw_order_;
}
- const ResourceProvider::ResourceIdSet& ResourcesForTesting() const {
+ const ResourceProvider::ResourceIdArray& ResourcesForTesting() const {
return resources_;
}
private:
- // Creates an ID with the resource provider for the child renderer
- // that will be sending quads to the layer.
- void CreateChildIdIfNeeded();
void ClearChildId();
void AppendRainbowDebugBorder(QuadSink* quad_sink,
@@ -71,8 +71,11 @@ class CC_EXPORT DelegatedRendererLayerImpl : public LayerImpl {
ScopedPtrVector<RenderPass>* render_passes_in_draw_order);
void ClearRenderPasses();
- RenderPass::Id ConvertDelegatedRenderPassId(
- RenderPass::Id delegated_render_pass_id) const;
+ // Returns |true| if the delegated_render_pass_id is part of the current
+ // frame and can be converted.
+ bool ConvertDelegatedRenderPassId(
+ RenderPass::Id delegated_render_pass_id,
+ RenderPass::Id* output_render_pass_id) const;
gfx::Transform DelegatedFrameToLayerSpaceTransform(gfx::Size frame_size)
const;
@@ -89,7 +92,7 @@ class CC_EXPORT DelegatedRendererLayerImpl : public LayerImpl {
bool have_render_passes_to_push_;
ScopedPtrVector<RenderPass> render_passes_in_draw_order_;
base::hash_map<RenderPass::Id, int> render_passes_index_by_id_;
- ResourceProvider::ResourceIdSet resources_;
+ ResourceProvider::ResourceIdArray resources_;
gfx::Size display_size_;
int child_id_;
diff --git a/chromium/cc/layers/delegated_renderer_layer_impl_unittest.cc b/chromium/cc/layers/delegated_renderer_layer_impl_unittest.cc
index 1584782f109..a641e00eaf0 100644
--- a/chromium/cc/layers/delegated_renderer_layer_impl_unittest.cc
+++ b/chromium/cc/layers/delegated_renderer_layer_impl_unittest.cc
@@ -5,6 +5,7 @@
#include "cc/layers/delegated_renderer_layer_impl.h"
#include "cc/base/scoped_ptr_vector.h"
+#include "cc/debug/test_web_graphics_context_3d.h"
#include "cc/layers/append_quads_data.h"
#include "cc/layers/quad_sink.h"
#include "cc/layers/solid_color_layer_impl.h"
@@ -20,7 +21,6 @@
#include "cc/test/mock_quad_culler.h"
#include "cc/test/render_pass_test_common.h"
#include "cc/test/render_pass_test_utils.h"
-#include "cc/test/test_web_graphics_context_3d.h"
#include "cc/trees/layer_tree_host_impl.h"
#include "cc/trees/layer_tree_impl.h"
#include "cc/trees/single_thread_proxy.h"
@@ -38,20 +38,15 @@ class DelegatedRendererLayerImplTest : public testing::Test {
LayerTreeSettings settings;
settings.minimum_occlusion_tracking_size = gfx::Size();
- host_impl_ = LayerTreeHostImpl::Create(settings,
- &client_,
- &proxy_,
- &stats_instrumentation_);
+ host_impl_.reset(new FakeLayerTreeHostImpl(settings, &proxy_));
host_impl_->InitializeRenderer(CreateFakeOutputSurface());
host_impl_->SetViewportSize(gfx::Size(10, 10));
}
protected:
FakeProxy proxy_;
- FakeLayerTreeHostImplClient client_;
DebugScopedSetImplThreadAndMainThreadBlocked
always_impl_thread_and_main_thread_blocked_;
- FakeRenderingStatsInstrumentation stats_instrumentation_;
scoped_ptr<LayerTreeHostImpl> host_impl_;
};
@@ -472,8 +467,8 @@ class DelegatedRendererLayerImplTestTransform
root_layer->SetBounds(gfx::Size(100, 100));
delegated_renderer_layer->SetPosition(gfx::Point(20, 20));
- delegated_renderer_layer->SetBounds(gfx::Size(30, 30));
- delegated_renderer_layer->SetContentBounds(gfx::Size(30, 30));
+ delegated_renderer_layer->SetBounds(gfx::Size(75, 75));
+ delegated_renderer_layer->SetContentBounds(gfx::Size(75, 75));
delegated_renderer_layer->SetDrawsContent(true);
gfx::Transform transform;
transform.Scale(2.0, 2.0);
@@ -518,8 +513,8 @@ class DelegatedRendererLayerImplTestTransform
quad_sink.Append(color_quad.PassAs<DrawQuad>(), &data);
}
- gfx::Size root_pass_content_bounds(50, 50);
- gfx::Rect root_pass_rect(0, 0, 50, 50);
+ gfx::Size root_pass_content_bounds(100, 100);
+ gfx::Rect root_pass_rect(0, 0, 100, 100);
gfx::Transform root_pass_transform;
root_pass_transform.Scale(1.5, 1.5);
root_pass_transform.Translate(7.0, 7.0);
@@ -653,7 +648,7 @@ TEST_F(DelegatedRendererLayerImplTestTransform, QuadsUnclipped_NoSurface) {
// When the quads don't have a clip of their own, the clip rect is set to
// the drawable_content_rect of the delegated renderer layer.
- EXPECT_EQ(gfx::Rect(42, 42, 120, 120).ToString(),
+ EXPECT_EQ(delegated_renderer_layer_->drawable_content_rect().ToString(),
root_delegated_shared_quad_state->clip_rect.ToString());
// Even though the quads in the root pass have no clip of their own, they
@@ -665,12 +660,12 @@ TEST_F(DelegatedRendererLayerImplTestTransform, QuadsUnclipped_NoSurface) {
// Device scale factor is 2.
expected.Scale(2.0, 2.0);
// This is the transform from the layer's space to its target.
- // The position (20) - the width / scale (30 / 2) = 20 - 15 = 5
- expected.Translate(5.0, 5.0);
+ // The position (20) - the width / scale (75 / 2) = 20 - 37.5 = -17.5
+ expected.Translate(-17.5, -17.5);
expected.Scale(2.0, 2.0);
expected.Translate(8.0, 8.0);
- // The frame has size 50x50 but the layer's bounds are 30x30.
- expected.Scale(30.0 / 50.0, 30.0 / 50.0);
+ // The frame has size 100x100 but the layer's bounds are 75x75.
+ expected.Scale(75.0 / 100.0, 75.0 / 100.0);
// This is the transform within the source frame.
expected.Scale(1.5, 1.5);
expected.Translate(7.0, 7.0);
@@ -710,15 +705,15 @@ TEST_F(DelegatedRendererLayerImplTestTransform, QuadsClipped_NoSurface) {
// Since the quads have a clip_rect it should be modified by delegated
// renderer layer's draw_transform.
// The position of the resulting clip_rect is:
- // (clip rect position (10) * scale to layer (30/50) + translate (8)) *
- // layer scale (2) + layer position (20) = 48
- // But the layer is centered, so: 48 - (width / 2) = 48 - 30 / 2 = 33
- // The device scale is 2, so everything gets doubled, giving 66.
+ // (clip rect position (10) * scale to layer (75/100) + translate (8)) *
+ // layer scale (2) + layer position (20) = 51
+ // But the layer is centered, so: 51 - (75 / 2) = 51 - 75 / 2 = 13.5
+ // The device scale is 2, so everything gets doubled, giving 27.
//
- // The size is 35x35 scaled to fit inside the layer's bounds at 30x30 from
- // a frame at 50x50: 35 * 2 (device scale) * 30 / 50 = 42. The device scale
- // doubles this to 84.
- EXPECT_EQ(gfx::Rect(66, 66, 84, 84).ToString(),
+ // The size is 35x35 scaled to fit inside the layer's bounds at 75x75 from
+ // a frame at 100x100: 35 * 2 (device scale) * 75 / 100 = 52.5. The device
+ // scale doubles this to 105.
+ EXPECT_EQ(gfx::Rect(27, 27, 105, 105).ToString(),
root_delegated_shared_quad_state->clip_rect.ToString());
// The quads had a clip and it should be preserved.
@@ -728,12 +723,12 @@ TEST_F(DelegatedRendererLayerImplTestTransform, QuadsClipped_NoSurface) {
// Device scale factor is 2.
expected.Scale(2.0, 2.0);
// This is the transform from the layer's space to its target.
- // The position (20) - the width / scale (30 / 2) = 20 - 15 = 5
- expected.Translate(5.0, 5.0);
+ // The position (20) - the width / scale (75 / 2) = 20 - 37.5 = -17.5
+ expected.Translate(-17.5, -17.5);
expected.Scale(2.0, 2.0);
expected.Translate(8.0, 8.0);
- // The frame has size 50x50 but the layer's bounds are 30x30.
- expected.Scale(30.0 / 50.0, 30.0 / 50.0);
+ // The frame has size 100x100 but the layer's bounds are 75x75.
+ expected.Scale(75.0 / 100.0, 75.0 / 100.0);
// This is the transform within the source frame.
expected.Scale(1.5, 1.5);
expected.Translate(7.0, 7.0);
@@ -775,10 +770,10 @@ TEST_F(DelegatedRendererLayerImplTestTransform, QuadsUnclipped_Surface) {
// When the layer owns a surface, then its position and translation are not
// a part of its draw transform.
// The position of the resulting clip_rect is:
- // (clip rect position (10) * scale to layer (30/50)) * device scale (2) = 12
- // The size is 35x35 scaled to fit inside the layer's bounds at 30x30 from
- // a frame at 50x50: 35 * 2 (device scale) * 30 / 50 = 42.
- EXPECT_EQ(gfx::Rect(12, 12, 42, 42).ToString(),
+ // (clip rect position (10) * scale to layer (75/100)) * device scale (2) = 15
+ // The size is 35x35 scaled to fit inside the layer's bounds at 75x75 from
+ // a frame at 100x100: 35 * 2 (device scale) * 75 / 100 = 52.5.
+ EXPECT_EQ(gfx::Rect(15, 15, 53, 53).ToString(),
root_delegated_shared_quad_state->clip_rect.ToString());
// Since the layer owns a surface it doesn't need to clip its quads, so
@@ -788,8 +783,8 @@ TEST_F(DelegatedRendererLayerImplTestTransform, QuadsUnclipped_Surface) {
gfx::Transform expected;
// Device scale factor is 2.
expected.Scale(2.0, 2.0);
- // The frame has size 50x50 but the layer's bounds are 30x30.
- expected.Scale(30.0 / 50.0, 30.0 / 50.0);
+ // The frame has size 100x100 but the layer's bounds are 75x75.
+ expected.Scale(75.0 / 100.0, 75.0 / 100.0);
// This is the transform within the source frame.
expected.Scale(1.5, 1.5);
expected.Translate(7.0, 7.0);
@@ -831,10 +826,10 @@ TEST_F(DelegatedRendererLayerImplTestTransform, QuadsClipped_Surface) {
// When the layer owns a surface, then its position and translation are not
// a part of its draw transform.
// The position of the resulting clip_rect is:
- // (clip rect position (10) * scale to layer (30/50)) * device scale (2) = 12
- // The size is 35x35 scaled to fit inside the layer's bounds at 30x30 from
- // a frame at 50x50: 35 * 2 (device scale) * 30 / 50 = 42.
- EXPECT_EQ(gfx::Rect(12, 12, 42, 42).ToString(),
+ // (clip rect position (10) * scale to layer (75/100)) * device scale (2) = 15
+ // The size is 35x35 scaled to fit inside the layer's bounds at 75x75 from
+ // a frame at 100x100: 35 * 2 (device scale) * 75 / 100 = 52.5.
+ EXPECT_EQ(gfx::Rect(15, 15, 53, 53).ToString(),
root_delegated_shared_quad_state->clip_rect.ToString());
// The quads had a clip and it should be preserved.
@@ -843,8 +838,8 @@ TEST_F(DelegatedRendererLayerImplTestTransform, QuadsClipped_Surface) {
gfx::Transform expected;
// Device scale factor is 2.
expected.Scale(2.0, 2.0);
- // The frame has size 50x50 but the layer's bounds are 30x30.
- expected.Scale(30.0 / 50.0, 30.0 / 50.0);
+ // The frame has size 100x100 but the layer's bounds are 75x75.
+ expected.Scale(75.0 / 100.0, 75.0 / 100.0);
// This is the transform within the source frame.
expected.Scale(1.5, 1.5);
expected.Translate(7.0, 7.0);
@@ -1250,5 +1245,58 @@ TEST_F(DelegatedRendererLayerImplTestClip, QuadsClipped_LayerClipped_Surface) {
host_impl_->DidDrawAllLayers(frame);
}
+TEST_F(DelegatedRendererLayerImplTest, InvalidRenderPassDrawQuad) {
+ scoped_ptr<LayerImpl> root_layer = LayerImpl::Create(
+ host_impl_->active_tree(), 1).PassAs<LayerImpl>();
+ scoped_ptr<FakeDelegatedRendererLayerImpl> delegated_renderer_layer =
+ FakeDelegatedRendererLayerImpl::Create(host_impl_->active_tree(), 4);
+
+ host_impl_->SetViewportSize(gfx::Size(100, 100));
+
+ delegated_renderer_layer->SetPosition(gfx::Point(3, 3));
+ delegated_renderer_layer->SetBounds(gfx::Size(10, 10));
+ delegated_renderer_layer->SetContentBounds(gfx::Size(10, 10));
+ delegated_renderer_layer->SetDrawsContent(true);
+
+ ScopedPtrVector<RenderPass> delegated_render_passes;
+ TestRenderPass* pass1 = AddRenderPass(
+ &delegated_render_passes,
+ RenderPass::Id(9, 6),
+ gfx::Rect(0, 0, 10, 10),
+ gfx::Transform());
+ AddQuad(pass1, gfx::Rect(0, 0, 6, 6), 33u);
+
+ // This render pass isn't part of the frame.
+ scoped_ptr<TestRenderPass> missing_pass(TestRenderPass::Create());
+ missing_pass->SetNew(RenderPass::Id(9, 7),
+ gfx::Rect(7, 7, 7, 7),
+ gfx::Rect(7, 7, 7, 7),
+ gfx::Transform());
+
+ // But a render pass quad refers to it.
+ AddRenderPassQuad(pass1, missing_pass.get());
+
+ delegated_renderer_layer->SetFrameDataForRenderPasses(
+ &delegated_render_passes);
+
+ // The RenderPasses should be taken by the layer.
+ EXPECT_EQ(0u, delegated_render_passes.size());
+
+ root_layer->AddChild(delegated_renderer_layer.PassAs<LayerImpl>());
+ host_impl_->active_tree()->SetRootLayer(root_layer.Pass());
+
+ LayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(host_impl_->PrepareToDraw(&frame, gfx::Rect()));
+
+ // The DelegatedRendererLayerImpl should drop the bad RenderPassDrawQuad.
+ ASSERT_EQ(1u, frame.render_passes.size());
+ ASSERT_EQ(1u, frame.render_passes[0]->quad_list.size());
+ EXPECT_EQ(DrawQuad::SOLID_COLOR,
+ frame.render_passes[0]->quad_list[0]->material);
+
+ host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
+ host_impl_->DidDrawAllLayers(frame);
+}
+
} // namespace
} // namespace cc
diff --git a/chromium/cc/layers/draw_properties.h b/chromium/cc/layers/draw_properties.h
index bb80dce0adf..181d900f267 100644
--- a/chromium/cc/layers/draw_properties.h
+++ b/chromium/cc/layers/draw_properties.h
@@ -27,6 +27,7 @@ struct CC_EXPORT DrawProperties {
contents_scale_x(1.f),
contents_scale_y(1.f),
num_descendants_that_draw_content(0),
+ num_unclipped_descendants(0),
descendants_can_clip_selves(false),
can_draw_directly_to_backbuffer(false),
layer_or_descendant_has_copy_request(false) {}
@@ -88,6 +89,10 @@ struct CC_EXPORT DrawProperties {
// Does not include this layer itself, only its children and descendants.
int num_descendants_that_draw_content;
+ // Number of descendants with a clip parent that is our ancestor. NB - this
+ // does not include our clip children because they are clipped by us.
+ int num_unclipped_descendants;
+
// If true, every descendant in the sub-tree can clip itself without the
// need to use hardware sissoring or a new render target.
bool descendants_can_clip_selves;
diff --git a/chromium/cc/layers/heads_up_display_layer.cc b/chromium/cc/layers/heads_up_display_layer.cc
index 92486cc8515..d9fab902ef9 100644
--- a/chromium/cc/layers/heads_up_display_layer.cc
+++ b/chromium/cc/layers/heads_up_display_layer.cc
@@ -55,4 +55,8 @@ scoped_ptr<LayerImpl> HeadsUpDisplayLayer::CreateLayerImpl(
PassAs<LayerImpl>();
}
+std::string HeadsUpDisplayLayer::DebugName() {
+ return std::string("Heads Up Display Layer");
+}
+
} // namespace cc
diff --git a/chromium/cc/layers/heads_up_display_layer.h b/chromium/cc/layers/heads_up_display_layer.h
index 7038cd480c3..f13d56bba67 100644
--- a/chromium/cc/layers/heads_up_display_layer.h
+++ b/chromium/cc/layers/heads_up_display_layer.h
@@ -5,6 +5,8 @@
#ifndef CC_LAYERS_HEADS_UP_DISPLAY_LAYER_H_
#define CC_LAYERS_HEADS_UP_DISPLAY_LAYER_H_
+#include <string>
+
#include "base/memory/scoped_ptr.h"
#include "cc/base/cc_export.h"
#include "cc/layers/contents_scaling_layer.h"
@@ -23,6 +25,8 @@ class CC_EXPORT HeadsUpDisplayLayer : public ContentsScalingLayer {
virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl)
OVERRIDE;
+ virtual std::string DebugName() OVERRIDE;
+
protected:
HeadsUpDisplayLayer();
diff --git a/chromium/cc/layers/heads_up_display_layer_impl.cc b/chromium/cc/layers/heads_up_display_layer_impl.cc
index 8728d704ca4..c1fa9f37dbc 100644
--- a/chromium/cc/layers/heads_up_display_layer_impl.cc
+++ b/chromium/cc/layers/heads_up_display_layer_impl.cc
@@ -94,8 +94,9 @@ bool HeadsUpDisplayLayerImpl::WillDraw(DrawMode draw_mode,
hud_resource_->Free();
if (!hud_resource_->id()) {
- hud_resource_->Allocate(
- content_bounds(), GL_RGBA, ResourceProvider::TextureUsageAny);
+ hud_resource_->Allocate(content_bounds(),
+ ResourceProvider::TextureUsageAny,
+ RGBA_8888);
}
return LayerImpl::WillDraw(draw_mode, resource_provider);
diff --git a/chromium/cc/layers/image_layer.cc b/chromium/cc/layers/image_layer.cc
index 8ba1908131d..bbf1aa82e34 100644
--- a/chromium/cc/layers/image_layer.cc
+++ b/chromium/cc/layers/image_layer.cc
@@ -56,9 +56,8 @@ void ImageLayer::CreateUpdaterIfNeeded() {
return;
updater_ = ImageLayerUpdater::Create();
- GLenum texture_format =
- layer_tree_host()->GetRendererCapabilities().best_texture_format;
- SetTextureFormat(texture_format);
+ SetTextureFormat(
+ layer_tree_host()->GetRendererCapabilities().best_texture_format);
}
LayerUpdater* ImageLayer::Updater() const {
diff --git a/chromium/cc/layers/io_surface_layer_impl.cc b/chromium/cc/layers/io_surface_layer_impl.cc
index f8577a37765..e8c627be08b 100644
--- a/chromium/cc/layers/io_surface_layer_impl.cc
+++ b/chromium/cc/layers/io_surface_layer_impl.cc
@@ -39,11 +39,11 @@ void IOSurfaceLayerImpl::DestroyTexture() {
}
if (io_surface_texture_id_) {
- OutputSurface* output_surface = layer_tree_impl()->output_surface();
+ ContextProvider* context_provider =
+ layer_tree_impl()->output_surface()->context_provider().get();
// TODO(skaslev): Implement this path for software compositing.
- WebKit::WebGraphicsContext3D* context3d = output_surface->context3d();
- if (context3d)
- context3d->deleteTexture(io_surface_texture_id_);
+ if (context_provider)
+ context_provider->Context3d()->deleteTexture(io_surface_texture_id_);
io_surface_texture_id_ = 0;
}
}
@@ -67,13 +67,15 @@ bool IOSurfaceLayerImpl::WillDraw(DrawMode draw_mode,
return false;
if (io_surface_changed_) {
- WebKit::WebGraphicsContext3D* context3d =
- resource_provider->GraphicsContext3D();
- if (!context3d) {
+ ContextProvider* context_provider =
+ layer_tree_impl()->output_surface()->context_provider().get();
+ if (!context_provider) {
// TODO(skaslev): Implement this path for software compositing.
return false;
}
+ WebKit::WebGraphicsContext3D* context3d = context_provider->Context3d();
+
// TODO(ernstm): Do this in a way that we can track memory usage.
if (!io_surface_texture_id_) {
io_surface_texture_id_ = context3d->createTexture();
diff --git a/chromium/cc/layers/layer.cc b/chromium/cc/layers/layer.cc
index aa4a002dff4..86887a6ad2c 100644
--- a/chromium/cc/layers/layer.cc
+++ b/chromium/cc/layers/layer.cc
@@ -6,12 +6,14 @@
#include <algorithm>
+#include "base/debug/trace_event.h"
#include "base/location.h"
#include "base/metrics/histogram.h"
#include "base/single_thread_task_runner.h"
#include "cc/animation/animation.h"
#include "cc/animation/animation_events.h"
#include "cc/animation/layer_animation_controller.h"
+#include "cc/layers/layer_client.h"
#include "cc/layers/layer_impl.h"
#include "cc/output/copy_output_request.h"
#include "cc/output/copy_output_result.h"
@@ -54,8 +56,11 @@ Layer::Layer()
use_parent_backface_visibility_(false),
draw_checkerboard_for_missing_tiles_(false),
force_render_surface_(false),
+ scroll_parent_(NULL),
+ clip_parent_(NULL),
replica_layer_(NULL),
- raster_scale_(0.f) {
+ raster_scale_(0.f),
+ client_(NULL) {
if (layer_id_ < 0) {
s_next_layer_id = 1;
layer_id_ = s_next_layer_id++;
@@ -85,6 +90,9 @@ Layer::~Layer() {
DCHECK_EQ(this, replica_layer_->parent());
replica_layer_->RemoveFromParent();
}
+
+ RemoveFromScrollTree();
+ RemoveFromClipTree();
}
void Layer::SetLayerTreeHost(LayerTreeHost* host) {
@@ -144,14 +152,11 @@ void Layer::SetNeedsFullTreeSync() {
layer_tree_host_->SetNeedsFullTreeSync();
}
-bool Layer::IsPropertyChangeAllowed() const {
+void Layer::SetNextCommitWaitsForActivation() {
if (!layer_tree_host_)
- return true;
-
- if (!layer_tree_host_->settings().strict_layer_property_change_checking)
- return true;
+ return;
- return !layer_tree_host_->in_paint_layer_contents();
+ layer_tree_host_->SetNextCommitWaitsForActivation();
}
void Layer::SetNeedsPushProperties() {
@@ -179,6 +184,16 @@ void Layer::RemoveDependentNeedsPushProperties() {
parent_->RemoveDependentNeedsPushProperties();
}
+bool Layer::IsPropertyChangeAllowed() const {
+ if (!layer_tree_host_)
+ return true;
+
+ if (!layer_tree_host_->settings().strict_layer_property_change_checking)
+ return true;
+
+ return !layer_tree_host_->in_paint_layer_contents();
+}
+
gfx::Rect Layer::LayerRectToContentRect(const gfx::RectF& layer_rect) const {
gfx::RectF content_rect =
gfx::ScaleRect(layer_rect, contents_scale_x(), contents_scale_y());
@@ -188,28 +203,14 @@ gfx::Rect Layer::LayerRectToContentRect(const gfx::RectF& layer_rect) const {
return gfx::ToEnclosingRect(content_rect);
}
-bool Layer::BlocksPendingCommit() const {
- return false;
+skia::RefPtr<SkPicture> Layer::GetPicture() const {
+ return skia::RefPtr<SkPicture>();
}
bool Layer::CanClipSelf() const {
return false;
}
-bool Layer::BlocksPendingCommitRecursive() const {
- if (BlocksPendingCommit())
- return true;
- if (mask_layer() && mask_layer()->BlocksPendingCommitRecursive())
- return true;
- if (replica_layer() && replica_layer()->BlocksPendingCommitRecursive())
- return true;
- for (size_t i = 0; i < children_.size(); ++i) {
- if (children_[i]->BlocksPendingCommitRecursive())
- return true;
- }
- return false;
-}
-
void Layer::SetParent(Layer* layer) {
DCHECK(!layer || !layer->HasAncestor(this));
@@ -559,6 +560,66 @@ bool Layer::TransformIsAnimating() const {
return layer_animation_controller_->IsAnimatingProperty(Animation::Transform);
}
+void Layer::SetScrollParent(Layer* parent) {
+ DCHECK(IsPropertyChangeAllowed());
+ if (scroll_parent_ == parent)
+ return;
+
+ if (scroll_parent_)
+ scroll_parent_->RemoveScrollChild(this);
+
+ scroll_parent_ = parent;
+
+ if (scroll_parent_)
+ scroll_parent_->AddScrollChild(this);
+
+ SetNeedsCommit();
+}
+
+void Layer::AddScrollChild(Layer* child) {
+ if (!scroll_children_)
+ scroll_children_.reset(new std::set<Layer*>);
+ scroll_children_->insert(child);
+ SetNeedsCommit();
+}
+
+void Layer::RemoveScrollChild(Layer* child) {
+ scroll_children_->erase(child);
+ if (scroll_children_->empty())
+ scroll_children_.reset();
+ SetNeedsCommit();
+}
+
+void Layer::SetClipParent(Layer* ancestor) {
+ DCHECK(IsPropertyChangeAllowed());
+ if (clip_parent_ == ancestor)
+ return;
+
+ if (clip_parent_)
+ clip_parent_->RemoveClipChild(this);
+
+ clip_parent_ = ancestor;
+
+ if (clip_parent_)
+ clip_parent_->AddClipChild(this);
+
+ SetNeedsCommit();
+}
+
+void Layer::AddClipChild(Layer* child) {
+ if (!clip_children_)
+ clip_children_.reset(new std::set<Layer*>);
+ clip_children_->insert(child);
+ SetNeedsCommit();
+}
+
+void Layer::RemoveClipChild(Layer* child) {
+ clip_children_->erase(child);
+ if (clip_children_->empty())
+ clip_children_.reset();
+ SetNeedsCommit();
+}
+
void Layer::SetScrollOffset(gfx::Vector2d scroll_offset) {
DCHECK(IsPropertyChangeAllowed());
if (scroll_offset_ == scroll_offset)
@@ -627,6 +688,7 @@ void Layer::SetTouchEventHandlerRegion(const Region& region) {
if (touch_event_handler_region_ == region)
return;
touch_event_handler_region_ = region;
+ SetNeedsCommit();
}
void Layer::SetDrawCheckerboardForMissingTiles(bool checkerboard) {
@@ -742,7 +804,15 @@ void Layer::PushPropertiesTo(LayerImpl* layer) {
: bounds_);
layer->SetContentBounds(content_bounds());
layer->SetContentsScale(contents_scale_x(), contents_scale_y());
- layer->SetDebugName(debug_name_);
+
+ bool is_tracing;
+ TRACE_EVENT_CATEGORY_GROUP_ENABLED(TRACE_DISABLED_BY_DEFAULT("cc.debug"),
+ &is_tracing);
+ if (is_tracing)
+ layer->SetDebugName(DebugName());
+ else
+ layer->SetDebugName(std::string());
+
layer->SetCompositingReasons(compositing_reasons_);
layer->SetDoubleSided(double_sided_);
layer->SetDrawCheckerboardForMissingTiles(
@@ -775,9 +845,50 @@ void Layer::PushPropertiesTo(LayerImpl* layer) {
DCHECK(!(TransformIsAnimating() && layer->TransformIsAnimatingOnImplOnly()));
layer->SetScrollable(scrollable_);
- layer->SetScrollOffset(scroll_offset_);
layer->SetMaxScrollOffset(max_scroll_offset_);
+ LayerImpl* scroll_parent = NULL;
+ if (scroll_parent_)
+ scroll_parent = layer->layer_tree_impl()->LayerById(scroll_parent_->id());
+
+ layer->SetScrollParent(scroll_parent);
+ if (scroll_children_) {
+ std::set<LayerImpl*>* scroll_children = new std::set<LayerImpl*>;
+ for (std::set<Layer*>::iterator it = scroll_children_->begin();
+ it != scroll_children_->end(); ++it)
+ scroll_children->insert(layer->layer_tree_impl()->LayerById((*it)->id()));
+ layer->SetScrollChildren(scroll_children);
+ }
+
+ LayerImpl* clip_parent = NULL;
+ if (clip_parent_) {
+ clip_parent =
+ layer->layer_tree_impl()->LayerById(clip_parent_->id());
+ }
+
+ layer->SetClipParent(clip_parent);
+ if (clip_children_) {
+ std::set<LayerImpl*>* clip_children = new std::set<LayerImpl*>;
+ for (std::set<Layer*>::iterator it = clip_children_->begin();
+ it != clip_children_->end(); ++it) {
+ LayerImpl* clip_child = layer->layer_tree_impl()->LayerById((*it)->id());
+ DCHECK(clip_child);
+ clip_children->insert(clip_child);
+ }
+ layer->SetClipChildren(clip_children);
+ }
+
+ // Adjust the scroll delta to be just the scrolls that have happened since
+ // the begin frame was sent. This happens for impl-side painting
+ // in LayerImpl::ApplyScrollDeltasSinceBeginFrame in a separate tree walk.
+ if (layer->layer_tree_impl()->settings().impl_side_painting) {
+ layer->SetScrollOffset(scroll_offset_);
+ } else {
+ layer->SetScrollOffsetAndDelta(
+ scroll_offset_, layer->ScrollDelta() - layer->sent_scroll_delta());
+ layer->SetSentScrollDelta(gfx::Vector2d());
+ }
+
// Wrap the copy_requests_ in a PostTask to the main thread.
ScopedPtrVector<CopyOutputRequest> main_thread_copy_requests;
for (ScopedPtrVector<CopyOutputRequest>::iterator it = copy_requests_.begin();
@@ -805,23 +916,6 @@ void Layer::PushPropertiesTo(LayerImpl* layer) {
update_rect_.Union(layer->update_rect());
layer->set_update_rect(update_rect_);
- if (layer->layer_tree_impl()->settings().impl_side_painting) {
- DCHECK(layer->layer_tree_impl()->IsPendingTree());
- LayerImpl* active_twin =
- layer->layer_tree_impl()->FindActiveTreeLayerById(id());
- // Update the scroll delta from the active layer, which may have
- // adjusted its scroll delta prior to this pending layer being created.
- // This code is identical to that in LayerImpl::SetScrollDelta.
- if (active_twin) {
- DCHECK(layer->sent_scroll_delta().IsZero());
- layer->SetScrollDelta(active_twin->ScrollDelta() -
- active_twin->sent_scroll_delta());
- }
- } else {
- layer->SetScrollDelta(layer->ScrollDelta() - layer->sent_scroll_delta());
- layer->SetSentScrollDelta(gfx::Vector2d());
- }
-
layer->SetStackingOrderChanged(stacking_order_changed_);
layer_animation_controller_->PushAnimationUpdatesTo(
@@ -868,9 +962,8 @@ bool Layer::NeedMoreUpdates() {
return false;
}
-void Layer::SetDebugName(const std::string& debug_name) {
- debug_name_ = debug_name;
- SetNeedsCommit();
+std::string Layer::DebugName() {
+ return client_ ? client_->DebugName() : std::string();
}
void Layer::SetCompositingReasons(CompositingReasons reasons) {
@@ -967,7 +1060,7 @@ Region Layer::VisibleContentOpaqueRegion() const {
return Region();
}
-ScrollbarLayer* Layer::ToScrollbarLayer() {
+ScrollbarLayerInterface* Layer::ToScrollbarLayer() {
return NULL;
}
@@ -979,4 +1072,30 @@ bool Layer::SupportsLCDText() const {
return false;
}
+void Layer::RemoveFromScrollTree() {
+ if (scroll_children_.get()) {
+ for (std::set<Layer*>::iterator it = scroll_children_->begin();
+ it != scroll_children_->end(); ++it)
+ (*it)->scroll_parent_ = NULL;
+ }
+
+ if (scroll_parent_)
+ scroll_parent_->RemoveScrollChild(this);
+
+ scroll_parent_ = NULL;
+}
+
+void Layer::RemoveFromClipTree() {
+ if (clip_children_.get()) {
+ for (std::set<Layer*>::iterator it = clip_children_->begin();
+ it != clip_children_->end(); ++it)
+ (*it)->clip_parent_ = NULL;
+ }
+
+ if (clip_parent_)
+ clip_parent_->RemoveClipChild(this);
+
+ clip_parent_ = NULL;
+}
+
} // namespace cc
diff --git a/chromium/cc/layers/layer.h b/chromium/cc/layers/layer.h
index 035e071a5fd..737e83df067 100644
--- a/chromium/cc/layers/layer.h
+++ b/chromium/cc/layers/layer.h
@@ -5,6 +5,7 @@
#ifndef CC_LAYERS_LAYER_H_
#define CC_LAYERS_LAYER_H_
+#include <set>
#include <string>
#include "base/callback.h"
@@ -26,10 +27,15 @@
#include "skia/ext/refptr.h"
#include "third_party/skia/include/core/SkColor.h"
#include "third_party/skia/include/core/SkImageFilter.h"
+#include "third_party/skia/include/core/SkPicture.h"
#include "ui/gfx/rect.h"
#include "ui/gfx/rect_f.h"
#include "ui/gfx/transform.h"
+namespace gfx {
+class BoxF;
+}
+
namespace cc {
class Animation;
@@ -38,13 +44,14 @@ struct AnimationEvent;
class CopyOutputRequest;
class LayerAnimationDelegate;
class LayerAnimationEventObserver;
+class LayerClient;
class LayerImpl;
class LayerTreeHost;
class LayerTreeImpl;
class PriorityCalculator;
class RenderingStatsInstrumentation;
class ResourceUpdateQueue;
-class ScrollbarLayer;
+class ScrollbarLayerInterface;
struct AnimationEvent;
// Base class for composited layers. Special layer types are derived from
@@ -151,6 +158,34 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>,
const gfx::Transform& transform() const { return transform_; }
bool TransformIsAnimating() const;
+ void SetScrollParent(Layer* parent);
+
+ Layer* scroll_parent() { return scroll_parent_; }
+ const Layer* scroll_parent() const { return scroll_parent_; }
+
+ void AddScrollChild(Layer* child);
+ void RemoveScrollChild(Layer* child);
+
+ std::set<Layer*>* scroll_children() { return scroll_children_.get(); }
+ const std::set<Layer*>* scroll_children() const {
+ return scroll_children_.get();
+ }
+
+ void SetClipParent(Layer* ancestor);
+
+ Layer* clip_parent() { return clip_parent_; }
+ const Layer* clip_parent() const {
+ return clip_parent_;
+ }
+
+ void AddClipChild(Layer* child);
+ void RemoveClipChild(Layer* child);
+
+ std::set<Layer*>* clip_children() { return clip_children_.get(); }
+ const std::set<Layer*>* clip_children() const {
+ return clip_children_.get();
+ }
+
DrawProperties<Layer, RenderSurface>& draw_properties() {
return draw_properties_;
}
@@ -201,6 +236,9 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>,
RenderSurface* render_surface() const {
return draw_properties_.render_surface.get();
}
+ int num_unclipped_descendants() const {
+ return draw_properties_.num_unclipped_descendants;
+ }
void SetScrollOffset(gfx::Vector2d scroll_offset);
gfx::Vector2d scroll_offset() const { return scroll_offset_; }
@@ -292,7 +330,10 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>,
virtual void SetIsMask(bool is_mask) {}
virtual void ReduceMemoryUsage() {}
- void SetDebugName(const std::string& debug_name);
+ virtual std::string DebugName();
+
+ void SetLayerClient(LayerClient* client) { client_ = client; }
+
void SetCompositingReasons(CompositingReasons reasons);
virtual void PushPropertiesTo(LayerImpl* layer);
@@ -316,7 +357,8 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>,
float* contents_scale_y,
gfx::Size* content_bounds);
- LayerTreeHost* layer_tree_host() const { return layer_tree_host_; }
+ LayerTreeHost* layer_tree_host() { return layer_tree_host_; }
+ const LayerTreeHost* layer_tree_host() const { return layer_tree_host_; }
// Set the priority of all desired textures in this layer.
virtual void SetTexturePriorities(const PriorityCalculator& priority_calc) {}
@@ -328,6 +370,10 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>,
void SuspendAnimations(double monotonic_time);
void ResumeAnimations(double monotonic_time);
+ bool AnimatedBoundsForBox(const gfx::BoxF& box, gfx::BoxF* bounds) {
+ return layer_animation_controller_->AnimatedBoundsForBox(box, bounds);
+ }
+
LayerAnimationController* layer_animation_controller() {
return layer_animation_controller_.get();
}
@@ -347,16 +393,11 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>,
virtual Region VisibleContentOpaqueRegion() const;
- virtual ScrollbarLayer* ToScrollbarLayer();
+ virtual ScrollbarLayerInterface* ToScrollbarLayer();
gfx::Rect LayerRectToContentRect(const gfx::RectF& layer_rect) const;
- // In impl-side painting, this returns true if this layer type is not
- // compatible with the main thread running freely, such as a double-buffered
- // canvas that doesn't want to be triple-buffered across all three trees.
- virtual bool BlocksPendingCommit() const;
- // Returns true if anything in this tree blocksPendingCommit.
- bool BlocksPendingCommitRecursive() const;
+ virtual skia::RefPtr<SkPicture> GetPicture() const;
virtual bool CanClipSelf() const;
@@ -407,7 +448,11 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>,
// Called when there's been a change in layer structure. Implies both
// SetNeedsUpdate and SetNeedsCommit, but not SetNeedsPushProperties.
void SetNeedsFullTreeSync();
- bool IsPropertyChangeAllowed() const;
+
+ // Called when the next commit should wait until the pending tree is activated
+ // before finishing the commit and unblocking the main thread. Used to ensure
+ // unused resources on the impl thread are returned before commit completes.
+ void SetNextCommitWaitsForActivation();
void SetNeedsPushProperties();
void AddDependentNeedsPushProperties();
@@ -416,6 +461,16 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>,
return needs_push_properties() || descendant_needs_push_properties();
}
+ bool IsPropertyChangeAllowed() const;
+
+ // If this layer has a scroll parent, it removes |this| from its list of
+ // scroll children.
+ void RemoveFromScrollTree();
+
+ // If this layer has a clip parent, it removes |this| from its list of clip
+ // children.
+ void RemoveFromClipTree();
+
void reset_raster_scale_to_unknown() { raster_scale_ = 0.f; }
// This flag is set when the layer needs to push properties to the impl
@@ -486,7 +541,6 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>,
gfx::PointF position_;
gfx::PointF anchor_point_;
SkColor background_color_;
- std::string debug_name_;
CompositingReasons compositing_reasons_;
float opacity_;
skia::RefPtr<SkImageFilter> filter_;
@@ -504,6 +558,11 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>,
bool use_parent_backface_visibility_;
bool draw_checkerboard_for_missing_tiles_;
bool force_render_surface_;
+ Layer* scroll_parent_;
+ scoped_ptr<std::set<Layer*> > scroll_children_;
+
+ Layer* clip_parent_;
+ scoped_ptr<std::set<Layer*> > clip_children_;
gfx::Transform transform_;
gfx::Transform sublayer_transform_;
@@ -514,6 +573,8 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>,
// Transient properties.
float raster_scale_;
+ LayerClient* client_;
+
ScopedPtrVector<CopyOutputRequest> copy_requests_;
base::Closure did_scroll_callback_;
diff --git a/chromium/cc/layers/layer_client.h b/chromium/cc/layers/layer_client.h
new file mode 100644
index 00000000000..73c0fd2514b
--- /dev/null
+++ b/chromium/cc/layers/layer_client.h
@@ -0,0 +1,24 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_LAYERS_LAYER_CLIENT_H_
+#define CC_LAYERS_LAYER_CLIENT_H_
+
+#include <string>
+
+#include "cc/base/cc_export.h"
+
+namespace cc {
+
+class CC_EXPORT LayerClient {
+ public:
+ virtual std::string DebugName() = 0;
+
+ protected:
+ virtual ~LayerClient() {}
+};
+
+} // namespace cc
+
+#endif // CC_LAYERS_LAYER_CLIENT_H_
diff --git a/chromium/cc/layers/layer_impl.cc b/chromium/cc/layers/layer_impl.cc
index b78cf1a45a8..b0c5399d32e 100644
--- a/chromium/cc/layers/layer_impl.cc
+++ b/chromium/cc/layers/layer_impl.cc
@@ -9,13 +9,14 @@
#include "cc/animation/animation_registrar.h"
#include "cc/animation/scrollbar_animation_controller.h"
#include "cc/animation/scrollbar_animation_controller_linear_fade.h"
+#include "cc/animation/scrollbar_animation_controller_thinning.h"
#include "cc/base/math_util.h"
#include "cc/debug/debug_colors.h"
#include "cc/debug/layer_tree_debug_state.h"
#include "cc/debug/traced_value.h"
#include "cc/input/layer_scroll_offset_delegate.h"
+#include "cc/layers/painted_scrollbar_layer_impl.h"
#include "cc/layers/quad_sink.h"
-#include "cc/layers/scrollbar_layer_impl.h"
#include "cc/output/copy_output_request.h"
#include "cc/quads/debug_border_draw_quad.h"
#include "cc/trees/layer_tree_impl.h"
@@ -29,6 +30,8 @@ namespace cc {
LayerImpl::LayerImpl(LayerTreeImpl* tree_impl, int id)
: parent_(NULL),
+ scroll_parent_(NULL),
+ clip_parent_(NULL),
mask_layer_id_(-1),
replica_layer_id_(-1),
layer_id_(id),
@@ -73,6 +76,24 @@ LayerImpl::~LayerImpl() {
layer_tree_impl_->UnregisterLayer(this);
layer_animation_controller_->RemoveValueObserver(this);
+
+ if (scroll_children_) {
+ for (std::set<LayerImpl*>::iterator it = scroll_children_->begin();
+ it != scroll_children_->end(); ++it)
+ (*it)->scroll_parent_ = NULL;
+ }
+
+ if (scroll_parent_)
+ scroll_parent_->RemoveScrollChild(this);
+
+ if (clip_children_) {
+ for (std::set<LayerImpl*>::iterator it = clip_children_->begin();
+ it != clip_children_->end(); ++it)
+ (*it)->clip_parent_ = NULL;
+ }
+
+ if (clip_parent_)
+ clip_parent_->RemoveClipChild(this);
}
void LayerImpl::AddChild(scoped_ptr<LayerImpl> child) {
@@ -104,6 +125,67 @@ void LayerImpl::ClearChildList() {
layer_tree_impl()->set_needs_update_draw_properties();
}
+bool LayerImpl::HasAncestor(const LayerImpl* ancestor) const {
+ if (!ancestor)
+ return false;
+
+ for (const LayerImpl* layer = this; layer; layer = layer->parent()) {
+ if (layer == ancestor)
+ return true;
+ }
+
+ return false;
+}
+
+void LayerImpl::SetScrollParent(LayerImpl* parent) {
+ if (scroll_parent_ == parent)
+ return;
+
+ // Having both a scroll parent and a scroll offset delegate is unsupported.
+ DCHECK(!scroll_offset_delegate_);
+
+ if (scroll_parent_)
+ scroll_parent_->RemoveScrollChild(this);
+
+ scroll_parent_ = parent;
+}
+
+void LayerImpl::SetScrollChildren(std::set<LayerImpl*>* children) {
+ if (scroll_children_.get() == children)
+ return;
+ scroll_children_.reset(children);
+}
+
+void LayerImpl::RemoveScrollChild(LayerImpl* child) {
+ DCHECK(scroll_children_);
+ scroll_children_->erase(child);
+ if (scroll_children_->empty())
+ scroll_children_.reset();
+}
+
+void LayerImpl::SetClipParent(LayerImpl* ancestor) {
+ if (clip_parent_ == ancestor)
+ return;
+
+ if (clip_parent_)
+ clip_parent_->RemoveClipChild(this);
+
+ clip_parent_ = ancestor;
+}
+
+void LayerImpl::SetClipChildren(std::set<LayerImpl*>* children) {
+ if (clip_children_.get() == children)
+ return;
+ clip_children_.reset(children);
+}
+
+void LayerImpl::RemoveClipChild(LayerImpl* child) {
+ DCHECK(clip_children_);
+ clip_children_->erase(child);
+ if (clip_children_->empty())
+ clip_children_.reset();
+}
+
void LayerImpl::PassCopyRequests(ScopedPtrVector<CopyOutputRequest>* requests) {
if (requests->empty())
return;
@@ -272,7 +354,7 @@ gfx::Vector2dF LayerImpl::ScrollBy(gfx::Vector2dF scroll) {
return unscrolled;
}
-void LayerImpl::ApplySentScrollDeltas() {
+void LayerImpl::ApplySentScrollDeltasFromAbortedCommit() {
// Pending tree never has sent scroll deltas
DCHECK(layer_tree_impl()->IsActiveTree());
@@ -292,6 +374,25 @@ void LayerImpl::ApplySentScrollDeltas() {
sent_scroll_delta_ = gfx::Vector2d();
}
+void LayerImpl::ApplyScrollDeltasSinceBeginFrame() {
+ // Only the pending tree can have missing scrolls.
+ DCHECK(layer_tree_impl()->IsPendingTree());
+ if (!scrollable())
+ return;
+
+ // Pending tree should never have sent scroll deltas.
+ DCHECK(sent_scroll_delta().IsZero());
+
+ LayerImpl* active_twin = layer_tree_impl()->FindActiveTreeLayerById(id());
+ if (active_twin) {
+ // Scrolls that happens after begin frame (where the sent scroll delta
+ // comes from) and commit need to be applied to the pending tree
+ // so that it is up to date with the total scroll.
+ SetScrollDelta(active_twin->ScrollDelta() -
+ active_twin->sent_scroll_delta());
+ }
+}
+
InputHandler::ScrollStatus LayerImpl::TryScroll(
gfx::PointF screen_space_point,
InputHandler::ScrollInputType type) const {
@@ -419,9 +520,40 @@ void LayerImpl::PushPropertiesTo(LayerImpl* layer) {
layer->SetTransform(transform_);
layer->SetScrollable(scrollable_);
- layer->SetScrollOffset(scroll_offset_);
+ layer->SetScrollOffsetAndDelta(
+ scroll_offset_, layer->ScrollDelta() - layer->sent_scroll_delta());
+ layer->SetSentScrollDelta(gfx::Vector2d());
+
layer->SetMaxScrollOffset(max_scroll_offset_);
+ LayerImpl* scroll_parent = NULL;
+ if (scroll_parent_)
+ scroll_parent = layer->layer_tree_impl()->LayerById(scroll_parent_->id());
+
+ layer->SetScrollParent(scroll_parent);
+ if (scroll_children_) {
+ std::set<LayerImpl*>* scroll_children = new std::set<LayerImpl*>;
+ for (std::set<LayerImpl*>::iterator it = scroll_children_->begin();
+ it != scroll_children_->end(); ++it)
+ scroll_children->insert(layer->layer_tree_impl()->LayerById((*it)->id()));
+ layer->SetScrollChildren(scroll_children);
+ }
+
+ LayerImpl* clip_parent = NULL;
+ if (clip_parent_) {
+ clip_parent = layer->layer_tree_impl()->LayerById(
+ clip_parent_->id());
+ }
+
+ layer->SetClipParent(clip_parent);
+ if (clip_children_) {
+ std::set<LayerImpl*>* clip_children = new std::set<LayerImpl*>;
+ for (std::set<LayerImpl*>::iterator it = clip_children_->begin();
+ it != clip_children_->end(); ++it)
+ clip_children->insert(layer->layer_tree_impl()->LayerById((*it)->id()));
+ layer->SetClipChildren(clip_children);
+ }
+
layer->PassCopyRequests(&copy_requests_);
// If the main thread commits multiple times before the impl thread actually
@@ -431,9 +563,6 @@ void LayerImpl::PushPropertiesTo(LayerImpl* layer) {
update_rect_.Union(layer->update_rect());
layer->set_update_rect(update_rect_);
- layer->SetScrollDelta(layer->ScrollDelta() - layer->sent_scroll_delta());
- layer->SetSentScrollDelta(gfx::Vector2d());
-
layer->SetStackingOrderChanged(stacking_order_changed_);
// Reset any state that should be cleared for the next update.
@@ -622,7 +751,7 @@ scoped_ptr<LayerImpl> LayerImpl::TakeReplicaLayer() {
return replica_layer_.Pass();
}
-ScrollbarLayerImpl* LayerImpl::ToScrollbarLayer() {
+ScrollbarLayerImplBase* LayerImpl::ToScrollbarLayer() {
return NULL;
}
@@ -847,10 +976,11 @@ void LayerImpl::UpdateScrollbarPositions() {
return;
last_scroll_offset_ = current_offset;
- if (scrollbar_animation_controller_ &&
- !scrollbar_animation_controller_->IsScrollGestureInProgress()) {
- scrollbar_animation_controller_->DidProgrammaticallyUpdateScroll(
+ if (scrollbar_animation_controller_) {
+ bool should_animate = scrollbar_animation_controller_->DidScrollUpdate(
layer_tree_impl_->CurrentPhysicalTimeTicks());
+ if (should_animate)
+ layer_tree_impl_->StartScrollbarAnimation();
}
// Get the current_offset_.y() value for a sanity-check on scrolling
@@ -863,6 +993,8 @@ void LayerImpl::UpdateScrollbarPositions() {
void LayerImpl::SetScrollOffsetDelegate(
LayerScrollOffsetDelegate* scroll_offset_delegate) {
+ // Having both a scroll parent and a scroll offset delegate is unsupported.
+ DCHECK(!scroll_parent_);
if (!scroll_offset_delegate && scroll_offset_delegate_) {
scroll_delta_ =
scroll_offset_delegate_->GetTotalScrollOffset() - scroll_offset_;
@@ -874,16 +1006,49 @@ void LayerImpl::SetScrollOffsetDelegate(
}
void LayerImpl::SetScrollOffset(gfx::Vector2d scroll_offset) {
- if (scroll_offset_ == scroll_offset)
- return;
+ SetScrollOffsetAndDelta(scroll_offset, ScrollDelta());
+}
- scroll_offset_ = scroll_offset;
+void LayerImpl::SetScrollOffsetAndDelta(gfx::Vector2d scroll_offset,
+ gfx::Vector2dF scroll_delta) {
+ bool changed = false;
- if (scroll_offset_delegate_)
- scroll_offset_delegate_->SetTotalScrollOffset(TotalScrollOffset());
+ if (scroll_offset_ != scroll_offset) {
+ changed = true;
+ scroll_offset_ = scroll_offset;
- NoteLayerPropertyChangedForSubtree();
- UpdateScrollbarPositions();
+ if (scroll_offset_delegate_)
+ scroll_offset_delegate_->SetTotalScrollOffset(TotalScrollOffset());
+ }
+
+ if (ScrollDelta() != scroll_delta) {
+ changed = true;
+ if (layer_tree_impl()->IsActiveTree()) {
+ LayerImpl* pending_twin =
+ layer_tree_impl()->FindPendingTreeLayerById(id());
+ if (pending_twin) {
+ // The pending twin can't mirror the scroll delta of the active
+ // layer. Although the delta - sent scroll delta difference is
+ // identical for both twins, the sent scroll delta for the pending
+ // layer is zero, as anything that has been sent has been baked
+ // into the layer's position/scroll offset as a part of commit.
+ DCHECK(pending_twin->sent_scroll_delta().IsZero());
+ pending_twin->SetScrollDelta(scroll_delta - sent_scroll_delta());
+ }
+ }
+
+ if (scroll_offset_delegate_) {
+ scroll_offset_delegate_->SetTotalScrollOffset(scroll_offset_ +
+ scroll_delta);
+ } else {
+ scroll_delta_ = scroll_delta;
+ }
+ }
+
+ if (changed) {
+ NoteLayerPropertyChangedForSubtree();
+ UpdateScrollbarPositions();
+ }
}
gfx::Vector2dF LayerImpl::ScrollDelta() const {
@@ -893,31 +1058,7 @@ gfx::Vector2dF LayerImpl::ScrollDelta() const {
}
void LayerImpl::SetScrollDelta(gfx::Vector2dF scroll_delta) {
- if (ScrollDelta() == scroll_delta)
- return;
-
- if (layer_tree_impl()->IsActiveTree()) {
- LayerImpl* pending_twin = layer_tree_impl()->FindPendingTreeLayerById(id());
- if (pending_twin) {
- // The pending twin can't mirror the scroll delta of the active
- // layer. Although the delta - sent scroll delta difference is
- // identical for both twins, the sent scroll delta for the pending
- // layer is zero, as anything that has been sent has been baked
- // into the layer's position/scroll offset as a part of commit.
- DCHECK(pending_twin->sent_scroll_delta().IsZero());
- pending_twin->SetScrollDelta(scroll_delta - sent_scroll_delta());
- }
- }
-
- if (scroll_offset_delegate_) {
- scroll_offset_delegate_->SetTotalScrollOffset(
- scroll_offset_ + scroll_delta);
- } else {
- scroll_delta_ = scroll_delta;
- }
-
- NoteLayerPropertyChangedForSubtree();
- UpdateScrollbarPositions();
+ SetScrollOffsetAndDelta(scroll_offset_, scroll_delta);
}
gfx::Vector2dF LayerImpl::TotalScrollOffset() const {
@@ -951,42 +1092,55 @@ void LayerImpl::SetMaxScrollOffset(gfx::Vector2d max_scroll_offset) {
UpdateScrollbarPositions();
}
-void LayerImpl::SetScrollbarOpacity(float opacity) {
- if (horizontal_scrollbar_layer_)
- horizontal_scrollbar_layer_->SetOpacity(opacity);
- if (vertical_scrollbar_layer_)
- vertical_scrollbar_layer_->SetOpacity(opacity);
-}
-
void LayerImpl::DidBecomeActive() {
- if (!layer_tree_impl_->settings().use_linear_fade_scrollbar_animator)
+ if (layer_tree_impl_->settings().scrollbar_animator ==
+ LayerTreeSettings::NoAnimator) {
return;
+ }
bool need_scrollbar_animation_controller = horizontal_scrollbar_layer_ ||
vertical_scrollbar_layer_;
- if (need_scrollbar_animation_controller) {
- if (!scrollbar_animation_controller_) {
- base::TimeDelta fadeout_delay = base::TimeDelta::FromMilliseconds(
- layer_tree_impl_->settings().scrollbar_linear_fade_delay_ms);
- base::TimeDelta fadeout_length = base::TimeDelta::FromMilliseconds(
- layer_tree_impl_->settings().scrollbar_linear_fade_length_ms);
- scrollbar_animation_controller_ =
- ScrollbarAnimationControllerLinearFade::Create(
- this, fadeout_delay, fadeout_length)
- .PassAs<ScrollbarAnimationController>();
- }
- } else {
+ if (!need_scrollbar_animation_controller) {
scrollbar_animation_controller_.reset();
+ return;
+ }
+
+ if (scrollbar_animation_controller_)
+ return;
+
+ switch (layer_tree_impl_->settings().scrollbar_animator) {
+ case LayerTreeSettings::LinearFade: {
+ base::TimeDelta fadeout_delay = base::TimeDelta::FromMilliseconds(
+ layer_tree_impl_->settings().scrollbar_linear_fade_delay_ms);
+ base::TimeDelta fadeout_length = base::TimeDelta::FromMilliseconds(
+ layer_tree_impl_->settings().scrollbar_linear_fade_length_ms);
+
+ scrollbar_animation_controller_ =
+ ScrollbarAnimationControllerLinearFade::Create(
+ this, fadeout_delay, fadeout_length)
+ .PassAs<ScrollbarAnimationController>();
+ break;
+ }
+ case LayerTreeSettings::Thinning: {
+ scrollbar_animation_controller_ =
+ ScrollbarAnimationControllerThinning::Create(this)
+ .PassAs<ScrollbarAnimationController>();
+ break;
+ }
+ case LayerTreeSettings::NoAnimator:
+ NOTREACHED();
+ break;
}
}
void LayerImpl::SetHorizontalScrollbarLayer(
- ScrollbarLayerImpl* scrollbar_layer) {
+ ScrollbarLayerImplBase* scrollbar_layer) {
horizontal_scrollbar_layer_ = scrollbar_layer;
if (horizontal_scrollbar_layer_)
horizontal_scrollbar_layer_->set_scroll_layer_id(id());
}
-void LayerImpl::SetVerticalScrollbarLayer(ScrollbarLayerImpl* scrollbar_layer) {
+void LayerImpl::SetVerticalScrollbarLayer(
+ ScrollbarLayerImplBase* scrollbar_layer) {
vertical_scrollbar_layer_ = scrollbar_layer;
if (vertical_scrollbar_layer_)
vertical_scrollbar_layer_->set_scroll_layer_id(id());
@@ -1034,9 +1188,6 @@ CompositingReasonsAsValue(CompositingReasons reasons) {
if (reasons & kCompositingReasonOverflowScrollingTouch)
reason_list->AppendString("Is a scrollable overflow element");
- if (reasons & kCompositingReasonBlending)
- reason_list->AppendString("Has a blend mode");
-
if (reasons & kCompositingReasonAssumedOverlap)
reason_list->AppendString("Might overlap a composited animation");
@@ -1109,12 +1260,19 @@ CompositingReasonsAsValue(CompositingReasons reasons) {
if (reasons & kCompositingReasonLayerForMask)
reason_list->AppendString("Is a mask layer");
+ if (reasons & kCompositingReasonOverflowScrollingParent)
+ reason_list->AppendString("Scroll parent is not an ancestor");
+
+ if (reasons & kCompositingReasonOutOfFlowClipping)
+ reason_list->AppendString("Has clipping ancestor");
+
return reason_list.PassAs<base::Value>();
}
void LayerImpl::AsValueInto(base::DictionaryValue* state) const {
TracedValue::MakeDictIntoImplicitSnapshot(state, LayerTypeAsString(), this);
state->SetInteger("layer_id", id());
+ state->SetString("layer_name", debug_name());
state->Set("bounds", MathUtil::AsValue(bounds()).release());
state->SetInteger("draws_content", DrawsContent());
state->SetInteger("gpu_memory_usage", GPUMemoryUsageInBytes());
@@ -1128,6 +1286,20 @@ void LayerImpl::AsValueInto(base::DictionaryValue* state) const {
&clipped);
state->Set("layer_quad", MathUtil::AsValue(layer_quad).release());
+ if (!touch_event_handler_region_.IsEmpty()) {
+ state->Set("touch_event_handler_region",
+ touch_event_handler_region_.AsValue().release());
+ }
+ if (have_wheel_event_handlers_) {
+ gfx::Rect wheel_rect(content_bounds());
+ Region wheel_region(wheel_rect);
+ state->Set("wheel_event_handler_region",
+ wheel_region.AsValue().release());
+ }
+ if (!non_fast_scrollable_region_.IsEmpty()) {
+ state->Set("non_fast_scrollable_region",
+ non_fast_scrollable_region_.AsValue().release());
+ }
scoped_ptr<base::ListValue> children_list(new base::ListValue());
for (size_t i = 0; i < children_.size(); ++i)
@@ -1137,6 +1309,12 @@ void LayerImpl::AsValueInto(base::DictionaryValue* state) const {
state->Set("mask_layer", mask_layer_->AsValue().release());
if (replica_layer_)
state->Set("replica_layer", replica_layer_->AsValue().release());
+
+ if (scroll_parent_)
+ state->SetInteger("scroll_parent", scroll_parent_->id());
+
+ if (clip_parent_)
+ state->SetInteger("clip_parent", clip_parent_->id());
}
size_t LayerImpl::GPUMemoryUsageInBytes() const { return 0; }
diff --git a/chromium/cc/layers/layer_impl.h b/chromium/cc/layers/layer_impl.h
index 90da41a1c6c..ba0ff7b8216 100644
--- a/chromium/cc/layers/layer_impl.h
+++ b/chromium/cc/layers/layer_impl.h
@@ -5,6 +5,7 @@
#ifndef CC_LAYERS_LAYER_IMPL_H_
#define CC_LAYERS_LAYER_IMPL_H_
+#include <set>
#include <string>
#include "base/logging.h"
@@ -44,7 +45,7 @@ class LayerTreeImpl;
class QuadSink;
class Renderer;
class ScrollbarAnimationController;
-class ScrollbarLayerImpl;
+class ScrollbarLayerImplBase;
class Layer;
struct AppendQuadsData;
@@ -83,6 +84,38 @@ class CC_EXPORT LayerImpl : LayerAnimationValueObserver {
// Warning: This does not preserve tree structure invariants.
void ClearChildList();
+ bool HasAncestor(const LayerImpl* ancestor) const;
+
+ void SetScrollParent(LayerImpl* parent);
+
+ LayerImpl* scroll_parent() { return scroll_parent_; }
+ const LayerImpl* scroll_parent() const { return scroll_parent_; }
+
+ void SetScrollChildren(std::set<LayerImpl*>* children);
+ void RemoveScrollChild(LayerImpl* child);
+
+ std::set<LayerImpl*>* scroll_children() { return scroll_children_.get(); }
+ const std::set<LayerImpl*>* scroll_children() const {
+ return scroll_children_.get();
+ }
+
+ void SetClipParent(LayerImpl* ancestor);
+
+ LayerImpl* clip_parent() {
+ return clip_parent_;
+ }
+ const LayerImpl* clip_parent() const {
+ return clip_parent_;
+ }
+
+ void SetClipChildren(std::set<LayerImpl*>* children);
+ void RemoveClipChild(LayerImpl* child);
+
+ std::set<LayerImpl*>* clip_children() { return clip_children_.get(); }
+ const std::set<LayerImpl*>* clip_children() const {
+ return clip_children_.get();
+ }
+
void PassCopyRequests(ScopedPtrVector<CopyOutputRequest>* requests);
void TakeCopyRequestsAndTransformToTarget(
ScopedPtrVector<CopyOutputRequest>* request);
@@ -128,7 +161,7 @@ class CC_EXPORT LayerImpl : LayerAnimationValueObserver {
virtual void UpdateTilePriorities() {}
- virtual ScrollbarLayerImpl* ToScrollbarLayer();
+ virtual ScrollbarLayerImplBase* ToScrollbarLayer();
// Returns true if this layer has content to draw.
void SetDrawsContent(bool draws_content);
@@ -284,6 +317,9 @@ class CC_EXPORT LayerImpl : LayerAnimationValueObserver {
RenderSurfaceImpl* render_surface() const {
return draw_properties_.render_surface.get();
}
+ int num_unclipped_descendants() const {
+ return draw_properties_.num_unclipped_descendants;
+ }
// The client should be responsible for setting bounds, content bounds and
// contents scale to appropriate values. LayerImpl doesn't calculate any of
@@ -310,6 +346,8 @@ class CC_EXPORT LayerImpl : LayerAnimationValueObserver {
void SetScrollOffsetDelegate(
LayerScrollOffsetDelegate* scroll_offset_delegate);
void SetScrollOffset(gfx::Vector2d scroll_offset);
+ void SetScrollOffsetAndDelta(gfx::Vector2d scroll_offset,
+ gfx::Vector2dF scroll_delta);
gfx::Vector2d scroll_offset() const { return scroll_offset_; }
void SetMaxScrollOffset(gfx::Vector2d max_scroll_offset);
@@ -330,7 +368,8 @@ class CC_EXPORT LayerImpl : LayerAnimationValueObserver {
void SetScrollable(bool scrollable) { scrollable_ = scrollable; }
bool scrollable() const { return scrollable_; }
- void ApplySentScrollDeltas();
+ void ApplySentScrollDeltasFromAbortedCommit();
+ void ApplyScrollDeltasSinceBeginFrame();
void SetShouldScrollOnMainThread(bool should_scroll_on_main_thread) {
should_scroll_on_main_thread_ = should_scroll_on_main_thread;
@@ -413,15 +452,13 @@ class CC_EXPORT LayerImpl : LayerAnimationValueObserver {
return scrollbar_animation_controller_.get();
}
- void SetScrollbarOpacity(float opacity);
-
- void SetHorizontalScrollbarLayer(ScrollbarLayerImpl* scrollbar_layer);
- ScrollbarLayerImpl* horizontal_scrollbar_layer() {
+ void SetHorizontalScrollbarLayer(ScrollbarLayerImplBase* scrollbar_layer);
+ ScrollbarLayerImplBase* horizontal_scrollbar_layer() {
return horizontal_scrollbar_layer_;
}
- void SetVerticalScrollbarLayer(ScrollbarLayerImpl* scrollbar_layer);
- ScrollbarLayerImpl* vertical_scrollbar_layer() {
+ void SetVerticalScrollbarLayer(ScrollbarLayerImplBase* scrollbar_layer);
+ ScrollbarLayerImplBase* vertical_scrollbar_layer() {
return vertical_scrollbar_layer_;
}
@@ -475,6 +512,18 @@ class CC_EXPORT LayerImpl : LayerAnimationValueObserver {
// Properties internal to LayerImpl
LayerImpl* parent_;
OwnedLayerImplList children_;
+
+ LayerImpl* scroll_parent_;
+
+ // Storing a pointer to a set rather than a set since this will be rarely
+ // used. If this pointer turns out to be too heavy, we could have this (and
+ // the scroll parent above) be stored in a LayerImpl -> scroll_info
+ // map somewhere.
+ scoped_ptr<std::set<LayerImpl*> > scroll_children_;
+
+ LayerImpl* clip_parent_;
+ scoped_ptr<std::set<LayerImpl*> > clip_children_;
+
// mask_layer_ can be temporarily stolen during tree sync, we need this ID to
// confirm newly assigned layer is still the previous one
int mask_layer_id_;
@@ -567,8 +616,8 @@ class CC_EXPORT LayerImpl : LayerAnimationValueObserver {
// Weak pointers to this layer's scrollbars, if it has them. Updated during
// tree synchronization.
- ScrollbarLayerImpl* horizontal_scrollbar_layer_;
- ScrollbarLayerImpl* vertical_scrollbar_layer_;
+ ScrollbarLayerImplBase* horizontal_scrollbar_layer_;
+ ScrollbarLayerImplBase* vertical_scrollbar_layer_;
ScopedPtrVector<CopyOutputRequest> copy_requests_;
diff --git a/chromium/cc/layers/layer_impl_unittest.cc b/chromium/cc/layers/layer_impl_unittest.cc
index 9c6dbfe3790..1ab7a71b2ca 100644
--- a/chromium/cc/layers/layer_impl_unittest.cc
+++ b/chromium/cc/layers/layer_impl_unittest.cc
@@ -502,7 +502,7 @@ TEST_F(LayerImplScrollTest, ApplySentScrollsNoDelegate) {
EXPECT_VECTOR_EQ(scroll_offset, layer()->scroll_offset());
EXPECT_VECTOR_EQ(sent_scroll_delta, layer()->sent_scroll_delta());
- layer()->ApplySentScrollDeltas();
+ layer()->ApplySentScrollDeltasFromAbortedCommit();
EXPECT_VECTOR_EQ(scroll_offset + scroll_delta, layer()->TotalScrollOffset());
EXPECT_VECTOR_EQ(scroll_delta - sent_scroll_delta, layer()->ScrollDelta());
@@ -527,7 +527,7 @@ TEST_F(LayerImplScrollTest, ApplySentScrollsWithIgnoringDelegate) {
EXPECT_VECTOR_EQ(scroll_offset, layer()->scroll_offset());
EXPECT_VECTOR_EQ(sent_scroll_delta, layer()->sent_scroll_delta());
- layer()->ApplySentScrollDeltas();
+ layer()->ApplySentScrollDeltasFromAbortedCommit();
EXPECT_VECTOR_EQ(fixed_offset, layer()->TotalScrollOffset());
EXPECT_VECTOR_EQ(scroll_offset + sent_scroll_delta, layer()->scroll_offset());
@@ -551,7 +551,7 @@ TEST_F(LayerImplScrollTest, ApplySentScrollsWithAcceptingDelegate) {
EXPECT_VECTOR_EQ(scroll_offset, layer()->scroll_offset());
EXPECT_VECTOR_EQ(sent_scroll_delta, layer()->sent_scroll_delta());
- layer()->ApplySentScrollDeltas();
+ layer()->ApplySentScrollDeltasFromAbortedCommit();
EXPECT_VECTOR_EQ(scroll_offset + scroll_delta, layer()->TotalScrollOffset());
EXPECT_VECTOR_EQ(scroll_offset + sent_scroll_delta, layer()->scroll_offset());
diff --git a/chromium/cc/layers/layer_unittest.cc b/chromium/cc/layers/layer_unittest.cc
index bd1e16fa658..c5c5286d2aa 100644
--- a/chromium/cc/layers/layer_unittest.cc
+++ b/chromium/cc/layers/layer_unittest.cc
@@ -559,7 +559,8 @@ TEST_F(LayerTest, CheckPropertyChangeCausesCorrectBehavior) {
EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetTransform(
gfx::Transform(0.0, 0.0, 0.0, 0.0, 0.0, 0.0)));
EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetDoubleSided(false));
- EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetDebugName("Test Layer"));
+ EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetTouchEventHandlerRegion(
+ gfx::Rect(10, 10)));
EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetDrawCheckerboardForMissingTiles(
!test_layer->DrawCheckerboardForMissingTiles()));
EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetForceRenderSurface(true));
diff --git a/chromium/cc/layers/nine_patch_layer.cc b/chromium/cc/layers/nine_patch_layer.cc
index e32f6e0b125..5e000072f28 100644
--- a/chromium/cc/layers/nine_patch_layer.cc
+++ b/chromium/cc/layers/nine_patch_layer.cc
@@ -8,16 +8,55 @@
#include "cc/resources/prioritized_resource.h"
#include "cc/resources/resource_update.h"
#include "cc/resources/resource_update_queue.h"
+#include "cc/resources/scoped_ui_resource.h"
+#include "cc/resources/ui_resource_bitmap.h"
#include "cc/trees/layer_tree_host.h"
namespace cc {
+
+namespace {
+
+class ScopedUIResourceHolder : public NinePatchLayer::UIResourceHolder {
+ public:
+ static scoped_ptr<ScopedUIResourceHolder> Create(LayerTreeHost* host,
+ const SkBitmap& skbitmap) {
+ return make_scoped_ptr(new ScopedUIResourceHolder(host, skbitmap));
+ }
+ virtual UIResourceId id() OVERRIDE { return resource_->id(); }
+
+ private:
+ ScopedUIResourceHolder(LayerTreeHost* host, const SkBitmap& skbitmap) {
+ resource_ = ScopedUIResource::Create(host, UIResourceBitmap(skbitmap));
+ }
+
+ scoped_ptr<ScopedUIResource> resource_;
+};
+
+class SharedUIResourceHolder : public NinePatchLayer::UIResourceHolder {
+ public:
+ static scoped_ptr<SharedUIResourceHolder> Create(UIResourceId id) {
+ return make_scoped_ptr(new SharedUIResourceHolder(id));
+ }
+
+ virtual UIResourceId id() OVERRIDE { return id_; }
+
+ private:
+ explicit SharedUIResourceHolder(UIResourceId id) : id_(id) {}
+
+ UIResourceId id_;
+};
+
+} // anonymous namespace
+
+
+NinePatchLayer::UIResourceHolder::~UIResourceHolder() {}
+
scoped_refptr<NinePatchLayer> NinePatchLayer::Create() {
return make_scoped_refptr(new NinePatchLayer());
}
-NinePatchLayer::NinePatchLayer()
- : bitmap_dirty_(false) {}
+NinePatchLayer::NinePatchLayer() : fill_center_(false) {}
NinePatchLayer::~NinePatchLayer() {}
@@ -26,90 +65,89 @@ scoped_ptr<LayerImpl> NinePatchLayer::CreateLayerImpl(
return NinePatchLayerImpl::Create(tree_impl, id()).PassAs<LayerImpl>();
}
-void NinePatchLayer::SetTexturePriorities(
- const PriorityCalculator& priority_calc) {
- if (resource_ && !resource_->texture()->resource_manager()) {
- // Release the resource here, as it is no longer tied to a resource manager.
- resource_.reset();
- if (!bitmap_.isNull())
- CreateResource();
- } else if (bitmap_dirty_ && DrawsContent()) {
- CreateResource();
- }
+void NinePatchLayer::SetLayerTreeHost(LayerTreeHost* host) {
+ if (host == layer_tree_host())
+ return;
- if (resource_) {
- resource_->texture()->set_request_priority(
- PriorityCalculator::UIPriority(true));
- GLenum texture_format =
- layer_tree_host()->GetRendererCapabilities().best_texture_format;
- resource_->texture()->SetDimensions(
- gfx::Size(bitmap_.width(), bitmap_.height()), texture_format);
- }
-}
+ Layer::SetLayerTreeHost(host);
-void NinePatchLayer::SetBitmap(const SkBitmap& bitmap, gfx::Rect aperture) {
- bitmap_ = bitmap;
- image_aperture_ = aperture;
- bitmap_dirty_ = true;
- SetNeedsDisplay();
+ // Recreate the resource hold against the new LTH.
+ RecreateUIResourceHolder();
}
-bool NinePatchLayer::Update(ResourceUpdateQueue* queue,
- const OcclusionTracker* occlusion) {
- bool updated = Layer::Update(queue, occlusion);
-
- CreateUpdaterIfNeeded();
-
- if (resource_ &&
- (bitmap_dirty_ || resource_->texture()->resource_id() == 0)) {
- gfx::Rect content_rect(0, 0, bitmap_.width(), bitmap_.height());
- ResourceUpdate upload = ResourceUpdate::Create(resource_->texture(),
- &bitmap_,
- content_rect,
- content_rect,
- gfx::Vector2d());
- queue->AppendFullUpload(upload);
- bitmap_dirty_ = false;
- updated = true;
- }
- return updated;
+void NinePatchLayer::RecreateUIResourceHolder() {
+ ui_resource_holder_.reset();
+ if (!layer_tree_host() || bitmap_.empty())
+ return;
+
+ ui_resource_holder_ =
+ ScopedUIResourceHolder::Create(layer_tree_host(), bitmap_);
}
-void NinePatchLayer::CreateUpdaterIfNeeded() {
- if (updater_.get())
+void NinePatchLayer::SetBorder(gfx::Rect border) {
+ if (border == border_)
return;
+ border_ = border;
+ SetNeedsCommit();
+}
- updater_ = ImageLayerUpdater::Create();
+void NinePatchLayer::SetBitmap(const SkBitmap& skbitmap, gfx::Rect aperture) {
+ image_aperture_ = aperture;
+ bitmap_ = skbitmap;
+
+ // TODO(ccameron): Remove this. This provides the default border that was
+ // provided before borders were required to be explicitly provided. Once Blink
+ // fixes its callers to call SetBorder, this can be removed.
+ SetBorder(gfx::Rect(aperture.x(),
+ aperture.y(),
+ skbitmap.width() - aperture.width(),
+ skbitmap.height() - aperture.height()));
+ RecreateUIResourceHolder();
+ SetNeedsCommit();
}
-void NinePatchLayer::CreateResource() {
- DCHECK(!bitmap_.isNull());
- CreateUpdaterIfNeeded();
- updater_->SetBitmap(bitmap_);
+void NinePatchLayer::SetUIResourceId(UIResourceId resource_id,
+ gfx::Rect aperture) {
+ if (ui_resource_holder_ && ui_resource_holder_->id() == resource_id &&
+ image_aperture_ == aperture)
+ return;
- if (!resource_) {
- resource_ = updater_->CreateResource(
- layer_tree_host()->contents_texture_manager());
+ image_aperture_ = aperture;
+ if (resource_id) {
+ ui_resource_holder_ = SharedUIResourceHolder::Create(resource_id);
+ } else {
+ ui_resource_holder_.reset();
}
+
+ SetNeedsCommit();
+}
+
+void NinePatchLayer::SetFillCenter(bool fill_center) {
+ if (fill_center_ == fill_center)
+ return;
+
+ fill_center_ = fill_center;
+ SetNeedsCommit();
}
bool NinePatchLayer::DrawsContent() const {
- bool draws = !bitmap_.isNull() &&
- Layer::DrawsContent() &&
- bitmap_.width() &&
- bitmap_.height();
- return draws;
+ return ui_resource_holder_ && ui_resource_holder_->id() &&
+ Layer::DrawsContent();
}
void NinePatchLayer::PushPropertiesTo(LayerImpl* layer) {
Layer::PushPropertiesTo(layer);
NinePatchLayerImpl* layer_impl = static_cast<NinePatchLayerImpl*>(layer);
- if (resource_) {
- DCHECK(!bitmap_.isNull());
- layer_impl->SetResourceId(resource_->texture()->resource_id());
- layer_impl->SetLayout(
- gfx::Size(bitmap_.width(), bitmap_.height()), image_aperture_);
+ if (!ui_resource_holder_) {
+ layer_impl->SetUIResourceId(0);
+ } else {
+ DCHECK(layer_tree_host());
+
+ gfx::Size image_size =
+ layer_tree_host()->GetUIResourceSize(ui_resource_holder_->id());
+ layer_impl->SetUIResourceId(ui_resource_holder_->id());
+ layer_impl->SetLayout(image_size, image_aperture_, border_, fill_center_);
}
}
diff --git a/chromium/cc/layers/nine_patch_layer.h b/chromium/cc/layers/nine_patch_layer.h
index 35d08812cff..5880635389a 100644
--- a/chromium/cc/layers/nine_patch_layer.h
+++ b/chromium/cc/layers/nine_patch_layer.h
@@ -8,47 +8,60 @@
#include "base/memory/scoped_ptr.h"
#include "cc/base/cc_export.h"
#include "cc/layers/layer.h"
-#include "cc/resources/image_layer_updater.h"
-#include "third_party/skia/include/core/SkBitmap.h"
+#include "cc/resources/ui_resource_client.h"
#include "ui/gfx/rect.h"
namespace cc {
-class ResourceUpdateQueue;
+class LayerTreeHost;
+class ScopedUIResource;
class CC_EXPORT NinePatchLayer : public Layer {
public:
static scoped_refptr<NinePatchLayer> Create();
virtual bool DrawsContent() const OVERRIDE;
- virtual void SetTexturePriorities(const PriorityCalculator& priority_calc)
- OVERRIDE;
- virtual bool Update(ResourceUpdateQueue* queue,
- const OcclusionTracker* occlusion) OVERRIDE;
+
virtual void PushPropertiesTo(LayerImpl* layer) OVERRIDE;
+ virtual void SetLayerTreeHost(LayerTreeHost* host) OVERRIDE;
+
+ // |border| is the space around the center rectangular region in layer space
+ // (known as aperture in image space). |border.x()| and |border.y()| are the
+ // size of the left and top boundary, respectively.
+ // |border.width()-border.x()| and |border.height()-border.y()| are the size
+ // of the right and bottom boundary, respectively.
+ void SetBorder(gfx::Rect border);
+
// aperture is in the pixel space of the bitmap resource and refers to
// the center patch of the ninepatch (which is unused in this
// implementation). We split off eight rects surrounding it and stick them
// on the edges of the layer. The corners are unscaled, the top and bottom
// rects are x-stretched to fit, and the left and right rects are
// y-stretched to fit.
- void SetBitmap(const SkBitmap& bitmap, gfx::Rect aperture);
+ void SetBitmap(const SkBitmap& skbitmap, gfx::Rect aperture);
+
+ // An alternative way of setting the resource to allow for sharing.
+ void SetUIResourceId(UIResourceId resource_id, gfx::Rect aperture);
+ void SetFillCenter(bool fill_center);
+
+ class UIResourceHolder {
+ public:
+ virtual UIResourceId id() = 0;
+ virtual ~UIResourceHolder();
+ };
private:
NinePatchLayer();
virtual ~NinePatchLayer();
virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl)
OVERRIDE;
+ void RecreateUIResourceHolder();
- void CreateUpdaterIfNeeded();
- void CreateResource();
-
- scoped_refptr<ImageLayerUpdater> updater_;
- scoped_ptr<LayerUpdater::Resource> resource_;
-
+ gfx::Rect border_;
+ bool fill_center_;
+ scoped_ptr<UIResourceHolder> ui_resource_holder_;
SkBitmap bitmap_;
- bool bitmap_dirty_;
// The transparent center region that shows the parent layer's contents in
// image space.
diff --git a/chromium/cc/layers/nine_patch_layer_impl.cc b/chromium/cc/layers/nine_patch_layer_impl.cc
index 103f7656eb8..f609c3d2fda 100644
--- a/chromium/cc/layers/nine_patch_layer_impl.cc
+++ b/chromium/cc/layers/nine_patch_layer_impl.cc
@@ -6,15 +6,18 @@
#include "base/strings/stringprintf.h"
#include "base/values.h"
+#include "cc/base/math_util.h"
#include "cc/layers/quad_sink.h"
#include "cc/quads/texture_draw_quad.h"
+#include "cc/trees/layer_tree_impl.h"
#include "ui/gfx/rect_f.h"
namespace cc {
NinePatchLayerImpl::NinePatchLayerImpl(LayerTreeImpl* tree_impl, int id)
: LayerImpl(tree_impl, id),
- resource_id_(0) {}
+ fill_center_(false),
+ ui_resource_id_(0) {}
NinePatchLayerImpl::~NinePatchLayerImpl() {}
@@ -31,11 +34,8 @@ void NinePatchLayerImpl::PushPropertiesTo(LayerImpl* layer) {
LayerImpl::PushPropertiesTo(layer);
NinePatchLayerImpl* layer_impl = static_cast<NinePatchLayerImpl*>(layer);
- if (!resource_id_)
- return;
-
- layer_impl->SetResourceId(resource_id_);
- layer_impl->SetLayout(image_bounds_, image_aperture_);
+ layer_impl->SetUIResourceId(ui_resource_id_);
+ layer_impl->SetLayout(image_bounds_, image_aperture_, border_, fill_center_);
}
static gfx::RectF NormalizedRect(float x,
@@ -50,120 +50,188 @@ static gfx::RectF NormalizedRect(float x,
height / total_height);
}
-void NinePatchLayerImpl::SetLayout(gfx::Size image_bounds, gfx::Rect aperture) {
+void NinePatchLayerImpl::SetUIResourceId(UIResourceId uid) {
+ if (uid == ui_resource_id_)
+ return;
+ ui_resource_id_ = uid;
+ NoteLayerPropertyChanged();
+}
+
+void NinePatchLayerImpl::SetLayout(gfx::Size image_bounds,
+ gfx::Rect aperture,
+ gfx::Rect border,
+ bool fill_center) {
+ // This check imposes an ordering on the call sequence. An UIResource must
+ // exist before SetLayout can be called.
+ DCHECK(ui_resource_id_);
+
+ // TODO(ccameron): the following "greater than or equal to" (GE) checks should
+ // be greater than (GT) to avoid degenerate nine-patches. The relaxed
+ // condition "equal to" is a workaround for the overhang shadow use case and
+ // should be investigated further.
+
+ // |border| is in layer space. It cannot exceed the bounds of the layer.
+ DCHECK(!border.size().IsEmpty());
+ DCHECK_GE(bounds().width(), border.width());
+ DCHECK_GE(bounds().height(), border.height());
+
+ // Sanity Check on |border|
+ DCHECK_LT(border.x(), border.width());
+ DCHECK_LT(border.y(), border.height());
+ DCHECK_GE(border.x(), 0);
+ DCHECK_GE(border.y(), 0);
+
+ // |aperture| is in image space. It cannot exceed the bounds of the bitmap.
+ DCHECK(!aperture.size().IsEmpty());
+ DCHECK(gfx::Rect(image_bounds.width(), image_bounds.height())
+ .Contains(aperture));
+
+ // Avoid the degenerate cases where the aperture touches the edge of the
+ // image.
+ DCHECK_LT(aperture.width(), image_bounds.width() - 1);
+ DCHECK_LT(aperture.height(), image_bounds.height() - 1);
+ DCHECK_GT(aperture.x(), 0);
+ DCHECK_GT(aperture.y(), 0);
+
+ if (image_bounds_ == image_bounds && image_aperture_ == aperture &&
+ border_ == border && fill_center_ == fill_center)
+ return;
+
image_bounds_ = image_bounds;
image_aperture_ = aperture;
+ border_ = border;
+ fill_center_ = fill_center;
+
+ NoteLayerPropertyChanged();
}
bool NinePatchLayerImpl::WillDraw(DrawMode draw_mode,
ResourceProvider* resource_provider) {
- if (!resource_id_ || draw_mode == DRAW_MODE_RESOURCELESS_SOFTWARE)
+ if (!ui_resource_id_ || draw_mode == DRAW_MODE_RESOURCELESS_SOFTWARE)
return false;
return LayerImpl::WillDraw(draw_mode, resource_provider);
}
void NinePatchLayerImpl::AppendQuads(QuadSink* quad_sink,
AppendQuadsData* append_quads_data) {
- DCHECK(resource_id_);
-
SharedQuadState* shared_quad_state =
quad_sink->UseSharedQuadState(CreateSharedQuadState());
AppendDebugBorderQuad(quad_sink, shared_quad_state, append_quads_data);
+ if (!ui_resource_id_)
+ return;
+
+ ResourceProvider::ResourceId resource =
+ layer_tree_impl()->ResourceIdForUIResource(ui_resource_id_);
+
+ if (!resource)
+ return;
+
static const bool flipped = false;
static const bool premultiplied_alpha = true;
DCHECK(!bounds().IsEmpty());
- // NinePatch border widths in bitmap pixel space
- int left_width = image_aperture_.x();
- int top_height = image_aperture_.y();
- int right_width = image_bounds_.width() - image_aperture_.right();
- int bottom_height = image_bounds_.height() - image_aperture_.bottom();
+ // NinePatch border widths in layer space.
+ int layer_left_width = border_.x();
+ int layer_top_height = border_.y();
+ int layer_right_width = border_.width() - layer_left_width;
+ int layer_bottom_height = border_.height() - layer_top_height;
- // If layer can't fit the corners, clip to show the outer edges of the
- // image.
- int corner_total_width = left_width + right_width;
- int middle_width = bounds().width() - corner_total_width;
- if (middle_width < 0) {
- float left_width_proportion =
- static_cast<float>(left_width) / corner_total_width;
- int left_width_crop = middle_width * left_width_proportion;
- left_width += left_width_crop;
- right_width = bounds().width() - left_width;
- middle_width = 0;
- }
- int corner_total_height = top_height + bottom_height;
- int middle_height = bounds().height() - corner_total_height;
- if (middle_height < 0) {
- float top_height_proportion =
- static_cast<float>(top_height) / corner_total_height;
- int top_height_crop = middle_height * top_height_proportion;
- top_height += top_height_crop;
- bottom_height = bounds().height() - top_height;
- middle_height = 0;
- }
+ int layer_middle_width = bounds().width() - border_.width();
+ int layer_middle_height = bounds().height() - border_.height();
// Patch positions in layer space
- gfx::Rect top_left(0, 0, left_width, top_height);
- gfx::Rect top_right(
- bounds().width() - right_width, 0, right_width, top_height);
- gfx::Rect bottom_left(
- 0, bounds().height() - bottom_height, left_width, bottom_height);
- gfx::Rect bottom_right(
- top_right.x(), bottom_left.y(), right_width, bottom_height);
- gfx::Rect top(top_left.right(), 0, middle_width, top_height);
- gfx::Rect left(0, top_left.bottom(), left_width, middle_height);
- gfx::Rect right(top_right.x(),
- top_right.bottom(),
- right_width,
- left.height());
- gfx::Rect bottom(top.x(), bottom_left.y(), top.width(), bottom_height);
-
- float img_width = image_bounds_.width();
- float img_height = image_bounds_.height();
-
+ gfx::Rect layer_top_left(0, 0, layer_left_width, layer_top_height);
+ gfx::Rect layer_top_right(bounds().width() - layer_right_width,
+ 0,
+ layer_right_width,
+ layer_top_height);
+ gfx::Rect layer_bottom_left(0,
+ bounds().height() - layer_bottom_height,
+ layer_left_width,
+ layer_bottom_height);
+ gfx::Rect layer_bottom_right(layer_top_right.x(),
+ layer_bottom_left.y(),
+ layer_right_width,
+ layer_bottom_height);
+ gfx::Rect layer_top(
+ layer_top_left.right(), 0, layer_middle_width, layer_top_height);
+ gfx::Rect layer_left(
+ 0, layer_top_left.bottom(), layer_left_width, layer_middle_height);
+ gfx::Rect layer_right(layer_top_right.x(),
+ layer_top_right.bottom(),
+ layer_right_width,
+ layer_left.height());
+ gfx::Rect layer_bottom(layer_top.x(),
+ layer_bottom_left.y(),
+ layer_top.width(),
+ layer_bottom_height);
+ gfx::Rect layer_center(layer_left_width,
+ layer_top_height,
+ layer_middle_width,
+ layer_middle_height);
+
+ // Note the following values are in image (bitmap) space.
+ float image_width = image_bounds_.width();
+ float image_height = image_bounds_.height();
+
+ int image_aperture_left_width = image_aperture_.x();
+ int image_aperture_top_height = image_aperture_.y();
+ int image_aperture_right_width = image_width - image_aperture_.right();
+ int image_aperture_bottom_height = image_height - image_aperture_.bottom();
// Patch positions in bitmap UV space (from zero to one)
gfx::RectF uv_top_left = NormalizedRect(0,
0,
- left_width,
- top_height,
- img_width,
- img_height);
- gfx::RectF uv_top_right = NormalizedRect(img_width - right_width,
- 0,
- right_width,
- top_height,
- img_width,
- img_height);
- gfx::RectF uv_bottom_left = NormalizedRect(0,
- img_height - bottom_height,
- left_width,
- bottom_height,
- img_width,
- img_height);
- gfx::RectF uv_bottom_right = NormalizedRect(img_width - right_width,
- img_height - bottom_height,
- right_width,
- bottom_height,
- img_width,
- img_height);
- gfx::RectF uv_top(uv_top_left.right(),
- 0,
- (img_width - left_width - right_width) / img_width,
- (top_height) / img_height);
+ image_aperture_left_width,
+ image_aperture_top_height,
+ image_width,
+ image_height);
+ gfx::RectF uv_top_right =
+ NormalizedRect(image_width - image_aperture_right_width,
+ 0,
+ image_aperture_right_width,
+ image_aperture_top_height,
+ image_width,
+ image_height);
+ gfx::RectF uv_bottom_left =
+ NormalizedRect(0,
+ image_height - image_aperture_bottom_height,
+ image_aperture_left_width,
+ image_aperture_bottom_height,
+ image_width,
+ image_height);
+ gfx::RectF uv_bottom_right =
+ NormalizedRect(image_width - image_aperture_right_width,
+ image_height - image_aperture_bottom_height,
+ image_aperture_right_width,
+ image_aperture_bottom_height,
+ image_width,
+ image_height);
+ gfx::RectF uv_top(
+ uv_top_left.right(),
+ 0,
+ (image_width - image_aperture_left_width - image_aperture_right_width) /
+ image_width,
+ (image_aperture_top_height) / image_height);
gfx::RectF uv_left(0,
- uv_top_left.bottom(),
- left_width / img_width,
- (img_height - top_height - bottom_height) / img_height);
+ uv_top_left.bottom(),
+ image_aperture_left_width / image_width,
+ (image_height - image_aperture_top_height -
+ image_aperture_bottom_height) /
+ image_height);
gfx::RectF uv_right(uv_top_right.x(),
- uv_top_right.bottom(),
- right_width / img_width,
- uv_left.height());
+ uv_top_right.bottom(),
+ image_aperture_right_width / image_width,
+ uv_left.height());
gfx::RectF uv_bottom(uv_top.x(),
- uv_bottom_left.y(),
- uv_top.width(),
- bottom_height / img_height);
+ uv_bottom_left.y(),
+ uv_top.width(),
+ image_aperture_bottom_height / image_height);
+ gfx::RectF uv_center(uv_top_left.right(),
+ uv_top_left.bottom(),
+ uv_top.width(),
+ uv_left.height());
// Nothing is opaque here.
// TODO(danakj): Should we look at the SkBitmaps to determine opaqueness?
@@ -173,9 +241,9 @@ void NinePatchLayerImpl::AppendQuads(QuadSink* quad_sink,
quad = TextureDrawQuad::Create();
quad->SetNew(shared_quad_state,
- top_left,
+ layer_top_left,
opaque_rect,
- resource_id_,
+ resource,
premultiplied_alpha,
uv_top_left.origin(),
uv_top_left.bottom_right(),
@@ -186,9 +254,9 @@ void NinePatchLayerImpl::AppendQuads(QuadSink* quad_sink,
quad = TextureDrawQuad::Create();
quad->SetNew(shared_quad_state,
- top_right,
+ layer_top_right,
opaque_rect,
- resource_id_,
+ resource,
premultiplied_alpha,
uv_top_right.origin(),
uv_top_right.bottom_right(),
@@ -199,9 +267,9 @@ void NinePatchLayerImpl::AppendQuads(QuadSink* quad_sink,
quad = TextureDrawQuad::Create();
quad->SetNew(shared_quad_state,
- bottom_left,
+ layer_bottom_left,
opaque_rect,
- resource_id_,
+ resource,
premultiplied_alpha,
uv_bottom_left.origin(),
uv_bottom_left.bottom_right(),
@@ -212,9 +280,9 @@ void NinePatchLayerImpl::AppendQuads(QuadSink* quad_sink,
quad = TextureDrawQuad::Create();
quad->SetNew(shared_quad_state,
- bottom_right,
+ layer_bottom_right,
opaque_rect,
- resource_id_,
+ resource,
premultiplied_alpha,
uv_bottom_right.origin(),
uv_bottom_right.bottom_right(),
@@ -225,9 +293,9 @@ void NinePatchLayerImpl::AppendQuads(QuadSink* quad_sink,
quad = TextureDrawQuad::Create();
quad->SetNew(shared_quad_state,
- top,
+ layer_top,
opaque_rect,
- resource_id_,
+ resource,
premultiplied_alpha,
uv_top.origin(),
uv_top.bottom_right(),
@@ -238,9 +306,9 @@ void NinePatchLayerImpl::AppendQuads(QuadSink* quad_sink,
quad = TextureDrawQuad::Create();
quad->SetNew(shared_quad_state,
- left,
+ layer_left,
opaque_rect,
- resource_id_,
+ resource,
premultiplied_alpha,
uv_left.origin(),
uv_left.bottom_right(),
@@ -251,9 +319,9 @@ void NinePatchLayerImpl::AppendQuads(QuadSink* quad_sink,
quad = TextureDrawQuad::Create();
quad->SetNew(shared_quad_state,
- right,
+ layer_right,
opaque_rect,
- resource_id_,
+ resource,
premultiplied_alpha,
uv_right.origin(),
uv_right.bottom_right(),
@@ -264,9 +332,9 @@ void NinePatchLayerImpl::AppendQuads(QuadSink* quad_sink,
quad = TextureDrawQuad::Create();
quad->SetNew(shared_quad_state,
- bottom,
+ layer_bottom,
opaque_rect,
- resource_id_,
+ resource,
premultiplied_alpha,
uv_bottom.origin(),
uv_bottom.bottom_right(),
@@ -274,10 +342,21 @@ void NinePatchLayerImpl::AppendQuads(QuadSink* quad_sink,
vertex_opacity,
flipped);
quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data);
-}
-void NinePatchLayerImpl::DidLoseOutputSurface() {
- resource_id_ = 0;
+ if (fill_center_) {
+ quad = TextureDrawQuad::Create();
+ quad->SetNew(shared_quad_state,
+ layer_center,
+ opaque_rect,
+ resource,
+ premultiplied_alpha,
+ uv_center.origin(),
+ uv_center.bottom_right(),
+ SK_ColorTRANSPARENT,
+ vertex_opacity,
+ flipped);
+ quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data);
+ }
}
const char* NinePatchLayerImpl::LayerTypeAsString() const {
@@ -294,10 +373,12 @@ base::DictionaryValue* NinePatchLayerImpl::LayerTreeAsJson() const {
list->AppendInteger(image_aperture_.size().height());
result->Set("ImageAperture", list);
- list = new base::ListValue;
- list->AppendInteger(image_bounds_.width());
- list->AppendInteger(image_bounds_.height());
- result->Set("ImageBounds", list);
+ result->Set("ImageBounds", MathUtil::AsValue(image_bounds_).release());
+ result->Set("Border", MathUtil::AsValue(border_).release());
+
+ base::FundamentalValue* fill_center =
+ base::Value::CreateBooleanValue(fill_center_);
+ result->Set("FillCenter", fill_center);
return result;
}
diff --git a/chromium/cc/layers/nine_patch_layer_impl.h b/chromium/cc/layers/nine_patch_layer_impl.h
index bc442d43f33..ba414aed391 100644
--- a/chromium/cc/layers/nine_patch_layer_impl.h
+++ b/chromium/cc/layers/nine_patch_layer_impl.h
@@ -10,6 +10,7 @@
#include "cc/base/cc_export.h"
#include "cc/layers/layer_impl.h"
#include "cc/resources/resource_provider.h"
+#include "cc/resources/ui_resource_client.h"
#include "ui/gfx/rect.h"
#include "ui/gfx/size.h"
@@ -27,8 +28,37 @@ class CC_EXPORT NinePatchLayerImpl : public LayerImpl {
}
virtual ~NinePatchLayerImpl();
- void SetResourceId(unsigned id) { resource_id_ = id; }
- void SetLayout(gfx::Size image_bounds, gfx::Rect aperture);
+
+ void SetUIResourceId(UIResourceId uid);
+
+ // The bitmap stretches out the bounds of the layer. The following picture
+ // illustrates the parameters associated with the dimensions.
+ //
+ // Layer space layout Bitmap space layout
+ //
+ // ------------------------ ~~~~~~~~~~ W ~~~~~~~~~~
+ // | : | : : |
+ // | C | : Y |
+ // | : | : : |
+ // | ------------ | :~~X~~------------ |
+ // | | | | : | : |
+ // | | | | : | : |
+ // |~~A~~| |~~B~~| H | Q |
+ // | | | | : | : |
+ // | ------------ | : ~~~~~P~~~~~ |
+ // | : | : |
+ // | D | : |
+ // | : | : |
+ // ------------------------ ------------------------
+ //
+ // |image_bounds| = (W, H)
+ // |image_aperture| = (X, Y, P, Q)
+ // |border| = (A, C, A + B, C + D)
+ // |fill_center| indicates whether to draw the center quad or not.
+ void SetLayout(gfx::Size image_bounds,
+ gfx::Rect image_aperture,
+ gfx::Rect border,
+ bool fill_center);
virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl)
OVERRIDE;
@@ -39,7 +69,6 @@ class CC_EXPORT NinePatchLayerImpl : public LayerImpl {
virtual void AppendQuads(QuadSink* quad_sink,
AppendQuadsData* append_quads_data) OVERRIDE;
virtual ResourceProvider::ResourceId ContentsResourceId() const OVERRIDE;
- virtual void DidLoseOutputSurface() OVERRIDE;
virtual base::DictionaryValue* LayerTreeAsJson() const OVERRIDE;
@@ -56,7 +85,12 @@ class CC_EXPORT NinePatchLayerImpl : public LayerImpl {
// image space.
gfx::Rect image_aperture_;
- ResourceProvider::ResourceId resource_id_;
+ // An inset border that the patches will be mapped to.
+ gfx::Rect border_;
+
+ bool fill_center_;
+
+ UIResourceId ui_resource_id_;
DISALLOW_COPY_AND_ASSIGN(NinePatchLayerImpl);
};
diff --git a/chromium/cc/layers/nine_patch_layer_impl_unittest.cc b/chromium/cc/layers/nine_patch_layer_impl_unittest.cc
index 1985bd4c29c..0fbc645be1b 100644
--- a/chromium/cc/layers/nine_patch_layer_impl_unittest.cc
+++ b/chromium/cc/layers/nine_patch_layer_impl_unittest.cc
@@ -2,11 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include <stdio.h>
-
+#include "base/containers/hash_tables.h"
#include "cc/layers/append_quads_data.h"
#include "cc/layers/nine_patch_layer_impl.h"
#include "cc/quads/texture_draw_quad.h"
+#include "cc/resources/ui_resource_bitmap.h"
+#include "cc/resources/ui_resource_client.h"
#include "cc/test/fake_impl_proxy.h"
#include "cc/test/fake_layer_tree_host_impl.h"
#include "cc/test/geometry_test_utils.h"
@@ -22,6 +23,40 @@
namespace cc {
namespace {
+class FakeUIResourceLayerTreeHostImpl : public FakeLayerTreeHostImpl {
+ public:
+ explicit FakeUIResourceLayerTreeHostImpl(Proxy* proxy)
+ : FakeLayerTreeHostImpl(proxy), fake_next_resource_id_(1) {}
+
+ virtual void CreateUIResource(
+ UIResourceId uid,
+ const UIResourceBitmap& bitmap) OVERRIDE {
+ if (ResourceIdForUIResource(uid))
+ DeleteUIResource(uid);
+ fake_ui_resource_map_[uid] = fake_next_resource_id_;
+ }
+
+ virtual void DeleteUIResource(UIResourceId uid) OVERRIDE {
+ ResourceProvider::ResourceId id = ResourceIdForUIResource(uid);
+ if (id)
+ fake_ui_resource_map_.erase(uid);
+ }
+
+ virtual ResourceProvider::ResourceId ResourceIdForUIResource(
+ UIResourceId uid) const OVERRIDE {
+ UIResourceMap::const_iterator iter = fake_ui_resource_map_.find(uid);
+ if (iter != fake_ui_resource_map_.end())
+ return iter->second;
+ return 0;
+ }
+
+ private:
+ ResourceProvider::ResourceId fake_next_resource_id_;
+ typedef base::hash_map<UIResourceId, ResourceProvider::ResourceId>
+ UIResourceMap;
+ UIResourceMap fake_ui_resource_map_;
+};
+
gfx::Rect ToRoundedIntRect(gfx::RectF rect_f) {
return gfx::Rect(gfx::ToRoundedInt(rect_f.x()),
gfx::ToRoundedInt(rect_f.y()),
@@ -29,19 +64,21 @@ gfx::Rect ToRoundedIntRect(gfx::RectF rect_f) {
gfx::ToRoundedInt(rect_f.height()));
}
-TEST(NinePatchLayerImplTest, VerifyDrawQuads) {
- // Input is a 100x100 bitmap with a 40x50 aperture at x=20, y=30.
- // The bounds of the layer are set to 400x400, so the draw quads
- // generated should leave the border width (40) intact.
+void NinePatchLayerLayoutTest(gfx::Size bitmap_size,
+ gfx::Rect aperture_rect,
+ gfx::Size layer_size,
+ gfx::Rect border,
+ bool fill_center,
+ size_t expected_quad_size) {
MockQuadCuller quad_culler;
- gfx::Size bitmap_size(100, 100);
- gfx::Size layer_size(400, 400);
gfx::Rect visible_content_rect(layer_size);
- gfx::Rect aperture_rect(20, 30, 40, 50);
- gfx::Rect scaled_aperture_non_uniform(20, 30, 340, 350);
+ gfx::Rect expected_remaining(border.x(),
+ border.y(),
+ layer_size.width() - border.width(),
+ layer_size.height() - border.height());
FakeImplProxy proxy;
- FakeLayerTreeHostImpl host_impl(&proxy);
+ FakeUIResourceLayerTreeHostImpl host_impl(&proxy);
scoped_ptr<NinePatchLayerImpl> layer =
NinePatchLayerImpl::Create(host_impl.active_tree(), 1);
layer->draw_properties().visible_content_rect = visible_content_rect;
@@ -49,21 +86,26 @@ TEST(NinePatchLayerImplTest, VerifyDrawQuads) {
layer->SetContentBounds(layer_size);
layer->CreateRenderSurface();
layer->draw_properties().render_target = layer.get();
- layer->SetLayout(bitmap_size, aperture_rect);
- layer->SetResourceId(1);
- // This scale should not affect the generated quad geometry, but only
- // the shared draw transform.
- gfx::Transform transform;
- transform.Scale(10, 10);
- layer->draw_properties().target_space_transform = transform;
+ UIResourceId uid = 1;
+ SkBitmap skbitmap;
+ skbitmap.setConfig(
+ SkBitmap::kARGB_8888_Config, bitmap_size.width(), bitmap_size.height());
+ skbitmap.allocPixels();
+ skbitmap.setImmutable();
+ UIResourceBitmap bitmap(skbitmap);
+
+ host_impl.CreateUIResource(uid, bitmap);
+ layer->SetUIResourceId(uid);
+ layer->SetLayout(bitmap_size, aperture_rect, border, fill_center);
AppendQuadsData data;
layer->AppendQuads(&quad_culler, &data);
// Verify quad rects
const QuadList& quads = quad_culler.quad_list();
- EXPECT_EQ(8u, quads.size());
+ EXPECT_EQ(expected_quad_size, quads.size());
+
Region remaining(visible_content_rect);
for (size_t i = 0; i < quads.size(); ++i) {
DrawQuad* quad = quads[i];
@@ -71,12 +113,16 @@ TEST(NinePatchLayerImplTest, VerifyDrawQuads) {
EXPECT_TRUE(visible_content_rect.Contains(quad_rect)) << i;
EXPECT_TRUE(remaining.Contains(quad_rect)) << i;
- EXPECT_EQ(transform, quad->quadTransform());
remaining.Subtract(Region(quad_rect));
}
- EXPECT_RECT_EQ(scaled_aperture_non_uniform, remaining.bounds());
- Region scaled_aperture_region(scaled_aperture_non_uniform);
- EXPECT_EQ(scaled_aperture_region, remaining);
+
+ // Check if the left-over quad is the same size as the mapped aperture quad in
+ // layer space.
+ if (!fill_center) {
+ EXPECT_RECT_EQ(expected_remaining, gfx::ToEnclosedRect(remaining.bounds()));
+ } else {
+ EXPECT_TRUE(remaining.bounds().IsEmpty());
+ }
// Verify UV rects
gfx::Rect bitmap_rect(bitmap_size);
@@ -89,66 +135,59 @@ TEST(NinePatchLayerImplTest, VerifyDrawQuads) {
tex_rect.Scale(bitmap_size.width(), bitmap_size.height());
tex_remaining.Subtract(Region(ToRoundedIntRect(tex_rect)));
}
- EXPECT_RECT_EQ(aperture_rect, tex_remaining.bounds());
- Region aperture_region(aperture_rect);
- EXPECT_EQ(aperture_region, tex_remaining);
-}
-
-TEST(NinePatchLayerImplTest, VerifyDrawQuadsForSqueezedLayer) {
- // Test with a layer much smaller than the bitmap.
- MockQuadCuller quad_culler;
- gfx::Size bitmap_size(101, 101);
- gfx::Size layer_size(51, 51);
- gfx::Rect visible_content_rect(layer_size);
- gfx::Rect aperture_rect(20, 30, 40, 45); // rightWidth: 40, botHeight: 25
-
- FakeImplProxy proxy;
- FakeLayerTreeHostImpl host_impl(&proxy);
- scoped_ptr<NinePatchLayerImpl> layer =
- NinePatchLayerImpl::Create(host_impl.active_tree(), 1);
- layer->draw_properties().visible_content_rect = visible_content_rect;
- layer->SetBounds(layer_size);
- layer->SetContentBounds(layer_size);
- layer->CreateRenderSurface();
- layer->draw_properties().render_target = layer.get();
- layer->SetLayout(bitmap_size, aperture_rect);
- layer->SetResourceId(1);
- AppendQuadsData data;
- layer->AppendQuads(&quad_culler, &data);
-
- // Verify corner rects fill the layer and don't overlap
- const QuadList& quads = quad_culler.quad_list();
- EXPECT_EQ(4u, quads.size());
- Region filled;
- for (size_t i = 0; i < quads.size(); ++i) {
- DrawQuad* quad = quads[i];
- gfx::Rect quad_rect = quad->rect;
-
- EXPECT_FALSE(filled.Intersects(quad_rect));
- filled.Union(quad_rect);
+ if (!fill_center) {
+ EXPECT_RECT_EQ(aperture_rect, tex_remaining.bounds());
+ Region aperture_region(aperture_rect);
+ EXPECT_EQ(aperture_region, tex_remaining);
+ } else {
+ EXPECT_TRUE(remaining.bounds().IsEmpty());
}
- Region expected_full(visible_content_rect);
- EXPECT_EQ(expected_full, filled);
+}
- // Verify UV rects cover the corners of the bitmap and the crop is weighted
- // proportionately to the relative corner sizes (for uneven apertures).
- gfx::Rect bitmap_rect(bitmap_size);
- Region tex_remaining(bitmap_rect);
- for (size_t i = 0; i < quads.size(); ++i) {
- DrawQuad* quad = quads[i];
- const TextureDrawQuad* tex_quad = TextureDrawQuad::MaterialCast(quad);
- gfx::RectF tex_rect =
- gfx::BoundingRect(tex_quad->uv_top_left, tex_quad->uv_bottom_right);
- tex_rect.Scale(bitmap_size.width(), bitmap_size.height());
- tex_remaining.Subtract(Region(ToRoundedIntRect(tex_rect)));
- }
- Region expected_remaining_region = Region(gfx::Rect(bitmap_size));
- expected_remaining_region.Subtract(gfx::Rect(0, 0, 17, 28));
- expected_remaining_region.Subtract(gfx::Rect(67, 0, 34, 28));
- expected_remaining_region.Subtract(gfx::Rect(0, 78, 17, 23));
- expected_remaining_region.Subtract(gfx::Rect(67, 78, 34, 23));
- EXPECT_EQ(expected_remaining_region, tex_remaining);
+TEST(NinePatchLayerImplTest, VerifyDrawQuads) {
+ // Input is a 100x100 bitmap with a 40x50 aperture at x=20, y=30.
+ // The bounds of the layer are set to 400x400.
+ gfx::Size bitmap_size(100, 100);
+ gfx::Size layer_size(400, 500);
+ gfx::Rect aperture_rect(20, 30, 40, 50);
+ gfx::Rect border(40, 40, 80, 80);
+ bool fill_center = false;
+ size_t expected_quad_size = 8;
+ NinePatchLayerLayoutTest(bitmap_size,
+ aperture_rect,
+ layer_size,
+ border,
+ fill_center,
+ expected_quad_size);
+
+ // The bounds of the layer are set to less than the bitmap size.
+ bitmap_size = gfx::Size(100, 100);
+ layer_size = gfx::Size(40, 50);
+ aperture_rect = gfx::Rect(20, 30, 40, 50);
+ border = gfx::Rect(10, 10, 25, 15);
+ fill_center = true;
+ expected_quad_size = 9;
+ NinePatchLayerLayoutTest(bitmap_size,
+ aperture_rect,
+ layer_size,
+ border,
+ fill_center,
+ expected_quad_size);
+
+ // Layer and image sizes are equal.
+ bitmap_size = gfx::Size(100, 100);
+ layer_size = gfx::Size(100, 100);
+ aperture_rect = gfx::Rect(20, 30, 40, 50);
+ border = gfx::Rect(20, 30, 40, 50);
+ fill_center = true;
+ expected_quad_size = 9;
+ NinePatchLayerLayoutTest(bitmap_size,
+ aperture_rect,
+ layer_size,
+ border,
+ fill_center,
+ expected_quad_size);
}
} // namespace
diff --git a/chromium/cc/layers/nine_patch_layer_unittest.cc b/chromium/cc/layers/nine_patch_layer_unittest.cc
index 5268d02472f..101146c0077 100644
--- a/chromium/cc/layers/nine_patch_layer_unittest.cc
+++ b/chromium/cc/layers/nine_patch_layer_unittest.cc
@@ -8,9 +8,11 @@
#include "cc/resources/prioritized_resource_manager.h"
#include "cc/resources/resource_provider.h"
#include "cc/resources/resource_update_queue.h"
+#include "cc/resources/scoped_ui_resource.h"
#include "cc/scheduler/texture_uploader.h"
#include "cc/test/fake_layer_tree_host_client.h"
#include "cc/test/fake_output_surface.h"
+#include "cc/test/fake_output_surface_client.h"
#include "cc/test/geometry_test_utils.h"
#include "cc/trees/layer_tree_host.h"
#include "cc/trees/occlusion_tracker.h"
@@ -54,7 +56,7 @@ class NinePatchLayerTest : public testing::Test {
FakeLayerTreeHostClient fake_client_;
};
-TEST_F(NinePatchLayerTest, TriggerFullUploadOnceWhenChangingBitmap) {
+TEST_F(NinePatchLayerTest, SetBitmap) {
scoped_refptr<NinePatchLayer> test_layer = NinePatchLayer::Create();
ASSERT_TRUE(test_layer.get());
test_layer->SetIsDrawable(true);
@@ -66,80 +68,60 @@ TEST_F(NinePatchLayerTest, TriggerFullUploadOnceWhenChangingBitmap) {
layer_tree_host_->InitializeOutputSurfaceIfNeeded();
- PriorityCalculator calculator;
ResourceUpdateQueue queue;
OcclusionTracker occlusion_tracker(gfx::Rect(), false);
-
- // No bitmap set should not trigger any uploads.
test_layer->SavePaintProperties();
- test_layer->SetTexturePriorities(calculator);
test_layer->Update(&queue, &occlusion_tracker);
- EXPECT_EQ(0u, queue.FullUploadSize());
- EXPECT_EQ(0u, queue.PartialUploadSize());
- // Setting a bitmap set should trigger a single full upload.
+ EXPECT_FALSE(test_layer->DrawsContent());
+
SkBitmap bitmap;
bitmap.setConfig(SkBitmap::kARGB_8888_Config, 10, 10);
bitmap.allocPixels();
- test_layer->SetBitmap(bitmap, gfx::Rect(5, 5, 1, 1));
- test_layer->SavePaintProperties();
- test_layer->SetTexturePriorities(calculator);
+ bitmap.setImmutable();
+
+ gfx::Rect aperture(5, 5, 1, 1);
+ bool fill_center = false;
+ test_layer->SetBitmap(bitmap, aperture);
+ test_layer->SetFillCenter(fill_center);
test_layer->Update(&queue, &occlusion_tracker);
- EXPECT_EQ(1u, queue.FullUploadSize());
- EXPECT_EQ(0u, queue.PartialUploadSize());
- ResourceUpdate params = queue.TakeFirstFullUpload();
- EXPECT_TRUE(params.texture != NULL);
-
- // Upload the texture.
- layer_tree_host_->contents_texture_manager()->SetMaxMemoryLimitBytes(
- 1024 * 1024);
- layer_tree_host_->contents_texture_manager()->PrioritizeTextures();
-
- scoped_ptr<OutputSurface> output_surface;
- scoped_ptr<ResourceProvider> resource_provider;
- {
- DebugScopedSetImplThread impl_thread(Proxy());
- DebugScopedSetMainThreadBlocked main_thread_blocked(Proxy());
- output_surface = CreateFakeOutputSurface();
- resource_provider = ResourceProvider::Create(output_surface.get(), 0);
- params.texture->AcquireBackingTexture(resource_provider.get());
- ASSERT_TRUE(params.texture->have_backing_texture());
- }
- // Nothing changed, so no repeated upload.
+ EXPECT_TRUE(test_layer->DrawsContent());
+}
+
+TEST_F(NinePatchLayerTest, SetUIResourceId) {
+ scoped_refptr<NinePatchLayer> test_layer = NinePatchLayer::Create();
+ ASSERT_TRUE(test_layer.get());
+ test_layer->SetIsDrawable(true);
+ test_layer->SetBounds(gfx::Size(100, 100));
+
+ layer_tree_host_->SetRootLayer(test_layer);
+ Mock::VerifyAndClearExpectations(layer_tree_host_.get());
+ EXPECT_EQ(test_layer->layer_tree_host(), layer_tree_host_.get());
+
+ layer_tree_host_->InitializeOutputSurfaceIfNeeded();
+
+ ResourceUpdateQueue queue;
+ OcclusionTracker occlusion_tracker(gfx::Rect(), false);
test_layer->SavePaintProperties();
- test_layer->SetTexturePriorities(calculator);
test_layer->Update(&queue, &occlusion_tracker);
- EXPECT_EQ(0u, queue.FullUploadSize());
- EXPECT_EQ(0u, queue.PartialUploadSize());
- {
- DebugScopedSetImplThread impl_thread(Proxy());
- DebugScopedSetMainThreadBlocked main_thread_blocked(Proxy());
- layer_tree_host_->contents_texture_manager()->ClearAllMemory(
- resource_provider.get());
- }
- // Reupload after eviction
- test_layer->SavePaintProperties();
- test_layer->SetTexturePriorities(calculator);
+ EXPECT_FALSE(test_layer->DrawsContent());
+
+ SkBitmap bitmap;
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, 10, 10);
+ bitmap.allocPixels();
+ bitmap.setImmutable();
+
+ scoped_ptr<ScopedUIResource> resource = ScopedUIResource::Create(
+ layer_tree_host_.get(), UIResourceBitmap(bitmap));
+ gfx::Rect aperture(5, 5, 1, 1);
+ bool fill_center = true;
+ test_layer->SetUIResourceId(resource->id(), aperture);
+ test_layer->SetFillCenter(fill_center);
test_layer->Update(&queue, &occlusion_tracker);
- EXPECT_EQ(1u, queue.FullUploadSize());
- EXPECT_EQ(0u, queue.PartialUploadSize());
- // PrioritizedResourceManager clearing
- layer_tree_host_->contents_texture_manager()->UnregisterTexture(
- params.texture);
- EXPECT_EQ(NULL, params.texture->resource_manager());
- test_layer->SavePaintProperties();
- test_layer->SetTexturePriorities(calculator);
- ResourceUpdateQueue queue2;
- test_layer->Update(&queue2, &occlusion_tracker);
- EXPECT_EQ(1u, queue2.FullUploadSize());
- EXPECT_EQ(0u, queue2.PartialUploadSize());
- params = queue2.TakeFirstFullUpload();
- EXPECT_TRUE(params.texture != NULL);
- EXPECT_EQ(params.texture->resource_manager(),
- layer_tree_host_->contents_texture_manager());
+ EXPECT_TRUE(test_layer->DrawsContent());
}
} // namespace
diff --git a/chromium/cc/layers/painted_scrollbar_layer.cc b/chromium/cc/layers/painted_scrollbar_layer.cc
new file mode 100644
index 00000000000..4272fe688cc
--- /dev/null
+++ b/chromium/cc/layers/painted_scrollbar_layer.cc
@@ -0,0 +1,244 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/layers/painted_scrollbar_layer.h"
+
+#include "base/auto_reset.h"
+#include "base/basictypes.h"
+#include "base/debug/trace_event.h"
+#include "cc/layers/painted_scrollbar_layer_impl.h"
+#include "cc/resources/ui_resource_bitmap.h"
+#include "cc/trees/layer_tree_host.h"
+#include "cc/trees/layer_tree_impl.h"
+#include "skia/ext/platform_canvas.h"
+#include "skia/ext/refptr.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkSize.h"
+#include "ui/gfx/skia_util.h"
+
+namespace cc {
+
+scoped_ptr<LayerImpl> PaintedScrollbarLayer::CreateLayerImpl(
+ LayerTreeImpl* tree_impl) {
+ return PaintedScrollbarLayerImpl::Create(
+ tree_impl, id(), scrollbar_->Orientation()).PassAs<LayerImpl>();
+}
+
+scoped_refptr<PaintedScrollbarLayer> PaintedScrollbarLayer::Create(
+ scoped_ptr<Scrollbar> scrollbar,
+ int scroll_layer_id) {
+ return make_scoped_refptr(
+ new PaintedScrollbarLayer(scrollbar.Pass(), scroll_layer_id));
+}
+
+PaintedScrollbarLayer::PaintedScrollbarLayer(
+ scoped_ptr<Scrollbar> scrollbar,
+ int scroll_layer_id)
+ : scrollbar_(scrollbar.Pass()),
+ scroll_layer_id_(scroll_layer_id),
+ thumb_thickness_(scrollbar_->ThumbThickness()),
+ thumb_length_(scrollbar_->ThumbLength()) {
+ if (!scrollbar_->IsOverlay())
+ SetShouldScrollOnMainThread(true);
+}
+
+PaintedScrollbarLayer::~PaintedScrollbarLayer() {}
+
+int PaintedScrollbarLayer::ScrollLayerId() const {
+ return scroll_layer_id_;
+}
+
+void PaintedScrollbarLayer::SetScrollLayerId(int id) {
+ if (id == scroll_layer_id_)
+ return;
+
+ scroll_layer_id_ = id;
+ SetNeedsFullTreeSync();
+}
+
+bool PaintedScrollbarLayer::OpacityCanAnimateOnImplThread() const {
+ return scrollbar_->IsOverlay();
+}
+
+ScrollbarOrientation PaintedScrollbarLayer::orientation() const {
+ return scrollbar_->Orientation();
+}
+
+int PaintedScrollbarLayer::MaxTextureSize() {
+ DCHECK(layer_tree_host());
+ return layer_tree_host()->GetRendererCapabilities().max_texture_size;
+}
+
+float PaintedScrollbarLayer::ClampScaleToMaxTextureSize(float scale) {
+ // If the scaled content_bounds() is bigger than the max texture size of the
+ // device, we need to clamp it by rescaling, since content_bounds() is used
+ // below to set the texture size.
+ gfx::Size scaled_bounds = ComputeContentBoundsForScale(scale, scale);
+ if (scaled_bounds.width() > MaxTextureSize() ||
+ scaled_bounds.height() > MaxTextureSize()) {
+ if (scaled_bounds.width() > scaled_bounds.height())
+ return (MaxTextureSize() - 1) / static_cast<float>(bounds().width());
+ else
+ return (MaxTextureSize() - 1) / static_cast<float>(bounds().height());
+ }
+ return scale;
+}
+
+void PaintedScrollbarLayer::CalculateContentsScale(
+ float ideal_contents_scale,
+ float device_scale_factor,
+ float page_scale_factor,
+ bool animating_transform_to_screen,
+ float* contents_scale_x,
+ float* contents_scale_y,
+ gfx::Size* content_bounds) {
+ ContentsScalingLayer::CalculateContentsScale(
+ ClampScaleToMaxTextureSize(ideal_contents_scale),
+ device_scale_factor,
+ page_scale_factor,
+ animating_transform_to_screen,
+ contents_scale_x,
+ contents_scale_y,
+ content_bounds);
+}
+
+void PaintedScrollbarLayer::PushPropertiesTo(LayerImpl* layer) {
+ ContentsScalingLayer::PushPropertiesTo(layer);
+
+ PaintedScrollbarLayerImpl* scrollbar_layer =
+ static_cast<PaintedScrollbarLayerImpl*>(layer);
+
+ scrollbar_layer->SetThumbThickness(thumb_thickness_);
+ scrollbar_layer->SetThumbLength(thumb_length_);
+ if (orientation() == HORIZONTAL) {
+ scrollbar_layer->SetTrackStart(
+ track_rect_.x() - location_.x());
+ scrollbar_layer->SetTrackLength(track_rect_.width());
+ } else {
+ scrollbar_layer->SetTrackStart(
+ track_rect_.y() - location_.y());
+ scrollbar_layer->SetTrackLength(track_rect_.height());
+ }
+
+ if (track_resource_.get())
+ scrollbar_layer->set_track_ui_resource_id(track_resource_->id());
+ if (thumb_resource_.get())
+ scrollbar_layer->set_thumb_ui_resource_id(thumb_resource_->id());
+
+ scrollbar_layer->set_is_overlay_scrollbar(scrollbar_->IsOverlay());
+
+ // PaintedScrollbarLayer must push properties every frame. crbug.com/259095
+ needs_push_properties_ = true;
+}
+
+ScrollbarLayerInterface* PaintedScrollbarLayer::ToScrollbarLayer() {
+ return this;
+}
+
+void PaintedScrollbarLayer::SetLayerTreeHost(LayerTreeHost* host) {
+ // When the LTH is set to null or has changed, then this layer should remove
+ // all of its associated resources.
+ if (!host || host != layer_tree_host()) {
+ track_resource_.reset();
+ thumb_resource_.reset();
+ }
+
+ ContentsScalingLayer::SetLayerTreeHost(host);
+}
+
+gfx::Rect PaintedScrollbarLayer::ScrollbarLayerRectToContentRect(
+ gfx::Rect layer_rect) const {
+ // Don't intersect with the bounds as in LayerRectToContentRect() because
+ // layer_rect here might be in coordinates of the containing layer.
+ gfx::Rect expanded_rect = gfx::ScaleToEnclosingRect(
+ layer_rect, contents_scale_y(), contents_scale_y());
+ // We should never return a rect bigger than the content_bounds().
+ gfx::Size clamped_size = expanded_rect.size();
+ clamped_size.SetToMin(content_bounds());
+ expanded_rect.set_size(clamped_size);
+ return expanded_rect;
+}
+
+gfx::Rect PaintedScrollbarLayer::OriginThumbRect() const {
+ gfx::Size thumb_size;
+ if (orientation() == HORIZONTAL) {
+ thumb_size =
+ gfx::Size(scrollbar_->ThumbLength(), scrollbar_->ThumbThickness());
+ } else {
+ thumb_size =
+ gfx::Size(scrollbar_->ThumbThickness(), scrollbar_->ThumbLength());
+ }
+ return ScrollbarLayerRectToContentRect(gfx::Rect(thumb_size));
+}
+
+void PaintedScrollbarLayer::UpdateThumbAndTrackGeometry() {
+ track_rect_ = scrollbar_->TrackRect();
+ location_ = scrollbar_->Location();
+ if (scrollbar_->HasThumb()) {
+ thumb_thickness_ = scrollbar_->ThumbThickness();
+ thumb_length_ = scrollbar_->ThumbLength();
+ }
+}
+
+bool PaintedScrollbarLayer::Update(ResourceUpdateQueue* queue,
+ const OcclusionTracker* occlusion) {
+ UpdateThumbAndTrackGeometry();
+
+ gfx::Rect scaled_track_rect = ScrollbarLayerRectToContentRect(
+ gfx::Rect(location_, bounds()));
+
+ if (track_rect_.IsEmpty() || scaled_track_rect.IsEmpty())
+ return false;
+
+ {
+ base::AutoReset<bool> ignore_set_needs_commit(&ignore_set_needs_commit_,
+ true);
+ ContentsScalingLayer::Update(queue, occlusion);
+ }
+
+ track_resource_ = ScopedUIResource::Create(
+ layer_tree_host(), RasterizeScrollbarPart(scaled_track_rect, TRACK));
+ gfx::Rect thumb_rect = OriginThumbRect();
+
+ if (scrollbar_->HasThumb() && !thumb_rect.IsEmpty()) {
+ thumb_resource_ = ScopedUIResource::Create(
+ layer_tree_host(), RasterizeScrollbarPart(thumb_rect, THUMB));
+ }
+
+ return true;
+}
+
+UIResourceBitmap PaintedScrollbarLayer::RasterizeScrollbarPart(
+ gfx::Rect rect,
+ ScrollbarPart part) {
+ DCHECK(!rect.size().IsEmpty());
+
+ SkBitmap skbitmap;
+ skbitmap.setConfig(SkBitmap::kARGB_8888_Config, rect.width(), rect.height());
+ skbitmap.allocPixels();
+
+ SkCanvas skcanvas(skbitmap);
+ skcanvas.translate(SkFloatToScalar(-rect.x()), SkFloatToScalar(-rect.y()));
+ skcanvas.scale(SkFloatToScalar(contents_scale_x()),
+ SkFloatToScalar(contents_scale_y()));
+
+ gfx::Rect layer_rect = gfx::ScaleToEnclosingRect(
+ rect, 1.f / contents_scale_x(), 1.f / contents_scale_y());
+ SkRect layer_skrect = RectToSkRect(layer_rect);
+ SkPaint paint;
+ paint.setAntiAlias(false);
+ paint.setXfermodeMode(SkXfermode::kClear_Mode);
+ skcanvas.drawRect(layer_skrect, paint);
+ skcanvas.clipRect(layer_skrect);
+
+ scrollbar_->PaintPart(&skcanvas, part, layer_rect);
+ // Make sure that the pixels are no longer mutable to unavoid unnecessary
+ // allocation and copying.
+ skbitmap.setImmutable();
+
+ return UIResourceBitmap(skbitmap);
+}
+
+} // namespace cc
diff --git a/chromium/cc/layers/scrollbar_layer.h b/chromium/cc/layers/painted_scrollbar_layer.h
index 6d0e58f7a4e..e84377d1e5a 100644
--- a/chromium/cc/layers/scrollbar_layer.h
+++ b/chromium/cc/layers/painted_scrollbar_layer.h
@@ -1,40 +1,41 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
+// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef CC_LAYERS_SCROLLBAR_LAYER_H_
-#define CC_LAYERS_SCROLLBAR_LAYER_H_
+#ifndef CC_LAYERS_PAINTED_SCROLLBAR_LAYER_H_
+#define CC_LAYERS_PAINTED_SCROLLBAR_LAYER_H_
#include "cc/base/cc_export.h"
#include "cc/input/scrollbar.h"
#include "cc/layers/contents_scaling_layer.h"
+#include "cc/layers/scrollbar_layer_interface.h"
#include "cc/layers/scrollbar_theme_painter.h"
#include "cc/resources/layer_updater.h"
+#include "cc/resources/scoped_ui_resource.h"
namespace cc {
-class CachingBitmapContentLayerUpdater;
-class ResourceUpdateQueue;
class ScrollbarThemeComposite;
-class CC_EXPORT ScrollbarLayer : public ContentsScalingLayer {
+class CC_EXPORT PaintedScrollbarLayer : public ScrollbarLayerInterface,
+ public ContentsScalingLayer {
public:
virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl)
OVERRIDE;
- static scoped_refptr<ScrollbarLayer> Create(
+ static scoped_refptr<PaintedScrollbarLayer> Create(
scoped_ptr<Scrollbar> scrollbar,
int scroll_layer_id);
- int scroll_layer_id() const { return scroll_layer_id_; }
- void SetScrollLayerId(int id);
-
virtual bool OpacityCanAnimateOnImplThread() const OVERRIDE;
+ virtual ScrollbarLayerInterface* ToScrollbarLayer() OVERRIDE;
+
+ // ScrollbarLayerInterface
+ virtual int ScrollLayerId() const OVERRIDE;
+ virtual void SetScrollLayerId(int id) OVERRIDE;
- ScrollbarOrientation Orientation() const;
+ virtual ScrollbarOrientation orientation() const OVERRIDE;
// Layer interface
- virtual void SetTexturePriorities(const PriorityCalculator& priority_calc)
- OVERRIDE;
virtual bool Update(ResourceUpdateQueue* queue,
const OcclusionTracker* occlusion) OVERRIDE;
virtual void SetLayerTreeHost(LayerTreeHost* host) OVERRIDE;
@@ -47,49 +48,45 @@ class CC_EXPORT ScrollbarLayer : public ContentsScalingLayer {
float* contents_scale_y,
gfx::Size* content_bounds) OVERRIDE;
- virtual ScrollbarLayer* ToScrollbarLayer() OVERRIDE;
-
protected:
- ScrollbarLayer(scoped_ptr<Scrollbar> scrollbar,
- int scroll_layer_id);
- virtual ~ScrollbarLayer();
+ PaintedScrollbarLayer(scoped_ptr<Scrollbar> scrollbar, int scroll_layer_id);
+ virtual ~PaintedScrollbarLayer();
+
+ // For unit tests
+ UIResourceId track_resource_id() {
+ return track_resource_.get() ? track_resource_->id() : 0;
+ }
+ UIResourceId thumb_resource_id() {
+ return thumb_resource_.get() ? thumb_resource_->id() : 0;
+ }
+ void UpdateThumbAndTrackGeometry();
private:
- bool UpdatePart(CachingBitmapContentLayerUpdater* painter,
- LayerUpdater::Resource* resource,
- gfx::Rect rect,
- ResourceUpdateQueue* queue);
- void CreateUpdaterIfNeeded();
gfx::Rect ScrollbarLayerRectToContentRect(gfx::Rect layer_rect) const;
gfx::Rect OriginThumbRect() const;
- bool is_dirty() const { return !dirty_rect_.IsEmpty(); }
-
int MaxTextureSize();
float ClampScaleToMaxTextureSize(float scale);
+ UIResourceBitmap RasterizeScrollbarPart(gfx::Rect rect,
+ ScrollbarPart part);
+
scoped_ptr<Scrollbar> scrollbar_;
+ int scroll_layer_id_;
+ // Snapshot of properties taken in UpdateThumbAndTrackGeometry and used in
+ // PushPropertiesTo.
int thumb_thickness_;
int thumb_length_;
- gfx::Rect track_rect_;
gfx::Point location_;
- int scroll_layer_id_;
-
- unsigned texture_format_;
-
- gfx::RectF dirty_rect_;
-
- scoped_refptr<CachingBitmapContentLayerUpdater> track_updater_;
- scoped_refptr<CachingBitmapContentLayerUpdater> thumb_updater_;
+ gfx::Rect track_rect_;
- // All the parts of the scrollbar except the thumb
- scoped_ptr<LayerUpdater::Resource> track_;
- scoped_ptr<LayerUpdater::Resource> thumb_;
+ scoped_ptr<ScopedUIResource> track_resource_;
+ scoped_ptr<ScopedUIResource> thumb_resource_;
- DISALLOW_COPY_AND_ASSIGN(ScrollbarLayer);
+ DISALLOW_COPY_AND_ASSIGN(PaintedScrollbarLayer);
};
} // namespace cc
-#endif // CC_LAYERS_SCROLLBAR_LAYER_H_
+#endif // CC_LAYERS_PAINTED_SCROLLBAR_LAYER_H_
diff --git a/chromium/cc/layers/painted_scrollbar_layer_impl.cc b/chromium/cc/layers/painted_scrollbar_layer_impl.cc
new file mode 100644
index 00000000000..e38a1a640be
--- /dev/null
+++ b/chromium/cc/layers/painted_scrollbar_layer_impl.cc
@@ -0,0 +1,181 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/layers/painted_scrollbar_layer_impl.h"
+
+#include <algorithm>
+
+#include "cc/animation/scrollbar_animation_controller.h"
+#include "cc/layers/layer.h"
+#include "cc/layers/quad_sink.h"
+#include "cc/quads/solid_color_draw_quad.h"
+#include "cc/quads/texture_draw_quad.h"
+#include "cc/trees/layer_tree_impl.h"
+#include "cc/trees/layer_tree_settings.h"
+#include "ui/gfx/rect_conversions.h"
+
+namespace cc {
+
+scoped_ptr<PaintedScrollbarLayerImpl> PaintedScrollbarLayerImpl::Create(
+ LayerTreeImpl* tree_impl,
+ int id,
+ ScrollbarOrientation orientation) {
+ return make_scoped_ptr(
+ new PaintedScrollbarLayerImpl(tree_impl, id, orientation));
+}
+
+PaintedScrollbarLayerImpl::PaintedScrollbarLayerImpl(
+ LayerTreeImpl* tree_impl,
+ int id,
+ ScrollbarOrientation orientation)
+ : ScrollbarLayerImplBase(tree_impl, id, orientation, false),
+ track_ui_resource_id_(0),
+ thumb_ui_resource_id_(0),
+ thumb_thickness_(0),
+ thumb_length_(0),
+ track_start_(0),
+ track_length_(0),
+ vertical_adjust_(0.f),
+ scroll_layer_id_(Layer::INVALID_ID) {}
+
+PaintedScrollbarLayerImpl::~PaintedScrollbarLayerImpl() {}
+
+scoped_ptr<LayerImpl> PaintedScrollbarLayerImpl::CreateLayerImpl(
+ LayerTreeImpl* tree_impl) {
+ return PaintedScrollbarLayerImpl::Create(tree_impl, id(), orientation())
+ .PassAs<LayerImpl>();
+}
+
+void PaintedScrollbarLayerImpl::PushPropertiesTo(LayerImpl* layer) {
+ ScrollbarLayerImplBase::PushPropertiesTo(layer);
+
+ PaintedScrollbarLayerImpl* scrollbar_layer =
+ static_cast<PaintedScrollbarLayerImpl*>(layer);
+
+ scrollbar_layer->SetThumbThickness(thumb_thickness_);
+ scrollbar_layer->SetThumbLength(thumb_length_);
+ scrollbar_layer->SetTrackStart(track_start_);
+ scrollbar_layer->SetTrackLength(track_length_);
+
+ scrollbar_layer->set_track_ui_resource_id(track_ui_resource_id_);
+ scrollbar_layer->set_thumb_ui_resource_id(thumb_ui_resource_id_);
+}
+
+bool PaintedScrollbarLayerImpl::WillDraw(DrawMode draw_mode,
+ ResourceProvider* resource_provider) {
+ DCHECK(draw_mode != DRAW_MODE_RESOURCELESS_SOFTWARE);
+ return LayerImpl::WillDraw(draw_mode, resource_provider);
+}
+
+void PaintedScrollbarLayerImpl::AppendQuads(
+ QuadSink* quad_sink,
+ AppendQuadsData* append_quads_data) {
+ bool premultipled_alpha = true;
+ bool flipped = false;
+ gfx::PointF uv_top_left(0.f, 0.f);
+ gfx::PointF uv_bottom_right(1.f, 1.f);
+ gfx::Rect bounds_rect(bounds());
+ gfx::Rect content_bounds_rect(content_bounds());
+
+ SharedQuadState* shared_quad_state =
+ quad_sink->UseSharedQuadState(CreateSharedQuadState());
+ AppendDebugBorderQuad(quad_sink, shared_quad_state, append_quads_data);
+
+ gfx::Rect thumb_quad_rect = ComputeThumbQuadRect();
+
+ ResourceProvider::ResourceId thumb_resource_id =
+ layer_tree_impl()->ResourceIdForUIResource(thumb_ui_resource_id_);
+ ResourceProvider::ResourceId track_resource_id =
+ layer_tree_impl()->ResourceIdForUIResource(track_ui_resource_id_);
+
+ if (thumb_resource_id && !thumb_quad_rect.IsEmpty()) {
+ gfx::Rect opaque_rect;
+ const float opacity[] = {1.0f, 1.0f, 1.0f, 1.0f};
+ scoped_ptr<TextureDrawQuad> quad = TextureDrawQuad::Create();
+ quad->SetNew(shared_quad_state,
+ thumb_quad_rect,
+ opaque_rect,
+ thumb_resource_id,
+ premultipled_alpha,
+ uv_top_left,
+ uv_bottom_right,
+ SK_ColorTRANSPARENT,
+ opacity,
+ flipped);
+ quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data);
+ }
+
+ gfx::Rect track_quad_rect = content_bounds_rect;
+ if (track_resource_id && !track_quad_rect.IsEmpty()) {
+ gfx::Rect opaque_rect(contents_opaque() ? track_quad_rect : gfx::Rect());
+ const float opacity[] = {1.0f, 1.0f, 1.0f, 1.0f};
+ scoped_ptr<TextureDrawQuad> quad = TextureDrawQuad::Create();
+ quad->SetNew(shared_quad_state,
+ track_quad_rect,
+ opaque_rect,
+ track_resource_id,
+ premultipled_alpha,
+ uv_top_left,
+ uv_bottom_right,
+ SK_ColorTRANSPARENT,
+ opacity,
+ flipped);
+ quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data);
+ }
+}
+
+void PaintedScrollbarLayerImpl::DidLoseOutputSurface() {
+ track_ui_resource_id_ = 0;
+ thumb_ui_resource_id_ = 0;
+}
+
+void PaintedScrollbarLayerImpl::SetThumbThickness(int thumb_thickness) {
+ if (thumb_thickness_ == thumb_thickness)
+ return;
+ thumb_thickness_ = thumb_thickness;
+ NoteLayerPropertyChanged();
+}
+
+int PaintedScrollbarLayerImpl::ThumbThickness() const {
+ return thumb_thickness_;
+}
+
+void PaintedScrollbarLayerImpl::SetThumbLength(int thumb_length) {
+ if (thumb_length_ == thumb_length)
+ return;
+ thumb_length_ = thumb_length;
+ NoteLayerPropertyChanged();
+}
+
+int PaintedScrollbarLayerImpl::ThumbLength() const {
+ return thumb_length_;
+}
+
+void PaintedScrollbarLayerImpl::SetTrackStart(int track_start) {
+ if (track_start_ == track_start)
+ return;
+ track_start_ = track_start;
+ NoteLayerPropertyChanged();
+}
+
+int PaintedScrollbarLayerImpl::TrackStart() const {
+ return track_start_;
+}
+
+void PaintedScrollbarLayerImpl::SetTrackLength(int track_length) {
+ if (track_length_ == track_length)
+ return;
+ track_length_ = track_length;
+ NoteLayerPropertyChanged();
+}
+
+float PaintedScrollbarLayerImpl::TrackLength() const {
+ return track_length_ + (orientation() == VERTICAL ? vertical_adjust() : 0);
+}
+
+const char* PaintedScrollbarLayerImpl::LayerTypeAsString() const {
+ return "cc::PaintedScrollbarLayerImpl";
+}
+
+} // namespace cc
diff --git a/chromium/cc/layers/painted_scrollbar_layer_impl.h b/chromium/cc/layers/painted_scrollbar_layer_impl.h
new file mode 100644
index 00000000000..c9d1f3dc0fc
--- /dev/null
+++ b/chromium/cc/layers/painted_scrollbar_layer_impl.h
@@ -0,0 +1,82 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_LAYERS_PAINTED_SCROLLBAR_LAYER_IMPL_H_
+#define CC_LAYERS_PAINTED_SCROLLBAR_LAYER_IMPL_H_
+
+#include "cc/base/cc_export.h"
+#include "cc/input/scrollbar.h"
+#include "cc/layers/scrollbar_layer_impl_base.h"
+#include "cc/resources/ui_resource_client.h"
+
+namespace cc {
+
+class LayerTreeImpl;
+class ScrollView;
+
+class CC_EXPORT PaintedScrollbarLayerImpl : public ScrollbarLayerImplBase {
+ public:
+ static scoped_ptr<PaintedScrollbarLayerImpl> Create(
+ LayerTreeImpl* tree_impl,
+ int id,
+ ScrollbarOrientation orientation);
+ virtual ~PaintedScrollbarLayerImpl();
+
+ // LayerImpl implementation.
+ virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl)
+ OVERRIDE;
+ virtual void PushPropertiesTo(LayerImpl* layer) OVERRIDE;
+
+ virtual bool WillDraw(DrawMode draw_mode,
+ ResourceProvider* resource_provider) OVERRIDE;
+ virtual void AppendQuads(QuadSink* quad_sink,
+ AppendQuadsData* append_quads_data) OVERRIDE;
+
+ virtual void DidLoseOutputSurface() OVERRIDE;
+
+ void SetThumbThickness(int thumb_thickness);
+ void SetThumbLength(int thumb_length);
+ void SetTrackStart(int track_start);
+ void SetTrackLength(int track_length);
+
+ void set_track_ui_resource_id(UIResourceId uid) {
+ track_ui_resource_id_ = uid;
+ }
+ void set_thumb_ui_resource_id(UIResourceId uid) {
+ thumb_ui_resource_id_ = uid;
+ }
+
+ protected:
+ PaintedScrollbarLayerImpl(LayerTreeImpl* tree_impl,
+ int id,
+ ScrollbarOrientation orientation);
+
+ // ScrollbarLayerImplBase implementation.
+ virtual int ThumbThickness() const OVERRIDE;
+ virtual int ThumbLength() const OVERRIDE;
+ virtual float TrackLength() const OVERRIDE;
+ virtual int TrackStart() const OVERRIDE;
+
+ private:
+ virtual const char* LayerTypeAsString() const OVERRIDE;
+
+ UIResourceId track_ui_resource_id_;
+ UIResourceId thumb_ui_resource_id_;
+
+ int thumb_thickness_;
+ int thumb_length_;
+ int track_start_;
+ int track_length_;
+
+ // Difference between the clip layer's height and the visible viewport
+ // height (which may differ in the presence of top-controls hiding).
+ float vertical_adjust_;
+
+ int scroll_layer_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(PaintedScrollbarLayerImpl);
+};
+
+} // namespace cc
+#endif // CC_LAYERS_PAINTED_SCROLLBAR_LAYER_IMPL_H_
diff --git a/chromium/cc/layers/picture_layer.cc b/chromium/cc/layers/picture_layer.cc
index 889e2fbd3f6..4226f1ba713 100644
--- a/chromium/cc/layers/picture_layer.cc
+++ b/chromium/cc/layers/picture_layer.cc
@@ -6,6 +6,7 @@
#include "cc/debug/benchmark_instrumentation.h"
#include "cc/debug/devtools_instrumentation.h"
+#include "cc/layers/content_layer_client.h"
#include "cc/layers/picture_layer_impl.h"
#include "cc/trees/layer_tree_impl.h"
#include "ui/gfx/rect_conversions.h"
@@ -36,25 +37,26 @@ scoped_ptr<LayerImpl> PictureLayer::CreateLayerImpl(LayerTreeImpl* tree_impl) {
void PictureLayer::PushPropertiesTo(LayerImpl* base_layer) {
Layer::PushPropertiesTo(base_layer);
-
PictureLayerImpl* layer_impl = static_cast<PictureLayerImpl*>(base_layer);
- // This should be first so others can use it.
- layer_impl->UpdateTwinLayer();
+
+ if (layer_impl->bounds().IsEmpty()) {
+ // Update may not get called for an empty layer, so resize here instead.
+ // Using layer_impl because either bounds() or paint_properties().bounds
+ // may disagree and either one could have been pushed to layer_impl.
+ pile_->Resize(layer_impl->bounds());
+ pile_->UpdateRecordedRegion();
+ }
+
+ if (DrawsContent()) {
+ DCHECK(paint_properties().bounds == pile_->size());
+ }
layer_impl->SetIsMask(is_mask_);
- layer_impl->CreateTilingSetIfNeeded();
// Unlike other properties, invalidation must always be set on layer_impl.
// See PictureLayerImpl::PushPropertiesTo for more details.
layer_impl->invalidation_.Clear();
layer_impl->invalidation_.Swap(&pile_invalidation_);
layer_impl->pile_ = PicturePileImpl::CreateFromOther(pile_.get());
- layer_impl->SyncFromActiveLayer();
-
- // PictureLayer must push properties every frame.
- // TODO(danakj): If we can avoid requiring to do CreateTilingSetIfNeeded() and
- // SyncFromActiveLayer() on every commit then this could go away, maybe
- // conditionally. crbug.com/259402
- needs_push_properties_ = true;
}
void PictureLayer::SetLayerTreeHost(LayerTreeHost* host) {
@@ -114,11 +116,14 @@ bool PictureLayer::Update(ResourceUpdateQueue* queue,
pile_invalidation_,
visible_layer_rect,
rendering_stats_instrumentation());
- if (!updated) {
+ if (updated) {
+ SetNeedsPushProperties();
+ } else {
// If this invalidation did not affect the pile, then it can be cleared as
// an optimization.
pile_invalidation_.Clear();
}
+
return updated;
}
@@ -130,4 +135,22 @@ bool PictureLayer::SupportsLCDText() const {
return true;
}
+skia::RefPtr<SkPicture> PictureLayer::GetPicture() const {
+ // We could either flatten the PicturePile into a single SkPicture,
+ // or paint a fresh one depending on what we intend to do with the
+ // picture. For now we just paint a fresh one to get consistent results.
+ if (!DrawsContent())
+ return skia::RefPtr<SkPicture>();
+
+ int width = bounds().width();
+ int height = bounds().height();
+ gfx::RectF opaque;
+
+ skia::RefPtr<SkPicture> picture = skia::AdoptRef(new SkPicture);
+ SkCanvas* canvas = picture->beginRecording(width, height);
+ client_->PaintContents(canvas, gfx::Rect(width, height), &opaque);
+ picture->endRecording();
+ return picture;
+}
+
} // namespace cc
diff --git a/chromium/cc/layers/picture_layer.h b/chromium/cc/layers/picture_layer.h
index 7a9a427d427..d7943307ddc 100644
--- a/chromium/cc/layers/picture_layer.h
+++ b/chromium/cc/layers/picture_layer.h
@@ -34,6 +34,7 @@ class CC_EXPORT PictureLayer : public Layer {
const OcclusionTracker* occlusion) OVERRIDE;
virtual void SetIsMask(bool is_mask) OVERRIDE;
virtual bool SupportsLCDText() const OVERRIDE;
+ virtual skia::RefPtr<SkPicture> GetPicture() const OVERRIDE;
protected:
explicit PictureLayer(ContentLayerClient* client);
diff --git a/chromium/cc/layers/picture_layer_impl.cc b/chromium/cc/layers/picture_layer_impl.cc
index 2f1878bc3a6..95a4a076520 100644
--- a/chromium/cc/layers/picture_layer_impl.cc
+++ b/chromium/cc/layers/picture_layer_impl.cc
@@ -45,11 +45,11 @@ PictureLayerImpl::PictureLayerImpl(LayerTreeImpl* tree_impl, int id)
raster_contents_scale_(0.f),
low_res_raster_contents_scale_(0.f),
raster_source_scale_was_animating_(false),
- is_using_lcd_text_(tree_impl->settings().can_use_lcd_text) {
-}
+ is_using_lcd_text_(tree_impl->settings().can_use_lcd_text),
+ needs_post_commit_initialization_(true),
+ should_update_tile_priorities_(false) {}
-PictureLayerImpl::~PictureLayerImpl() {
-}
+PictureLayerImpl::~PictureLayerImpl() {}
const char* PictureLayerImpl::LayerTypeAsString() const {
return "cc::PictureLayerImpl";
@@ -60,13 +60,11 @@ scoped_ptr<LayerImpl> PictureLayerImpl::CreateLayerImpl(
return PictureLayerImpl::Create(tree_impl, id()).PassAs<LayerImpl>();
}
-void PictureLayerImpl::CreateTilingSetIfNeeded() {
- DCHECK(layer_tree_impl()->IsPendingTree());
- if (!tilings_)
- tilings_.reset(new PictureLayerTilingSet(this, bounds()));
-}
-
void PictureLayerImpl::PushPropertiesTo(LayerImpl* base_layer) {
+ // It's possible this layer was never drawn or updated (e.g. because it was
+ // a descendant of an opacity 0 layer).
+ DoPostCommitInitializationIfNeeded();
+
LayerImpl::PushPropertiesTo(base_layer);
PictureLayerImpl* layer_impl = static_cast<PictureLayerImpl*>(base_layer);
@@ -78,8 +76,9 @@ void PictureLayerImpl::PushPropertiesTo(LayerImpl* base_layer) {
layer_impl->SetIsMask(is_mask_);
layer_impl->pile_ = pile_;
- pile_ = NULL;
+ // Tilings would be expensive to push, so we swap. This optimization requires
+ // an extra invalidation in SyncFromActiveLayer.
layer_impl->tilings_.swap(tilings_);
layer_impl->tilings_->SetClient(layer_impl);
if (tilings_)
@@ -92,19 +91,19 @@ void PictureLayerImpl::PushPropertiesTo(LayerImpl* base_layer) {
layer_impl->low_res_raster_contents_scale_ = low_res_raster_contents_scale_;
layer_impl->UpdateLCDTextStatus(is_using_lcd_text_);
+ layer_impl->needs_post_commit_initialization_ = false;
- // As an optimization, don't make a copy of this potentially complex region,
- // and swap it directly from the pending to the active layer. In general, any
- // property pushed to a LayerImpl continues to live on that LayerImpl.
- // However, invalidation is the difference between two main thread frames, so
- // it no longer makes sense once the pending tree gets recycled. It will
- // always get pushed during PictureLayer::PushPropertiesTo.
+ // The invalidation on this soon-to-be-recycled layer must be cleared to
+ // mirror clearing the invalidation in PictureLayer's version of this function
+ // in case push properties is skipped.
layer_impl->invalidation_.Swap(&invalidation_);
invalidation_.Clear();
+ needs_post_commit_initialization_ = true;
}
void PictureLayerImpl::AppendQuads(QuadSink* quad_sink,
AppendQuadsData* append_quads_data) {
+ DCHECK(!needs_post_commit_initialization_);
gfx::Rect rect(visible_content_rect());
gfx::Rect content_rect(content_bounds());
@@ -137,7 +136,7 @@ void PictureLayerImpl::AppendQuads(QuadSink* quad_sink,
opaque_rect,
texture_rect,
texture_size,
- false,
+ RGBA_8888,
quad_content_rect,
contents_scale,
draw_direct_to_backbuffer,
@@ -149,11 +148,6 @@ void PictureLayerImpl::AppendQuads(QuadSink* quad_sink,
AppendDebugBorderQuad(quad_sink, shared_quad_state, append_quads_data);
- bool clipped = false;
- gfx::QuadF target_quad = MathUtil::MapQuad(
- draw_transform(),
- gfx::QuadF(rect),
- &clipped);
if (ShowDebugBorders()) {
for (PictureLayerTilingSet::CoverageIterator iter(
tilings_.get(), contents_scale_x(), rect, ideal_contents_scale_);
@@ -228,6 +222,7 @@ void PictureLayerImpl::AppendQuads(QuadSink* quad_sink,
const ManagedTileState::TileVersion& tile_version =
iter->GetTileVersionForDrawing();
+ scoped_ptr<DrawQuad> draw_quad;
switch (tile_version.mode()) {
case ManagedTileState::TileVersion::RESOURCE_MODE: {
gfx::RectF texture_rect = iter.texture_rect();
@@ -245,7 +240,7 @@ void PictureLayerImpl::AppendQuads(QuadSink* quad_sink,
texture_rect,
iter.texture_size(),
tile_version.contents_swizzled());
- quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data);
+ draw_quad = quad.PassAs<DrawQuad>();
break;
}
case ManagedTileState::TileVersion::PICTURE_PILE_MODE: {
@@ -253,22 +248,22 @@ void PictureLayerImpl::AppendQuads(QuadSink* quad_sink,
gfx::Rect opaque_rect = iter->opaque_rect();
opaque_rect.Intersect(content_rect);
+ ResourceProvider* resource_provider =
+ layer_tree_impl()->resource_provider();
+ ResourceFormat format =
+ resource_provider->memory_efficient_texture_format();
scoped_ptr<PictureDrawQuad> quad = PictureDrawQuad::Create();
quad->SetNew(shared_quad_state,
geometry_rect,
opaque_rect,
texture_rect,
iter.texture_size(),
- // TODO(reveman): This assumes the renderer will use
- // GL_RGBA as format of temporary resource. The need
- // to swizzle should instead be determined by the
- // renderer.
- !PlatformColor::SameComponentOrder(GL_RGBA),
+ format,
iter->content_rect(),
iter->contents_scale(),
draw_direct_to_backbuffer,
pile_);
- quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data);
+ draw_quad = quad.PassAs<DrawQuad>();
break;
}
case ManagedTileState::TileVersion::SOLID_COLOR_MODE: {
@@ -277,14 +272,15 @@ void PictureLayerImpl::AppendQuads(QuadSink* quad_sink,
geometry_rect,
tile_version.get_solid_color(),
false);
- quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data);
+ draw_quad = quad.PassAs<DrawQuad>();
break;
}
- default:
- NOTREACHED();
}
- if (!seen_tilings.size() || seen_tilings.back() != iter.CurrentTiling())
+ DCHECK(draw_quad);
+ quad_sink->Append(draw_quad.Pass(), append_quads_data);
+
+ if (seen_tilings.empty() || seen_tilings.back() != iter.CurrentTiling())
seen_tilings.push_back(iter.CurrentTiling());
}
@@ -296,6 +292,15 @@ void PictureLayerImpl::AppendQuads(QuadSink* quad_sink,
}
void PictureLayerImpl::UpdateTilePriorities() {
+ DCHECK(!needs_post_commit_initialization_);
+ CHECK(should_update_tile_priorities_);
+
+ if (!layer_tree_impl()->device_viewport_valid_for_tile_management()) {
+ for (size_t i = 0; i < tilings_->num_tilings(); ++i)
+ DCHECK(tilings_->tiling_at(i)->has_ever_been_updated());
+ return;
+ }
+
if (!tilings_->num_tilings())
return;
@@ -314,19 +319,17 @@ void PictureLayerImpl::UpdateTilePriorities() {
if (!tiling_needs_update)
return;
- // At this point, tile priorities are going to be modified.
- layer_tree_impl()->WillModifyTilePriorities();
-
UpdateLCDTextStatus(can_use_lcd_text());
gfx::Transform current_screen_space_transform = screen_space_transform();
+ gfx::Size viewport_size = layer_tree_impl()->DrawViewportSize();
gfx::Rect viewport_in_content_space;
gfx::Transform screen_to_layer(gfx::Transform::kSkipInitialization);
if (screen_space_transform().GetInverse(&screen_to_layer)) {
- gfx::Rect device_viewport(layer_tree_impl()->device_viewport_size());
- viewport_in_content_space = gfx::ToEnclosingRect(
- MathUtil::ProjectClippedRect(screen_to_layer, device_viewport));
+ viewport_in_content_space =
+ gfx::ToEnclosingRect(MathUtil::ProjectClippedRect(
+ screen_to_layer, gfx::Rect(viewport_size)));
}
WhichTree tree =
@@ -335,7 +338,7 @@ void PictureLayerImpl::UpdateTilePriorities() {
layer_tree_impl()->settings().max_tiles_for_interest_area;
tilings_->UpdateTilePriorities(
tree,
- layer_tree_impl()->device_viewport_size(),
+ viewport_size,
viewport_in_content_space,
visible_content_rect(),
last_bounds_,
@@ -353,12 +356,15 @@ void PictureLayerImpl::UpdateTilePriorities() {
last_screen_space_transform_ = current_screen_space_transform;
last_bounds_ = bounds();
last_content_scale_ = contents_scale_x();
+
+ // Tile priorities were modified.
+ layer_tree_impl()->DidModifyTilePriorities();
}
void PictureLayerImpl::DidBecomeActive() {
LayerImpl::DidBecomeActive();
tilings_->DidBecomeActive();
- layer_tree_impl()->WillModifyTilePriorities();
+ layer_tree_impl()->DidModifyTilePriorities();
}
void PictureLayerImpl::DidBeginTracing() {
@@ -380,6 +386,12 @@ void PictureLayerImpl::CalculateContentsScale(
float* contents_scale_x,
float* contents_scale_y,
gfx::Size* content_bounds) {
+ DoPostCommitInitializationIfNeeded();
+
+ // This function sets valid raster scales and manages tilings, so tile
+ // priorities can now be updated.
+ should_update_tile_priorities_ = true;
+
if (!CanHaveTilings()) {
ideal_page_scale_ = page_scale_factor;
ideal_device_scale_ = device_scale_factor;
@@ -525,14 +537,10 @@ gfx::Size PictureLayerImpl::CalculateTileSize(
return default_tile_size;
}
-void PictureLayerImpl::SyncFromActiveLayer() {
- DCHECK(layer_tree_impl()->IsPendingTree());
-
- if (twin_layer_)
- SyncFromActiveLayer(twin_layer_);
-}
-
void PictureLayerImpl::SyncFromActiveLayer(const PictureLayerImpl* other) {
+ DCHECK(!other->needs_post_commit_initialization_);
+ DCHECK(other->tilings_);
+
UpdateLCDTextStatus(other->is_using_lcd_text_);
if (!DrawsContent()) {
@@ -581,6 +589,8 @@ void PictureLayerImpl::SyncFromActiveLayer(const PictureLayerImpl* other) {
} else {
tilings_->RemoveAllTilings();
}
+
+ SanityCheckTilingState();
}
void PictureLayerImpl::SyncTiling(
@@ -593,19 +603,11 @@ void PictureLayerImpl::SyncTiling(
// get updated prior to drawing or activation. If this tree does not
// need update draw properties, then its transforms are up to date and
// we can create tiles for this tiling immediately.
- if (!layer_tree_impl()->needs_update_draw_properties())
+ if (!layer_tree_impl()->needs_update_draw_properties() &&
+ should_update_tile_priorities_)
UpdateTilePriorities();
}
-void PictureLayerImpl::UpdateTwinLayer() {
- DCHECK(layer_tree_impl()->IsPendingTree());
-
- twin_layer_ = static_cast<PictureLayerImpl*>(
- layer_tree_impl()->FindActiveTreeLayerById(id()));
- if (twin_layer_)
- twin_layer_->twin_layer_ = this;
-}
-
void PictureLayerImpl::SetIsMask(bool is_mask) {
if (is_mask_ == is_mask)
return;
@@ -617,27 +619,24 @@ void PictureLayerImpl::SetIsMask(bool is_mask) {
ResourceProvider::ResourceId PictureLayerImpl::ContentsResourceId() const {
gfx::Rect content_rect(content_bounds());
float scale = contents_scale_x();
- for (PictureLayerTilingSet::CoverageIterator
- iter(tilings_.get(), scale, content_rect, ideal_contents_scale_);
- iter;
- ++iter) {
- // Mask resource not ready yet.
- if (!*iter)
- return 0;
+ PictureLayerTilingSet::CoverageIterator iter(
+ tilings_.get(), scale, content_rect, ideal_contents_scale_);
- const ManagedTileState::TileVersion& tile_version =
- iter->GetTileVersionForDrawing();
- if (!tile_version.IsReadyToDraw() ||
- tile_version.mode() != ManagedTileState::TileVersion::RESOURCE_MODE)
- return 0;
+ // Mask resource not ready yet.
+ if (!iter || !*iter)
+ return 0;
- // Masks only supported if they fit on exactly one tile.
- if (iter.geometry_rect() != content_rect)
- return 0;
+ // Masks only supported if they fit on exactly one tile.
+ if (iter.geometry_rect() != content_rect)
+ return 0;
- return tile_version.get_resource_id();
- }
- return 0;
+ const ManagedTileState::TileVersion& tile_version =
+ iter->GetTileVersionForDrawing();
+ if (!tile_version.IsReadyToDraw() ||
+ tile_version.mode() != ManagedTileState::TileVersion::RESOURCE_MODE)
+ return 0;
+
+ return tile_version.get_resource_id();
}
void PictureLayerImpl::MarkVisibleResourcesAsRequired() const {
@@ -695,7 +694,7 @@ void PictureLayerImpl::MarkVisibleResourcesAsRequired() const {
continue;
missing_region.Subtract(iter.geometry_rect());
- iter->mark_required_for_activation();
+ iter->MarkRequiredForActivation();
}
}
@@ -720,8 +719,30 @@ void PictureLayerImpl::MarkVisibleResourcesAsRequired() const {
if (!missing_region.Intersects(iter.geometry_rect()))
continue;
- iter->mark_required_for_activation();
+ iter->MarkRequiredForActivation();
+ }
+}
+
+void PictureLayerImpl::DoPostCommitInitialization() {
+ DCHECK(needs_post_commit_initialization_);
+ DCHECK(layer_tree_impl()->IsPendingTree());
+
+ if (!tilings_)
+ tilings_.reset(new PictureLayerTilingSet(this, bounds()));
+
+ DCHECK(!twin_layer_);
+ twin_layer_ = static_cast<PictureLayerImpl*>(
+ layer_tree_impl()->FindActiveTreeLayerById(id()));
+ if (twin_layer_) {
+ DCHECK(!twin_layer_->twin_layer_);
+ twin_layer_->twin_layer_ = this;
+ // If the twin has never been pushed to, do not sync from it.
+ // This can happen if this function is called during activation.
+ if (!twin_layer_->needs_post_commit_initialization_)
+ SyncFromActiveLayer(twin_layer_);
}
+
+ needs_post_commit_initialization_ = false;
}
PictureLayerTiling* PictureLayerImpl::AddTiling(float contents_scale) {
@@ -747,6 +768,7 @@ void PictureLayerImpl::RemoveTiling(float contents_scale) {
break;
}
}
+ SanityCheckTilingState();
}
namespace {
@@ -775,6 +797,7 @@ void PictureLayerImpl::ManageTilings(bool animating_transform_to_screen) {
DCHECK(ideal_device_scale_);
DCHECK(ideal_source_scale_);
DCHECK(CanHaveTilings());
+ DCHECK(!needs_post_commit_initialization_);
bool change_target_tiling =
raster_page_scale_ == 0.f ||
@@ -790,6 +813,9 @@ void PictureLayerImpl::ManageTilings(bool animating_transform_to_screen) {
if (!change_target_tiling)
return;
+ if (!layer_tree_impl()->device_viewport_valid_for_tile_management())
+ return;
+
raster_page_scale_ = ideal_page_scale_;
raster_device_scale_ = ideal_device_scale_;
raster_source_scale_ = ideal_source_scale_;
@@ -829,12 +855,13 @@ void PictureLayerImpl::ManageTilings(bool animating_transform_to_screen) {
low_res != high_res)
low_res = AddTiling(low_res_raster_contents_scale_);
- if (high_res)
- high_res->set_resolution(HIGH_RESOLUTION);
+ high_res->set_resolution(HIGH_RESOLUTION);
if (low_res && low_res != high_res)
low_res->set_resolution(LOW_RESOLUTION);
else if (!low_res && previous_low_res)
previous_low_res->set_resolution(LOW_RESOLUTION);
+
+ SanityCheckTilingState();
}
bool PictureLayerImpl::ShouldAdjustRasterScale(
@@ -947,6 +974,8 @@ void PictureLayerImpl::CleanUpTilingsOnActiveLayer(
twin->RemoveTiling(to_remove[i]->contents_scale());
tilings_->Remove(to_remove[i]);
}
+
+ SanityCheckTilingState();
}
float PictureLayerImpl::MinimumContentsScale() const {
@@ -981,6 +1010,10 @@ void PictureLayerImpl::ResetRasterScale() {
raster_source_scale_ = 0.f;
raster_contents_scale_ = 0.f;
low_res_raster_contents_scale_ = 0.f;
+
+ // When raster scales aren't valid, don't update tile priorities until
+ // this layer has been updated via UpdateDrawProperties.
+ should_update_tile_priorities_ = false;
}
bool PictureLayerImpl::CanHaveTilings() const {
@@ -1002,6 +1035,22 @@ bool PictureLayerImpl::CanHaveTilingWithScale(float contents_scale) const {
return true;
}
+void PictureLayerImpl::SanityCheckTilingState() const {
+ if (!DCHECK_IS_ON())
+ return;
+
+ if (!CanHaveTilings()) {
+ DCHECK_EQ(0u, tilings_->num_tilings());
+ return;
+ }
+ if (tilings_->num_tilings() == 0)
+ return;
+
+ // MarkVisibleResourcesAsRequired depends on having exactly 1 high res
+ // tiling to mark its tiles as being required for activation.
+ DCHECK_EQ(1, tilings_->NumHighResTilings());
+}
+
void PictureLayerImpl::GetDebugBorderProperties(
SkColor* color,
float* width) const {
@@ -1012,6 +1061,7 @@ void PictureLayerImpl::GetDebugBorderProperties(
void PictureLayerImpl::AsValueInto(base::DictionaryValue* state) const {
LayerImpl::AsValueInto(state);
state->SetDouble("ideal_contents_scale", ideal_contents_scale_);
+ state->SetDouble("geometry_contents_scale", contents_scale_x());
state->Set("tilings", tilings_->AsValue().release());
state->Set("pictures", pile_->AsValue().release());
state->Set("invalidation", invalidation_.AsValue().release());
@@ -1019,7 +1069,7 @@ void PictureLayerImpl::AsValueInto(base::DictionaryValue* state) const {
scoped_ptr<base::ListValue> coverage_tiles(new base::ListValue);
for (PictureLayerTilingSet::CoverageIterator iter(tilings_.get(),
contents_scale_x(),
- gfx::Rect(bounds()),
+ gfx::Rect(content_bounds()),
ideal_contents_scale_);
iter;
++iter) {
diff --git a/chromium/cc/layers/picture_layer_impl.h b/chromium/cc/layers/picture_layer_impl.h
index 25cc5d0b7e7..fd5c0677a98 100644
--- a/chromium/cc/layers/picture_layer_impl.h
+++ b/chromium/cc/layers/picture_layer_impl.h
@@ -62,11 +62,7 @@ class CC_EXPORT PictureLayerImpl
const PictureLayerTiling* tiling) OVERRIDE;
// PushPropertiesTo active tree => pending tree.
- void SyncFromActiveLayer();
void SyncTiling(const PictureLayerTiling* tiling);
- void UpdateTwinLayer();
-
- void CreateTilingSetIfNeeded();
// Mask-related functions
void SetIsMask(bool is_mask);
@@ -92,9 +88,15 @@ class CC_EXPORT PictureLayerImpl
void UpdateLCDTextStatus(bool new_status);
void ResetRasterScale();
void MarkVisibleResourcesAsRequired() const;
+ void DoPostCommitInitializationIfNeeded() {
+ if (needs_post_commit_initialization_)
+ DoPostCommitInitialization();
+ }
+ void DoPostCommitInitialization();
bool CanHaveTilings() const;
bool CanHaveTilingWithScale(float contents_scale) const;
+ void SanityCheckTilingState() const;
virtual void GetDebugBorderProperties(
SkColor* color, float* width) const OVERRIDE;
@@ -124,6 +126,10 @@ class CC_EXPORT PictureLayerImpl
bool raster_source_scale_was_animating_;
bool is_using_lcd_text_;
+ bool needs_post_commit_initialization_;
+ // A sanity state check to make sure UpdateTilePriorities only gets called
+ // after a CalculateContentsScale/ManageTilings.
+ bool should_update_tile_priorities_;
friend class PictureLayer;
DISALLOW_COPY_AND_ASSIGN(PictureLayerImpl);
diff --git a/chromium/cc/layers/picture_layer_impl_unittest.cc b/chromium/cc/layers/picture_layer_impl_unittest.cc
index 047a529924a..e1cd39ada41 100644
--- a/chromium/cc/layers/picture_layer_impl_unittest.cc
+++ b/chromium/cc/layers/picture_layer_impl_unittest.cc
@@ -6,6 +6,7 @@
#include <utility>
+#include "cc/debug/test_web_graphics_context_3d.h"
#include "cc/layers/append_quads_data.h"
#include "cc/layers/picture_layer.h"
#include "cc/test/fake_content_layer_client.h"
@@ -19,7 +20,7 @@
#include "cc/test/mock_quad_culler.h"
#include "cc/trees/layer_tree_impl.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/skia/include/core/SkDevice.h"
+#include "third_party/skia/include/core/SkBitmapDevice.h"
#include "ui/gfx/rect_conversions.h"
namespace cc {
@@ -27,7 +28,7 @@ namespace {
class MockCanvas : public SkCanvas {
public:
- explicit MockCanvas(SkDevice* device) : SkCanvas(device) {}
+ explicit MockCanvas(SkBaseDevice* device) : SkCanvas(device) {}
virtual void drawRect(const SkRect& rect, const SkPaint& paint) OVERRIDE {
// Capture calls before SkCanvas quickReject() kicks in.
@@ -40,14 +41,22 @@ class MockCanvas : public SkCanvas {
class PictureLayerImplTest : public testing::Test {
public:
PictureLayerImplTest()
- : host_impl_(ImplSidePaintingSettings(), &proxy_),
- id_(7) {
- host_impl_.InitializeRenderer(CreateFakeOutputSurface());
- }
+ : host_impl_(ImplSidePaintingSettings(), &proxy_), id_(7) {}
+
+ explicit PictureLayerImplTest(const LayerTreeSettings& settings)
+ : host_impl_(settings, &proxy_), id_(7) {}
virtual ~PictureLayerImplTest() {
}
+ virtual void SetUp() OVERRIDE {
+ InitializeRenderer();
+ }
+
+ virtual void InitializeRenderer() {
+ host_impl_.InitializeRenderer(CreateFakeOutputSurface());
+ }
+
void SetupDefaultTrees(gfx::Size layer_bounds) {
gfx::Size tile_size(100, 100);
@@ -69,7 +78,6 @@ class PictureLayerImplTest : public testing::Test {
host_impl_.active_tree()->LayerById(id_));
SetupPendingTree(pending_pile);
- pending_layer_->UpdateTwinLayer();
}
void AddDefaultTilingsWithInvalidation(const Region& invalidation) {
@@ -79,7 +87,6 @@ class PictureLayerImplTest : public testing::Test {
for (size_t i = 0; i < active_layer_->tilings()->num_tilings(); ++i)
active_layer_->tilings()->tiling_at(i)->CreateAllTilesForTesting();
pending_layer_->set_invalidation(invalidation);
- pending_layer_->SyncFromActiveLayer();
for (size_t i = 0; i < pending_layer_->tilings()->num_tilings(); ++i)
pending_layer_->tilings()->tiling_at(i)->CreateAllTilesForTesting();
}
@@ -98,6 +105,7 @@ class PictureLayerImplTest : public testing::Test {
pending_layer_ = static_cast<FakePictureLayerImpl*>(
host_impl_.pending_tree()->LayerById(id_));
+ pending_layer_->DoPostCommitInitializationIfNeeded();
}
static void VerifyAllTilesExistAndHavePile(
@@ -180,7 +188,7 @@ class PictureLayerImplTest : public testing::Test {
SkBitmap store;
store.setConfig(SkBitmap::kNo_Config, 1000, 1000);
- SkDevice device(store);
+ SkBitmapDevice device(store);
std::vector<SkRect>::const_iterator rect_iter = rects.begin();
for (tile_iter = tiles.begin(); tile_iter < tiles.end(); tile_iter++) {
@@ -241,6 +249,62 @@ TEST_F(PictureLayerImplTest, CloneNoInvalidation) {
VerifyAllTilesExistAndHavePile(tilings->tiling_at(i), active_pile.get());
}
+TEST_F(PictureLayerImplTest, SuppressUpdateTilePriorities) {
+ base::TimeTicks time_ticks;
+ host_impl_.SetCurrentFrameTimeTicks(time_ticks);
+
+ gfx::Size tile_size(100, 100);
+ gfx::Size layer_bounds(400, 400);
+
+ scoped_refptr<FakePicturePileImpl> pending_pile =
+ FakePicturePileImpl::CreateFilledPile(tile_size, layer_bounds);
+ scoped_refptr<FakePicturePileImpl> active_pile =
+ FakePicturePileImpl::CreateFilledPile(tile_size, layer_bounds);
+
+ SetupTrees(pending_pile, active_pile);
+
+ Region invalidation;
+ AddDefaultTilingsWithInvalidation(invalidation);
+ float dummy_contents_scale_x;
+ float dummy_contents_scale_y;
+ gfx::Size dummy_content_bounds;
+ active_layer_->CalculateContentsScale(1.f,
+ 1.f,
+ 1.f,
+ false,
+ &dummy_contents_scale_x,
+ &dummy_contents_scale_y,
+ &dummy_content_bounds);
+
+ EXPECT_TRUE(host_impl_.manage_tiles_needed());
+ active_layer_->UpdateTilePriorities();
+ host_impl_.ManageTiles();
+ EXPECT_FALSE(host_impl_.manage_tiles_needed());
+
+ time_ticks += base::TimeDelta::FromMilliseconds(200);
+ host_impl_.SetCurrentFrameTimeTicks(time_ticks);
+
+ // Setting this boolean should cause an early out in UpdateTilePriorities.
+ bool valid_for_tile_management = false;
+ host_impl_.SetExternalDrawConstraints(gfx::Transform(),
+ gfx::Rect(layer_bounds),
+ gfx::Rect(layer_bounds),
+ valid_for_tile_management);
+ active_layer_->UpdateTilePriorities();
+ EXPECT_FALSE(host_impl_.manage_tiles_needed());
+
+ time_ticks += base::TimeDelta::FromMilliseconds(200);
+ host_impl_.SetCurrentFrameTimeTicks(time_ticks);
+
+ valid_for_tile_management = true;
+ host_impl_.SetExternalDrawConstraints(gfx::Transform(),
+ gfx::Rect(layer_bounds),
+ gfx::Rect(layer_bounds),
+ valid_for_tile_management);
+ active_layer_->UpdateTilePriorities();
+ EXPECT_TRUE(host_impl_.manage_tiles_needed());
+}
+
TEST_F(PictureLayerImplTest, ClonePartialInvalidation) {
gfx::Size tile_size(100, 100);
gfx::Size layer_bounds(400, 400);
@@ -822,7 +886,7 @@ TEST_F(PictureLayerImplTest, ClampTilesToToMaxTileSize) {
TestWebGraphicsContext3D::Create();
context->set_max_texture_size(140);
host_impl_.InitializeRenderer(FakeOutputSurface::Create3d(
- context.PassAs<WebKit::WebGraphicsContext3D>()).PassAs<OutputSurface>());
+ context.Pass()).PassAs<OutputSurface>());
pending_layer_->CalculateContentsScale(
1.f, 1.f, 1.f, false, &result_scale_x, &result_scale_y, &result_bounds);
@@ -873,7 +937,7 @@ TEST_F(PictureLayerImplTest, ClampSingleTileToToMaxTileSize) {
TestWebGraphicsContext3D::Create();
context->set_max_texture_size(140);
host_impl_.InitializeRenderer(FakeOutputSurface::Create3d(
- context.PassAs<WebKit::WebGraphicsContext3D>()).PassAs<OutputSurface>());
+ context.Pass()).PassAs<OutputSurface>());
pending_layer_->CalculateContentsScale(
1.f, 1.f, 1.f, false, &result_scale_x, &result_scale_y, &result_bounds);
@@ -1007,5 +1071,98 @@ TEST_F(PictureLayerImplTest, MarkRequiredOffscreenTiles) {
EXPECT_GT(num_offscreen, 0);
}
+TEST_F(PictureLayerImplTest, ActivateUninitializedLayer) {
+ gfx::Size tile_size(100, 100);
+ gfx::Size layer_bounds(400, 400);
+ scoped_refptr<FakePicturePileImpl> pending_pile =
+ FakePicturePileImpl::CreateFilledPile(tile_size, layer_bounds);
+
+ host_impl_.CreatePendingTree();
+ LayerTreeImpl* pending_tree = host_impl_.pending_tree();
+
+ scoped_ptr<FakePictureLayerImpl> pending_layer =
+ FakePictureLayerImpl::CreateWithPile(pending_tree, id_, pending_pile);
+ pending_layer->SetDrawsContent(true);
+ pending_tree->SetRootLayer(pending_layer.PassAs<LayerImpl>());
+
+ pending_layer_ = static_cast<FakePictureLayerImpl*>(
+ host_impl_.pending_tree()->LayerById(id_));
+
+ // Set some state on the pending layer, make sure it is not clobbered
+ // by a sync from the active layer. This could happen because if the
+ // pending layer has not been post-commit initialized it will attempt
+ // to sync from the active layer.
+ bool default_lcd_text_setting = pending_layer_->is_using_lcd_text();
+ pending_layer_->force_set_lcd_text(!default_lcd_text_setting);
+ EXPECT_TRUE(pending_layer_->needs_post_commit_initialization());
+
+ host_impl_.ActivatePendingTree();
+
+ active_layer_ = static_cast<FakePictureLayerImpl*>(
+ host_impl_.active_tree()->LayerById(id_));
+
+ EXPECT_EQ(0u, active_layer_->num_tilings());
+ EXPECT_EQ(!default_lcd_text_setting, active_layer_->is_using_lcd_text());
+ EXPECT_FALSE(active_layer_->needs_post_commit_initialization());
+}
+
+// Solid color scrollbar setting is required for deferred initialization.
+class ImplSidePaintingSolidColorScrollbarSettings
+ : public ImplSidePaintingSettings {
+ public:
+ ImplSidePaintingSolidColorScrollbarSettings() {
+ solid_color_scrollbars = true;
+ }
+};
+
+class DeferredInitPictureLayerImplTest : public PictureLayerImplTest {
+ public:
+ DeferredInitPictureLayerImplTest()
+ : PictureLayerImplTest(ImplSidePaintingSolidColorScrollbarSettings()) {}
+
+ virtual void InitializeRenderer() OVERRIDE {
+ host_impl_.InitializeRenderer(FakeOutputSurface::CreateDeferredGL(
+ scoped_ptr<SoftwareOutputDevice>(new SoftwareOutputDevice))
+ .PassAs<OutputSurface>());
+ }
+
+ virtual void SetUp() OVERRIDE {
+ PictureLayerImplTest::SetUp();
+
+ // Create some default active and pending trees.
+ gfx::Size tile_size(100, 100);
+ gfx::Size layer_bounds(400, 400);
+
+ scoped_refptr<FakePicturePileImpl> pending_pile =
+ FakePicturePileImpl::CreateFilledPile(tile_size, layer_bounds);
+ scoped_refptr<FakePicturePileImpl> active_pile =
+ FakePicturePileImpl::CreateFilledPile(tile_size, layer_bounds);
+
+ SetupTrees(pending_pile, active_pile);
+ }
+};
+
+// This test is really a LayerTreeHostImpl test, in that it makes sure
+// that trees need update draw properties after deferred initialization.
+// However, this is also a regression test for PictureLayerImpl in that
+// not having this update will cause a crash.
+TEST_F(DeferredInitPictureLayerImplTest,
+ PreventUpdateTilePrioritiesDuringLostContext) {
+ host_impl_.pending_tree()->UpdateDrawProperties();
+ host_impl_.active_tree()->UpdateDrawProperties();
+ EXPECT_FALSE(host_impl_.pending_tree()->needs_update_draw_properties());
+ EXPECT_FALSE(host_impl_.active_tree()->needs_update_draw_properties());
+
+ FakeOutputSurface* fake_output_surface =
+ static_cast<FakeOutputSurface*>(host_impl_.output_surface());
+ ASSERT_TRUE(fake_output_surface->InitializeAndSetContext3d(
+ TestContextProvider::Create(), NULL));
+
+ // These will crash PictureLayerImpl if this is not true.
+ ASSERT_TRUE(host_impl_.pending_tree()->needs_update_draw_properties());
+ ASSERT_TRUE(host_impl_.active_tree()->needs_update_draw_properties());
+ host_impl_.active_tree()->UpdateDrawProperties();
+}
+
} // namespace
} // namespace cc
diff --git a/chromium/cc/layers/picture_layer_unittest.cc b/chromium/cc/layers/picture_layer_unittest.cc
new file mode 100644
index 00000000000..c3dd244c2ef
--- /dev/null
+++ b/chromium/cc/layers/picture_layer_unittest.cc
@@ -0,0 +1,69 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/layers/picture_layer.h"
+
+#include "cc/layers/content_layer_client.h"
+#include "cc/layers/picture_layer_impl.h"
+#include "cc/resources/resource_update_queue.h"
+#include "cc/test/fake_layer_tree_host.h"
+#include "cc/test/fake_picture_layer_impl.h"
+#include "cc/test/fake_proxy.h"
+#include "cc/test/impl_side_painting_settings.h"
+#include "cc/trees/occlusion_tracker.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cc {
+namespace {
+
+class MockContentLayerClient : public ContentLayerClient {
+ public:
+ virtual void PaintContents(SkCanvas* canvas,
+ gfx::Rect clip,
+ gfx::RectF* opaque) OVERRIDE {}
+ virtual void DidChangeLayerCanUseLCDText() OVERRIDE {}
+};
+
+TEST(PictureLayerTest, NoTilesIfEmptyBounds) {
+ MockContentLayerClient client;
+ scoped_refptr<PictureLayer> layer = PictureLayer::Create(&client);
+ layer->SetBounds(gfx::Size(10, 10));
+
+ scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create();
+ host->SetRootLayer(layer);
+ layer->SetIsDrawable(true);
+ layer->SavePaintProperties();
+
+ OcclusionTracker occlusion(gfx::Rect(0, 0, 1000, 1000), false);
+ scoped_ptr<ResourceUpdateQueue> queue(new ResourceUpdateQueue);
+ layer->Update(queue.get(), &occlusion);
+
+ layer->SetBounds(gfx::Size(0, 0));
+ layer->SavePaintProperties();
+ // Intentionally skipping Update since it would normally be skipped on
+ // a layer with empty bounds.
+
+ FakeProxy proxy;
+#ifndef NDEBUG
+ proxy.SetCurrentThreadIsImplThread(true);
+#endif
+ {
+ FakeLayerTreeHostImpl host_impl(ImplSidePaintingSettings(), &proxy);
+ host_impl.CreatePendingTree();
+ scoped_ptr<FakePictureLayerImpl> layer_impl =
+ FakePictureLayerImpl::Create(host_impl.pending_tree(), 1);
+
+ layer->PushPropertiesTo(layer_impl.get());
+ EXPECT_FALSE(layer_impl->CanHaveTilings());
+ EXPECT_TRUE(layer_impl->bounds() == gfx::Size(0, 0));
+ EXPECT_TRUE(layer_impl->pile()->size() == gfx::Size(0, 0));
+ EXPECT_TRUE(layer_impl->pile()->recorded_region().IsEmpty());
+ }
+#ifndef NDEBUG
+ proxy.SetCurrentThreadIsImplThread(false);
+#endif
+}
+
+} // namespace
+} // namespace cc
diff --git a/chromium/cc/layers/scrollbar_layer.cc b/chromium/cc/layers/scrollbar_layer.cc
deleted file mode 100644
index 95eb6384732..00000000000
--- a/chromium/cc/layers/scrollbar_layer.cc
+++ /dev/null
@@ -1,353 +0,0 @@
-
-// Copyright 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "cc/layers/scrollbar_layer.h"
-
-#include "base/auto_reset.h"
-#include "base/basictypes.h"
-#include "base/debug/trace_event.h"
-#include "cc/layers/scrollbar_layer_impl.h"
-#include "cc/resources/caching_bitmap_content_layer_updater.h"
-#include "cc/resources/layer_painter.h"
-#include "cc/resources/prioritized_resource.h"
-#include "cc/resources/resource_update_queue.h"
-#include "cc/trees/layer_tree_host.h"
-#include "ui/gfx/rect_conversions.h"
-
-namespace cc {
-
-scoped_ptr<LayerImpl> ScrollbarLayer::CreateLayerImpl(
- LayerTreeImpl* tree_impl) {
- return ScrollbarLayerImpl::Create(
- tree_impl, id(), scrollbar_->Orientation()).PassAs<LayerImpl>();
-}
-
-scoped_refptr<ScrollbarLayer> ScrollbarLayer::Create(
- scoped_ptr<Scrollbar> scrollbar,
- int scroll_layer_id) {
- return make_scoped_refptr(new ScrollbarLayer(scrollbar.Pass(),
- scroll_layer_id));
-}
-
-ScrollbarLayer::ScrollbarLayer(
- scoped_ptr<Scrollbar> scrollbar,
- int scroll_layer_id)
- : scrollbar_(scrollbar.Pass()),
- scroll_layer_id_(scroll_layer_id),
- texture_format_(GL_INVALID_ENUM) {
- if (!scrollbar_->IsOverlay())
- SetShouldScrollOnMainThread(true);
-}
-
-ScrollbarLayer::~ScrollbarLayer() {}
-
-void ScrollbarLayer::SetScrollLayerId(int id) {
- if (id == scroll_layer_id_)
- return;
-
- scroll_layer_id_ = id;
- SetNeedsFullTreeSync();
-}
-
-bool ScrollbarLayer::OpacityCanAnimateOnImplThread() const {
- return scrollbar_->IsOverlay();
-}
-
-ScrollbarOrientation ScrollbarLayer::Orientation() const {
- return scrollbar_->Orientation();
-}
-
-int ScrollbarLayer::MaxTextureSize() {
- DCHECK(layer_tree_host());
- return layer_tree_host()->GetRendererCapabilities().max_texture_size;
-}
-
-float ScrollbarLayer::ClampScaleToMaxTextureSize(float scale) {
- if (layer_tree_host()->settings().solid_color_scrollbars)
- return scale;
-
- // If the scaled content_bounds() is bigger than the max texture size of the
- // device, we need to clamp it by rescaling, since content_bounds() is used
- // below to set the texture size.
- gfx::Size scaled_bounds = ComputeContentBoundsForScale(scale, scale);
- if (scaled_bounds.width() > MaxTextureSize() ||
- scaled_bounds.height() > MaxTextureSize()) {
- if (scaled_bounds.width() > scaled_bounds.height())
- return (MaxTextureSize() - 1) / static_cast<float>(bounds().width());
- else
- return (MaxTextureSize() - 1) / static_cast<float>(bounds().height());
- }
- return scale;
-}
-
-void ScrollbarLayer::CalculateContentsScale(float ideal_contents_scale,
- float device_scale_factor,
- float page_scale_factor,
- bool animating_transform_to_screen,
- float* contents_scale_x,
- float* contents_scale_y,
- gfx::Size* content_bounds) {
- ContentsScalingLayer::CalculateContentsScale(
- ClampScaleToMaxTextureSize(ideal_contents_scale),
- device_scale_factor,
- page_scale_factor,
- animating_transform_to_screen,
- contents_scale_x,
- contents_scale_y,
- content_bounds);
-}
-
-void ScrollbarLayer::PushPropertiesTo(LayerImpl* layer) {
- ContentsScalingLayer::PushPropertiesTo(layer);
-
- ScrollbarLayerImpl* scrollbar_layer = static_cast<ScrollbarLayerImpl*>(layer);
-
- if (layer_tree_host() &&
- layer_tree_host()->settings().solid_color_scrollbars) {
- int thickness_override =
- layer_tree_host()->settings().solid_color_scrollbar_thickness_dip;
- if (thickness_override != -1) {
- scrollbar_layer->SetThumbThickness(thickness_override);
- } else {
- if (Orientation() == HORIZONTAL)
- scrollbar_layer->SetThumbThickness(bounds().height());
- else
- scrollbar_layer->SetThumbThickness(bounds().width());
- }
- } else {
- scrollbar_layer->SetThumbThickness(thumb_thickness_);
- }
- scrollbar_layer->SetThumbLength(thumb_length_);
- if (Orientation() == HORIZONTAL) {
- scrollbar_layer->SetTrackStart(track_rect_.x() - location_.x());
- scrollbar_layer->SetTrackLength(track_rect_.width());
- } else {
- scrollbar_layer->SetTrackStart(track_rect_.y() - location_.y());
- scrollbar_layer->SetTrackLength(track_rect_.height());
- }
-
- if (track_ && track_->texture()->have_backing_texture())
- scrollbar_layer->set_track_resource_id(track_->texture()->resource_id());
- else
- scrollbar_layer->set_track_resource_id(0);
-
- if (thumb_ && thumb_->texture()->have_backing_texture())
- scrollbar_layer->set_thumb_resource_id(thumb_->texture()->resource_id());
- else
- scrollbar_layer->set_thumb_resource_id(0);
-
- scrollbar_layer->set_is_overlay_scrollbar(scrollbar_->IsOverlay());
-
- // ScrollbarLayer must push properties every frame. crbug.com/259095
- needs_push_properties_ = true;
-}
-
-ScrollbarLayer* ScrollbarLayer::ToScrollbarLayer() {
- return this;
-}
-
-void ScrollbarLayer::SetLayerTreeHost(LayerTreeHost* host) {
- if (!host || host != layer_tree_host()) {
- track_updater_ = NULL;
- track_.reset();
- thumb_updater_ = NULL;
- thumb_.reset();
- }
-
- ContentsScalingLayer::SetLayerTreeHost(host);
-}
-
-class ScrollbarPartPainter : public LayerPainter {
- public:
- ScrollbarPartPainter(Scrollbar* scrollbar, ScrollbarPart part)
- : scrollbar_(scrollbar),
- part_(part) {}
- virtual ~ScrollbarPartPainter() {}
-
- // LayerPainter implementation
- virtual void Paint(SkCanvas* canvas,
- gfx::Rect content_rect,
- gfx::RectF* opaque) OVERRIDE {
- scrollbar_->PaintPart(canvas, part_, content_rect);
- }
-
- private:
- Scrollbar* scrollbar_;
- ScrollbarPart part_;
-};
-
-void ScrollbarLayer::CreateUpdaterIfNeeded() {
- if (layer_tree_host()->settings().solid_color_scrollbars)
- return;
-
- texture_format_ =
- layer_tree_host()->GetRendererCapabilities().best_texture_format;
-
- if (!track_updater_.get()) {
- track_updater_ = CachingBitmapContentLayerUpdater::Create(
- scoped_ptr<LayerPainter>(
- new ScrollbarPartPainter(scrollbar_.get(), TRACK))
- .Pass(),
- rendering_stats_instrumentation(),
- id());
- }
- if (!track_) {
- track_ = track_updater_->CreateResource(
- layer_tree_host()->contents_texture_manager());
- }
-
- if (!thumb_updater_.get()) {
- thumb_updater_ = CachingBitmapContentLayerUpdater::Create(
- scoped_ptr<LayerPainter>(
- new ScrollbarPartPainter(scrollbar_.get(), THUMB))
- .Pass(),
- rendering_stats_instrumentation(),
- id());
- }
- if (!thumb_ && scrollbar_->HasThumb()) {
- thumb_ = thumb_updater_->CreateResource(
- layer_tree_host()->contents_texture_manager());
- }
-}
-
-bool ScrollbarLayer::UpdatePart(CachingBitmapContentLayerUpdater* painter,
- LayerUpdater::Resource* resource,
- gfx::Rect rect,
- ResourceUpdateQueue* queue) {
- if (layer_tree_host()->settings().solid_color_scrollbars)
- return false;
-
- // Skip painting and uploading if there are no invalidations and
- // we already have valid texture data.
- if (resource->texture()->have_backing_texture() &&
- resource->texture()->size() == rect.size() &&
- !is_dirty())
- return false;
-
- // We should always have enough memory for UI.
- DCHECK(resource->texture()->can_acquire_backing_texture());
- if (!resource->texture()->can_acquire_backing_texture())
- return false;
-
- // Paint and upload the entire part.
- gfx::Rect painted_opaque_rect;
- painter->PrepareToUpdate(rect,
- rect.size(),
- contents_scale_x(),
- contents_scale_y(),
- &painted_opaque_rect);
- if (!painter->pixels_did_change() &&
- resource->texture()->have_backing_texture()) {
- TRACE_EVENT_INSTANT0("cc",
- "ScrollbarLayer::UpdatePart no texture upload needed",
- TRACE_EVENT_SCOPE_THREAD);
- return false;
- }
-
- bool partial_updates_allowed =
- layer_tree_host()->settings().max_partial_texture_updates > 0;
- if (!partial_updates_allowed)
- resource->texture()->ReturnBackingTexture();
-
- gfx::Vector2d dest_offset(0, 0);
- resource->Update(queue, rect, dest_offset, partial_updates_allowed);
- return true;
-}
-
-gfx::Rect ScrollbarLayer::ScrollbarLayerRectToContentRect(
- gfx::Rect layer_rect) const {
- // Don't intersect with the bounds as in LayerRectToContentRect() because
- // layer_rect here might be in coordinates of the containing layer.
- gfx::Rect expanded_rect = gfx::ScaleToEnclosingRect(
- layer_rect, contents_scale_y(), contents_scale_y());
- // We should never return a rect bigger than the content_bounds().
- gfx::Size clamped_size = expanded_rect.size();
- clamped_size.SetToMin(content_bounds());
- expanded_rect.set_size(clamped_size);
- return expanded_rect;
-}
-
-void ScrollbarLayer::SetTexturePriorities(
- const PriorityCalculator& priority_calc) {
- if (layer_tree_host()->settings().solid_color_scrollbars)
- return;
-
- if (content_bounds().IsEmpty())
- return;
- DCHECK_LE(content_bounds().width(), MaxTextureSize());
- DCHECK_LE(content_bounds().height(), MaxTextureSize());
-
- CreateUpdaterIfNeeded();
-
- bool draws_to_root = !render_target()->parent();
- if (track_) {
- track_->texture()->SetDimensions(content_bounds(), texture_format_);
- track_->texture()->set_request_priority(
- PriorityCalculator::UIPriority(draws_to_root));
- }
- if (thumb_) {
- gfx::Size thumb_size = OriginThumbRect().size();
- thumb_->texture()->SetDimensions(thumb_size, texture_format_);
- thumb_->texture()->set_request_priority(
- PriorityCalculator::UIPriority(draws_to_root));
- }
-}
-
-bool ScrollbarLayer::Update(ResourceUpdateQueue* queue,
- const OcclusionTracker* occlusion) {
- track_rect_ = scrollbar_->TrackRect();
- location_ = scrollbar_->Location();
-
- if (layer_tree_host()->settings().solid_color_scrollbars)
- return false;
-
- bool updated = false;
-
- {
- base::AutoReset<bool> ignore_set_needs_commit(&ignore_set_needs_commit_,
- true);
- updated = ContentsScalingLayer::Update(queue, occlusion);
- }
-
- dirty_rect_.Union(update_rect_);
- if (content_bounds().IsEmpty())
- return false;
- if (visible_content_rect().IsEmpty())
- return false;
-
- CreateUpdaterIfNeeded();
-
- gfx::Rect content_rect = ScrollbarLayerRectToContentRect(
- gfx::Rect(scrollbar_->Location(), bounds()));
- updated |= UpdatePart(track_updater_.get(), track_.get(), content_rect,
- queue);
-
- if (scrollbar_->HasThumb()) {
- thumb_thickness_ = scrollbar_->ThumbThickness();
- thumb_length_ = scrollbar_->ThumbLength();
- gfx::Rect origin_thumb_rect = OriginThumbRect();
- if (!origin_thumb_rect.IsEmpty()) {
- updated |= UpdatePart(thumb_updater_.get(), thumb_.get(),
- origin_thumb_rect, queue);
- }
- }
-
- dirty_rect_ = gfx::RectF();
- return updated;
-}
-
-gfx::Rect ScrollbarLayer::OriginThumbRect() const {
- gfx::Size thumb_size;
- if (Orientation() == HORIZONTAL) {
- thumb_size = gfx::Size(scrollbar_->ThumbLength(),
- scrollbar_->ThumbThickness());
- } else {
- thumb_size = gfx::Size(scrollbar_->ThumbThickness(),
- scrollbar_->ThumbLength());
- }
- return ScrollbarLayerRectToContentRect(gfx::Rect(thumb_size));
-}
-
-} // namespace cc
diff --git a/chromium/cc/layers/scrollbar_layer_impl.cc b/chromium/cc/layers/scrollbar_layer_impl.cc
deleted file mode 100644
index 1de185a77dd..00000000000
--- a/chromium/cc/layers/scrollbar_layer_impl.cc
+++ /dev/null
@@ -1,328 +0,0 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "cc/layers/scrollbar_layer_impl.h"
-
-#include <algorithm>
-
-#include "cc/animation/scrollbar_animation_controller.h"
-#include "cc/layers/layer.h"
-#include "cc/layers/quad_sink.h"
-#include "cc/quads/solid_color_draw_quad.h"
-#include "cc/quads/texture_draw_quad.h"
-#include "cc/trees/layer_tree_impl.h"
-#include "cc/trees/layer_tree_settings.h"
-#include "ui/gfx/rect_conversions.h"
-
-namespace cc {
-
-scoped_ptr<ScrollbarLayerImpl> ScrollbarLayerImpl::Create(
- LayerTreeImpl* tree_impl,
- int id,
- ScrollbarOrientation orientation) {
- return make_scoped_ptr(new ScrollbarLayerImpl(tree_impl,
- id,
- orientation));
-}
-
-ScrollbarLayerImpl::ScrollbarLayerImpl(
- LayerTreeImpl* tree_impl,
- int id,
- ScrollbarOrientation orientation)
- : LayerImpl(tree_impl, id),
- track_resource_id_(0),
- thumb_resource_id_(0),
- current_pos_(0.f),
- maximum_(0),
- thumb_thickness_(0),
- thumb_length_(0),
- track_start_(0),
- track_length_(0),
- orientation_(orientation),
- vertical_adjust_(0.f),
- visible_to_total_length_ratio_(1.f),
- scroll_layer_id_(Layer::INVALID_ID),
- is_overlay_scrollbar_(false) {}
-
-ScrollbarLayerImpl::~ScrollbarLayerImpl() {}
-
-ScrollbarLayerImpl* ScrollbarLayerImpl::ToScrollbarLayer() {
- return this;
-}
-
-scoped_ptr<LayerImpl> ScrollbarLayerImpl::CreateLayerImpl(
- LayerTreeImpl* tree_impl) {
- return ScrollbarLayerImpl::Create(tree_impl,
- id(),
- orientation_).PassAs<LayerImpl>();
-}
-
-void ScrollbarLayerImpl::PushPropertiesTo(LayerImpl* layer) {
- LayerImpl::PushPropertiesTo(layer);
-
- ScrollbarLayerImpl* scrollbar_layer = static_cast<ScrollbarLayerImpl*>(layer);
-
- scrollbar_layer->SetThumbThickness(thumb_thickness_);
- scrollbar_layer->SetThumbLength(thumb_length_);
- scrollbar_layer->SetTrackStart(track_start_);
- scrollbar_layer->SetTrackLength(track_length_);
- scrollbar_layer->set_is_overlay_scrollbar(is_overlay_scrollbar_);
-
- scrollbar_layer->set_track_resource_id(track_resource_id_);
- scrollbar_layer->set_thumb_resource_id(thumb_resource_id_);
-}
-
-bool ScrollbarLayerImpl::WillDraw(DrawMode draw_mode,
- ResourceProvider* resource_provider) {
- if (draw_mode == DRAW_MODE_RESOURCELESS_SOFTWARE &&
- !layer_tree_impl()->settings().solid_color_scrollbars)
- return false;
- return LayerImpl::WillDraw(draw_mode, resource_provider);
-}
-
-void ScrollbarLayerImpl::AppendQuads(QuadSink* quad_sink,
- AppendQuadsData* append_quads_data) {
- bool premultipled_alpha = true;
- bool flipped = false;
- gfx::PointF uv_top_left(0.f, 0.f);
- gfx::PointF uv_bottom_right(1.f, 1.f);
- gfx::Rect bounds_rect(bounds());
- gfx::Rect content_bounds_rect(content_bounds());
-
- SharedQuadState* shared_quad_state =
- quad_sink->UseSharedQuadState(CreateSharedQuadState());
- AppendDebugBorderQuad(quad_sink, shared_quad_state, append_quads_data);
-
- gfx::Rect thumb_quad_rect = ComputeThumbQuadRect();
-
- if (layer_tree_impl()->settings().solid_color_scrollbars) {
- scoped_ptr<SolidColorDrawQuad> quad = SolidColorDrawQuad::Create();
- quad->SetNew(shared_quad_state,
- thumb_quad_rect,
- layer_tree_impl()->settings().solid_color_scrollbar_color,
- false);
- quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data);
- return;
- }
-
- if (thumb_resource_id_ && !thumb_quad_rect.IsEmpty()) {
- gfx::Rect opaque_rect;
- const float opacity[] = {1.0f, 1.0f, 1.0f, 1.0f};
- scoped_ptr<TextureDrawQuad> quad = TextureDrawQuad::Create();
- quad->SetNew(shared_quad_state,
- thumb_quad_rect,
- opaque_rect,
- thumb_resource_id_,
- premultipled_alpha,
- uv_top_left,
- uv_bottom_right,
- SK_ColorTRANSPARENT,
- opacity,
- flipped);
- quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data);
- }
-
- if (!track_resource_id_)
- return;
-
- // Order matters here: since the back track texture is being drawn to the
- // entire contents rect, we must append it after the thumb and fore track
- // quads. The back track texture contains (and displays) the buttons.
- if (!content_bounds_rect.IsEmpty()) {
- gfx::Rect quad_rect(content_bounds_rect);
- gfx::Rect opaque_rect(contents_opaque() ? quad_rect : gfx::Rect());
- const float opacity[] = {1.0f, 1.0f, 1.0f, 1.0f};
- scoped_ptr<TextureDrawQuad> quad = TextureDrawQuad::Create();
- quad->SetNew(shared_quad_state,
- quad_rect,
- opaque_rect,
- track_resource_id_,
- premultipled_alpha,
- uv_top_left,
- uv_bottom_right,
- SK_ColorTRANSPARENT,
- opacity,
- flipped);
- quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data);
- }
-}
-
-ScrollbarOrientation ScrollbarLayerImpl::Orientation() const {
- return orientation_;
-}
-
-float ScrollbarLayerImpl::CurrentPos() const {
- return current_pos_;
-}
-
-int ScrollbarLayerImpl::Maximum() const {
- return maximum_;
-}
-
-gfx::Rect ScrollbarLayerImpl::ScrollbarLayerRectToContentRect(
- gfx::RectF layer_rect) const {
- // Don't intersect with the bounds as in layerRectToContentRect() because
- // layer_rect here might be in coordinates of the containing layer.
- gfx::RectF content_rect = gfx::ScaleRect(layer_rect,
- contents_scale_x(),
- contents_scale_y());
- return gfx::ToEnclosingRect(content_rect);
-}
-
-void ScrollbarLayerImpl::SetThumbThickness(int thumb_thickness) {
- if (thumb_thickness_ == thumb_thickness)
- return;
- thumb_thickness_ = thumb_thickness;
- NoteLayerPropertyChanged();
-}
-
-void ScrollbarLayerImpl::SetThumbLength(int thumb_length) {
- if (thumb_length_ == thumb_length)
- return;
- thumb_length_ = thumb_length;
- NoteLayerPropertyChanged();
-}
-void ScrollbarLayerImpl::SetTrackStart(int track_start) {
- if (track_start_ == track_start)
- return;
- track_start_ = track_start;
- NoteLayerPropertyChanged();
-}
-
-void ScrollbarLayerImpl::SetTrackLength(int track_length) {
- if (track_length_ == track_length)
- return;
- track_length_ = track_length;
- NoteLayerPropertyChanged();
-}
-
-void ScrollbarLayerImpl::SetVerticalAdjust(float vertical_adjust) {
- if (vertical_adjust_ == vertical_adjust)
- return;
- vertical_adjust_ = vertical_adjust;
- NoteLayerPropertyChanged();
-}
-
-void ScrollbarLayerImpl::SetVisibleToTotalLengthRatio(float ratio) {
- if (visible_to_total_length_ratio_ == ratio)
- return;
- visible_to_total_length_ratio_ = ratio;
- NoteLayerPropertyChanged();
-}
-
-void ScrollbarLayerImpl::SetCurrentPos(float current_pos) {
- if (current_pos_ == current_pos)
- return;
- current_pos_ = current_pos;
- NoteLayerPropertyChanged();
-}
-
-void ScrollbarLayerImpl::SetMaximum(int maximum) {
- if (maximum_ == maximum)
- return;
- maximum_ = maximum;
- NoteLayerPropertyChanged();
-}
-
-gfx::Rect ScrollbarLayerImpl::ComputeThumbQuadRect() const {
- // Thumb extent is the length of the thumb in the scrolling direction, thumb
- // thickness is in the perpendicular direction. Here's an example of a
- // horizontal scrollbar - inputs are above the scrollbar, computed values
- // below:
- //
- // |<------------------- track_length_ ------------------->|
- //
- // |--| <-- start_offset
- //
- // +--+----------------------------+------------------+-------+--+
- // |<|| |##################| ||>|
- // +--+----------------------------+------------------+-------+--+
- //
- // |<- thumb_length ->|
- //
- // |<------- thumb_offset -------->|
- //
- // For painted, scrollbars, the length is fixed. For solid color scrollbars we
- // have to compute it. The ratio of the thumb's length to the track's length
- // is the same as that of the visible viewport to the total viewport, unless
- // that would make the thumb's length less than its thickness.
- //
- // vertical_adjust_ is used when the layer geometry from the main thread is
- // not in sync with what the user sees. For instance on Android scrolling the
- // top bar controls out of view reveals more of the page content. We want the
- // root layer scrollbars to reflect what the user sees even if we haven't
- // received new layer geometry from the main thread. If the user has scrolled
- // down by 50px and the initial viewport size was 950px the geometry would
- // look something like this:
- //
- // vertical_adjust_ = 50, scroll position 0, visible ratios 99%
- // Layer geometry: Desired thumb positions:
- // +--------------------+-+ +----------------------+ <-- 0px
- // | |v| | #|
- // | |e| | #|
- // | |r| | #|
- // | |t| | #|
- // | |i| | #|
- // | |c| | #|
- // | |a| | #|
- // | |l| | #|
- // | | | | #|
- // | |l| | #|
- // | |a| | #|
- // | |y| | #|
- // | |e| | #|
- // | |r| | #|
- // +--------------------+-+ | #|
- // | horizontal layer | | | #|
- // +--------------------+-+ | #| <-- 950px
- // | | | #|
- // | | |##################### |
- // +----------------------+ +----------------------+ <-- 1000px
- //
- // The layer geometry is set up for a 950px tall viewport, but the user can
- // actually see down to 1000px. Thus we have to move the quad for the
- // horizontal scrollbar down by the vertical_adjust_ factor and lay the
- // vertical thumb out on a track lengthed by the vertical_adjust_ factor. This
- // means the quads may extend outside the layer's bounds.
-
- int thumb_length = thumb_length_;
- float track_length = track_length_;
- if (orientation_ == VERTICAL)
- track_length += vertical_adjust_;
-
- if (layer_tree_impl()->settings().solid_color_scrollbars) {
- thumb_length = std::max(
- static_cast<int>(visible_to_total_length_ratio_ * track_length),
- thumb_thickness_);
- }
-
- // With the length known, we can compute the thumb's position.
- float clamped_current_pos =
- std::min(std::max(current_pos_, 0.f), static_cast<float>(maximum_));
- float ratio = clamped_current_pos / maximum_;
- float max_offset = track_length - thumb_length;
- int thumb_offset = static_cast<int>(ratio * max_offset) + track_start_;
-
- gfx::RectF thumb_rect;
- if (orientation_ == HORIZONTAL) {
- thumb_rect = gfx::RectF(thumb_offset, vertical_adjust_,
- thumb_length, thumb_thickness_);
- } else {
- thumb_rect = gfx::RectF(0.f, thumb_offset,
- thumb_thickness_, thumb_length);
- }
-
- return ScrollbarLayerRectToContentRect(thumb_rect);
-}
-
-void ScrollbarLayerImpl::DidLoseOutputSurface() {
- track_resource_id_ = 0;
- thumb_resource_id_ = 0;
-}
-
-const char* ScrollbarLayerImpl::LayerTypeAsString() const {
- return "cc::ScrollbarLayerImpl";
-}
-
-} // namespace cc
diff --git a/chromium/cc/layers/scrollbar_layer_impl.h b/chromium/cc/layers/scrollbar_layer_impl.h
deleted file mode 100644
index 6347c413dfc..00000000000
--- a/chromium/cc/layers/scrollbar_layer_impl.h
+++ /dev/null
@@ -1,103 +0,0 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CC_LAYERS_SCROLLBAR_LAYER_IMPL_H_
-#define CC_LAYERS_SCROLLBAR_LAYER_IMPL_H_
-
-#include "cc/base/cc_export.h"
-#include "cc/input/scrollbar.h"
-#include "cc/layers/layer_impl.h"
-
-namespace cc {
-
-class LayerTreeImpl;
-class ScrollView;
-
-class CC_EXPORT ScrollbarLayerImpl : public LayerImpl {
- public:
- static scoped_ptr<ScrollbarLayerImpl> Create(
- LayerTreeImpl* tree_impl,
- int id,
- ScrollbarOrientation orientation);
- virtual ~ScrollbarLayerImpl();
-
- // LayerImpl implementation.
- virtual ScrollbarLayerImpl* ToScrollbarLayer() OVERRIDE;
- virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl)
- OVERRIDE;
- virtual void PushPropertiesTo(LayerImpl* layer) OVERRIDE;
-
- virtual bool WillDraw(DrawMode draw_mode,
- ResourceProvider* resource_provider) OVERRIDE;
- virtual void AppendQuads(QuadSink* quad_sink,
- AppendQuadsData* append_quads_data) OVERRIDE;
-
- virtual void DidLoseOutputSurface() OVERRIDE;
-
- int scroll_layer_id() const { return scroll_layer_id_; }
- void set_scroll_layer_id(int id) { scroll_layer_id_ = id; }
-
- ScrollbarOrientation Orientation() const;
- float CurrentPos() const;
- int Maximum() const;
-
- void SetThumbThickness(int thumb_thickness);
- int thumb_thickness() const { return thumb_thickness_; }
- void SetThumbLength(int thumb_length);
- void SetTrackStart(int track_start);
- void SetTrackLength(int track_length);
- void SetVerticalAdjust(float vertical_adjust);
- void set_track_resource_id(ResourceProvider::ResourceId id) {
- track_resource_id_ = id;
- }
- void set_thumb_resource_id(ResourceProvider::ResourceId id) {
- thumb_resource_id_ = id;
- }
- void SetVisibleToTotalLengthRatio(float ratio);
- void set_is_overlay_scrollbar(bool is_overlay_scrollbar) {
- is_overlay_scrollbar_ = is_overlay_scrollbar;
- }
- bool is_overlay_scrollbar() const { return is_overlay_scrollbar_; }
-
- void SetCurrentPos(float current_pos);
- void SetMaximum(int maximum);
-
- gfx::Rect ComputeThumbQuadRect() const;
-
- protected:
- ScrollbarLayerImpl(LayerTreeImpl* tree_impl,
- int id,
- ScrollbarOrientation orientation);
-
- private:
- virtual const char* LayerTypeAsString() const OVERRIDE;
-
- gfx::Rect ScrollbarLayerRectToContentRect(gfx::RectF layer_rect) const;
-
- ResourceProvider::ResourceId track_resource_id_;
- ResourceProvider::ResourceId thumb_resource_id_;
-
- float current_pos_;
- int maximum_;
- int thumb_thickness_;
- int thumb_length_;
- int track_start_;
- int track_length_;
- ScrollbarOrientation orientation_;
-
- // Difference between the clip layer's height and the visible viewport
- // height (which may differ in the presence of top-controls hiding).
- float vertical_adjust_;
-
- float visible_to_total_length_ratio_;
-
- int scroll_layer_id_;
-
- bool is_overlay_scrollbar_;
-
- DISALLOW_COPY_AND_ASSIGN(ScrollbarLayerImpl);
-};
-
-} // namespace cc
-#endif // CC_LAYERS_SCROLLBAR_LAYER_IMPL_H_
diff --git a/chromium/cc/layers/scrollbar_layer_impl_base.cc b/chromium/cc/layers/scrollbar_layer_impl_base.cc
new file mode 100644
index 00000000000..c1e7126ff52
--- /dev/null
+++ b/chromium/cc/layers/scrollbar_layer_impl_base.cc
@@ -0,0 +1,170 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/layers/scrollbar_layer_impl_base.h"
+
+#include <algorithm>
+#include "cc/layers/layer.h"
+#include "ui/gfx/rect_conversions.h"
+
+namespace cc {
+
+ScrollbarLayerImplBase::ScrollbarLayerImplBase(
+ LayerTreeImpl* tree_impl,
+ int id,
+ ScrollbarOrientation orientation,
+ bool is_left_side_vertical_scrollbar)
+ : LayerImpl(tree_impl, id),
+ scroll_layer_id_(Layer::INVALID_ID),
+ is_overlay_scrollbar_(false),
+ thumb_thickness_scale_factor_(1.f),
+ current_pos_(0.f),
+ maximum_(0),
+ orientation_(orientation),
+ is_left_side_vertical_scrollbar_(is_left_side_vertical_scrollbar),
+ vertical_adjust_(0.f),
+ visible_to_total_length_ratio_(1.f) {}
+
+void ScrollbarLayerImplBase::PushPropertiesTo(LayerImpl* layer) {
+ LayerImpl::PushPropertiesTo(layer);
+}
+
+ScrollbarLayerImplBase* ScrollbarLayerImplBase::ToScrollbarLayer() {
+ return this;
+}
+
+gfx::Rect ScrollbarLayerImplBase::ScrollbarLayerRectToContentRect(
+ gfx::RectF layer_rect) const {
+ // Don't intersect with the bounds as in LayerRectToContentRect() because
+ // layer_rect here might be in coordinates of the containing layer.
+ gfx::RectF content_rect = gfx::ScaleRect(layer_rect,
+ contents_scale_x(),
+ contents_scale_y());
+ return gfx::ToEnclosingRect(content_rect);
+}
+
+void ScrollbarLayerImplBase::SetCurrentPos(float current_pos) {
+ if (current_pos_ == current_pos)
+ return;
+ current_pos_ = current_pos;
+ NoteLayerPropertyChanged();
+}
+
+void ScrollbarLayerImplBase::SetMaximum(int maximum) {
+ if (maximum_ == maximum)
+ return;
+ maximum_ = maximum;
+ NoteLayerPropertyChanged();
+}
+
+void ScrollbarLayerImplBase::SetVerticalAdjust(float vertical_adjust) {
+ if (vertical_adjust_ == vertical_adjust)
+ return;
+ vertical_adjust_ = vertical_adjust;
+ NoteLayerPropertyChanged();
+}
+
+void ScrollbarLayerImplBase::SetVisibleToTotalLengthRatio(float ratio) {
+ if (visible_to_total_length_ratio_ == ratio)
+ return;
+ visible_to_total_length_ratio_ = ratio;
+ NoteLayerPropertyChanged();
+}
+
+gfx::Rect ScrollbarLayerImplBase::ComputeThumbQuadRect() const {
+ // Thumb extent is the length of the thumb in the scrolling direction, thumb
+ // thickness is in the perpendicular direction. Here's an example of a
+ // horizontal scrollbar - inputs are above the scrollbar, computed values
+ // below:
+ //
+ // |<------------------- track_length_ ------------------->|
+ //
+ // |--| <-- start_offset
+ //
+ // +--+----------------------------+------------------+-------+--+
+ // |<|| |##################| ||>|
+ // +--+----------------------------+------------------+-------+--+
+ //
+ // |<- thumb_length ->|
+ //
+ // |<------- thumb_offset -------->|
+ //
+ // For painted, scrollbars, the length is fixed. For solid color scrollbars we
+ // have to compute it. The ratio of the thumb's length to the track's length
+ // is the same as that of the visible viewport to the total viewport, unless
+ // that would make the thumb's length less than its thickness.
+ //
+ // vertical_adjust_ is used when the layer geometry from the main thread is
+ // not in sync with what the user sees. For instance on Android scrolling the
+ // top bar controls out of view reveals more of the page content. We want the
+ // root layer scrollbars to reflect what the user sees even if we haven't
+ // received new layer geometry from the main thread. If the user has scrolled
+ // down by 50px and the initial viewport size was 950px the geometry would
+ // look something like this:
+ //
+ // vertical_adjust_ = 50, scroll position 0, visible ratios 99%
+ // Layer geometry: Desired thumb positions:
+ // +--------------------+-+ +----------------------+ <-- 0px
+ // | |v| | #|
+ // | |e| | #|
+ // | |r| | #|
+ // | |t| | #|
+ // | |i| | #|
+ // | |c| | #|
+ // | |a| | #|
+ // | |l| | #|
+ // | | | | #|
+ // | |l| | #|
+ // | |a| | #|
+ // | |y| | #|
+ // | |e| | #|
+ // | |r| | #|
+ // +--------------------+-+ | #|
+ // | horizontal layer | | | #|
+ // +--------------------+-+ | #| <-- 950px
+ // | | | #|
+ // | | |##################### |
+ // +----------------------+ +----------------------+ <-- 1000px
+ //
+ // The layer geometry is set up for a 950px tall viewport, but the user can
+ // actually see down to 1000px. Thus we have to move the quad for the
+ // horizontal scrollbar down by the vertical_adjust_ factor and lay the
+ // vertical thumb out on a track lengthed by the vertical_adjust_ factor. This
+ // means the quads may extend outside the layer's bounds.
+
+ // With the length known, we can compute the thumb's position.
+ float track_length = TrackLength();
+ int thumb_length = ThumbLength();
+ int thumb_thickness = ThumbThickness();
+
+ // With the length known, we can compute the thumb's position.
+ float clamped_current_pos =
+ std::min(std::max(current_pos_, 0.f), static_cast<float>(maximum_));
+ float ratio = clamped_current_pos / maximum_;
+ float max_offset = track_length - thumb_length;
+ int thumb_offset = static_cast<int>(ratio * max_offset) + TrackStart();
+
+ float thumb_thickness_adjustment =
+ thumb_thickness * (1.f - thumb_thickness_scale_factor_);
+
+ gfx::RectF thumb_rect;
+ if (orientation_ == HORIZONTAL) {
+ thumb_rect = gfx::RectF(thumb_offset,
+ vertical_adjust_ + thumb_thickness_adjustment,
+ thumb_length,
+ thumb_thickness - thumb_thickness_adjustment);
+ } else {
+ thumb_rect = gfx::RectF(
+ is_left_side_vertical_scrollbar_
+ ? bounds().width() - thumb_thickness
+ : thumb_thickness_adjustment,
+ thumb_offset,
+ thumb_thickness - thumb_thickness_adjustment,
+ thumb_length);
+ }
+
+ return ScrollbarLayerRectToContentRect(thumb_rect);
+}
+
+} // namespace cc
diff --git a/chromium/cc/layers/scrollbar_layer_impl_base.h b/chromium/cc/layers/scrollbar_layer_impl_base.h
new file mode 100644
index 00000000000..55ade6ab04f
--- /dev/null
+++ b/chromium/cc/layers/scrollbar_layer_impl_base.h
@@ -0,0 +1,91 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_LAYERS_SCROLLBAR_LAYER_IMPL_BASE_H_
+#define CC_LAYERS_SCROLLBAR_LAYER_IMPL_BASE_H_
+
+#include "cc/base/cc_export.h"
+#include "cc/input/scrollbar.h"
+#include "cc/layers/layer_impl.h"
+
+namespace cc {
+
+class LayerTreeImpl;
+
+class CC_EXPORT ScrollbarLayerImplBase : public LayerImpl {
+ public:
+ int ScrollLayerId() const { return scroll_layer_id_; }
+ void set_scroll_layer_id(int id) { scroll_layer_id_ = id; }
+
+ float current_pos() const { return current_pos_; }
+ void SetCurrentPos(float current_pos);
+ int maximum() const { return maximum_; }
+ void SetMaximum(int maximum);
+
+ void SetVerticalAdjust(float vertical_adjust);
+
+ bool is_overlay_scrollbar() const { return is_overlay_scrollbar_; }
+ void set_is_overlay_scrollbar(bool is_overlay) {
+ is_overlay_scrollbar_ = is_overlay;
+ }
+
+ ScrollbarOrientation orientation() const { return orientation_; }
+ bool is_left_side_vertical_scrollbar() {
+ return is_left_side_vertical_scrollbar_;
+ }
+
+ virtual void PushPropertiesTo(LayerImpl* layer) OVERRIDE;
+ virtual ScrollbarLayerImplBase* ToScrollbarLayer() OVERRIDE;
+
+ void SetVisibleToTotalLengthRatio(float ratio);
+ virtual gfx::Rect ComputeThumbQuadRect() const;
+
+ float thumb_thickness_scale_factor() {
+ return thumb_thickness_scale_factor_;
+ }
+ void set_thumb_thickness_scale_factor(float thumb_thickness_scale_factor) {
+ thumb_thickness_scale_factor_ = thumb_thickness_scale_factor;
+ }
+
+ protected:
+ ScrollbarLayerImplBase(LayerTreeImpl* tree_impl,
+ int id,
+ ScrollbarOrientation orientation,
+ bool is_left_side_vertical_scrollbar);
+ virtual ~ScrollbarLayerImplBase() {}
+
+ gfx::Rect ScrollbarLayerRectToContentRect(gfx::RectF layer_rect) const;
+
+ float visible_to_total_length_ratio() const {
+ return visible_to_total_length_ratio_;
+ }
+ float vertical_adjust() const { return vertical_adjust_; }
+
+ virtual int ThumbThickness() const = 0;
+ virtual int ThumbLength() const = 0;
+ virtual float TrackLength() const = 0;
+ virtual int TrackStart() const = 0;
+
+ private:
+ int scroll_layer_id_;
+ bool is_overlay_scrollbar_;
+
+ float thumb_thickness_scale_factor_;
+ float current_pos_;
+ int maximum_;
+ ScrollbarOrientation orientation_;
+ bool is_left_side_vertical_scrollbar_;
+
+ // Difference between the clip layer's height and the visible viewport
+ // height (which may differ in the presence of top-controls hiding).
+ float vertical_adjust_;
+
+ float visible_to_total_length_ratio_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScrollbarLayerImplBase);
+};
+
+} // namespace cc
+
+#endif // CC_LAYERS_SCROLLBAR_LAYER_IMPL_BASE_H_
diff --git a/chromium/cc/layers/scrollbar_layer_interface.h b/chromium/cc/layers/scrollbar_layer_interface.h
new file mode 100644
index 00000000000..bbb0da6ae2b
--- /dev/null
+++ b/chromium/cc/layers/scrollbar_layer_interface.h
@@ -0,0 +1,30 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_LAYERS_SCROLLBAR_LAYER_INTERFACE_H_
+#define CC_LAYERS_SCROLLBAR_LAYER_INTERFACE_H_
+
+#include "cc/base/cc_export.h"
+#include "cc/input/scrollbar.h"
+
+namespace cc {
+
+class CC_EXPORT ScrollbarLayerInterface {
+ public:
+ virtual int ScrollLayerId() const = 0;
+ virtual void SetScrollLayerId(int id) = 0;
+
+ virtual ScrollbarOrientation orientation() const = 0;
+
+ protected:
+ ScrollbarLayerInterface() {}
+ virtual ~ScrollbarLayerInterface() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ScrollbarLayerInterface);
+};
+
+} // namespace cc
+
+#endif // CC_LAYERS_SCROLLBAR_LAYER_INTERFACE_H_
diff --git a/chromium/cc/layers/scrollbar_layer_unittest.cc b/chromium/cc/layers/scrollbar_layer_unittest.cc
index b7981041f09..2e1b4b6f483 100644
--- a/chromium/cc/layers/scrollbar_layer_unittest.cc
+++ b/chromium/cc/layers/scrollbar_layer_unittest.cc
@@ -2,24 +2,27 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "cc/layers/scrollbar_layer.h"
-
+#include "base/containers/hash_tables.h"
#include "cc/animation/scrollbar_animation_controller.h"
+#include "cc/debug/test_web_graphics_context_3d.h"
#include "cc/layers/append_quads_data.h"
-#include "cc/layers/scrollbar_layer_impl.h"
+#include "cc/layers/painted_scrollbar_layer.h"
+#include "cc/layers/painted_scrollbar_layer_impl.h"
+#include "cc/layers/scrollbar_layer_interface.h"
+#include "cc/layers/solid_color_scrollbar_layer.h"
+#include "cc/layers/solid_color_scrollbar_layer_impl.h"
#include "cc/quads/solid_color_draw_quad.h"
-#include "cc/resources/prioritized_resource_manager.h"
-#include "cc/resources/priority_calculator.h"
#include "cc/resources/resource_update_queue.h"
#include "cc/test/fake_impl_proxy.h"
#include "cc/test/fake_layer_tree_host.h"
#include "cc/test/fake_layer_tree_host_client.h"
#include "cc/test/fake_layer_tree_host_impl.h"
+#include "cc/test/fake_painted_scrollbar_layer.h"
#include "cc/test/fake_scrollbar.h"
#include "cc/test/geometry_test_utils.h"
#include "cc/test/layer_tree_test.h"
#include "cc/test/mock_quad_culler.h"
-#include "cc/test/test_web_graphics_context_3d.h"
+#include "cc/trees/layer_tree_host.h"
#include "cc/trees/layer_tree_impl.h"
#include "cc/trees/single_thread_proxy.h"
#include "cc/trees/tree_synchronizer.h"
@@ -32,12 +35,20 @@ namespace {
LayerImpl* LayerImplForScrollAreaAndScrollbar(
FakeLayerTreeHost* host,
scoped_ptr<Scrollbar> scrollbar,
- bool reverse_order) {
+ bool reverse_order,
+ bool use_solid_color_scrollbar,
+ int thumb_thickness) {
scoped_refptr<Layer> layer_tree_root = Layer::Create();
scoped_refptr<Layer> child1 = Layer::Create();
- scoped_refptr<Layer> child2 =
- ScrollbarLayer::Create(scrollbar.Pass(),
- child1->id());
+ scoped_refptr<Layer> child2;
+ if (use_solid_color_scrollbar) {
+ const bool kIsLeftSideVerticalScrollbar = false;
+ child2 = SolidColorScrollbarLayer::Create(
+ scrollbar->Orientation(), thumb_thickness,
+ kIsLeftSideVerticalScrollbar, child1->id());
+ } else {
+ child2 = PaintedScrollbarLayer::Create(scrollbar.Pass(), child1->id());
+ }
layer_tree_root->AddChild(child1);
layer_tree_root->InsertChild(child2, reverse_order ? 0 : 1);
host->SetRootLayer(layer_tree_root);
@@ -47,12 +58,13 @@ LayerImpl* LayerImplForScrollAreaAndScrollbar(
TEST(ScrollbarLayerTest, ResolveScrollLayerPointer) {
scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create();
scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar);
- LayerImpl* layer_impl_tree_root =
- LayerImplForScrollAreaAndScrollbar(host.get(), scrollbar.Pass(), false);
+ LayerImpl* layer_impl_tree_root = LayerImplForScrollAreaAndScrollbar(
+ host.get(), scrollbar.Pass(), false, false, 0);
LayerImpl* cc_child1 = layer_impl_tree_root->children()[0];
- ScrollbarLayerImpl* cc_child2 = static_cast<ScrollbarLayerImpl*>(
- layer_impl_tree_root->children()[1]);
+ PaintedScrollbarLayerImpl* cc_child2 =
+ static_cast<PaintedScrollbarLayerImpl*>(
+ layer_impl_tree_root->children()[1]);
EXPECT_EQ(cc_child1->horizontal_scrollbar_layer(), cc_child2);
}
@@ -60,11 +72,12 @@ TEST(ScrollbarLayerTest, ResolveScrollLayerPointer) {
TEST(ScrollbarLayerTest, ResolveScrollLayerPointer_ReverseOrder) {
scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create();
scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar);
- LayerImpl* layer_impl_tree_root =
- LayerImplForScrollAreaAndScrollbar(host.get(), scrollbar.Pass(), true);
+ LayerImpl* layer_impl_tree_root = LayerImplForScrollAreaAndScrollbar(
+ host.get(), scrollbar.Pass(), true, false, 0);
- ScrollbarLayerImpl* cc_child1 = static_cast<ScrollbarLayerImpl*>(
- layer_impl_tree_root->children()[0]);
+ PaintedScrollbarLayerImpl* cc_child1 =
+ static_cast<PaintedScrollbarLayerImpl*>(
+ layer_impl_tree_root->children()[0]);
LayerImpl* cc_child2 = layer_impl_tree_root->children()[1];
EXPECT_EQ(cc_child2->horizontal_scrollbar_layer(), cc_child1);
@@ -75,10 +88,11 @@ TEST(ScrollbarLayerTest, ShouldScrollNonOverlayOnMainThread) {
// Create and attach a non-overlay scrollbar.
scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar);
- LayerImpl* layer_impl_tree_root =
- LayerImplForScrollAreaAndScrollbar(host.get(), scrollbar.Pass(), false);
- ScrollbarLayerImpl* scrollbar_layer_impl =
- static_cast<ScrollbarLayerImpl*>(layer_impl_tree_root->children()[1]);
+ LayerImpl* layer_impl_tree_root = LayerImplForScrollAreaAndScrollbar(
+ host.get(), scrollbar.Pass(), false, false, 0);
+ PaintedScrollbarLayerImpl* scrollbar_layer_impl =
+ static_cast<PaintedScrollbarLayerImpl*>(
+ layer_impl_tree_root->children()[1]);
// When the scrollbar is not an overlay scrollbar, the scroll should be
// responded to on the main thread as the compositor does not yet implement
@@ -90,10 +104,10 @@ TEST(ScrollbarLayerTest, ShouldScrollNonOverlayOnMainThread) {
// Create and attach an overlay scrollbar.
scrollbar.reset(new FakeScrollbar(false, false, true));
- layer_impl_tree_root =
- LayerImplForScrollAreaAndScrollbar(host.get(), scrollbar.Pass(), false);
- scrollbar_layer_impl =
- static_cast<ScrollbarLayerImpl*>(layer_impl_tree_root->children()[1]);
+ layer_impl_tree_root = LayerImplForScrollAreaAndScrollbar(
+ host.get(), scrollbar.Pass(), false, false, 0);
+ scrollbar_layer_impl = static_cast<PaintedScrollbarLayerImpl*>(
+ layer_impl_tree_root->children()[1]);
// The user shouldn't be able to drag an overlay scrollbar and the scroll
// may be handled in the compositor.
@@ -102,15 +116,14 @@ TEST(ScrollbarLayerTest, ShouldScrollNonOverlayOnMainThread) {
InputHandler::Gesture));
}
-TEST(ScrollbarLayerTest, ScrollOffsetSynchronization) {
+TEST(PaintedScrollbarLayerTest, ScrollOffsetSynchronization) {
scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create();
scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar);
scoped_refptr<Layer> layer_tree_root = Layer::Create();
scoped_refptr<Layer> content_layer = Layer::Create();
scoped_refptr<Layer> scrollbar_layer =
- ScrollbarLayer::Create(scrollbar.Pass(),
- layer_tree_root->id());
+ PaintedScrollbarLayer::Create(scrollbar.Pass(), layer_tree_root->id());
layer_tree_root->SetScrollable(true);
layer_tree_root->SetScrollOffset(gfx::Vector2d(10, 20));
@@ -127,11 +140,12 @@ TEST(ScrollbarLayerTest, ScrollOffsetSynchronization) {
LayerImpl* layer_impl_tree_root = host->CommitAndCreateLayerImplTree();
- ScrollbarLayerImpl* cc_scrollbar_layer =
- static_cast<ScrollbarLayerImpl*>(layer_impl_tree_root->children()[1]);
+ ScrollbarLayerImplBase* cc_scrollbar_layer =
+ static_cast<PaintedScrollbarLayerImpl*>(
+ layer_impl_tree_root->children()[1]);
- EXPECT_EQ(10.f, cc_scrollbar_layer->CurrentPos());
- EXPECT_EQ(30, cc_scrollbar_layer->Maximum());
+ EXPECT_EQ(10.f, cc_scrollbar_layer->current_pos());
+ EXPECT_EQ(30, cc_scrollbar_layer->maximum());
layer_tree_root->SetScrollOffset(gfx::Vector2d(100, 200));
layer_tree_root->SetMaxScrollOffset(gfx::Vector2d(300, 500));
@@ -146,31 +160,124 @@ TEST(ScrollbarLayerTest, ScrollOffsetSynchronization) {
EXPECT_EQ(scrollbar_controller,
layer_impl_tree_root->scrollbar_animation_controller());
- EXPECT_EQ(100.f, cc_scrollbar_layer->CurrentPos());
- EXPECT_EQ(300, cc_scrollbar_layer->Maximum());
+ EXPECT_EQ(100.f, cc_scrollbar_layer->current_pos());
+ EXPECT_EQ(300, cc_scrollbar_layer->maximum());
layer_impl_tree_root->ScrollBy(gfx::Vector2d(12, 34));
- EXPECT_EQ(112.f, cc_scrollbar_layer->CurrentPos());
- EXPECT_EQ(300, cc_scrollbar_layer->Maximum());
+ EXPECT_EQ(112.f, cc_scrollbar_layer->current_pos());
+ EXPECT_EQ(300, cc_scrollbar_layer->maximum());
+}
+
+TEST(ScrollbarLayerTest, ThumbRect) {
+ scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create();
+ scoped_refptr<Layer> root_layer = Layer::Create();
+ scoped_refptr<Layer> content_layer = Layer::Create();
+ scoped_refptr<FakePaintedScrollbarLayer> scrollbar_layer =
+ FakePaintedScrollbarLayer::Create(false, true, root_layer->id());
+
+ root_layer->SetScrollable(true);
+ root_layer->SetMaxScrollOffset(gfx::Vector2d(80, 0));
+ root_layer->SetBounds(gfx::Size(100, 50));
+ content_layer->SetBounds(gfx::Size(100, 50));
+
+ host->SetRootLayer(root_layer);
+ root_layer->AddChild(content_layer);
+ root_layer->AddChild(scrollbar_layer);
+
+ root_layer->SetScrollOffset(gfx::Vector2d(0, 0));
+ scrollbar_layer->SetBounds(gfx::Size(70, 10));
+ scrollbar_layer->fake_scrollbar()->set_location(gfx::Point(20, 10));
+ scrollbar_layer->fake_scrollbar()->set_track_rect(gfx::Rect(30, 10, 50, 10));
+ scrollbar_layer->fake_scrollbar()->set_thumb_thickness(10);
+ scrollbar_layer->fake_scrollbar()->set_thumb_length(4);
+ scrollbar_layer->UpdateThumbAndTrackGeometry();
+ LayerImpl* root_layer_impl = NULL;
+ PaintedScrollbarLayerImpl* scrollbar_layer_impl = NULL;
+
+ // Thumb is at the edge of the scrollbar (should be inset to
+ // the start of the track within the scrollbar layer's
+ // position).
+ scrollbar_layer->UpdateThumbAndTrackGeometry();
+ root_layer_impl = host->CommitAndCreateLayerImplTree();
+ scrollbar_layer_impl = static_cast<PaintedScrollbarLayerImpl*>(
+ root_layer_impl->children()[1]);
+ EXPECT_EQ(gfx::Rect(10, 0, 4, 10).ToString(),
+ scrollbar_layer_impl->ComputeThumbQuadRect().ToString());
+
+ // Under-scroll (thumb position should clamp and be unchanged).
+ root_layer->SetScrollOffset(gfx::Vector2d(-5, 0));
+
+ scrollbar_layer->UpdateThumbAndTrackGeometry();
+ root_layer_impl = host->CommitAndCreateLayerImplTree();
+ scrollbar_layer_impl = static_cast<PaintedScrollbarLayerImpl*>(
+ root_layer_impl->children()[1]);
+ EXPECT_EQ(gfx::Rect(10, 0, 4, 10).ToString(),
+ scrollbar_layer_impl->ComputeThumbQuadRect().ToString());
+
+ // Over-scroll (thumb position should clamp on the far side).
+ root_layer->SetScrollOffset(gfx::Vector2d(85, 0));
+
+ scrollbar_layer->UpdateThumbAndTrackGeometry();
+ root_layer_impl = host->CommitAndCreateLayerImplTree();
+ scrollbar_layer_impl = static_cast<PaintedScrollbarLayerImpl*>(
+ root_layer_impl->children()[1]);
+ EXPECT_EQ(gfx::Rect(56, 0, 4, 10).ToString(),
+ scrollbar_layer_impl->ComputeThumbQuadRect().ToString());
+
+ // Change thumb thickness and length.
+ scrollbar_layer->fake_scrollbar()->set_thumb_thickness(4);
+ scrollbar_layer->fake_scrollbar()->set_thumb_length(6);
+
+ scrollbar_layer->UpdateThumbAndTrackGeometry();
+ root_layer_impl = host->CommitAndCreateLayerImplTree();
+ scrollbar_layer_impl = static_cast<PaintedScrollbarLayerImpl*>(
+ root_layer_impl->children()[1]);
+ EXPECT_EQ(gfx::Rect(54, 0, 6, 4).ToString(),
+ scrollbar_layer_impl->ComputeThumbQuadRect().ToString());
+
+ // Shrink the scrollbar layer to cover only the track.
+ scrollbar_layer->SetBounds(gfx::Size(50, 10));
+ scrollbar_layer->fake_scrollbar()->set_location(gfx::Point(30, 10));
+ scrollbar_layer->fake_scrollbar()->set_track_rect(gfx::Rect(30, 10, 50, 10));
+
+ scrollbar_layer->UpdateThumbAndTrackGeometry();
+ root_layer_impl = host->CommitAndCreateLayerImplTree();
+ scrollbar_layer_impl = static_cast<PaintedScrollbarLayerImpl*>(
+ root_layer_impl->children()[1]);
+ EXPECT_EQ(gfx::Rect(44, 0, 6, 4).ToString(),
+ scrollbar_layer_impl->ComputeThumbQuadRect().ToString());
+
+ // Shrink the track in the non-scrolling dimension so that it only covers the
+ // middle third of the scrollbar layer (this does not affect the thumb
+ // position).
+ scrollbar_layer->fake_scrollbar()->set_track_rect(gfx::Rect(30, 12, 50, 6));
+
+ scrollbar_layer->UpdateThumbAndTrackGeometry();
+ root_layer_impl = host->CommitAndCreateLayerImplTree();
+ scrollbar_layer_impl = static_cast<PaintedScrollbarLayerImpl*>(
+ root_layer_impl->children()[1]);
+ EXPECT_EQ(gfx::Rect(44, 0, 6, 4).ToString(),
+ scrollbar_layer_impl->ComputeThumbQuadRect().ToString());
}
TEST(ScrollbarLayerTest, SolidColorDrawQuads) {
+ const int kThumbThickness = 3;
+ const int kTrackLength = 100;
+
LayerTreeSettings layer_tree_settings;
- layer_tree_settings.solid_color_scrollbars = true;
- layer_tree_settings.solid_color_scrollbar_thickness_dip = 3;
scoped_ptr<FakeLayerTreeHost> host =
FakeLayerTreeHost::Create(layer_tree_settings);
scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar(false, true, true));
- LayerImpl* layer_impl_tree_root =
- LayerImplForScrollAreaAndScrollbar(host.get(), scrollbar.Pass(), false);
- ScrollbarLayerImpl* scrollbar_layer_impl =
- static_cast<ScrollbarLayerImpl*>(layer_impl_tree_root->children()[1]);
- scrollbar_layer_impl->SetThumbThickness(3);
+ LayerImpl* layer_impl_tree_root = LayerImplForScrollAreaAndScrollbar(
+ host.get(), scrollbar.Pass(), false, true, kThumbThickness);
+ ScrollbarLayerImplBase* scrollbar_layer_impl =
+ static_cast<SolidColorScrollbarLayerImpl*>(
+ layer_impl_tree_root->children()[1]);
+ scrollbar_layer_impl->SetBounds(gfx::Size(kTrackLength, kThumbThickness));
scrollbar_layer_impl->SetCurrentPos(10.f);
scrollbar_layer_impl->SetMaximum(100);
- scrollbar_layer_impl->SetTrackLength(100);
scrollbar_layer_impl->SetVisibleToTotalLengthRatio(0.4f);
// Thickness should be overridden to 3.
@@ -217,20 +324,21 @@ TEST(ScrollbarLayerTest, SolidColorDrawQuads) {
}
TEST(ScrollbarLayerTest, LayerDrivenSolidColorDrawQuads) {
+ const int kThumbThickness = 3;
+ const int kTrackLength = 10;
+
LayerTreeSettings layer_tree_settings;
- layer_tree_settings.solid_color_scrollbars = true;
- layer_tree_settings.solid_color_scrollbar_thickness_dip = 3;
scoped_ptr<FakeLayerTreeHost> host =
FakeLayerTreeHost::Create(layer_tree_settings);
scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar(false, true, true));
- LayerImpl* layer_impl_tree_root =
- LayerImplForScrollAreaAndScrollbar(host.get(), scrollbar.Pass(), false);
- ScrollbarLayerImpl* scrollbar_layer_impl =
- static_cast<ScrollbarLayerImpl*>(layer_impl_tree_root->children()[1]);
+ LayerImpl* layer_impl_tree_root = LayerImplForScrollAreaAndScrollbar(
+ host.get(), scrollbar.Pass(), false, true, kThumbThickness);
+ ScrollbarLayerImplBase* scrollbar_layer_impl =
+ static_cast<PaintedScrollbarLayerImpl*>(
+ layer_impl_tree_root->children()[1]);
- scrollbar_layer_impl->SetThumbThickness(3);
- scrollbar_layer_impl->SetTrackLength(10);
+ scrollbar_layer_impl->SetBounds(gfx::Size(kTrackLength, kThumbThickness));
scrollbar_layer_impl->SetCurrentPos(4.f);
scrollbar_layer_impl->SetMaximum(8);
@@ -259,40 +367,43 @@ class ScrollbarLayerSolidColorThumbTest : public testing::Test {
layer_tree_settings.solid_color_scrollbars = true;
host_impl_.reset(new FakeLayerTreeHostImpl(layer_tree_settings, &proxy_));
- horizontal_scrollbar_layer_ = ScrollbarLayerImpl::Create(
- host_impl_->active_tree(), 1, HORIZONTAL);
- vertical_scrollbar_layer_ = ScrollbarLayerImpl::Create(
- host_impl_->active_tree(), 2, VERTICAL);
+ const int kThumbThickness = 3;
+ const bool kIsLeftSideVerticalScrollbar = false;
+
+ horizontal_scrollbar_layer_ = SolidColorScrollbarLayerImpl::Create(
+ host_impl_->active_tree(), 1, HORIZONTAL, kThumbThickness,
+ kIsLeftSideVerticalScrollbar);
+ vertical_scrollbar_layer_ = SolidColorScrollbarLayerImpl::Create(
+ host_impl_->active_tree(), 2, VERTICAL, kThumbThickness,
+ kIsLeftSideVerticalScrollbar);
}
protected:
FakeImplProxy proxy_;
scoped_ptr<FakeLayerTreeHostImpl> host_impl_;
- scoped_ptr<ScrollbarLayerImpl> horizontal_scrollbar_layer_;
- scoped_ptr<ScrollbarLayerImpl> vertical_scrollbar_layer_;
+ scoped_ptr<SolidColorScrollbarLayerImpl> horizontal_scrollbar_layer_;
+ scoped_ptr<SolidColorScrollbarLayerImpl> vertical_scrollbar_layer_;
};
TEST_F(ScrollbarLayerSolidColorThumbTest, SolidColorThumbLength) {
horizontal_scrollbar_layer_->SetCurrentPos(0);
horizontal_scrollbar_layer_->SetMaximum(10);
- horizontal_scrollbar_layer_->SetThumbThickness(3);
// Simple case - one third of the scrollable area is visible, so the thumb
// should be one third as long as the track.
horizontal_scrollbar_layer_->SetVisibleToTotalLengthRatio(0.33f);
- horizontal_scrollbar_layer_->SetTrackLength(100);
+ horizontal_scrollbar_layer_->SetBounds(gfx::Size(100, 3));
EXPECT_EQ(33, horizontal_scrollbar_layer_->ComputeThumbQuadRect().width());
// The thumb's length should never be less than its thickness.
horizontal_scrollbar_layer_->SetVisibleToTotalLengthRatio(0.01f);
- horizontal_scrollbar_layer_->SetTrackLength(100);
+ horizontal_scrollbar_layer_->SetBounds(gfx::Size(100, 3));
EXPECT_EQ(3, horizontal_scrollbar_layer_->ComputeThumbQuadRect().width());
}
TEST_F(ScrollbarLayerSolidColorThumbTest, SolidColorThumbPosition) {
- horizontal_scrollbar_layer_->SetTrackLength(100);
+ horizontal_scrollbar_layer_->SetBounds(gfx::Size(100, 3));
horizontal_scrollbar_layer_->SetVisibleToTotalLengthRatio(0.1f);
- horizontal_scrollbar_layer_->SetThumbThickness(3);
horizontal_scrollbar_layer_->SetCurrentPos(0);
horizontal_scrollbar_layer_->SetMaximum(100);
@@ -311,15 +422,15 @@ TEST_F(ScrollbarLayerSolidColorThumbTest, SolidColorThumbPosition) {
}
TEST_F(ScrollbarLayerSolidColorThumbTest, SolidColorThumbVerticalAdjust) {
- ScrollbarLayerImpl* layers[2] =
+ SolidColorScrollbarLayerImpl* layers[2] =
{ horizontal_scrollbar_layer_.get(), vertical_scrollbar_layer_.get() };
for (size_t i = 0; i < 2; ++i) {
- layers[i]->SetTrackLength(100);
layers[i]->SetVisibleToTotalLengthRatio(0.2f);
- layers[i]->SetThumbThickness(3);
layers[i]->SetCurrentPos(25);
layers[i]->SetMaximum(100);
}
+ layers[0]->SetBounds(gfx::Size(100, 3));
+ layers[1]->SetBounds(gfx::Size(3, 100));
EXPECT_RECT_EQ(gfx::RectF(20.f, 0.f, 20.f, 3.f),
horizontal_scrollbar_layer_->ComputeThumbQuadRect());
@@ -347,7 +458,7 @@ class ScrollbarLayerTestMaxTextureSize : public LayerTreeTest {
virtual void BeginTest() OVERRIDE {
scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar);
- scrollbar_layer_ = ScrollbarLayer::Create(scrollbar.Pass(), 1);
+ scrollbar_layer_ = PaintedScrollbarLayer::Create(scrollbar.Pass(), 1);
scrollbar_layer_->SetLayerTreeHost(layer_tree_host());
scrollbar_layer_->SetBounds(bounds_);
layer_tree_host()->root_layer()->AddChild(scrollbar_layer_);
@@ -377,7 +488,7 @@ class ScrollbarLayerTestMaxTextureSize : public LayerTreeTest {
virtual void AfterTest() OVERRIDE {}
private:
- scoped_refptr<ScrollbarLayer> scrollbar_layer_;
+ scoped_refptr<PaintedScrollbarLayer> scrollbar_layer_;
scoped_refptr<Layer> scroll_layer_;
gfx::Size bounds_;
};
@@ -404,9 +515,49 @@ class MockLayerTreeHost : public LayerTreeHost {
public:
MockLayerTreeHost(LayerTreeHostClient* client,
const LayerTreeSettings& settings)
- : LayerTreeHost(client, settings) {
+ : LayerTreeHost(client, settings),
+ next_id_(1),
+ total_ui_resource_created_(0),
+ total_ui_resource_deleted_(0) {
Initialize(NULL);
}
+
+ virtual UIResourceId CreateUIResource(UIResourceClient* content) OVERRIDE {
+ total_ui_resource_created_++;
+ UIResourceId nid = next_id_++;
+ ui_resource_bitmap_map_.insert(
+ std::make_pair(nid, content->GetBitmap(nid, false)));
+ return nid;
+ }
+
+ // Deletes a UI resource. May safely be called more than once.
+ virtual void DeleteUIResource(UIResourceId id) OVERRIDE {
+ UIResourceBitmapMap::iterator iter = ui_resource_bitmap_map_.find(id);
+ if (iter != ui_resource_bitmap_map_.end()) {
+ ui_resource_bitmap_map_.erase(iter);
+ total_ui_resource_deleted_++;
+ }
+ }
+
+ size_t UIResourceCount() { return ui_resource_bitmap_map_.size(); }
+ int TotalUIResourceDeleted() { return total_ui_resource_deleted_; }
+ int TotalUIResourceCreated() { return total_ui_resource_created_; }
+
+ gfx::Size ui_resource_size(UIResourceId id) {
+ UIResourceBitmapMap::iterator iter = ui_resource_bitmap_map_.find(id);
+ if (iter != ui_resource_bitmap_map_.end())
+ return iter->second.GetSize();
+ return gfx::Size();
+ }
+
+ private:
+ typedef base::hash_map<UIResourceId, UIResourceBitmap>
+ UIResourceBitmapMap;
+ UIResourceBitmapMap ui_resource_bitmap_map_;
+
+ int next_id_;
+ int total_ui_resource_created_;
+ int total_ui_resource_deleted_;
};
@@ -415,21 +566,34 @@ class ScrollbarLayerTestResourceCreation : public testing::Test {
ScrollbarLayerTestResourceCreation()
: fake_client_(FakeLayerTreeHostClient::DIRECT_3D) {}
- void TestResourceUpload(size_t expected_resources) {
+ void TestResourceUpload(int num_updates,
+ size_t expected_resources,
+ int expected_created,
+ int expected_deleted,
+ bool use_solid_color_scrollbar) {
layer_tree_host_.reset(
new MockLayerTreeHost(&fake_client_, layer_tree_settings_));
scoped_ptr<Scrollbar> scrollbar(new FakeScrollbar(false, true, false));
scoped_refptr<Layer> layer_tree_root = Layer::Create();
scoped_refptr<Layer> content_layer = Layer::Create();
- scoped_refptr<Layer> scrollbar_layer =
- ScrollbarLayer::Create(scrollbar.Pass(), layer_tree_root->id());
+ scoped_refptr<Layer> scrollbar_layer;
+ if (use_solid_color_scrollbar) {
+ const int kThumbThickness = 3;
+ const bool kIsLeftSideVerticalScrollbar = false;
+ scrollbar_layer =
+ SolidColorScrollbarLayer::Create(scrollbar->Orientation(),
+ kThumbThickness,
+ kIsLeftSideVerticalScrollbar,
+ layer_tree_root->id());
+ } else {
+ scrollbar_layer = PaintedScrollbarLayer::Create(scrollbar.Pass(),
+ layer_tree_root->id());
+ }
layer_tree_root->AddChild(content_layer);
layer_tree_root->AddChild(scrollbar_layer);
layer_tree_host_->InitializeOutputSurfaceIfNeeded();
- layer_tree_host_->contents_texture_manager()->
- SetMaxMemoryLimitBytes(1024 * 1024);
layer_tree_host_->SetRootLayer(layer_tree_root);
scrollbar_layer->SetIsDrawable(true);
@@ -447,16 +611,17 @@ class ScrollbarLayerTestResourceCreation : public testing::Test {
testing::Mock::VerifyAndClearExpectations(layer_tree_host_.get());
EXPECT_EQ(scrollbar_layer->layer_tree_host(), layer_tree_host_.get());
- PriorityCalculator calculator;
ResourceUpdateQueue queue;
OcclusionTracker occlusion_tracker(gfx::Rect(), false);
scrollbar_layer->SavePaintProperties();
- scrollbar_layer->SetTexturePriorities(calculator);
- layer_tree_host_->contents_texture_manager()->PrioritizeTextures();
- scrollbar_layer->Update(&queue, &occlusion_tracker);
- EXPECT_EQ(0u, queue.FullUploadSize());
- EXPECT_EQ(expected_resources, queue.PartialUploadSize());
+ for (int update_counter = 0; update_counter < num_updates; update_counter++)
+ scrollbar_layer->Update(&queue, &occlusion_tracker);
+
+ // A non-solid-color scrollbar should have requested two textures.
+ EXPECT_EQ(expected_resources, layer_tree_host_->UIResourceCount());
+ EXPECT_EQ(expected_created, layer_tree_host_->TotalUIResourceCreated());
+ EXPECT_EQ(expected_deleted, layer_tree_host_->TotalUIResourceDeleted());
testing::Mock::VerifyAndClearExpectations(layer_tree_host_.get());
@@ -470,13 +635,22 @@ class ScrollbarLayerTestResourceCreation : public testing::Test {
};
TEST_F(ScrollbarLayerTestResourceCreation, ResourceUpload) {
- layer_tree_settings_.solid_color_scrollbars = false;
- TestResourceUpload(2);
+ bool use_solid_color_scrollbars = false;
+ TestResourceUpload(0, 0, 0, 0, use_solid_color_scrollbars);
+ int num_updates[3] = {1, 5, 10};
+ for (int j = 0; j < 3; j++) {
+ TestResourceUpload(num_updates[j],
+ 2,
+ num_updates[j] * 2,
+ (num_updates[j] - 1) * 2,
+ use_solid_color_scrollbars);
+ }
}
TEST_F(ScrollbarLayerTestResourceCreation, SolidColorNoResourceUpload) {
- layer_tree_settings_.solid_color_scrollbars = true;
- TestResourceUpload(0);
+ bool use_solid_color_scrollbars = true;
+ TestResourceUpload(0, 0, 0, 0, use_solid_color_scrollbars);
+ TestResourceUpload(1, 0, 0, 0, use_solid_color_scrollbars);
}
class ScaledScrollbarLayerTestResourceCreation : public testing::Test {
@@ -484,25 +658,20 @@ class ScaledScrollbarLayerTestResourceCreation : public testing::Test {
ScaledScrollbarLayerTestResourceCreation()
: fake_client_(FakeLayerTreeHostClient::DIRECT_3D) {}
- void TestResourceUpload(size_t expected_resources, const float test_scale) {
+ void TestResourceUpload(const float test_scale) {
layer_tree_host_.reset(
new MockLayerTreeHost(&fake_client_, layer_tree_settings_));
gfx::Point scrollbar_location(0, 185);
- scoped_ptr<FakeScrollbar> scrollbar(new FakeScrollbar(false, true, false));
- scrollbar->set_location(scrollbar_location);
-
scoped_refptr<Layer> layer_tree_root = Layer::Create();
scoped_refptr<Layer> content_layer = Layer::Create();
- scoped_refptr<Layer> scrollbar_layer =
- ScrollbarLayer::Create(scrollbar.PassAs<cc::Scrollbar>(),
- layer_tree_root->id());
+ scoped_refptr<FakePaintedScrollbarLayer> scrollbar_layer =
+ FakePaintedScrollbarLayer::Create(false, true, layer_tree_root->id());
+
layer_tree_root->AddChild(content_layer);
layer_tree_root->AddChild(scrollbar_layer);
layer_tree_host_->InitializeOutputSurfaceIfNeeded();
- layer_tree_host_->contents_texture_manager()->
- SetMaxMemoryLimitBytes(1024 * 1024);
layer_tree_host_->SetRootLayer(layer_tree_root);
scrollbar_layer->SetIsDrawable(true);
@@ -529,30 +698,23 @@ class ScaledScrollbarLayerTestResourceCreation : public testing::Test {
testing::Mock::VerifyAndClearExpectations(layer_tree_host_.get());
EXPECT_EQ(scrollbar_layer->layer_tree_host(), layer_tree_host_.get());
- PriorityCalculator calculator;
ResourceUpdateQueue queue;
OcclusionTracker occlusion_tracker(gfx::Rect(), false);
-
scrollbar_layer->SavePaintProperties();
- scrollbar_layer->SetTexturePriorities(calculator);
- layer_tree_host_->contents_texture_manager()->PrioritizeTextures();
scrollbar_layer->Update(&queue, &occlusion_tracker);
- EXPECT_EQ(expected_resources, queue.PartialUploadSize());
// Verify that we have not generated any content uploads that are larger
// than their destination textures.
- while (queue.HasMoreUpdates()) {
- ResourceUpdate update = queue.TakeFirstPartialUpload();
- EXPECT_LE(update.texture->size().width(),
- scrollbar_layer->content_bounds().width());
- EXPECT_LE(update.texture->size().height(),
- scrollbar_layer->content_bounds().height());
-
- EXPECT_LE(update.dest_offset.x() + update.content_rect.width(),
- update.texture->size().width());
- EXPECT_LE(update.dest_offset.y() + update.content_rect.height(),
- update.texture->size().height());
- }
+
+ gfx::Size track_size = layer_tree_host_->ui_resource_size(
+ scrollbar_layer->track_resource_id());
+ gfx::Size thumb_size = layer_tree_host_->ui_resource_size(
+ scrollbar_layer->thumb_resource_id());
+
+ EXPECT_LE(track_size.width(), scrollbar_layer->content_bounds().width());
+ EXPECT_LE(track_size.height(), scrollbar_layer->content_bounds().height());
+ EXPECT_LE(thumb_size.width(), scrollbar_layer->content_bounds().width());
+ EXPECT_LE(thumb_size.height(), scrollbar_layer->content_bounds().height());
testing::Mock::VerifyAndClearExpectations(layer_tree_host_.get());
@@ -566,10 +728,11 @@ class ScaledScrollbarLayerTestResourceCreation : public testing::Test {
};
TEST_F(ScaledScrollbarLayerTestResourceCreation, ScaledResourceUpload) {
- layer_tree_settings_.solid_color_scrollbars = false;
// Pick a test scale that moves the scrollbar's (non-zero) position to
// a non-pixel-aligned location.
- TestResourceUpload(2, 1.41f);
+ TestResourceUpload(.041f);
+ TestResourceUpload(1.41f);
+ TestResourceUpload(4.1f);
}
} // namespace
diff --git a/chromium/cc/layers/solid_color_scrollbar_layer.cc b/chromium/cc/layers/solid_color_scrollbar_layer.cc
new file mode 100644
index 00000000000..d9ed922e5d8
--- /dev/null
+++ b/chromium/cc/layers/solid_color_scrollbar_layer.cc
@@ -0,0 +1,68 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/layers/solid_color_scrollbar_layer.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "cc/layers/layer_impl.h"
+#include "cc/layers/solid_color_scrollbar_layer_impl.h"
+
+namespace cc {
+
+scoped_ptr<LayerImpl> SolidColorScrollbarLayer::CreateLayerImpl(
+ LayerTreeImpl* tree_impl) {
+ return SolidColorScrollbarLayerImpl::Create(
+ tree_impl, id(), orientation(), thumb_thickness_,
+ is_left_side_vertical_scrollbar_).PassAs<LayerImpl>();
+}
+
+scoped_refptr<SolidColorScrollbarLayer> SolidColorScrollbarLayer::Create(
+ ScrollbarOrientation orientation,
+ int thumb_thickness,
+ bool is_left_side_vertical_scrollbar,
+ int scroll_layer_id) {
+ return make_scoped_refptr(new SolidColorScrollbarLayer(
+ orientation,
+ thumb_thickness,
+ is_left_side_vertical_scrollbar,
+ scroll_layer_id));
+}
+
+SolidColorScrollbarLayer::SolidColorScrollbarLayer(
+ ScrollbarOrientation orientation,
+ int thumb_thickness,
+ bool is_left_side_vertical_scrollbar,
+ int scroll_layer_id)
+ : scroll_layer_id_(scroll_layer_id),
+ orientation_(orientation),
+ thumb_thickness_(thumb_thickness),
+ is_left_side_vertical_scrollbar_(is_left_side_vertical_scrollbar) {}
+
+SolidColorScrollbarLayer::~SolidColorScrollbarLayer() {}
+
+ScrollbarLayerInterface* SolidColorScrollbarLayer::ToScrollbarLayer() {
+ return this;
+}
+
+bool SolidColorScrollbarLayer::OpacityCanAnimateOnImplThread() const {
+ return true;
+}
+
+int SolidColorScrollbarLayer::ScrollLayerId() const {
+ return scroll_layer_id_;
+}
+
+void SolidColorScrollbarLayer::SetScrollLayerId(int id) {
+ if (id == scroll_layer_id_)
+ return;
+
+ scroll_layer_id_ = id;
+ SetNeedsFullTreeSync();
+}
+
+ScrollbarOrientation SolidColorScrollbarLayer::orientation() const {
+ return orientation_;
+}
+
+} // namespace cc
diff --git a/chromium/cc/layers/solid_color_scrollbar_layer.h b/chromium/cc/layers/solid_color_scrollbar_layer.h
new file mode 100644
index 00000000000..9c0dc1ed473
--- /dev/null
+++ b/chromium/cc/layers/solid_color_scrollbar_layer.h
@@ -0,0 +1,54 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_LAYERS_SOLID_COLOR_SCROLLBAR_LAYER_H_
+#define CC_LAYERS_SOLID_COLOR_SCROLLBAR_LAYER_H_
+
+#include "cc/base/cc_export.h"
+#include "cc/layers/layer.h"
+#include "cc/layers/scrollbar_layer_interface.h"
+
+namespace cc {
+
+class CC_EXPORT SolidColorScrollbarLayer : public ScrollbarLayerInterface,
+ public Layer {
+ public:
+ virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl)
+ OVERRIDE;
+
+ static scoped_refptr<SolidColorScrollbarLayer> Create(
+ ScrollbarOrientation orientation,
+ int thumb_thickness,
+ bool is_left_side_vertical_scrollbar,
+ int scroll_layer_id);
+
+ // Layer overrides.
+ virtual bool OpacityCanAnimateOnImplThread() const OVERRIDE;
+ virtual ScrollbarLayerInterface* ToScrollbarLayer() OVERRIDE;
+
+ // ScrollbarLayerInterface
+ virtual int ScrollLayerId() const OVERRIDE;
+ virtual void SetScrollLayerId(int id) OVERRIDE;
+
+ virtual ScrollbarOrientation orientation() const OVERRIDE;
+
+ protected:
+ SolidColorScrollbarLayer(ScrollbarOrientation orientation,
+ int thumb_thickness,
+ bool is_left_side_vertical_scrollbar,
+ int scroll_layer_id);
+ virtual ~SolidColorScrollbarLayer();
+
+ private:
+ int scroll_layer_id_;
+ ScrollbarOrientation orientation_;
+ int thumb_thickness_;
+ bool is_left_side_vertical_scrollbar_;
+
+ DISALLOW_COPY_AND_ASSIGN(SolidColorScrollbarLayer);
+};
+
+} // namespace cc
+
+#endif // CC_LAYERS_SOLID_COLOR_SCROLLBAR_LAYER_H_
diff --git a/chromium/cc/layers/solid_color_scrollbar_layer_impl.cc b/chromium/cc/layers/solid_color_scrollbar_layer_impl.cc
new file mode 100644
index 00000000000..9f46a8080f5
--- /dev/null
+++ b/chromium/cc/layers/solid_color_scrollbar_layer_impl.cc
@@ -0,0 +1,88 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/layers/quad_sink.h"
+#include "cc/layers/solid_color_scrollbar_layer_impl.h"
+#include "cc/quads/solid_color_draw_quad.h"
+#include "cc/trees/layer_tree_impl.h"
+#include "cc/trees/layer_tree_settings.h"
+
+namespace cc {
+
+scoped_ptr<SolidColorScrollbarLayerImpl> SolidColorScrollbarLayerImpl::Create(
+ LayerTreeImpl* tree_impl,
+ int id,
+ ScrollbarOrientation orientation,
+ int thumb_thickness,
+ bool is_left_side_vertical_scrollbar) {
+ return make_scoped_ptr(new SolidColorScrollbarLayerImpl(
+ tree_impl, id, orientation, thumb_thickness,
+ is_left_side_vertical_scrollbar));
+}
+
+SolidColorScrollbarLayerImpl::~SolidColorScrollbarLayerImpl() {}
+
+scoped_ptr<LayerImpl> SolidColorScrollbarLayerImpl::CreateLayerImpl(
+ LayerTreeImpl* tree_impl) {
+ return SolidColorScrollbarLayerImpl::Create(
+ tree_impl, id(), orientation(), thumb_thickness_,
+ is_left_side_vertical_scrollbar()).PassAs<LayerImpl>();
+}
+
+SolidColorScrollbarLayerImpl::SolidColorScrollbarLayerImpl(
+ LayerTreeImpl* tree_impl,
+ int id,
+ ScrollbarOrientation orientation,
+ int thumb_thickness,
+ bool is_left_side_vertical_scrollbar)
+ : ScrollbarLayerImplBase(tree_impl, id, orientation,
+ is_left_side_vertical_scrollbar),
+ thumb_thickness_(thumb_thickness),
+ color_(tree_impl->settings().solid_color_scrollbar_color) {}
+
+void SolidColorScrollbarLayerImpl::PushPropertiesTo(LayerImpl* layer) {
+ ScrollbarLayerImplBase::PushPropertiesTo(layer);
+}
+
+int SolidColorScrollbarLayerImpl::ThumbThickness() const {
+ if (thumb_thickness_ != -1)
+ return thumb_thickness_;
+
+ if (orientation() == HORIZONTAL)
+ return bounds().height();
+ else
+ return bounds().width();
+}
+
+int SolidColorScrollbarLayerImpl::ThumbLength() const {
+ return std::max(
+ static_cast<int>(visible_to_total_length_ratio() * TrackLength()),
+ ThumbThickness());
+}
+
+float SolidColorScrollbarLayerImpl::TrackLength() const {
+ if (orientation() == HORIZONTAL)
+ return bounds().width();
+ else
+ return bounds().height() + vertical_adjust();
+}
+
+int SolidColorScrollbarLayerImpl::TrackStart() const {
+ return 0;
+}
+
+void SolidColorScrollbarLayerImpl::AppendQuads(QuadSink* quad_sink,
+ AppendQuadsData* append_quads_data) {
+ gfx::Rect thumb_quad_rect = ComputeThumbQuadRect();
+
+ SharedQuadState* shared_quad_state =
+ quad_sink->UseSharedQuadState(CreateSharedQuadState());
+ AppendDebugBorderQuad(quad_sink, shared_quad_state, append_quads_data);
+
+ scoped_ptr<SolidColorDrawQuad> quad = SolidColorDrawQuad::Create();
+ quad->SetNew(shared_quad_state, thumb_quad_rect, color_, false);
+ quad_sink->Append(quad.PassAs<DrawQuad>(), append_quads_data);
+}
+
+} // namespace cc
diff --git a/chromium/cc/layers/solid_color_scrollbar_layer_impl.h b/chromium/cc/layers/solid_color_scrollbar_layer_impl.h
new file mode 100644
index 00000000000..56cbacd888c
--- /dev/null
+++ b/chromium/cc/layers/solid_color_scrollbar_layer_impl.h
@@ -0,0 +1,52 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_LAYERS_SOLID_COLOR_SCROLLBAR_LAYER_IMPL_H_
+#define CC_LAYERS_SOLID_COLOR_SCROLLBAR_LAYER_IMPL_H_
+
+#include "cc/base/cc_export.h"
+#include "cc/layers/scrollbar_layer_impl_base.h"
+
+namespace cc {
+
+class CC_EXPORT SolidColorScrollbarLayerImpl : public ScrollbarLayerImplBase {
+ public:
+ static scoped_ptr<SolidColorScrollbarLayerImpl> Create(
+ LayerTreeImpl* tree_impl,
+ int id,
+ ScrollbarOrientation orientation,
+ int thumb_thickness,
+ bool is_left_side_vertical_scrollbar);
+ virtual ~SolidColorScrollbarLayerImpl();
+
+ // LayerImpl overrides.
+ virtual scoped_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl)
+ OVERRIDE;
+ virtual void PushPropertiesTo(LayerImpl* layer) OVERRIDE;
+
+ virtual void AppendQuads(QuadSink* quad_sink,
+ AppendQuadsData* append_quads_data) OVERRIDE;
+
+
+ protected:
+ SolidColorScrollbarLayerImpl(LayerTreeImpl* tree_impl,
+ int id,
+ ScrollbarOrientation orientation,
+ int thumb_thickness,
+ bool is_left_side_vertical_scrollbar);
+
+ // ScrollbarLayerImplBase implementation.
+ virtual int ThumbThickness() const OVERRIDE;
+ virtual int ThumbLength() const OVERRIDE;
+ virtual float TrackLength() const OVERRIDE;
+ virtual int TrackStart() const OVERRIDE;
+
+ private:
+ int thumb_thickness_;
+ SkColor color_;
+};
+
+} // namespace cc
+
+#endif // CC_LAYERS_SOLID_COLOR_SCROLLBAR_LAYER_IMPL_H_
diff --git a/chromium/cc/layers/texture_layer.cc b/chromium/cc/layers/texture_layer.cc
index 11009fa1b68..a845709448f 100644
--- a/chromium/cc/layers/texture_layer.cc
+++ b/chromium/cc/layers/texture_layer.cc
@@ -5,10 +5,13 @@
#include "cc/layers/texture_layer.h"
#include "base/bind.h"
+#include "base/callback_helpers.h"
#include "base/location.h"
-#include "base/message_loop/message_loop_proxy.h"
+#include "base/synchronization/lock.h"
#include "cc/layers/texture_layer_client.h"
#include "cc/layers/texture_layer_impl.h"
+#include "cc/resources/single_release_callback.h"
+#include "cc/trees/blocking_task_runner.h"
#include "cc/trees/layer_tree_host.h"
#include "third_party/khronos/GLES2/gl2.h"
#include "third_party/WebKit/public/platform/WebGraphicsContext3D.h"
@@ -51,7 +54,7 @@ void TextureLayer::ClearClient() {
layer_tree_host()->StopRateLimiter(client_->Context3d());
client_ = NULL;
if (uses_mailbox_)
- SetTextureMailbox(TextureMailbox());
+ SetTextureMailbox(TextureMailbox(), scoped_ptr<SingleReleaseCallback>());
else
SetTextureId(0);
}
@@ -125,23 +128,34 @@ void TextureLayer::SetTextureId(unsigned id) {
layer_tree_host()->AcquireLayerTextures();
texture_id_ = id;
SetNeedsCommit();
+ // The texture id needs to be removed from the active tree before the
+ // commit is called complete.
+ SetNextCommitWaitsForActivation();
}
-void TextureLayer::SetTextureMailbox(const TextureMailbox& mailbox) {
+void TextureLayer::SetTextureMailbox(
+ const TextureMailbox& mailbox,
+ scoped_ptr<SingleReleaseCallback> release_callback) {
DCHECK(uses_mailbox_);
DCHECK(!mailbox.IsValid() || !holder_ref_ ||
!mailbox.Equals(holder_ref_->holder()->mailbox()));
+ DCHECK_EQ(mailbox.IsValid(), !!release_callback);
+
// If we never commited the mailbox, we need to release it here.
if (mailbox.IsValid())
- holder_ref_ = MailboxHolder::Create(mailbox);
+ holder_ref_ = MailboxHolder::Create(mailbox, release_callback.Pass());
else
holder_ref_.reset();
needs_set_mailbox_ = true;
SetNeedsCommit();
+ // The active frame needs to be replaced and the mailbox returned before the
+ // commit is called complete.
+ SetNextCommitWaitsForActivation();
}
void TextureLayer::WillModifyTexture() {
- if (layer_tree_host() && (DrawsContent() || content_committed_)) {
+ if (!uses_mailbox_ && layer_tree_host() && (DrawsContent() ||
+ content_committed_)) {
layer_tree_host()->AcquireLayerTextures();
content_committed_ = false;
}
@@ -161,16 +175,24 @@ void TextureLayer::SetLayerTreeHost(LayerTreeHost* host) {
}
if (layer_tree_host()) {
- if (texture_id_)
+ if (texture_id_) {
layer_tree_host()->AcquireLayerTextures();
+ // The texture id needs to be removed from the active tree before the
+ // commit is called complete.
+ SetNextCommitWaitsForActivation();
+ }
if (rate_limit_context_ && client_)
layer_tree_host()->StopRateLimiter(client_->Context3d());
}
// If we're removed from the tree, the TextureLayerImpl will be destroyed, and
// we will need to set the mailbox again on a new TextureLayerImpl the next
// time we push.
- if (!host && uses_mailbox_ && holder_ref_)
+ if (!host && uses_mailbox_ && holder_ref_) {
needs_set_mailbox_ = true;
+ // The active frame needs to be replaced and the mailbox returned before the
+ // commit is called complete.
+ SetNextCommitWaitsForActivation();
+ }
Layer::SetLayerTreeHost(host);
}
@@ -184,19 +206,25 @@ bool TextureLayer::Update(ResourceUpdateQueue* queue,
if (client_) {
if (uses_mailbox_) {
TextureMailbox mailbox;
+ scoped_ptr<SingleReleaseCallback> release_callback;
if (client_->PrepareTextureMailbox(
- &mailbox, layer_tree_host()->UsingSharedMemoryResources())) {
- SetTextureMailbox(mailbox);
+ &mailbox,
+ &release_callback,
+ layer_tree_host()->UsingSharedMemoryResources())) {
+ SetTextureMailbox(mailbox, release_callback.Pass());
updated = true;
}
} else {
- DCHECK(client_->Context3d());
texture_id_ = client_->PrepareTexture();
+ DCHECK_EQ(!!texture_id_, !!client_->Context3d());
if (client_->Context3d() &&
client_->Context3d()->getGraphicsResetStatusARB() != GL_NO_ERROR)
texture_id_ = 0;
updated = true;
SetNeedsPushProperties();
+ // The texture id needs to be removed from the active tree before the
+ // commit is called complete.
+ SetNextCommitWaitsForActivation();
}
}
@@ -218,18 +246,18 @@ void TextureLayer::PushPropertiesTo(LayerImpl* layer) {
texture_layer->set_blend_background_color(blend_background_color_);
if (uses_mailbox_ && needs_set_mailbox_) {
TextureMailbox texture_mailbox;
+ scoped_ptr<SingleReleaseCallback> release_callback;
if (holder_ref_) {
MailboxHolder* holder = holder_ref_->holder();
- TextureMailbox::ReleaseCallback callback =
- holder->GetCallbackForImplThread();
- texture_mailbox = holder->mailbox().CopyWithNewCallback(callback);
+ texture_mailbox = holder->mailbox();
+ release_callback = holder->GetCallbackForImplThread();
}
- texture_layer->SetTextureMailbox(texture_mailbox);
+ texture_layer->SetTextureMailbox(texture_mailbox, release_callback.Pass());
needs_set_mailbox_ = false;
} else {
texture_layer->set_texture_id(texture_id_);
+ content_committed_ = DrawsContent();
}
- content_committed_ = DrawsContent();
}
Region TextureLayer::VisibleContentOpaqueRegion() const {
@@ -242,13 +270,6 @@ Region TextureLayer::VisibleContentOpaqueRegion() const {
return Region();
}
-bool TextureLayer::BlocksPendingCommit() const {
- // Double-buffered texture layers need to be blocked until they can be made
- // triple-buffered. Single-buffered layers already prevent draws, so
- // can block too for simplicity.
- return DrawsContent();
-}
-
bool TextureLayer::CanClipSelf() const {
return true;
}
@@ -263,10 +284,13 @@ TextureLayer::MailboxHolder::MainThreadReference::~MainThreadReference() {
holder_->InternalRelease();
}
-TextureLayer::MailboxHolder::MailboxHolder(const TextureMailbox& mailbox)
- : message_loop_(base::MessageLoopProxy::current()),
+TextureLayer::MailboxHolder::MailboxHolder(
+ const TextureMailbox& mailbox,
+ scoped_ptr<SingleReleaseCallback> release_callback)
+ : message_loop_(BlockingTaskRunner::current()),
internal_references_(0),
mailbox_(mailbox),
+ release_callback_(release_callback.Pass()),
sync_point_(mailbox.sync_point()),
is_lost_(false) {
}
@@ -276,23 +300,27 @@ TextureLayer::MailboxHolder::~MailboxHolder() {
}
scoped_ptr<TextureLayer::MailboxHolder::MainThreadReference>
-TextureLayer::MailboxHolder::Create(const TextureMailbox& mailbox) {
+TextureLayer::MailboxHolder::Create(
+ const TextureMailbox& mailbox,
+ scoped_ptr<SingleReleaseCallback> release_callback) {
return scoped_ptr<MainThreadReference>(new MainThreadReference(
- new MailboxHolder(mailbox)));
+ new MailboxHolder(mailbox, release_callback.Pass())));
}
void TextureLayer::MailboxHolder::Return(unsigned sync_point, bool is_lost) {
+ base::AutoLock lock(arguments_lock_);
sync_point_ = sync_point;
is_lost_ = is_lost;
}
-TextureMailbox::ReleaseCallback
+scoped_ptr<SingleReleaseCallback>
TextureLayer::MailboxHolder::GetCallbackForImplThread() {
// We can't call GetCallbackForImplThread if we released the main thread
// reference.
DCHECK_GT(internal_references_, 0u);
InternalAddRef();
- return base::Bind(&MailboxHolder::ReturnAndReleaseOnImplThread, this);
+ return SingleReleaseCallback::Create(
+ base::Bind(&MailboxHolder::ReturnAndReleaseOnImplThread, this));
}
void TextureLayer::MailboxHolder::InternalAddRef() {
@@ -302,23 +330,17 @@ void TextureLayer::MailboxHolder::InternalAddRef() {
void TextureLayer::MailboxHolder::InternalRelease() {
DCHECK(message_loop_->BelongsToCurrentThread());
if (!--internal_references_) {
- mailbox_.RunReleaseCallback(sync_point_, is_lost_);
+ release_callback_->Run(sync_point_, is_lost_);
mailbox_ = TextureMailbox();
+ release_callback_.reset();
}
}
-void TextureLayer::MailboxHolder::ReturnAndReleaseOnMainThread(
- unsigned sync_point, bool is_lost) {
- DCHECK(message_loop_->BelongsToCurrentThread());
- Return(sync_point, is_lost);
- InternalRelease();
-}
-
void TextureLayer::MailboxHolder::ReturnAndReleaseOnImplThread(
unsigned sync_point, bool is_lost) {
- message_loop_->PostTask(FROM_HERE, base::Bind(
- &MailboxHolder::ReturnAndReleaseOnMainThread,
- this, sync_point, is_lost));
+ Return(sync_point, is_lost);
+ message_loop_->PostTask(FROM_HERE,
+ base::Bind(&MailboxHolder::InternalRelease, this));
}
} // namespace cc
diff --git a/chromium/cc/layers/texture_layer.h b/chromium/cc/layers/texture_layer.h
index 9e63b6825eb..5fb214c2c13 100644
--- a/chromium/cc/layers/texture_layer.h
+++ b/chromium/cc/layers/texture_layer.h
@@ -8,23 +8,80 @@
#include <string>
#include "base/callback.h"
+#include "base/synchronization/lock.h"
#include "cc/base/cc_export.h"
#include "cc/layers/layer.h"
#include "cc/resources/texture_mailbox.h"
namespace WebKit { class WebGraphicsContext3D; }
-namespace base {
-class MessageLoopProxy;
-}
-
namespace cc {
-
+class BlockingTaskRunner;
+class SingleReleaseCallback;
class TextureLayerClient;
// A Layer containing a the rendered output of a plugin instance.
class CC_EXPORT TextureLayer : public Layer {
public:
+ class CC_EXPORT MailboxHolder
+ : public base::RefCountedThreadSafe<MailboxHolder> {
+ public:
+ class CC_EXPORT MainThreadReference {
+ public:
+ explicit MainThreadReference(MailboxHolder* holder);
+ ~MainThreadReference();
+ MailboxHolder* holder() { return holder_.get(); }
+
+ private:
+ scoped_refptr<MailboxHolder> holder_;
+ DISALLOW_COPY_AND_ASSIGN(MainThreadReference);
+ };
+
+ const TextureMailbox& mailbox() const { return mailbox_; }
+ void Return(unsigned sync_point, bool is_lost);
+
+ // Gets a ReleaseCallback that can be called from another thread. Note: the
+ // caller must ensure the callback is called.
+ scoped_ptr<SingleReleaseCallback> GetCallbackForImplThread();
+
+ protected:
+ friend class TextureLayer;
+
+ // Protected visiblity so only TextureLayer and unit tests can create these.
+ static scoped_ptr<MainThreadReference> Create(
+ const TextureMailbox& mailbox,
+ scoped_ptr<SingleReleaseCallback> release_callback);
+ virtual ~MailboxHolder();
+
+ private:
+ friend class base::RefCountedThreadSafe<MailboxHolder>;
+ friend class MainThreadReference;
+ explicit MailboxHolder(const TextureMailbox& mailbox,
+ scoped_ptr<SingleReleaseCallback> release_callback);
+
+ void InternalAddRef();
+ void InternalRelease();
+ void ReturnAndReleaseOnImplThread(unsigned sync_point, bool is_lost);
+
+ // This member is thread safe, and is accessed on main and impl threads.
+ const scoped_refptr<BlockingTaskRunner> message_loop_;
+
+ // These members are only accessed on the main thread, or on the impl thread
+ // during commit where the main thread is blocked.
+ unsigned internal_references_;
+ TextureMailbox mailbox_;
+ scoped_ptr<SingleReleaseCallback> release_callback_;
+
+ // This lock guards the sync_point_ and is_lost_ fields because they can be
+ // accessed on both the impl and main thread. We do this to ensure that the
+ // values of these fields are well-ordered such that the last call to
+ // ReturnAndReleaseOnImplThread() defines their values.
+ base::Lock arguments_lock_;
+ unsigned sync_point_;
+ bool is_lost_;
+ DISALLOW_COPY_AND_ASSIGN(MailboxHolder);
+ };
+
// If this texture layer requires special preparation logic for each frame
// driven by the compositor, pass in a non-nil client. Pass in a nil client
// pointer if texture updates are driven by an external process.
@@ -67,11 +124,13 @@ class CC_EXPORT TextureLayer : public Layer {
void SetRateLimitContext(bool rate_limit);
// Code path for plugins which supply their own texture ID.
+ // DEPRECATED. DO NOT USE.
void SetTextureId(unsigned texture_id);
// Code path for plugins which supply their own mailbox.
bool uses_mailbox() const { return uses_mailbox_; }
- void SetTextureMailbox(const TextureMailbox& mailbox);
+ void SetTextureMailbox(const TextureMailbox& mailbox,
+ scoped_ptr<SingleReleaseCallback> release_callback);
void WillModifyTexture();
@@ -83,7 +142,6 @@ class CC_EXPORT TextureLayer : public Layer {
const OcclusionTracker* occlusion) OVERRIDE;
virtual void PushPropertiesTo(LayerImpl* layer) OVERRIDE;
virtual Region VisibleContentOpaqueRegion() const OVERRIDE;
- virtual bool BlocksPendingCommit() const OVERRIDE;
virtual bool CanClipSelf() const OVERRIDE;
@@ -92,50 +150,6 @@ class CC_EXPORT TextureLayer : public Layer {
virtual ~TextureLayer();
private:
- class MailboxHolder : public base::RefCountedThreadSafe<MailboxHolder> {
- public:
- class MainThreadReference {
- public:
- explicit MainThreadReference(MailboxHolder* holder);
- ~MainThreadReference();
- MailboxHolder* holder() { return holder_.get(); }
-
- private:
- scoped_refptr<MailboxHolder> holder_;
- DISALLOW_COPY_AND_ASSIGN(MainThreadReference);
- };
-
- static scoped_ptr<MainThreadReference> Create(
- const TextureMailbox& mailbox);
-
- const TextureMailbox& mailbox() const { return mailbox_; }
- void Return(unsigned sync_point, bool is_lost);
-
- // Gets a ReleaseCallback that can be called from another thread. Note: the
- // caller must ensure the callback is called.
- TextureMailbox::ReleaseCallback GetCallbackForImplThread();
-
- private:
- friend class base::RefCountedThreadSafe<MailboxHolder>;
- friend class MainThreadReference;
- explicit MailboxHolder(const TextureMailbox& mailbox);
- ~MailboxHolder();
- void InternalAddRef();
- void InternalRelease();
- void ReturnAndReleaseOnMainThread(unsigned sync_point, bool is_lost);
- void ReturnAndReleaseOnImplThread(unsigned sync_point, bool is_lost);
-
- // Thread safety notes: except for the thread-safe message_loop_, all fields
- // are only used on the main thread, or on the impl thread during commit
- // where the main thread is blocked.
- const scoped_refptr<base::MessageLoopProxy> message_loop_;
- unsigned internal_references_;
- TextureMailbox mailbox_;
- unsigned sync_point_;
- bool is_lost_;
- DISALLOW_COPY_AND_ASSIGN(MailboxHolder);
- };
-
TextureLayerClient* client_;
bool uses_mailbox_;
diff --git a/chromium/cc/layers/texture_layer_client.h b/chromium/cc/layers/texture_layer_client.h
index 73d34b30775..187b12f16c4 100644
--- a/chromium/cc/layers/texture_layer_client.h
+++ b/chromium/cc/layers/texture_layer_client.h
@@ -5,6 +5,8 @@
#ifndef CC_LAYERS_TEXTURE_LAYER_CLIENT_H_
#define CC_LAYERS_TEXTURE_LAYER_CLIENT_H_
+#include "cc/resources/single_release_callback.h"
+
namespace WebKit { class WebGraphicsContext3D; }
namespace cc {
@@ -24,8 +26,10 @@ class TextureLayerClient {
// Returns true and provides a mailbox if a new frame is available.
// Returns false if no new data is available
// and the old mailbox is to be reused.
- virtual bool PrepareTextureMailbox(TextureMailbox* mailbox,
- bool use_shared_memory) = 0;
+ virtual bool PrepareTextureMailbox(
+ TextureMailbox* mailbox,
+ scoped_ptr<SingleReleaseCallback>* release_callback,
+ bool use_shared_memory) = 0;
protected:
virtual ~TextureLayerClient() {}
diff --git a/chromium/cc/layers/texture_layer_impl.cc b/chromium/cc/layers/texture_layer_impl.cc
index 851bf9a145b..e16fd3c3f18 100644
--- a/chromium/cc/layers/texture_layer_impl.cc
+++ b/chromium/cc/layers/texture_layer_impl.cc
@@ -4,12 +4,15 @@
#include "cc/layers/texture_layer_impl.h"
+#include <vector>
+
#include "base/strings/stringprintf.h"
#include "cc/layers/quad_sink.h"
#include "cc/output/renderer.h"
#include "cc/quads/texture_draw_quad.h"
#include "cc/resources/platform_color.h"
#include "cc/resources/scoped_resource.h"
+#include "cc/resources/single_release_callback.h"
#include "cc/trees/layer_tree_impl.h"
namespace cc {
@@ -36,10 +39,14 @@ TextureLayerImpl::TextureLayerImpl(LayerTreeImpl* tree_impl,
TextureLayerImpl::~TextureLayerImpl() { FreeTextureMailbox(); }
-void TextureLayerImpl::SetTextureMailbox(const TextureMailbox& mailbox) {
+void TextureLayerImpl::SetTextureMailbox(
+ const TextureMailbox& mailbox,
+ scoped_ptr<SingleReleaseCallback> release_callback) {
DCHECK(uses_mailbox_);
+ DCHECK_EQ(mailbox.IsValid(), !!release_callback);
FreeTextureMailbox();
texture_mailbox_ = mailbox;
+ release_callback_ = release_callback.Pass();
own_mailbox_ = true;
valid_texture_copy_ = false;
}
@@ -60,7 +67,8 @@ void TextureLayerImpl::PushPropertiesTo(LayerImpl* layer) {
texture_layer->set_vertex_opacity(vertex_opacity_);
texture_layer->set_premultiplied_alpha(premultiplied_alpha_);
if (uses_mailbox_ && own_mailbox_) {
- texture_layer->SetTextureMailbox(texture_mailbox_);
+ texture_layer->SetTextureMailbox(texture_mailbox_,
+ release_callback_.Pass());
own_mailbox_ = false;
} else {
texture_layer->set_texture_id(texture_id_);
@@ -80,7 +88,8 @@ bool TextureLayerImpl::WillDraw(DrawMode draw_mode,
texture_mailbox_.IsSharedMemory())) {
external_texture_resource_ =
resource_provider->CreateResourceFromTextureMailbox(
- texture_mailbox_);
+ texture_mailbox_,
+ release_callback_.Pass());
DCHECK(external_texture_resource_);
texture_copy_.reset();
valid_texture_copy_ = false;
@@ -102,8 +111,8 @@ bool TextureLayerImpl::WillDraw(DrawMode draw_mode,
if (!texture_copy_->id()) {
texture_copy_->Allocate(texture_mailbox_.shared_memory_size(),
- resource_provider->best_texture_format(),
- ResourceProvider::TextureUsageAny);
+ ResourceProvider::TextureUsageAny,
+ resource_provider->best_texture_format());
}
if (texture_copy_->id()) {
@@ -229,7 +238,10 @@ void TextureLayerImpl::FreeTextureMailbox() {
return;
if (own_mailbox_) {
DCHECK(!external_texture_resource_);
- texture_mailbox_.RunReleaseCallback(texture_mailbox_.sync_point(), false);
+ if (release_callback_)
+ release_callback_->Run(texture_mailbox_.sync_point(), false);
+ texture_mailbox_ = TextureMailbox();
+ release_callback_.reset();
} else if (external_texture_resource_) {
DCHECK(!own_mailbox_);
ResourceProvider* resource_provider =
diff --git a/chromium/cc/layers/texture_layer_impl.h b/chromium/cc/layers/texture_layer_impl.h
index f85720688a6..90c26923c8e 100644
--- a/chromium/cc/layers/texture_layer_impl.h
+++ b/chromium/cc/layers/texture_layer_impl.h
@@ -12,6 +12,7 @@
#include "cc/layers/layer_impl.h"
namespace cc {
+class SingleReleaseCallback;
class ScopedResource;
class CC_EXPORT TextureLayerImpl : public LayerImpl {
@@ -61,7 +62,8 @@ class CC_EXPORT TextureLayerImpl : public LayerImpl {
virtual bool CanClipSelf() const OVERRIDE;
- void SetTextureMailbox(const TextureMailbox& mailbox);
+ void SetTextureMailbox(const TextureMailbox& mailbox,
+ scoped_ptr<SingleReleaseCallback> release_callback);
private:
TextureLayerImpl(LayerTreeImpl* tree_impl, int id, bool uses_mailbox);
@@ -81,6 +83,7 @@ class CC_EXPORT TextureLayerImpl : public LayerImpl {
scoped_ptr<ScopedResource> texture_copy_;
TextureMailbox texture_mailbox_;
+ scoped_ptr<SingleReleaseCallback> release_callback_;
bool uses_mailbox_;
bool own_mailbox_;
bool valid_texture_copy_;
diff --git a/chromium/cc/layers/texture_layer_unittest.cc b/chromium/cc/layers/texture_layer_unittest.cc
index aa08e9f81ee..239c5b8fa22 100644
--- a/chromium/cc/layers/texture_layer_unittest.cc
+++ b/chromium/cc/layers/texture_layer_unittest.cc
@@ -4,16 +4,28 @@
#include "cc/layers/texture_layer.h"
+#include <algorithm>
#include <string>
+#include "base/bind.h"
#include "base/callback.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "base/time/time.h"
+#include "cc/debug/test_web_graphics_context_3d.h"
+#include "cc/layers/solid_color_layer.h"
#include "cc/layers/texture_layer_client.h"
#include "cc/layers/texture_layer_impl.h"
+#include "cc/output/compositor_frame_ack.h"
+#include "cc/output/context_provider.h"
+#include "cc/resources/returned_resource.h"
#include "cc/test/fake_impl_proxy.h"
#include "cc/test/fake_layer_tree_host_client.h"
#include "cc/test/fake_layer_tree_host_impl.h"
+#include "cc/test/fake_output_surface.h"
#include "cc/test/layer_test_common.h"
#include "cc/test/layer_tree_test.h"
+#include "cc/trees/blocking_task_runner.h"
#include "cc/trees/layer_tree_host.h"
#include "cc/trees/layer_tree_impl.h"
#include "cc/trees/single_thread_proxy.h"
@@ -187,7 +199,7 @@ TEST_F(TextureLayerTest, SyncImplWhenRemovingFromTree) {
TEST_F(TextureLayerTest, CheckPropertyChangeCausesCorrectBehavior) {
scoped_refptr<TextureLayer> test_layer = TextureLayer::Create(NULL);
- layer_tree_host_->SetRootLayer(test_layer);
+ EXPECT_SET_NEEDS_COMMIT(1, layer_tree_host_->SetRootLayer(test_layer));
// Test properties that should call SetNeedsCommit. All properties need to
// be set to new values in order for SetNeedsCommit to be called.
@@ -243,9 +255,12 @@ class FakeTextureLayerClient : public TextureLayerClient {
return context_.get();
}
- virtual bool PrepareTextureMailbox(TextureMailbox* mailbox,
- bool use_shared_memory) OVERRIDE {
+ virtual bool PrepareTextureMailbox(
+ TextureMailbox* mailbox,
+ scoped_ptr<SingleReleaseCallback>* release_callback,
+ bool use_shared_memory) OVERRIDE {
*mailbox = TextureMailbox();
+ *release_callback = scoped_ptr<SingleReleaseCallback>();
return true;
}
@@ -328,25 +343,25 @@ struct CommonMailboxObjects {
mailbox_name2_);
gpu::Mailbox m1;
m1.SetName(reinterpret_cast<const int8*>(mailbox_name1_.data()));
- mailbox1_ = TextureMailbox(m1, release_mailbox1_, sync_point1_);
+ mailbox1_ = TextureMailbox(m1, sync_point1_);
gpu::Mailbox m2;
m2.SetName(reinterpret_cast<const int8*>(mailbox_name2_.data()));
- mailbox2_ = TextureMailbox(m2, release_mailbox2_, sync_point2_);
+ mailbox2_ = TextureMailbox(m2, sync_point2_);
gfx::Size size(128, 128);
EXPECT_TRUE(shared_memory_->CreateAndMapAnonymous(4 * size.GetArea()));
release_mailbox3_ = base::Bind(&MockMailboxCallback::Release2,
base::Unretained(&mock_callback_),
shared_memory_.get());
- mailbox3_ = TextureMailbox(shared_memory_.get(), size, release_mailbox3_);
+ mailbox3_ = TextureMailbox(shared_memory_.get(), size);
}
std::string mailbox_name1_;
std::string mailbox_name2_;
MockMailboxCallback mock_callback_;
- TextureMailbox::ReleaseCallback release_mailbox1_;
- TextureMailbox::ReleaseCallback release_mailbox2_;
- TextureMailbox::ReleaseCallback release_mailbox3_;
+ ReleaseCallback release_mailbox1_;
+ ReleaseCallback release_mailbox2_;
+ ReleaseCallback release_mailbox3_;
TextureMailbox mailbox1_;
TextureMailbox mailbox2_;
TextureMailbox mailbox3_;
@@ -355,6 +370,14 @@ struct CommonMailboxObjects {
scoped_ptr<base::SharedMemory> shared_memory_;
};
+class TestMailboxHolder : public TextureLayer::MailboxHolder {
+ public:
+ using TextureLayer::MailboxHolder::Create;
+
+ protected:
+ virtual ~TestMailboxHolder() {}
+};
+
class TextureLayerWithMailboxTest : public TextureLayerTest {
protected:
virtual void TearDown() {
@@ -380,7 +403,9 @@ TEST_F(TextureLayerWithMailboxTest, ReplaceMailboxOnMainThreadBeforeCommit) {
EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(0);
EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AtLeast(1));
- test_layer->SetTextureMailbox(test_data_.mailbox1_);
+ test_layer->SetTextureMailbox(
+ test_data_.mailbox1_,
+ SingleReleaseCallback::Create(test_data_.release_mailbox1_));
Mock::VerifyAndClearExpectations(layer_tree_host_.get());
EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(0);
@@ -390,7 +415,9 @@ TEST_F(TextureLayerWithMailboxTest, ReplaceMailboxOnMainThreadBeforeCommit) {
test_data_.sync_point1_,
false))
.Times(1);
- test_layer->SetTextureMailbox(test_data_.mailbox2_);
+ test_layer->SetTextureMailbox(
+ test_data_.mailbox2_,
+ SingleReleaseCallback::Create(test_data_.release_mailbox2_));
Mock::VerifyAndClearExpectations(layer_tree_host_.get());
Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);
@@ -401,11 +428,16 @@ TEST_F(TextureLayerWithMailboxTest, ReplaceMailboxOnMainThreadBeforeCommit) {
test_data_.sync_point2_,
false))
.Times(1);
- test_layer->SetTextureMailbox(TextureMailbox());
+ test_layer->SetTextureMailbox(TextureMailbox(),
+ scoped_ptr<SingleReleaseCallback>());
Mock::VerifyAndClearExpectations(layer_tree_host_.get());
Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);
- test_layer->SetTextureMailbox(test_data_.mailbox3_);
+ EXPECT_CALL(*layer_tree_host_, AcquireLayerTextures()).Times(0);
+ EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AtLeast(1));
+ test_layer->SetTextureMailbox(
+ test_data_.mailbox3_,
+ SingleReleaseCallback::Create(test_data_.release_mailbox3_));
Mock::VerifyAndClearExpectations(layer_tree_host_.get());
Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);
@@ -415,13 +447,309 @@ TEST_F(TextureLayerWithMailboxTest, ReplaceMailboxOnMainThreadBeforeCommit) {
Release2(test_data_.shared_memory_.get(),
0, false))
.Times(1);
- test_layer->SetTextureMailbox(TextureMailbox());
+ test_layer->SetTextureMailbox(TextureMailbox(),
+ scoped_ptr<SingleReleaseCallback>());
Mock::VerifyAndClearExpectations(layer_tree_host_.get());
Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);
// Test destructor.
EXPECT_CALL(*layer_tree_host_, SetNeedsCommit()).Times(AtLeast(1));
- test_layer->SetTextureMailbox(test_data_.mailbox1_);
+ test_layer->SetTextureMailbox(
+ test_data_.mailbox1_,
+ SingleReleaseCallback::Create(test_data_.release_mailbox1_));
+}
+
+class TextureLayerMailboxHolderTest : public TextureLayerTest {
+ public:
+ TextureLayerMailboxHolderTest()
+ : main_thread_("MAIN") {
+ main_thread_.Start();
+ }
+
+ void Wait(const base::Thread& thread) {
+ bool manual_reset = false;
+ bool initially_signaled = false;
+ base::WaitableEvent event(manual_reset, initially_signaled);
+ thread.message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&base::WaitableEvent::Signal, base::Unretained(&event)));
+ event.Wait();
+ }
+
+ void CreateMainRef() {
+ main_ref_ = TestMailboxHolder::Create(
+ test_data_.mailbox1_,
+ SingleReleaseCallback::Create(test_data_.release_mailbox1_)).Pass();
+ }
+
+ void ReleaseMainRef() {
+ main_ref_.reset();
+ }
+
+ void CreateImplRef(scoped_ptr<SingleReleaseCallback>* impl_ref) {
+ *impl_ref = main_ref_->holder()->GetCallbackForImplThread();
+ }
+
+ void CapturePostTasksAndWait(base::WaitableEvent* begin_capture,
+ base::WaitableEvent* wait_for_capture,
+ base::WaitableEvent* stop_capture) {
+ begin_capture->Wait();
+ BlockingTaskRunner::CapturePostTasks capture;
+ wait_for_capture->Signal();
+ stop_capture->Wait();
+ }
+
+ protected:
+ scoped_ptr<TestMailboxHolder::MainThreadReference>
+ main_ref_;
+ base::Thread main_thread_;
+ CommonMailboxObjects test_data_;
+};
+
+TEST_F(TextureLayerMailboxHolderTest, TwoCompositors_BothReleaseThenMain) {
+ scoped_refptr<TextureLayer> test_layer = TextureLayer::CreateForMailbox(NULL);
+ ASSERT_TRUE(test_layer.get());
+
+ main_thread_.message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&TextureLayerMailboxHolderTest::CreateMainRef,
+ base::Unretained(this)));
+
+ Wait(main_thread_);
+
+ // The texture layer is attached to compositor1, and passes a reference to its
+ // impl tree.
+ scoped_ptr<SingleReleaseCallback> compositor1;
+ main_thread_.message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef,
+ base::Unretained(this),
+ &compositor1));
+
+ // Then the texture layer is removed and attached to compositor2, and passes a
+ // reference to its impl tree.
+ scoped_ptr<SingleReleaseCallback> compositor2;
+ main_thread_.message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef,
+ base::Unretained(this),
+ &compositor2));
+
+ Wait(main_thread_);
+ Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);
+
+ // The compositors both destroy their impl trees before the main thread layer
+ // is destroyed.
+ compositor1->Run(100, false);
+ compositor2->Run(200, false);
+
+ Wait(main_thread_);
+
+ EXPECT_CALL(test_data_.mock_callback_, Release(_, _, _)).Times(0);
+ Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);
+
+ // The main thread ref is the last one, so the mailbox is released back to the
+ // embedder, with the last sync point provided by the impl trees.
+ EXPECT_CALL(test_data_.mock_callback_,
+ Release(test_data_.mailbox_name1_, 200, false)).Times(1);
+
+ main_thread_.message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&TextureLayerMailboxHolderTest::ReleaseMainRef,
+ base::Unretained(this)));
+ Wait(main_thread_);
+ Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);
+}
+
+TEST_F(TextureLayerMailboxHolderTest, TwoCompositors_MainReleaseBetween) {
+ scoped_refptr<TextureLayer> test_layer = TextureLayer::CreateForMailbox(NULL);
+ ASSERT_TRUE(test_layer.get());
+
+ main_thread_.message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&TextureLayerMailboxHolderTest::CreateMainRef,
+ base::Unretained(this)));
+
+ Wait(main_thread_);
+
+ // The texture layer is attached to compositor1, and passes a reference to its
+ // impl tree.
+ scoped_ptr<SingleReleaseCallback> compositor1;
+ main_thread_.message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef,
+ base::Unretained(this),
+ &compositor1));
+
+ // Then the texture layer is removed and attached to compositor2, and passes a
+ // reference to its impl tree.
+ scoped_ptr<SingleReleaseCallback> compositor2;
+ main_thread_.message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef,
+ base::Unretained(this),
+ &compositor2));
+
+ Wait(main_thread_);
+ Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);
+
+ // One compositor destroys their impl tree.
+ compositor1->Run(100, false);
+
+ // Then the main thread reference is destroyed.
+ main_thread_.message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&TextureLayerMailboxHolderTest::ReleaseMainRef,
+ base::Unretained(this)));
+
+ Wait(main_thread_);
+
+ EXPECT_CALL(test_data_.mock_callback_, Release(_, _, _)).Times(0);
+ Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);
+
+ // The second impl reference is destroyed last, causing the mailbox to be
+ // released back to the embedder with the last sync point from the impl tree.
+ EXPECT_CALL(test_data_.mock_callback_,
+ Release(test_data_.mailbox_name1_, 200, true)).Times(1);
+
+ compositor2->Run(200, true);
+ Wait(main_thread_);
+ Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);
+}
+
+TEST_F(TextureLayerMailboxHolderTest, TwoCompositors_MainReleasedFirst) {
+ scoped_refptr<TextureLayer> test_layer = TextureLayer::CreateForMailbox(NULL);
+ ASSERT_TRUE(test_layer.get());
+
+ main_thread_.message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&TextureLayerMailboxHolderTest::CreateMainRef,
+ base::Unretained(this)));
+
+ Wait(main_thread_);
+
+ // The texture layer is attached to compositor1, and passes a reference to its
+ // impl tree.
+ scoped_ptr<SingleReleaseCallback> compositor1;
+ main_thread_.message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef,
+ base::Unretained(this),
+ &compositor1));
+
+ // Then the texture layer is removed and attached to compositor2, and passes a
+ // reference to its impl tree.
+ scoped_ptr<SingleReleaseCallback> compositor2;
+ main_thread_.message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef,
+ base::Unretained(this),
+ &compositor2));
+
+ Wait(main_thread_);
+ Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);
+
+ // The main thread reference is destroyed first.
+ main_thread_.message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&TextureLayerMailboxHolderTest::ReleaseMainRef,
+ base::Unretained(this)));
+
+ // One compositor destroys their impl tree.
+ compositor2->Run(200, false);
+
+ Wait(main_thread_);
+
+ EXPECT_CALL(test_data_.mock_callback_, Release(_, _, _)).Times(0);
+ Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);
+
+ // The second impl reference is destroyed last, causing the mailbox to be
+ // released back to the embedder with the last sync point from the impl tree.
+ EXPECT_CALL(test_data_.mock_callback_,
+ Release(test_data_.mailbox_name1_, 100, true)).Times(1);
+
+ compositor1->Run(100, true);
+ Wait(main_thread_);
+ Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);
+}
+
+TEST_F(TextureLayerMailboxHolderTest, TwoCompositors_SecondImplRefShortcut) {
+ scoped_refptr<TextureLayer> test_layer = TextureLayer::CreateForMailbox(NULL);
+ ASSERT_TRUE(test_layer.get());
+
+ main_thread_.message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&TextureLayerMailboxHolderTest::CreateMainRef,
+ base::Unretained(this)));
+
+ Wait(main_thread_);
+
+ // The texture layer is attached to compositor1, and passes a reference to its
+ // impl tree.
+ scoped_ptr<SingleReleaseCallback> compositor1;
+ main_thread_.message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef,
+ base::Unretained(this),
+ &compositor1));
+
+ // Then the texture layer is removed and attached to compositor2, and passes a
+ // reference to its impl tree.
+ scoped_ptr<SingleReleaseCallback> compositor2;
+ main_thread_.message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&TextureLayerMailboxHolderTest::CreateImplRef,
+ base::Unretained(this),
+ &compositor2));
+
+ Wait(main_thread_);
+ Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);
+
+ // The main thread reference is destroyed first.
+ main_thread_.message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&TextureLayerMailboxHolderTest::ReleaseMainRef,
+ base::Unretained(this)));
+
+ EXPECT_CALL(test_data_.mock_callback_,
+ Release(test_data_.mailbox_name1_, 200, true)).Times(1);
+
+ bool manual_reset = false;
+ bool initially_signaled = false;
+ base::WaitableEvent begin_capture(manual_reset, initially_signaled);
+ base::WaitableEvent wait_for_capture(manual_reset, initially_signaled);
+ base::WaitableEvent stop_capture(manual_reset, initially_signaled);
+
+ // Post a task to start capturing tasks on the main thread. This will block
+ // the main thread until we signal the |stop_capture| event.
+ main_thread_.message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&TextureLayerMailboxHolderTest::CapturePostTasksAndWait,
+ base::Unretained(this),
+ &begin_capture,
+ &wait_for_capture,
+ &stop_capture));
+
+ // Before the main thread capturing starts, one compositor destroys their
+ // impl reference. Since capturing did not start, this gets post-tasked to
+ // the main thread.
+ compositor1->Run(100, false);
+
+ // Start capturing on the main thread.
+ begin_capture.Signal();
+ wait_for_capture.Wait();
+
+ // Meanwhile, the second compositor released its impl reference, but this task
+ // gets shortcutted directly to the main thread. This means the reference is
+ // released before compositor1, whose reference will be released later when
+ // the post-task is serviced. But since it was destroyed _on the impl thread_
+ // last, its sync point values should be used.
+ compositor2->Run(200, true);
+
+ stop_capture.Signal();
+ Wait(main_thread_);
+
+ Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);
}
class TextureLayerImplWithMailboxThreadedCallback : public LayerTreeTest {
@@ -432,21 +760,24 @@ class TextureLayerImplWithMailboxThreadedCallback : public LayerTreeTest {
// Make sure callback is received on main and doesn't block the impl thread.
void ReleaseCallback(unsigned sync_point, bool lost_resource) {
- EXPECT_EQ(true, proxy()->IsMainThread());
+ EXPECT_EQ(true, main_thread_.CalledOnValidThread());
EXPECT_FALSE(lost_resource);
++callback_count_;
}
void SetMailbox(char mailbox_char) {
- TextureMailbox mailbox(
- std::string(64, mailbox_char),
+ EXPECT_EQ(true, main_thread_.CalledOnValidThread());
+ TextureMailbox mailbox(std::string(64, mailbox_char));
+ scoped_ptr<SingleReleaseCallback> callback = SingleReleaseCallback::Create(
base::Bind(
&TextureLayerImplWithMailboxThreadedCallback::ReleaseCallback,
base::Unretained(this)));
- layer_->SetTextureMailbox(mailbox);
+ layer_->SetTextureMailbox(mailbox, callback.Pass());
}
virtual void BeginTest() OVERRIDE {
+ EXPECT_EQ(true, main_thread_.CalledOnValidThread());
+
gfx::Size bounds(100, 100);
root_ = Layer::Create();
root_->SetAnchorPoint(gfx::PointF());
@@ -481,61 +812,58 @@ class TextureLayerImplWithMailboxThreadedCallback : public LayerTreeTest {
EXPECT_EQ(1, callback_count_);
break;
case 2:
- // Old mailbox was released, task was posted, but won't execute
- // until this DidCommit returns.
- // TODO(piman): fix this.
- EXPECT_EQ(1, callback_count_);
- layer_tree_host()->SetNeedsCommit();
- break;
- case 3:
EXPECT_EQ(2, callback_count_);
// Case #3: change mailbox when the layer doesn't draw. The old
// mailbox should be released during the next commit.
layer_->SetBounds(gfx::Size());
SetMailbox('4');
break;
- case 4:
- // Old mailbox was released, task was posted, but won't execute
- // until this DidCommit returns.
- // TODO(piman): fix this.
- EXPECT_EQ(2, callback_count_);
- layer_tree_host()->SetNeedsCommit();
- break;
- case 5:
+ case 3:
EXPECT_EQ(3, callback_count_);
// Case #4: release mailbox that was committed but never drawn. The
// old mailbox should be released during the next commit.
- layer_->SetTextureMailbox(TextureMailbox());
- break;
- case 6:
- // Old mailbox was released, task was posted, but won't execute
- // until this DidCommit returns.
- // TODO(piman): fix this.
- EXPECT_EQ(3, callback_count_);
- layer_tree_host()->SetNeedsCommit();
+ layer_->SetTextureMailbox(TextureMailbox(),
+ scoped_ptr<SingleReleaseCallback>());
break;
- case 7:
+ case 4:
+ if (layer_tree_host()->settings().impl_side_painting) {
+ // With impl painting, the texture mailbox will still be on the impl
+ // thread when the commit finishes, because the layer is not drawble
+ // when it has no texture mailbox, and thus does not block the commit
+ // on activation. So, we wait for activation.
+ // TODO(danakj): fix this. crbug.com/277953
+ layer_tree_host()->SetNeedsCommit();
+ break;
+ } else {
+ ++commit_count_;
+ }
+ case 5:
EXPECT_EQ(4, callback_count_);
// Restore a mailbox for the next step.
SetMailbox('5');
break;
- case 8:
+ case 6:
// Case #5: remove layer from tree. Callback should *not* be called, the
// mailbox is returned to the main thread.
EXPECT_EQ(4, callback_count_);
layer_->RemoveFromParent();
break;
- case 9:
- // Mailbox was released to the main thread, task was posted, but won't
- // execute until this DidCommit returns.
- // TODO(piman): fix this.
- EXPECT_EQ(4, callback_count_);
- layer_tree_host()->SetNeedsCommit();
- break;
- case 10:
+ case 7:
+ if (layer_tree_host()->settings().impl_side_painting) {
+ // With impl painting, the texture mailbox will still be on the impl
+ // thread when the commit finishes, because the layer is not around to
+ // block the commit on activation anymore. So, we wait for activation.
+ // TODO(danakj): fix this. crbug.com/277953
+ layer_tree_host()->SetNeedsCommit();
+ break;
+ } else {
+ ++commit_count_;
+ }
+ case 8:
EXPECT_EQ(4, callback_count_);
// Resetting the mailbox will call the callback now.
- layer_->SetTextureMailbox(TextureMailbox());
+ layer_->SetTextureMailbox(TextureMailbox(),
+ scoped_ptr<SingleReleaseCallback>());
EXPECT_EQ(5, callback_count_);
EndTest();
break;
@@ -548,6 +876,7 @@ class TextureLayerImplWithMailboxThreadedCallback : public LayerTreeTest {
virtual void AfterTest() OVERRIDE {}
private:
+ base::ThreadChecker main_thread_;
int callback_count_;
int commit_count_;
scoped_refptr<Layer> root_;
@@ -557,6 +886,247 @@ class TextureLayerImplWithMailboxThreadedCallback : public LayerTreeTest {
SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F(
TextureLayerImplWithMailboxThreadedCallback);
+
+class TextureLayerNoMailboxIsActivatedDuringCommit : public LayerTreeTest,
+ public TextureLayerClient {
+ protected:
+ TextureLayerNoMailboxIsActivatedDuringCommit()
+ : wait_thread_("WAIT"),
+ wait_event_(false, false) {
+ wait_thread_.Start();
+ }
+
+ virtual void BeginTest() OVERRIDE {
+ activate_count_ = 0;
+
+ gfx::Size bounds(100, 100);
+ root_ = Layer::Create();
+ root_->SetAnchorPoint(gfx::PointF());
+ root_->SetBounds(bounds);
+
+ layer_ = TextureLayer::Create(this);
+ layer_->SetIsDrawable(true);
+ layer_->SetAnchorPoint(gfx::PointF());
+ layer_->SetBounds(bounds);
+
+ root_->AddChild(layer_);
+ layer_tree_host()->SetRootLayer(root_);
+ layer_tree_host()->SetViewportSize(bounds);
+
+ PostSetNeedsCommitToMainThread();
+ }
+
+ // TextureLayerClient implementation.
+ virtual unsigned PrepareTexture() OVERRIDE {
+ return OffscreenContextProviderForMainThread()
+ ->Context3d()->createTexture();
+ }
+ virtual WebKit::WebGraphicsContext3D* Context3d() OVERRIDE {
+ return OffscreenContextProviderForMainThread()->Context3d();
+ }
+ virtual bool PrepareTextureMailbox(
+ TextureMailbox* mailbox,
+ scoped_ptr<SingleReleaseCallback>* release_callback,
+ bool use_shared_memory) OVERRIDE {
+ return false;
+ }
+
+ virtual void WillActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE {
+ // Slow down activation so the main thread DidCommit() will run if
+ // not blocked.
+ wait_thread_.message_loop()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&base::WaitableEvent::Signal,
+ base::Unretained(&wait_event_)),
+ base::TimeDelta::FromMilliseconds(10));
+ wait_event_.Wait();
+
+ base::AutoLock lock(activate_lock_);
+ ++activate_count_;
+ }
+
+ virtual void DidActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE {
+ // The main thread is awake now, and will run DidCommit() immediately.
+ // Run DidActivate() afterwards by posting it now.
+ proxy()->MainThreadTaskRunner()->PostTask(
+ FROM_HERE,
+ base::Bind(&TextureLayerNoMailboxIsActivatedDuringCommit::DidActivate,
+ base::Unretained(this)));
+ }
+
+ void DidActivate() {
+ base::AutoLock lock(activate_lock_);
+ switch (activate_count_) {
+ case 1:
+ // The first texture has been activated. Invalidate the layer so it
+ // grabs a new texture id from the client.
+ layer_->SetNeedsDisplay();
+ // So this commit number should complete after the second activate.
+ EXPECT_EQ(1, layer_tree_host()->source_frame_number());
+ break;
+ case 2:
+ // The second mailbox has been activated. Remove the layer from
+ // the tree to cause another commit/activation. The commit should
+ // finish *after* the layer is removed from the active tree.
+ layer_->RemoveFromParent();
+ // So this commit number should complete after the third activate.
+ EXPECT_EQ(2, layer_tree_host()->source_frame_number());
+ break;
+ case 3:
+ EndTest();
+ break;
+ }
+ }
+
+ virtual void DidCommit() OVERRIDE {
+ switch (layer_tree_host()->source_frame_number()) {
+ case 2: {
+ // The activate for the 2nd texture should have happened before now.
+ base::AutoLock lock(activate_lock_);
+ EXPECT_EQ(2, activate_count_);
+ break;
+ }
+ case 3: {
+ // The activate to remove the layer should have happened before now.
+ base::AutoLock lock(activate_lock_);
+ EXPECT_EQ(3, activate_count_);
+ break;
+ }
+ }
+ }
+
+
+ virtual void AfterTest() OVERRIDE {}
+
+ base::Thread wait_thread_;
+ base::WaitableEvent wait_event_;
+ base::Lock activate_lock_;
+ int activate_count_;
+ int activate_commit_;
+ scoped_refptr<Layer> root_;
+ scoped_refptr<TextureLayer> layer_;
+};
+
+SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F(
+ TextureLayerNoMailboxIsActivatedDuringCommit);
+
+class TextureLayerMailboxIsActivatedDuringCommit : public LayerTreeTest {
+ protected:
+ TextureLayerMailboxIsActivatedDuringCommit()
+ : wait_thread_("WAIT"),
+ wait_event_(false, false) {
+ wait_thread_.Start();
+ }
+
+ static void ReleaseCallback(unsigned sync_point, bool lost_resource) {}
+
+ void SetMailbox(char mailbox_char) {
+ TextureMailbox mailbox(std::string(64, mailbox_char));
+ scoped_ptr<SingleReleaseCallback> callback = SingleReleaseCallback::Create(
+ base::Bind(
+ &TextureLayerMailboxIsActivatedDuringCommit::ReleaseCallback));
+ layer_->SetTextureMailbox(mailbox, callback.Pass());
+ }
+
+ virtual void BeginTest() OVERRIDE {
+ activate_count_ = 0;
+
+ gfx::Size bounds(100, 100);
+ root_ = Layer::Create();
+ root_->SetAnchorPoint(gfx::PointF());
+ root_->SetBounds(bounds);
+
+ layer_ = TextureLayer::CreateForMailbox(NULL);
+ layer_->SetIsDrawable(true);
+ layer_->SetAnchorPoint(gfx::PointF());
+ layer_->SetBounds(bounds);
+
+ root_->AddChild(layer_);
+ layer_tree_host()->SetRootLayer(root_);
+ layer_tree_host()->SetViewportSize(bounds);
+ SetMailbox('1');
+
+ PostSetNeedsCommitToMainThread();
+ }
+
+ virtual void WillActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE {
+ // Slow down activation so the main thread DidCommit() will run if
+ // not blocked.
+ wait_thread_.message_loop()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&base::WaitableEvent::Signal,
+ base::Unretained(&wait_event_)),
+ base::TimeDelta::FromMilliseconds(10));
+ wait_event_.Wait();
+
+ base::AutoLock lock(activate_lock_);
+ ++activate_count_;
+ }
+
+ virtual void DidActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE {
+ // The main thread is awake now, and will run DidCommit() immediately.
+ // Run DidActivate() afterwards by posting it now.
+ proxy()->MainThreadTaskRunner()->PostTask(
+ FROM_HERE,
+ base::Bind(&TextureLayerMailboxIsActivatedDuringCommit::DidActivate,
+ base::Unretained(this)));
+ }
+
+ void DidActivate() {
+ base::AutoLock lock(activate_lock_);
+ switch (activate_count_) {
+ case 1:
+ // The first mailbox has been activated. Set a new mailbox, and
+ // expect the next commit to finish *after* it is activated.
+ SetMailbox('2');
+ // So this commit number should complete after the second activate.
+ EXPECT_EQ(1, layer_tree_host()->source_frame_number());
+ break;
+ case 2:
+ // The second mailbox has been activated. Remove the layer from
+ // the tree to cause another commit/activation. The commit should
+ // finish *after* the layer is removed from the active tree.
+ layer_->RemoveFromParent();
+ // So this commit number should complete after the third activate.
+ EXPECT_EQ(2, layer_tree_host()->source_frame_number());
+ break;
+ case 3:
+ EndTest();
+ break;
+ }
+ }
+
+ virtual void DidCommit() OVERRIDE {
+ switch (layer_tree_host()->source_frame_number()) {
+ case 2: {
+ // The activate for the 2nd mailbox should have happened before now.
+ base::AutoLock lock(activate_lock_);
+ EXPECT_EQ(2, activate_count_);
+ break;
+ }
+ case 3: {
+ // The activate to remove the layer should have happened before now.
+ base::AutoLock lock(activate_lock_);
+ EXPECT_EQ(3, activate_count_);
+ break;
+ }
+ }
+ }
+
+
+ virtual void AfterTest() OVERRIDE {}
+
+ base::Thread wait_thread_;
+ base::WaitableEvent wait_event_;
+ base::Lock activate_lock_;
+ int activate_count_;
+ scoped_refptr<Layer> root_;
+ scoped_refptr<TextureLayer> layer_;
+};
+
+SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F(
+ TextureLayerMailboxIsActivatedDuringCommit);
+
class TextureLayerImplWithMailboxTest : public TextureLayerTest {
protected:
TextureLayerImplWithMailboxTest()
@@ -596,14 +1166,17 @@ TEST_F(TextureLayerImplWithMailboxTest, TestWillDraw) {
{
scoped_ptr<TextureLayerImpl> impl_layer =
TextureLayerImpl::Create(host_impl_.active_tree(), 1, true);
- impl_layer->SetTextureMailbox(test_data_.mailbox1_);
+ impl_layer->SetTextureMailbox(
+ test_data_.mailbox1_,
+ SingleReleaseCallback::Create(test_data_.release_mailbox1_));
EXPECT_TRUE(WillDraw(impl_layer.get(), DRAW_MODE_HARDWARE));
}
{
scoped_ptr<TextureLayerImpl> impl_layer =
TextureLayerImpl::Create(host_impl_.active_tree(), 1, true);
- impl_layer->SetTextureMailbox(TextureMailbox());
+ impl_layer->SetTextureMailbox(TextureMailbox(),
+ scoped_ptr<SingleReleaseCallback>());
EXPECT_FALSE(WillDraw(impl_layer.get(), DRAW_MODE_HARDWARE));
}
@@ -611,15 +1184,19 @@ TEST_F(TextureLayerImplWithMailboxTest, TestWillDraw) {
// Software resource.
scoped_ptr<TextureLayerImpl> impl_layer =
TextureLayerImpl::Create(host_impl_.active_tree(), 1, true);
- impl_layer->SetTextureMailbox(test_data_.mailbox3_);
+ impl_layer->SetTextureMailbox(
+ test_data_.mailbox3_,
+ SingleReleaseCallback::Create(test_data_.release_mailbox3_));
EXPECT_TRUE(WillDraw(impl_layer.get(), DRAW_MODE_HARDWARE));
}
{
scoped_ptr<TextureLayerImpl> impl_layer =
TextureLayerImpl::Create(host_impl_.active_tree(), 1, false);
+ ContextProvider* context_provider =
+ host_impl_.output_surface()->context_provider();
unsigned texture =
- host_impl_.output_surface()->context3d()->createTexture();
+ context_provider->Context3d()->createTexture();
impl_layer->set_texture_id(texture);
EXPECT_TRUE(WillDraw(impl_layer.get(), DRAW_MODE_HARDWARE));
}
@@ -635,14 +1212,17 @@ TEST_F(TextureLayerImplWithMailboxTest, TestWillDraw) {
{
scoped_ptr<TextureLayerImpl> impl_layer =
TextureLayerImpl::Create(host_impl_.active_tree(), 1, true);
- impl_layer->SetTextureMailbox(test_data_.mailbox1_);
+ impl_layer->SetTextureMailbox(
+ test_data_.mailbox1_,
+ SingleReleaseCallback::Create(test_data_.release_mailbox1_));
EXPECT_FALSE(WillDraw(impl_layer.get(), DRAW_MODE_SOFTWARE));
}
{
scoped_ptr<TextureLayerImpl> impl_layer =
TextureLayerImpl::Create(host_impl_.active_tree(), 1, true);
- impl_layer->SetTextureMailbox(TextureMailbox());
+ impl_layer->SetTextureMailbox(TextureMailbox(),
+ scoped_ptr<SingleReleaseCallback>());
EXPECT_FALSE(WillDraw(impl_layer.get(), DRAW_MODE_SOFTWARE));
}
@@ -650,15 +1230,19 @@ TEST_F(TextureLayerImplWithMailboxTest, TestWillDraw) {
// Software resource.
scoped_ptr<TextureLayerImpl> impl_layer =
TextureLayerImpl::Create(host_impl_.active_tree(), 1, true);
- impl_layer->SetTextureMailbox(test_data_.mailbox3_);
+ impl_layer->SetTextureMailbox(
+ test_data_.mailbox3_,
+ SingleReleaseCallback::Create(test_data_.release_mailbox3_));
EXPECT_TRUE(WillDraw(impl_layer.get(), DRAW_MODE_SOFTWARE));
}
{
scoped_ptr<TextureLayerImpl> impl_layer =
TextureLayerImpl::Create(host_impl_.active_tree(), 1, false);
+ ContextProvider* context_provider =
+ host_impl_.output_surface()->context_provider();
unsigned texture =
- host_impl_.output_surface()->context3d()->createTexture();
+ context_provider->Context3d()->createTexture();
impl_layer->set_texture_id(texture);
EXPECT_FALSE(WillDraw(impl_layer.get(), DRAW_MODE_SOFTWARE));
}
@@ -674,15 +1258,19 @@ TEST_F(TextureLayerImplWithMailboxTest, TestWillDraw) {
{
scoped_ptr<TextureLayerImpl> impl_layer =
TextureLayerImpl::Create(host_impl_.active_tree(), 1, true);
- impl_layer->SetTextureMailbox(test_data_.mailbox1_);
+ impl_layer->SetTextureMailbox(
+ test_data_.mailbox1_,
+ SingleReleaseCallback::Create(test_data_.release_mailbox1_));
EXPECT_FALSE(WillDraw(impl_layer.get(), DRAW_MODE_RESOURCELESS_SOFTWARE));
}
{
scoped_ptr<TextureLayerImpl> impl_layer =
TextureLayerImpl::Create(host_impl_.active_tree(), 1, false);
+ ContextProvider* context_provider =
+ host_impl_.output_surface()->context_provider();
unsigned texture =
- host_impl_.output_surface()->context3d()->createTexture();
+ context_provider->Context3d()->createTexture();
impl_layer->set_texture_id(texture);
EXPECT_FALSE(WillDraw(impl_layer.get(), DRAW_MODE_RESOURCELESS_SOFTWARE));
}
@@ -698,7 +1286,9 @@ TEST_F(TextureLayerImplWithMailboxTest, TestImplLayerCallbacks) {
pending_layer->CreateLayerImpl(host_impl_.active_tree()));
ASSERT_TRUE(active_layer);
- pending_layer->SetTextureMailbox(test_data_.mailbox1_);
+ pending_layer->SetTextureMailbox(
+ test_data_.mailbox1_,
+ SingleReleaseCallback::Create(test_data_.release_mailbox1_));
// Test multiple commits without an activation.
EXPECT_CALL(test_data_.mock_callback_,
@@ -706,7 +1296,9 @@ TEST_F(TextureLayerImplWithMailboxTest, TestImplLayerCallbacks) {
test_data_.sync_point1_,
false))
.Times(1);
- pending_layer->SetTextureMailbox(test_data_.mailbox2_);
+ pending_layer->SetTextureMailbox(
+ test_data_.mailbox2_,
+ SingleReleaseCallback::Create(test_data_.release_mailbox2_));
Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);
// Test callback after activation.
@@ -714,7 +1306,9 @@ TEST_F(TextureLayerImplWithMailboxTest, TestImplLayerCallbacks) {
active_layer->DidBecomeActive();
EXPECT_CALL(test_data_.mock_callback_, Release(_, _, _)).Times(0);
- pending_layer->SetTextureMailbox(test_data_.mailbox1_);
+ pending_layer->SetTextureMailbox(
+ test_data_.mailbox1_,
+ SingleReleaseCallback::Create(test_data_.release_mailbox1_));
Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);
EXPECT_CALL(test_data_.mock_callback_,
@@ -728,7 +1322,8 @@ TEST_F(TextureLayerImplWithMailboxTest, TestImplLayerCallbacks) {
EXPECT_CALL(test_data_.mock_callback_,
Release(test_data_.mailbox_name1_, _, false))
.Times(1);
- pending_layer->SetTextureMailbox(TextureMailbox());
+ pending_layer->SetTextureMailbox(TextureMailbox(),
+ scoped_ptr<SingleReleaseCallback>());
pending_layer->PushPropertiesTo(active_layer.get());
active_layer->DidBecomeActive();
Mock::VerifyAndClearExpectations(&test_data_.mock_callback_);
@@ -739,7 +1334,9 @@ TEST_F(TextureLayerImplWithMailboxTest, TestImplLayerCallbacks) {
test_data_.sync_point1_,
false))
.Times(1);
- pending_layer->SetTextureMailbox(test_data_.mailbox1_);
+ pending_layer->SetTextureMailbox(
+ test_data_.mailbox1_,
+ SingleReleaseCallback::Create(test_data_.release_mailbox1_));
}
TEST_F(TextureLayerImplWithMailboxTest,
@@ -751,18 +1348,23 @@ TEST_F(TextureLayerImplWithMailboxTest,
EXPECT_CALL(test_data_.mock_callback_,
Release(test_data_.mailbox_name1_, _, false))
.Times(1);
- impl_layer->SetTextureMailbox(test_data_.mailbox1_);
+ impl_layer->SetTextureMailbox(
+ test_data_.mailbox1_,
+ SingleReleaseCallback::Create(test_data_.release_mailbox1_));
impl_layer->DidBecomeActive();
EXPECT_TRUE(impl_layer->WillDraw(
DRAW_MODE_HARDWARE, host_impl_.active_tree()->resource_provider()));
impl_layer->DidDraw(host_impl_.active_tree()->resource_provider());
- impl_layer->SetTextureMailbox(TextureMailbox());
+ impl_layer->SetTextureMailbox(TextureMailbox(),
+ scoped_ptr<SingleReleaseCallback>());
}
TEST_F(TextureLayerImplWithMailboxTest, TestCallbackOnInUseResource) {
ResourceProvider* provider = host_impl_.active_tree()->resource_provider();
ResourceProvider::ResourceId id =
- provider->CreateResourceFromTextureMailbox(test_data_.mailbox1_);
+ provider->CreateResourceFromTextureMailbox(
+ test_data_.mailbox1_,
+ SingleReleaseCallback::Create(test_data_.release_mailbox1_));
provider->AllocateForTesting(id);
// Transfer some resources to the parent.
@@ -777,7 +1379,9 @@ TEST_F(TextureLayerImplWithMailboxTest, TestCallbackOnInUseResource) {
EXPECT_CALL(test_data_.mock_callback_,
Release(test_data_.mailbox_name1_, _, false))
.Times(1);
- provider->ReceiveFromParent(list);
+ ReturnedResourceArray returned;
+ TransferableResource::ReturnResources(list, &returned);
+ provider->ReceiveReturnsFromParent(returned);
}
// Check that ClearClient correctly clears the state so that the impl side
@@ -799,8 +1403,7 @@ class TextureLayerClientTest
TestWebGraphicsContext3D::Create());
context_ = context.get();
texture_ = context->createTexture();
- return FakeOutputSurface::Create3d(
- context.PassAs<WebKit::WebGraphicsContext3D>()).PassAs<OutputSurface>();
+ return FakeOutputSurface::Create3d(context.Pass()).PassAs<OutputSurface>();
}
virtual unsigned PrepareTexture() OVERRIDE {
@@ -812,7 +1415,9 @@ class TextureLayerClientTest
}
virtual bool PrepareTextureMailbox(
- cc::TextureMailbox* mailbox, bool use_shared_memory) OVERRIDE {
+ TextureMailbox* mailbox,
+ scoped_ptr<SingleReleaseCallback>* release_callback,
+ bool use_shared_memory) OVERRIDE {
return false;
}
@@ -899,6 +1504,343 @@ class TextureLayerClientTest
// renderer.
SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F(TextureLayerClientTest);
+
+// Checks that changing a texture in the client for a TextureLayer that's
+// invisible correctly works without drawing a deleted texture. See
+// crbug.com/266628
+class TextureLayerChangeInvisibleTest
+ : public LayerTreeTest,
+ public TextureLayerClient {
+ public:
+ TextureLayerChangeInvisibleTest()
+ : client_context_(TestWebGraphicsContext3D::Create()),
+ texture_(client_context_->createTexture()),
+ texture_to_delete_on_next_commit_(0),
+ prepare_called_(0),
+ commit_count_(0),
+ expected_texture_on_draw_(0) {}
+
+ // TextureLayerClient implementation.
+ virtual unsigned PrepareTexture() OVERRIDE {
+ ++prepare_called_;
+ return texture_;
+ }
+
+ // TextureLayerClient implementation.
+ virtual WebKit::WebGraphicsContext3D* Context3d() OVERRIDE {
+ return client_context_.get();
+ }
+
+ // TextureLayerClient implementation.
+ virtual bool PrepareTextureMailbox(
+ cc::TextureMailbox* mailbox,
+ scoped_ptr<SingleReleaseCallback>* release_callback,
+ bool use_shared_memory) OVERRIDE {
+ return false;
+ }
+
+ virtual void SetupTree() OVERRIDE {
+ scoped_refptr<Layer> root = Layer::Create();
+ root->SetBounds(gfx::Size(10, 10));
+ root->SetAnchorPoint(gfx::PointF());
+ root->SetIsDrawable(true);
+
+ solid_layer_ = SolidColorLayer::Create();
+ solid_layer_->SetBounds(gfx::Size(10, 10));
+ solid_layer_->SetIsDrawable(true);
+ solid_layer_->SetBackgroundColor(SK_ColorWHITE);
+ root->AddChild(solid_layer_);
+
+ parent_layer_ = Layer::Create();
+ parent_layer_->SetBounds(gfx::Size(10, 10));
+ parent_layer_->SetIsDrawable(true);
+ root->AddChild(parent_layer_);
+
+ texture_layer_ = TextureLayer::Create(this);
+ texture_layer_->SetBounds(gfx::Size(10, 10));
+ texture_layer_->SetAnchorPoint(gfx::PointF());
+ texture_layer_->SetIsDrawable(true);
+ parent_layer_->AddChild(texture_layer_);
+
+ layer_tree_host()->SetRootLayer(root);
+ LayerTreeTest::SetupTree();
+ }
+
+ virtual void BeginTest() OVERRIDE {
+ PostSetNeedsCommitToMainThread();
+ }
+
+ virtual void DidCommitAndDrawFrame() OVERRIDE {
+ ++commit_count_;
+ switch (commit_count_) {
+ case 1:
+ // We should have updated the layer, committing the texture.
+ EXPECT_EQ(1, prepare_called_);
+ // Make layer invisible.
+ parent_layer_->SetOpacity(0.f);
+ break;
+ case 2: {
+ // Layer shouldn't have been updated.
+ EXPECT_EQ(1, prepare_called_);
+ // Change the texture.
+ texture_to_delete_on_next_commit_ = texture_;
+ texture_ = client_context_->createTexture();
+ texture_layer_->SetNeedsDisplay();
+ // Force a change to make sure we draw a frame.
+ solid_layer_->SetBackgroundColor(SK_ColorGRAY);
+ break;
+ }
+ case 3:
+ EXPECT_EQ(1, prepare_called_);
+ client_context_->deleteTexture(texture_to_delete_on_next_commit_);
+ texture_to_delete_on_next_commit_ = 0;
+ // Make layer visible again.
+ parent_layer_->SetOpacity(1.f);
+ break;
+ case 4: {
+ // Layer should have been updated.
+ EXPECT_EQ(2, prepare_called_);
+ texture_layer_->ClearClient();
+ client_context_->deleteTexture(texture_);
+ texture_ = 0;
+ break;
+ }
+ case 5:
+ EndTest();
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+ }
+
+ virtual void BeginCommitOnThread(LayerTreeHostImpl* host_impl) OVERRIDE {
+ ASSERT_TRUE(proxy()->IsMainThreadBlocked());
+ // This is the only texture that can be drawn this frame.
+ expected_texture_on_draw_ = texture_;
+ }
+
+ virtual bool PrepareToDrawOnThread(LayerTreeHostImpl* host_impl,
+ LayerTreeHostImpl::FrameData* frame_data,
+ bool result) OVERRIDE {
+ ContextForImplThread(host_impl)->ResetUsedTextures();
+ return true;
+ }
+
+ virtual void SwapBuffersOnThread(LayerTreeHostImpl* host_impl,
+ bool result) OVERRIDE {
+ ASSERT_TRUE(result);
+ TestWebGraphicsContext3D* context = ContextForImplThread(host_impl);
+ int used_textures = context->NumUsedTextures();
+ switch (host_impl->active_tree()->source_frame_number()) {
+ case 0:
+ EXPECT_EQ(1, used_textures);
+ EXPECT_TRUE(context->UsedTexture(expected_texture_on_draw_));
+ break;
+ case 1:
+ case 2:
+ EXPECT_EQ(0, used_textures);
+ break;
+ case 3:
+ EXPECT_EQ(1, used_textures);
+ EXPECT_TRUE(context->UsedTexture(expected_texture_on_draw_));
+ break;
+ default:
+ break;
+ }
+ }
+
+ virtual void AfterTest() OVERRIDE {}
+
+ private:
+ TestWebGraphicsContext3D* ContextForImplThread(LayerTreeHostImpl* host_impl) {
+ return static_cast<TestWebGraphicsContext3D*>(
+ host_impl->output_surface()->context_provider()->Context3d());
+ }
+
+ scoped_refptr<SolidColorLayer> solid_layer_;
+ scoped_refptr<Layer> parent_layer_;
+ scoped_refptr<TextureLayer> texture_layer_;
+ scoped_ptr<TestWebGraphicsContext3D> client_context_;
+
+ // Used on the main thread, and on the impl thread while the main thread is
+ // blocked.
+ unsigned texture_;
+
+ // Used on the main thread.
+ unsigned texture_to_delete_on_next_commit_;
+ int prepare_called_;
+ int commit_count_;
+
+ // Used on the compositor thread.
+ unsigned expected_texture_on_draw_;
+};
+
+// The TextureLayerChangeInvisibleTest does not use mailboxes, so can't use a
+// delegating renderer.
+SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F(TextureLayerChangeInvisibleTest);
+
+// Checks that changing a mailbox in the client for a TextureLayer that's
+// invisible correctly works and uses the new mailbox as soon as the layer
+// becomes visible (and returns the old one).
+class TextureLayerChangeInvisibleMailboxTest
+ : public LayerTreeTest,
+ public TextureLayerClient {
+ public:
+ TextureLayerChangeInvisibleMailboxTest()
+ : mailbox_changed_(true),
+ mailbox_returned_(0),
+ prepare_called_(0),
+ commit_count_(0) {
+ mailbox_ = MakeMailbox('1');
+ }
+
+ // TextureLayerClient implementation.
+ virtual unsigned PrepareTexture() OVERRIDE {
+ NOTREACHED();
+ return 0;
+ }
+
+ // TextureLayerClient implementation.
+ virtual WebKit::WebGraphicsContext3D* Context3d() OVERRIDE {
+ NOTREACHED();
+ return NULL;
+ }
+
+ // TextureLayerClient implementation.
+ virtual bool PrepareTextureMailbox(
+ cc::TextureMailbox* mailbox,
+ scoped_ptr<SingleReleaseCallback>* release_callback,
+ bool use_shared_memory) OVERRIDE {
+ ++prepare_called_;
+ if (!mailbox_changed_)
+ return false;
+ *mailbox = mailbox_;
+ *release_callback = SingleReleaseCallback::Create(
+ base::Bind(&TextureLayerChangeInvisibleMailboxTest::MailboxReleased,
+ base::Unretained(this)));
+ return true;
+ }
+
+ TextureMailbox MakeMailbox(char name) {
+ return TextureMailbox(std::string(64, name));
+ }
+
+ void MailboxReleased(unsigned sync_point, bool lost_resource) {
+ ++mailbox_returned_;
+ }
+
+ virtual void SetupTree() OVERRIDE {
+ scoped_refptr<Layer> root = Layer::Create();
+ root->SetBounds(gfx::Size(10, 10));
+ root->SetAnchorPoint(gfx::PointF());
+ root->SetIsDrawable(true);
+
+ solid_layer_ = SolidColorLayer::Create();
+ solid_layer_->SetBounds(gfx::Size(10, 10));
+ solid_layer_->SetIsDrawable(true);
+ solid_layer_->SetBackgroundColor(SK_ColorWHITE);
+ root->AddChild(solid_layer_);
+
+ parent_layer_ = Layer::Create();
+ parent_layer_->SetBounds(gfx::Size(10, 10));
+ parent_layer_->SetIsDrawable(true);
+ root->AddChild(parent_layer_);
+
+ texture_layer_ = TextureLayer::CreateForMailbox(this);
+ texture_layer_->SetBounds(gfx::Size(10, 10));
+ texture_layer_->SetAnchorPoint(gfx::PointF());
+ texture_layer_->SetIsDrawable(true);
+ parent_layer_->AddChild(texture_layer_);
+
+ layer_tree_host()->SetRootLayer(root);
+ LayerTreeTest::SetupTree();
+ }
+
+ virtual void BeginTest() OVERRIDE {
+ PostSetNeedsCommitToMainThread();
+ }
+
+ virtual void DidCommitAndDrawFrame() OVERRIDE {
+ ++commit_count_;
+ switch (commit_count_) {
+ case 1:
+ // We should have updated the layer, committing the texture.
+ EXPECT_EQ(1, prepare_called_);
+ // Make layer invisible.
+ parent_layer_->SetOpacity(0.f);
+ break;
+ case 2:
+ // Layer shouldn't have been updated.
+ EXPECT_EQ(1, prepare_called_);
+ // Change the texture.
+ mailbox_ = MakeMailbox('2');
+ mailbox_changed_ = true;
+ texture_layer_->SetNeedsDisplay();
+ // Force a change to make sure we draw a frame.
+ solid_layer_->SetBackgroundColor(SK_ColorGRAY);
+ break;
+ case 3:
+ // Layer shouldn't have been updated.
+ EXPECT_EQ(1, prepare_called_);
+ // So the old mailbox isn't returned yet.
+ EXPECT_EQ(0, mailbox_returned_);
+ // Make layer visible again.
+ parent_layer_->SetOpacity(1.f);
+ break;
+ case 4:
+ // Layer should have been updated.
+ EXPECT_EQ(2, prepare_called_);
+ // So the old mailbox should have been returned already.
+ EXPECT_EQ(1, mailbox_returned_);
+ texture_layer_->ClearClient();
+ break;
+ case 5:
+ EXPECT_EQ(2, mailbox_returned_);
+ EndTest();
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+ }
+
+ virtual void SwapBuffersOnThread(LayerTreeHostImpl* host_impl,
+ bool result) OVERRIDE {
+ ASSERT_TRUE(result);
+ DelegatedFrameData* delegated_frame_data =
+ output_surface()->last_sent_frame().delegated_frame_data.get();
+ if (!delegated_frame_data)
+ return;
+
+ // Return all resources immediately.
+ TransferableResourceArray resources_to_return =
+ output_surface()->resources_held_by_parent();
+
+ CompositorFrameAck ack;
+ for (size_t i = 0; i < resources_to_return.size(); ++i)
+ output_surface()->ReturnResource(resources_to_return[i].id, &ack);
+ host_impl->ReclaimResources(&ack);
+ host_impl->OnSwapBuffersComplete();
+ }
+
+ virtual void AfterTest() OVERRIDE {}
+
+ private:
+ scoped_refptr<SolidColorLayer> solid_layer_;
+ scoped_refptr<Layer> parent_layer_;
+ scoped_refptr<TextureLayer> texture_layer_;
+
+ // Used on the main thread.
+ bool mailbox_changed_;
+ TextureMailbox mailbox_;
+ int mailbox_returned_;
+ int prepare_called_;
+ int commit_count_;
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(TextureLayerChangeInvisibleMailboxTest);
+
// Test recovering from a lost context.
class TextureLayerLostContextTest
: public LayerTreeTest,
@@ -928,7 +1870,9 @@ class TextureLayerLostContextTest
}
virtual bool PrepareTextureMailbox(
- cc::TextureMailbox* mailbox, bool use_shared_memory) OVERRIDE {
+ TextureMailbox* mailbox,
+ scoped_ptr<SingleReleaseCallback>* release_callback,
+ bool use_shared_memory) OVERRIDE {
return false;
}
@@ -981,19 +1925,20 @@ SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F(TextureLayerLostContextTest);
class TextureLayerWithMailboxMainThreadDeleted : public LayerTreeTest {
public:
void ReleaseCallback(unsigned sync_point, bool lost_resource) {
- EXPECT_EQ(true, proxy()->IsMainThread());
+ EXPECT_EQ(true, main_thread_.CalledOnValidThread());
EXPECT_FALSE(lost_resource);
++callback_count_;
EndTest();
}
void SetMailbox(char mailbox_char) {
- TextureMailbox mailbox(
- std::string(64, mailbox_char),
+ EXPECT_EQ(true, main_thread_.CalledOnValidThread());
+ TextureMailbox mailbox(std::string(64, mailbox_char));
+ scoped_ptr<SingleReleaseCallback> callback = SingleReleaseCallback::Create(
base::Bind(
&TextureLayerWithMailboxMainThreadDeleted::ReleaseCallback,
base::Unretained(this)));
- layer_->SetTextureMailbox(mailbox);
+ layer_->SetTextureMailbox(mailbox, callback.Pass());
}
virtual void SetupTree() OVERRIDE {
@@ -1013,6 +1958,8 @@ class TextureLayerWithMailboxMainThreadDeleted : public LayerTreeTest {
}
virtual void BeginTest() OVERRIDE {
+ EXPECT_EQ(true, main_thread_.CalledOnValidThread());
+
callback_count_ = 0;
// Set the mailbox on the main thread.
@@ -1038,6 +1985,7 @@ class TextureLayerWithMailboxMainThreadDeleted : public LayerTreeTest {
}
private:
+ base::ThreadChecker main_thread_;
int callback_count_;
scoped_refptr<Layer> root_;
scoped_refptr<TextureLayer> layer_;
@@ -1049,19 +1997,20 @@ SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F(
class TextureLayerWithMailboxImplThreadDeleted : public LayerTreeTest {
public:
void ReleaseCallback(unsigned sync_point, bool lost_resource) {
- EXPECT_EQ(true, proxy()->IsMainThread());
+ EXPECT_EQ(true, main_thread_.CalledOnValidThread());
EXPECT_FALSE(lost_resource);
++callback_count_;
EndTest();
}
void SetMailbox(char mailbox_char) {
- TextureMailbox mailbox(
- std::string(64, mailbox_char),
+ EXPECT_EQ(true, main_thread_.CalledOnValidThread());
+ TextureMailbox mailbox(std::string(64, mailbox_char));
+ scoped_ptr<SingleReleaseCallback> callback = SingleReleaseCallback::Create(
base::Bind(
&TextureLayerWithMailboxImplThreadDeleted::ReleaseCallback,
base::Unretained(this)));
- layer_->SetTextureMailbox(mailbox);
+ layer_->SetTextureMailbox(mailbox, callback.Pass());
}
virtual void SetupTree() OVERRIDE {
@@ -1081,6 +2030,8 @@ class TextureLayerWithMailboxImplThreadDeleted : public LayerTreeTest {
}
virtual void BeginTest() OVERRIDE {
+ EXPECT_EQ(true, main_thread_.CalledOnValidThread());
+
callback_count_ = 0;
// Set the mailbox on the main thread.
@@ -1109,6 +2060,7 @@ class TextureLayerWithMailboxImplThreadDeleted : public LayerTreeTest {
}
private:
+ base::ThreadChecker main_thread_;
int callback_count_;
scoped_refptr<Layer> root_;
scoped_refptr<TextureLayer> layer_;
diff --git a/chromium/cc/layers/tiled_layer.cc b/chromium/cc/layers/tiled_layer.cc
index bac12f0d422..8bbf2271bc4 100644
--- a/chromium/cc/layers/tiled_layer.cc
+++ b/chromium/cc/layers/tiled_layer.cc
@@ -86,7 +86,7 @@ class UpdatableTile : public LayerTilingData::Tile {
TiledLayer::TiledLayer()
: ContentsScalingLayer(),
- texture_format_(GL_INVALID_ENUM),
+ texture_format_(RGBA_8888),
skips_draw_(false),
failed_update_(false),
tiling_option_(AUTO_TILE) {
@@ -235,9 +235,7 @@ void TiledLayer::PushPropertiesTo(LayerImpl* layer) {
needs_push_properties_ = true;
}
-bool TiledLayer::BlocksPendingCommit() const { return true; }
-
-PrioritizedResourceManager* TiledLayer::ResourceManager() const {
+PrioritizedResourceManager* TiledLayer::ResourceManager() {
if (!layer_tree_host())
return NULL;
return layer_tree_host()->contents_texture_manager();
@@ -731,6 +729,10 @@ bool TiledLayer::Update(ResourceUpdateQueue* queue,
const OcclusionTracker* occlusion) {
DCHECK(!skips_draw_ && !failed_update_); // Did ResetUpdateState get skipped?
+ // Tiled layer always causes commits to wait for activation, as it does
+ // not support pending trees.
+ SetNextCommitWaitsForActivation();
+
bool updated = false;
{
diff --git a/chromium/cc/layers/tiled_layer.h b/chromium/cc/layers/tiled_layer.h
index 8ad7df95e38..3870d69956b 100644
--- a/chromium/cc/layers/tiled_layer.h
+++ b/chromium/cc/layers/tiled_layer.h
@@ -8,6 +8,7 @@
#include "cc/base/cc_export.h"
#include "cc/layers/contents_scaling_layer.h"
#include "cc/resources/layer_tiling_data.h"
+#include "cc/resources/resource_format.h"
namespace cc {
class LayerUpdater;
@@ -26,7 +27,6 @@ class CC_EXPORT TiledLayer : public ContentsScalingLayer {
// Layer implementation.
virtual void SetIsMask(bool is_mask) OVERRIDE;
virtual void PushPropertiesTo(LayerImpl* layer) OVERRIDE;
- virtual bool BlocksPendingCommit() const OVERRIDE;
virtual bool DrawsContent() const OVERRIDE;
virtual void ReduceMemoryUsage() OVERRIDE;
virtual void SetNeedsDisplayRect(const gfx::RectF& dirty_rect) OVERRIDE;
@@ -46,7 +46,7 @@ class CC_EXPORT TiledLayer : public ContentsScalingLayer {
// Exposed to subclasses for testing.
void SetTileSize(gfx::Size size);
- void SetTextureFormat(unsigned texture_format) {
+ void SetTextureFormat(ResourceFormat texture_format) {
texture_format_ = texture_format;
}
void SetBorderTexelOption(LayerTilingData::BorderTexelOption option);
@@ -68,7 +68,7 @@ class CC_EXPORT TiledLayer : public ContentsScalingLayer {
bool SkipsDraw() const { return skips_draw_; }
// Virtual for testing
- virtual PrioritizedResourceManager* ResourceManager() const;
+ virtual PrioritizedResourceManager* ResourceManager();
const LayerTilingData* TilerForTesting() const { return tiler_.get(); }
const PrioritizedResource* ResourceAtForTesting(int i, int j) const;
@@ -121,7 +121,7 @@ class CC_EXPORT TiledLayer : public ContentsScalingLayer {
bool IsSmallAnimatedLayer() const;
- unsigned texture_format_;
+ ResourceFormat texture_format_;
bool skips_draw_;
bool failed_update_;
diff --git a/chromium/cc/layers/tiled_layer_unittest.cc b/chromium/cc/layers/tiled_layer_unittest.cc
index a2f0ca7a64d..b6da0769f95 100644
--- a/chromium/cc/layers/tiled_layer_unittest.cc
+++ b/chromium/cc/layers/tiled_layer_unittest.cc
@@ -16,6 +16,7 @@
#include "cc/test/fake_layer_tree_host_client.h"
#include "cc/test/fake_layer_tree_host_impl.h"
#include "cc/test/fake_output_surface.h"
+#include "cc/test/fake_output_surface_client.h"
#include "cc/test/fake_proxy.h"
#include "cc/test/fake_rendering_stats_instrumentation.h"
#include "cc/test/geometry_test_utils.h"
@@ -47,7 +48,7 @@ class TiledLayerTest : public testing::Test {
public:
TiledLayerTest()
: proxy_(NULL),
- output_surface_(CreateFakeOutputSurface()),
+ output_surface_(FakeOutputSurface::Create3d()),
queue_(make_scoped_ptr(new ResourceUpdateQueue)),
fake_layer_impl_tree_host_client_(FakeLayerTreeHostClient::DIRECT_3D),
occlusion_(NULL) {
@@ -64,9 +65,12 @@ class TiledLayerTest : public testing::Test {
layer_tree_host_->InitializeOutputSurfaceIfNeeded();
layer_tree_host_->SetRootLayer(Layer::Create());
+ CHECK(output_surface_->BindToClient(&output_surface_client_));
+
DebugScopedSetImplThreadAndMainThreadBlocked
- impl_thread_and_main_thread_blocked(proxy_);
- resource_provider_ = ResourceProvider::Create(output_surface_.get(), 0);
+ impl_thread_and_main_thread_blocked(proxy_);
+ resource_provider_ =
+ ResourceProvider::Create(output_surface_.get(), 0, false);
host_impl_ = make_scoped_ptr(new FakeLayerTreeHostImpl(proxy_));
}
@@ -183,6 +187,7 @@ class TiledLayerTest : public testing::Test {
public:
Proxy* proxy_;
LayerTreeSettings settings_;
+ FakeOutputSurfaceClient output_surface_client_;
scoped_ptr<OutputSurface> output_surface_;
scoped_ptr<ResourceProvider> resource_provider_;
scoped_ptr<ResourceUpdateQueue> queue_;
@@ -951,8 +956,10 @@ TEST_F(TiledLayerTest, SkipsDrawGetsReset) {
layer_tree_host_->SetRootLayer(root_layer);
layer_tree_host_->SetViewportSize(gfx::Size(300, 300));
+ layer_tree_host_->contents_texture_manager()->SetMaxMemoryLimitBytes(
+ memory_limit);
- layer_tree_host_->UpdateLayers(queue_.get(), memory_limit);
+ layer_tree_host_->UpdateLayers(queue_.get());
// We'll skip the root layer.
EXPECT_TRUE(root_layer->SkipsDraw());
@@ -963,7 +970,7 @@ TEST_F(TiledLayerTest, SkipsDrawGetsReset) {
// Remove the child layer.
root_layer->RemoveAllChildren();
- layer_tree_host_->UpdateLayers(queue_.get(), memory_limit);
+ layer_tree_host_->UpdateLayers(queue_.get());
EXPECT_FALSE(root_layer->SkipsDraw());
ResourceManagerClearAllMemory(layer_tree_host_->contents_texture_manager(),
@@ -1029,8 +1036,7 @@ TEST_F(TiledLayerPartialUpdateTest, PartialUpdates) {
layer_tree_host_->SetViewportSize(gfx::Size(300, 200));
// Full update of all 6 tiles.
- layer_tree_host_->UpdateLayers(queue_.get(),
- std::numeric_limits<size_t>::max());
+ layer_tree_host_->UpdateLayers(queue_.get());
{
scoped_ptr<FakeTiledLayerImpl> layer_impl =
make_scoped_ptr(new FakeTiledLayerImpl(host_impl_->active_tree(), 1));
@@ -1046,8 +1052,7 @@ TEST_F(TiledLayerPartialUpdateTest, PartialUpdates) {
// Full update of 3 tiles and partial update of 3 tiles.
layer->InvalidateContentRect(gfx::Rect(0, 0, 300, 150));
- layer_tree_host_->UpdateLayers(queue_.get(),
- std::numeric_limits<size_t>::max());
+ layer_tree_host_->UpdateLayers(queue_.get());
{
scoped_ptr<FakeTiledLayerImpl> layer_impl =
make_scoped_ptr(new FakeTiledLayerImpl(host_impl_->active_tree(), 1));
@@ -1066,8 +1071,7 @@ TEST_F(TiledLayerPartialUpdateTest, PartialUpdates) {
{
scoped_ptr<FakeTiledLayerImpl> layer_impl =
make_scoped_ptr(new FakeTiledLayerImpl(host_impl_->active_tree(), 1));
- layer_tree_host_->UpdateLayers(queue_.get(),
- std::numeric_limits<size_t>::max());
+ layer_tree_host_->UpdateLayers(queue_.get());
EXPECT_EQ(2u, queue_->FullUploadSize());
EXPECT_EQ(4u, queue_->PartialUploadSize());
UpdateTextures();
@@ -1092,8 +1096,7 @@ TEST_F(TiledLayerPartialUpdateTest, PartialUpdates) {
{
scoped_ptr<FakeTiledLayerImpl> layer_impl =
make_scoped_ptr(new FakeTiledLayerImpl(host_impl_->active_tree(), 1));
- layer_tree_host_->UpdateLayers(queue_.get(),
- std::numeric_limits<size_t>::max());
+ layer_tree_host_->UpdateLayers(queue_.get());
EXPECT_EQ(6u, queue_->FullUploadSize());
EXPECT_EQ(0u, queue_->PartialUploadSize());
UpdateTextures();
@@ -1109,8 +1112,7 @@ TEST_F(TiledLayerPartialUpdateTest, PartialUpdates) {
{
scoped_ptr<FakeTiledLayerImpl> layer_impl =
make_scoped_ptr(new FakeTiledLayerImpl(host_impl_->active_tree(), 1));
- layer_tree_host_->UpdateLayers(queue_.get(),
- std::numeric_limits<size_t>::max());
+ layer_tree_host_->UpdateLayers(queue_.get());
EXPECT_EQ(0u, queue_->FullUploadSize());
EXPECT_EQ(4u, queue_->PartialUploadSize());
UpdateTextures();
@@ -1708,8 +1710,7 @@ TEST_F(TiledLayerTest, DontAllocateContentsWhenTargetSurfaceCantBeAllocated) {
root->InvalidateContentRect(root_rect);
child->InvalidateContentRect(child_rect);
child2->InvalidateContentRect(child2_rect);
- layer_tree_host_->UpdateLayers(queue_.get(),
- std::numeric_limits<size_t>::max());
+ layer_tree_host_->UpdateLayers(queue_.get());
{
UpdateTextures();
EXPECT_EQ(6, root->fake_layer_updater()->update_count());
@@ -1747,8 +1748,11 @@ TEST_F(TiledLayerTest, DontAllocateContentsWhenTargetSurfaceCantBeAllocated) {
root->InvalidateContentRect(root_rect);
child->InvalidateContentRect(child_rect);
child2->InvalidateContentRect(child2_rect);
- layer_tree_host_->UpdateLayers(queue_.get(),
- (3 * 2 + 3 * 1) * (100 * 100) * 4);
+
+ size_t memory_limit = (3 * 2 + 3 * 1) * (100 * 100) * 4;
+ layer_tree_host_->contents_texture_manager()->SetMaxMemoryLimitBytes(
+ memory_limit);
+ layer_tree_host_->UpdateLayers(queue_.get());
{
UpdateTextures();
EXPECT_EQ(6, root->fake_layer_updater()->update_count());
@@ -1786,7 +1790,11 @@ TEST_F(TiledLayerTest, DontAllocateContentsWhenTargetSurfaceCantBeAllocated) {
root->InvalidateContentRect(root_rect);
child->InvalidateContentRect(child_rect);
child2->InvalidateContentRect(child2_rect);
- layer_tree_host_->UpdateLayers(queue_.get(), (3 * 1) * (100 * 100) * 4);
+
+ memory_limit = (3 * 1) * (100 * 100) * 4;
+ layer_tree_host_->contents_texture_manager()->SetMaxMemoryLimitBytes(
+ memory_limit);
+ layer_tree_host_->UpdateLayers(queue_.get());
{
UpdateTextures();
EXPECT_EQ(0, root->fake_layer_updater()->update_count());
diff --git a/chromium/cc/layers/video_layer_impl.cc b/chromium/cc/layers/video_layer_impl.cc
index 4b5671ff90e..28e470e7c3b 100644
--- a/chromium/cc/layers/video_layer_impl.cc
+++ b/chromium/cc/layers/video_layer_impl.cc
@@ -13,6 +13,7 @@
#include "cc/quads/texture_draw_quad.h"
#include "cc/quads/yuv_video_draw_quad.h"
#include "cc/resources/resource_provider.h"
+#include "cc/resources/single_release_callback.h"
#include "cc/trees/layer_tree_impl.h"
#include "cc/trees/proxy.h"
#include "media/base/video_frame.h"
@@ -93,8 +94,11 @@ bool VideoLayerImpl::WillDraw(DrawMode draw_mode,
if (!LayerImpl::WillDraw(draw_mode, resource_provider))
return false;
- if (!updater_)
- updater_.reset(new VideoResourceUpdater(resource_provider));
+ if (!updater_) {
+ updater_.reset(
+ new VideoResourceUpdater(layer_tree_impl()->context_provider(),
+ layer_tree_impl()->resource_provider()));
+ }
VideoFrameExternalResources external_resources =
updater_->CreateExternalResourcesFromVideoFrame(frame_);
@@ -108,10 +112,13 @@ bool VideoLayerImpl::WillDraw(DrawMode draw_mode,
return true;
}
+ DCHECK_EQ(external_resources.mailboxes.size(),
+ external_resources.release_callbacks.size());
for (size_t i = 0; i < external_resources.mailboxes.size(); ++i) {
- frame_resources_.push_back(
- resource_provider->CreateResourceFromTextureMailbox(
- external_resources.mailboxes[i]));
+ unsigned resource_id = resource_provider->CreateResourceFromTextureMailbox(
+ external_resources.mailboxes[i],
+ SingleReleaseCallback::Create(external_resources.release_callbacks[i]));
+ frame_resources_.push_back(resource_id);
}
return true;
diff --git a/chromium/cc/layers/video_layer_impl.h b/chromium/cc/layers/video_layer_impl.h
index b07187115ed..b4df1417bf1 100644
--- a/chromium/cc/layers/video_layer_impl.h
+++ b/chromium/cc/layers/video_layer_impl.h
@@ -9,6 +9,7 @@
#include "cc/base/cc_export.h"
#include "cc/layers/layer_impl.h"
+#include "cc/resources/release_callback.h"
#include "cc/resources/video_resource_updater.h"
namespace media {
@@ -59,7 +60,8 @@ class CC_EXPORT VideoLayerImpl : public LayerImpl {
// TODO(danakj): Remove these, hide software path inside ResourceProvider and
// ExternalResource (aka TextureMailbox) classes.
std::vector<unsigned> software_resources_;
- TextureMailbox::ReleaseCallback software_release_callback_;
+ // Called once for each software resource.
+ ReleaseCallback software_release_callback_;
DISALLOW_COPY_AND_ASSIGN(VideoLayerImpl);
};
diff --git a/chromium/cc/output/begin_frame_args.cc b/chromium/cc/output/begin_frame_args.cc
index cefb22bdc13..d7da76c3bcb 100644
--- a/chromium/cc/output/begin_frame_args.cc
+++ b/chromium/cc/output/begin_frame_args.cc
@@ -48,10 +48,14 @@ BeginFrameArgs BeginFrameArgs::CreateExpiredForTesting() {
DefaultInterval());
}
+// This is a hard-coded deadline adjustment that assumes 60Hz, to be used in
+// cases where a good estimated draw time is not known. Using 1/3 of the vsync
+// as the default adjustment gives the Browser the last 1/3 of a frame to
+// produce output, the Renderer Impl thread the middle 1/3 of a frame to produce
+// ouput, and the Renderer Main thread the first 1/3 of a frame to produce
+// output.
base::TimeDelta BeginFrameArgs::DefaultDeadlineAdjustment() {
- // Using a large deadline adjustment will effectively revert BeginFrame
- // scheduling to the hard vsync scheduling we used to have.
- return base::TimeDelta::FromSeconds(-1);
+ return base::TimeDelta::FromMicroseconds(-16666 / 3);
}
base::TimeDelta BeginFrameArgs::DefaultInterval() {
@@ -62,5 +66,4 @@ base::TimeDelta BeginFrameArgs::DefaultRetroactiveBeginFramePeriod() {
return base::TimeDelta::FromMicroseconds(4444);
}
-
} // namespace cc
diff --git a/chromium/cc/output/begin_frame_args.h b/chromium/cc/output/begin_frame_args.h
index 22693dbef94..025a4075c25 100644
--- a/chromium/cc/output/begin_frame_args.h
+++ b/chromium/cc/output/begin_frame_args.h
@@ -37,9 +37,7 @@ struct CC_EXPORT BeginFrameArgs {
// retroactively.
static base::TimeDelta DefaultRetroactiveBeginFramePeriod();
- bool IsValid() const {
- return interval >= base::TimeDelta();
- }
+ bool IsValid() const { return interval >= base::TimeDelta(); }
base::TimeTicks frame_time;
base::TimeTicks deadline;
diff --git a/chromium/cc/output/compositor_frame_ack.h b/chromium/cc/output/compositor_frame_ack.h
index 276ecf0d96e..943a206e391 100644
--- a/chromium/cc/output/compositor_frame_ack.h
+++ b/chromium/cc/output/compositor_frame_ack.h
@@ -8,7 +8,7 @@
#include "base/memory/scoped_ptr.h"
#include "cc/base/cc_export.h"
#include "cc/output/gl_frame_data.h"
-#include "cc/resources/transferable_resource.h"
+#include "cc/resources/returned_resource.h"
namespace cc {
@@ -17,7 +17,7 @@ class CC_EXPORT CompositorFrameAck {
CompositorFrameAck();
~CompositorFrameAck();
- TransferableResourceArray resources;
+ ReturnedResourceArray resources;
scoped_ptr<GLFrameData> gl_frame_data;
unsigned last_software_frame_id;
diff --git a/chromium/cc/output/compositor_frame_metadata.h b/chromium/cc/output/compositor_frame_metadata.h
index 4e569bbb2c7..c084151643b 100644
--- a/chromium/cc/output/compositor_frame_metadata.h
+++ b/chromium/cc/output/compositor_frame_metadata.h
@@ -6,7 +6,7 @@
#define CC_OUTPUT_COMPOSITOR_FRAME_METADATA_H_
#include "cc/base/cc_export.h"
-#include "ui/base/latency_info.h"
+#include "ui/events/latency_info.h"
#include "ui/gfx/size_f.h"
#include "ui/gfx/vector2d_f.h"
diff --git a/chromium/cc/output/context_provider.cc b/chromium/cc/output/context_provider.cc
new file mode 100644
index 00000000000..7f46f7ccc67
--- /dev/null
+++ b/chromium/cc/output/context_provider.cc
@@ -0,0 +1,30 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/output/context_provider.h"
+
+#include <limits>
+
+namespace cc {
+
+ContextProvider::Capabilities::Capabilities()
+ : bind_uniform_location(false),
+ discard_backbuffer(false),
+ egl_image_external(false),
+ fast_npot_mo8_textures(false),
+ iosurface(false),
+ map_image(false),
+ map_sub(false),
+ post_sub_buffer(false),
+ set_visibility(false),
+ shallow_flush(false),
+ swapbuffers_complete_callback(false),
+ texture_format_bgra8888(false),
+ texture_rectangle(false),
+ texture_storage(false),
+ texture_usage(false),
+ discard_framebuffer(false),
+ max_transfer_buffer_usage_bytes(std::numeric_limits<size_t>::max()) {}
+
+} // namespace cc
diff --git a/chromium/cc/output/context_provider.h b/chromium/cc/output/context_provider.h
index 13b7df5948b..c873b58c8d6 100644
--- a/chromium/cc/output/context_provider.h
+++ b/chromium/cc/output/context_provider.h
@@ -7,11 +7,13 @@
#include "base/callback.h"
#include "base/memory/ref_counted.h"
+#include "cc/base/cc_export.h"
class GrContext;
namespace WebKit { class WebGraphicsContext3D; }
namespace cc {
+struct ManagedMemoryPolicy;
class ContextProvider : public base::RefCountedThreadSafe<ContextProvider> {
public:
@@ -24,6 +26,30 @@ class ContextProvider : public base::RefCountedThreadSafe<ContextProvider> {
virtual WebKit::WebGraphicsContext3D* Context3d() = 0;
virtual class GrContext* GrContext() = 0;
+ struct Capabilities {
+ bool bind_uniform_location;
+ bool discard_backbuffer;
+ bool egl_image_external;
+ bool fast_npot_mo8_textures;
+ bool iosurface;
+ bool map_image;
+ bool map_sub;
+ bool post_sub_buffer;
+ bool set_visibility;
+ bool shallow_flush;
+ bool swapbuffers_complete_callback;
+ bool texture_format_bgra8888;
+ bool texture_rectangle;
+ bool texture_storage;
+ bool texture_usage;
+ bool discard_framebuffer;
+ size_t max_transfer_buffer_usage_bytes;
+
+ CC_EXPORT Capabilities();
+ };
+ // Returns the capabilities of the currently bound 3d context.
+ virtual Capabilities ContextCapabilities() = 0;
+
// Ask the provider to check if the contexts are valid or lost. If they are,
// this should invalidate the provider so that it can be replaced with a new
// one.
@@ -41,6 +67,20 @@ class ContextProvider : public base::RefCountedThreadSafe<ContextProvider> {
virtual void SetLostContextCallback(
const LostContextCallback& lost_context_callback) = 0;
+ // Sets a callback to be called when swap buffers completes. This should be
+ // called from the same thread that the context is bound to.
+ typedef base::Closure SwapBuffersCompleteCallback;
+ virtual void SetSwapBuffersCompleteCallback(
+ const SwapBuffersCompleteCallback& swap_buffers_complete_callback) = 0;
+
+ // Sets a callback to be called when the memory policy changes. This should be
+ // called from the same thread that the context is bound to.
+ typedef base::Callback<void(
+ const cc::ManagedMemoryPolicy& policy,
+ bool discard_backbuffer_when_not_visible)> MemoryPolicyChangedCallback;
+ virtual void SetMemoryPolicyChangedCallback(
+ const MemoryPolicyChangedCallback& memory_policy_changed_callback) = 0;
+
protected:
friend class base::RefCountedThreadSafe<ContextProvider>;
virtual ~ContextProvider() {}
diff --git a/chromium/cc/output/copy_output_request.cc b/chromium/cc/output/copy_output_request.cc
index 995c3f4cfcc..6163d5d3c60 100644
--- a/chromium/cc/output/copy_output_request.cc
+++ b/chromium/cc/output/copy_output_request.cc
@@ -8,6 +8,7 @@
#include "base/callback_helpers.h"
#include "base/logging.h"
#include "cc/output/copy_output_result.h"
+#include "cc/resources/single_release_callback.h"
#include "cc/resources/texture_mailbox.h"
#include "third_party/skia/include/core/SkBitmap.h"
@@ -40,11 +41,13 @@ void CopyOutputRequest::SendBitmapResult(scoped_ptr<SkBitmap> bitmap) {
SendResult(CopyOutputResult::CreateBitmapResult(bitmap.Pass()).Pass());
}
-void CopyOutputRequest::SendTextureResult(gfx::Size size,
- scoped_ptr<TextureMailbox> texture) {
- DCHECK(texture->IsTexture());
- SendResult(CopyOutputResult::CreateTextureResult(size,
- texture.Pass()).Pass());
+void CopyOutputRequest::SendTextureResult(
+ gfx::Size size,
+ const TextureMailbox& texture_mailbox,
+ scoped_ptr<SingleReleaseCallback> release_callback) {
+ DCHECK(texture_mailbox.IsTexture());
+ SendResult(CopyOutputResult::CreateTextureResult(
+ size, texture_mailbox, release_callback.Pass()));
}
} // namespace cc
diff --git a/chromium/cc/output/copy_output_request.h b/chromium/cc/output/copy_output_request.h
index 3f4d925ee16..c4a92481dbc 100644
--- a/chromium/cc/output/copy_output_request.h
+++ b/chromium/cc/output/copy_output_request.h
@@ -14,6 +14,7 @@ class SkBitmap;
namespace cc {
class CopyOutputResult;
+class SingleReleaseCallback;
class TextureMailbox;
class CC_EXPORT CopyOutputRequest {
@@ -61,7 +62,8 @@ class CC_EXPORT CopyOutputRequest {
void SendEmptyResult();
void SendBitmapResult(scoped_ptr<SkBitmap> bitmap);
void SendTextureResult(gfx::Size size,
- scoped_ptr<TextureMailbox> texture_mailbox);
+ const TextureMailbox& texture_mailbox,
+ scoped_ptr<SingleReleaseCallback> release_callback);
void SendResult(scoped_ptr<CopyOutputResult> result);
diff --git a/chromium/cc/output/copy_output_result.cc b/chromium/cc/output/copy_output_result.cc
index 55213cde6b8..1831fba3be1 100644
--- a/chromium/cc/output/copy_output_result.cc
+++ b/chromium/cc/output/copy_output_result.cc
@@ -5,6 +5,7 @@
#include "cc/output/copy_output_result.h"
#include "base/logging.h"
+#include "cc/resources/single_release_callback.h"
#include "cc/resources/texture_mailbox.h"
#include "third_party/skia/include/core/SkBitmap.h"
@@ -18,25 +19,32 @@ CopyOutputResult::CopyOutputResult(scoped_ptr<SkBitmap> bitmap)
DCHECK(bitmap_);
}
-CopyOutputResult::CopyOutputResult(gfx::Size size,
- scoped_ptr<TextureMailbox> texture_mailbox)
+CopyOutputResult::CopyOutputResult(
+ gfx::Size size,
+ const TextureMailbox& texture_mailbox,
+ scoped_ptr<SingleReleaseCallback> release_callback)
: size_(size),
- texture_mailbox_(texture_mailbox.Pass()) {
- DCHECK(texture_mailbox_);
- DCHECK(texture_mailbox_->IsTexture());
+ texture_mailbox_(texture_mailbox),
+ release_callback_(release_callback.Pass()) {
+ DCHECK(texture_mailbox_.IsTexture());
}
CopyOutputResult::~CopyOutputResult() {
- if (texture_mailbox_)
- texture_mailbox_->RunReleaseCallback(0, false);
+ if (release_callback_)
+ release_callback_->Run(0, false);
}
scoped_ptr<SkBitmap> CopyOutputResult::TakeBitmap() {
return bitmap_.Pass();
}
-scoped_ptr<TextureMailbox> CopyOutputResult::TakeTexture() {
- return texture_mailbox_.Pass();
+void CopyOutputResult::TakeTexture(
+ TextureMailbox* texture_mailbox,
+ scoped_ptr<SingleReleaseCallback>* release_callback) {
+ *texture_mailbox = texture_mailbox_;
+ *release_callback = release_callback_.Pass();
+
+ texture_mailbox_ = TextureMailbox();
}
} // namespace cc
diff --git a/chromium/cc/output/copy_output_result.h b/chromium/cc/output/copy_output_result.h
index 04cf2c6d489..c2f011d20fc 100644
--- a/chromium/cc/output/copy_output_result.h
+++ b/chromium/cc/output/copy_output_result.h
@@ -7,11 +7,13 @@
#include "base/memory/scoped_ptr.h"
#include "cc/base/cc_export.h"
+#include "cc/resources/texture_mailbox.h"
#include "ui/gfx/size.h"
class SkBitmap;
namespace cc {
+class SingleReleaseCallback;
class TextureMailbox;
class CC_EXPORT CopyOutputResult {
@@ -25,29 +27,34 @@ class CC_EXPORT CopyOutputResult {
}
static scoped_ptr<CopyOutputResult> CreateTextureResult(
gfx::Size size,
- scoped_ptr<TextureMailbox> texture_mailbox) {
- return make_scoped_ptr(new CopyOutputResult(size, texture_mailbox.Pass()));
+ const TextureMailbox& texture_mailbox,
+ scoped_ptr<SingleReleaseCallback> release_callback) {
+ return make_scoped_ptr(
+ new CopyOutputResult(size, texture_mailbox, release_callback.Pass()));
}
~CopyOutputResult();
bool IsEmpty() const { return !HasBitmap() && !HasTexture(); }
bool HasBitmap() const { return !!bitmap_; }
- bool HasTexture() const { return !!texture_mailbox_; }
+ bool HasTexture() const { return texture_mailbox_.IsValid(); }
gfx::Size size() const { return size_; }
scoped_ptr<SkBitmap> TakeBitmap();
- scoped_ptr<TextureMailbox> TakeTexture();
+ void TakeTexture(TextureMailbox* texture_mailbox,
+ scoped_ptr<SingleReleaseCallback>* release_callback);
private:
CopyOutputResult();
explicit CopyOutputResult(scoped_ptr<SkBitmap> bitmap);
explicit CopyOutputResult(gfx::Size size,
- scoped_ptr<TextureMailbox> texture_mailbox);
+ const TextureMailbox& texture_mailbox,
+ scoped_ptr<SingleReleaseCallback> release_callback);
gfx::Size size_;
scoped_ptr<SkBitmap> bitmap_;
- scoped_ptr<TextureMailbox> texture_mailbox_;
+ TextureMailbox texture_mailbox_;
+ scoped_ptr<SingleReleaseCallback> release_callback_;
};
} // namespace cc
diff --git a/chromium/cc/output/delegating_renderer.cc b/chromium/cc/output/delegating_renderer.cc
index 0c3051cc6dd..1b4021fbbc0 100644
--- a/chromium/cc/output/delegating_renderer.cc
+++ b/chromium/cc/output/delegating_renderer.cc
@@ -32,20 +32,21 @@ namespace cc {
scoped_ptr<DelegatingRenderer> DelegatingRenderer::Create(
RendererClient* client,
+ const LayerTreeSettings* settings,
OutputSurface* output_surface,
ResourceProvider* resource_provider) {
- scoped_ptr<DelegatingRenderer> renderer(
- new DelegatingRenderer(client, output_surface, resource_provider));
+ scoped_ptr<DelegatingRenderer> renderer(new DelegatingRenderer(
+ client, settings, output_surface, resource_provider));
if (!renderer->Initialize())
return scoped_ptr<DelegatingRenderer>();
return renderer.Pass();
}
-DelegatingRenderer::DelegatingRenderer(
- RendererClient* client,
- OutputSurface* output_surface,
- ResourceProvider* resource_provider)
- : Renderer(client),
+DelegatingRenderer::DelegatingRenderer(RendererClient* client,
+ const LayerTreeSettings* settings,
+ OutputSurface* output_surface,
+ ResourceProvider* resource_provider)
+ : Renderer(client, settings),
output_surface_(output_surface),
resource_provider_(resource_provider),
visible_(true) {
@@ -59,57 +60,25 @@ bool DelegatingRenderer::Initialize() {
capabilities_.allow_partial_texture_updates = false;
capabilities_.using_offscreen_context3d = false;
- WebGraphicsContext3D* context3d = resource_provider_->GraphicsContext3D();
-
- if (!context3d) {
- // Software compositing.
+ if (!output_surface_->context_provider()) {
+ // TODO(danakj): Make software compositing work.
return true;
}
+ WebGraphicsContext3D* context3d =
+ output_surface_->context_provider()->Context3d();
+
if (!context3d->makeContextCurrent())
return false;
- std::string unique_context_name = base::StringPrintf(
- "%s-%p",
- Settings().compositor_name.c_str(),
- context3d);
- context3d->pushGroupMarkerEXT(unique_context_name.c_str());
-
- std::string extensions_string =
- UTF16ToASCII(context3d->getString(GL_EXTENSIONS));
-
- std::vector<std::string> extensions;
- base::SplitString(extensions_string, ' ', &extensions);
-
- // TODO(danakj): We need non-GPU-specific paths for these things. This
- // renderer shouldn't need to use context3d extensions directly.
- bool has_set_visibility = false;
- bool has_io_surface = false;
- bool has_arb_texture_rect = false;
- bool has_egl_image = false;
- bool has_map_image = false;
- for (size_t i = 0; i < extensions.size(); ++i) {
- if (extensions[i] == "GL_CHROMIUM_set_visibility") {
- has_set_visibility = true;
- } else if (extensions[i] == "GL_CHROMIUM_iosurface") {
- has_io_surface = true;
- } else if (extensions[i] == "GL_ARB_texture_rectangle") {
- has_arb_texture_rect = true;
- } else if (extensions[i] == "GL_OES_EGL_image_external") {
- has_egl_image = true;
- } else if (extensions[i] == "GL_CHROMIUM_map_image") {
- has_map_image = true;
- }
- }
-
- if (has_io_surface)
- DCHECK(has_arb_texture_rect);
-
- capabilities_.using_set_visibility = has_set_visibility;
+ const ContextProvider::Capabilities& caps =
+ output_surface_->context_provider()->ContextCapabilities();
- capabilities_.using_egl_image = has_egl_image;
+ DCHECK(!caps.iosurface || caps.texture_rectangle);
- capabilities_.using_map_image = has_map_image;
+ capabilities_.using_set_visibility = caps.set_visibility;
+ capabilities_.using_egl_image = caps.egl_image_external;
+ capabilities_.using_map_image = settings_->use_map_image && caps.map_image;
return true;
}
@@ -129,8 +98,10 @@ static ResourceProvider::ResourceId AppendToArray(
return id;
}
-void DelegatingRenderer::DrawFrame(
- RenderPassList* render_passes_in_draw_order) {
+void DelegatingRenderer::DrawFrame(RenderPassList* render_passes_in_draw_order,
+ ContextProvider* offscreen_context_provider,
+ float device_scale_factor,
+ bool allow_partial_swap) {
TRACE_EVENT0("cc", "DelegatingRenderer::DrawFrame");
DCHECK(!frame_for_swap_buffers_.delegated_frame_data);
@@ -168,14 +139,15 @@ void DelegatingRenderer::GetFramebufferPixels(void* pixels, gfx::Rect rect) {
void DelegatingRenderer::ReceiveSwapBuffersAck(
const CompositorFrameAck& ack) {
- resource_provider_->ReceiveFromParent(ack.resources);
+ resource_provider_->ReceiveReturnsFromParent(ack.resources);
}
bool DelegatingRenderer::IsContextLost() {
- WebGraphicsContext3D* context3d = resource_provider_->GraphicsContext3D();
- if (!context3d)
+ ContextProvider* context_provider = output_surface_->context_provider();
+ if (!context_provider)
return false;
- return context3d->getGraphicsResetStatusARB() != GL_NO_ERROR;
+ return context_provider->Context3d()->getGraphicsResetStatusARB() !=
+ GL_NO_ERROR;
}
void DelegatingRenderer::SetVisible(bool visible) {
@@ -183,27 +155,27 @@ void DelegatingRenderer::SetVisible(bool visible) {
return;
visible_ = visible;
- WebGraphicsContext3D* context = resource_provider_->GraphicsContext3D();
+ ContextProvider* context_provider = output_surface_->context_provider();
if (!visible_) {
TRACE_EVENT0("cc", "DelegatingRenderer::SetVisible dropping resources");
resource_provider_->ReleaseCachedData();
- if (context)
- context->flush();
+ if (context_provider)
+ context_provider->Context3d()->flush();
}
if (capabilities_.using_set_visibility) {
// We loop visibility to the GPU process, since that's what manages memory.
// That will allow it to feed us with memory allocations that we can act
// upon.
- DCHECK(context);
- context->setVisibilityCHROMIUM(visible);
+ DCHECK(context_provider);
+ context_provider->Context3d()->setVisibilityCHROMIUM(visible);
}
}
void DelegatingRenderer::SendManagedMemoryStats(size_t bytes_visible,
size_t bytes_visible_and_nearby,
size_t bytes_allocated) {
- WebGraphicsContext3D* context = resource_provider_->GraphicsContext3D();
- if (!context) {
+ ContextProvider* context_provider = output_surface_->context_provider();
+ if (!context_provider) {
// TODO(piman): software path.
NOTIMPLEMENTED();
return;
@@ -213,7 +185,7 @@ void DelegatingRenderer::SendManagedMemoryStats(size_t bytes_visible,
stats.bytesVisibleAndNearby = bytes_visible_and_nearby;
stats.bytesAllocated = bytes_allocated;
stats.backbufferRequested = false;
- context->sendManagedMemoryStatsCHROMIUM(&stats);
+ context_provider->Context3d()->sendManagedMemoryStatsCHROMIUM(&stats);
}
void DelegatingRenderer::SetDiscardBackBufferWhenNotVisible(bool discard) {
diff --git a/chromium/cc/output/delegating_renderer.h b/chromium/cc/output/delegating_renderer.h
index 11c8c6bd41d..57a2418c452 100644
--- a/chromium/cc/output/delegating_renderer.h
+++ b/chromium/cc/output/delegating_renderer.h
@@ -19,6 +19,7 @@ class CC_EXPORT DelegatingRenderer : public Renderer {
public:
static scoped_ptr<DelegatingRenderer> Create(
RendererClient* client,
+ const LayerTreeSettings* settings,
OutputSurface* output_surface,
ResourceProvider* resource_provider);
virtual ~DelegatingRenderer();
@@ -27,7 +28,10 @@ class CC_EXPORT DelegatingRenderer : public Renderer {
virtual bool CanReadPixels() const OVERRIDE;
- virtual void DrawFrame(RenderPassList* render_passes_in_draw_order) OVERRIDE;
+ virtual void DrawFrame(RenderPassList* render_passes_in_draw_order,
+ ContextProvider* offscreen_context_provider,
+ float device_scale_factor,
+ bool allow_partial_swap) OVERRIDE;
virtual void Finish() OVERRIDE {}
@@ -48,6 +52,7 @@ class CC_EXPORT DelegatingRenderer : public Renderer {
private:
DelegatingRenderer(RendererClient* client,
+ const LayerTreeSettings* settings,
OutputSurface* output_surface,
ResourceProvider* resource_provider);
bool Initialize();
diff --git a/chromium/cc/output/delegating_renderer_unittest.cc b/chromium/cc/output/delegating_renderer_unittest.cc
index 6cb532fe86b..ffa7e4f6e31 100644
--- a/chromium/cc/output/delegating_renderer_unittest.cc
+++ b/chromium/cc/output/delegating_renderer_unittest.cc
@@ -66,9 +66,8 @@ class DelegatingRendererTestDraw : public DelegatingRendererTest {
DelegatedFrameData* last_frame_data = last_frame.delegated_frame_data.get();
ASSERT_TRUE(last_frame.delegated_frame_data);
EXPECT_FALSE(last_frame.gl_frame_data);
- EXPECT_EQ(
- gfx::Rect(host_impl->device_viewport_size()).ToString(),
- last_frame_data->render_pass_list.back()->output_rect.ToString());
+ EXPECT_EQ(host_impl->DeviceViewport().ToString(),
+ last_frame_data->render_pass_list.back()->output_rect.ToString());
EXPECT_EQ(0.5f, last_frame.metadata.min_page_scale_factor);
EXPECT_EQ(4.f, last_frame.metadata.max_page_scale_factor);
@@ -129,11 +128,12 @@ class DelegatingRendererTestResources : public DelegatingRendererTest {
EXPECT_EQ(2u, last_frame.delegated_frame_data->render_pass_list.size());
// Each render pass has 10 resources in it. And the root render pass has a
- // mask resource used when drawing the child render pass. The number 10 may
- // change if AppendOneOfEveryQuadType() is updated, and the value here
- // should be updated accordingly.
+ // mask resource used when drawing the child render pass, as well as its
+ // replica (it's added twice). The number 10 may change if
+ // AppendOneOfEveryQuadType() is updated, and the value here should be
+ // updated accordingly.
EXPECT_EQ(
- 21u, last_frame.delegated_frame_data->resource_list.size());
+ 22u, last_frame.delegated_frame_data->resource_list.size());
EndTest();
}
diff --git a/chromium/cc/output/direct_renderer.cc b/chromium/cc/output/direct_renderer.cc
index a664d7df822..aad41ed293f 100644
--- a/chromium/cc/output/direct_renderer.cc
+++ b/chromium/cc/output/direct_renderer.cc
@@ -8,6 +8,7 @@
#include <vector>
#include "base/containers/hash_tables.h"
+#include "base/containers/scoped_ptr_hash_map.h"
#include "base/debug/trace_event.h"
#include "base/metrics/histogram.h"
#include "cc/base/math_util.h"
@@ -58,7 +59,8 @@ namespace cc {
DirectRenderer::DrawingFrame::DrawingFrame()
: root_render_pass(NULL),
current_render_pass(NULL),
- current_texture(NULL) {}
+ current_texture(NULL),
+ offscreen_context_provider(NULL) {}
DirectRenderer::DrawingFrame::~DrawingFrame() {}
@@ -125,9 +127,10 @@ gfx::Rect DirectRenderer::MoveFromDrawToWindowSpace(
}
DirectRenderer::DirectRenderer(RendererClient* client,
+ const LayerTreeSettings* settings,
OutputSurface* output_surface,
ResourceProvider* resource_provider)
- : Renderer(client),
+ : Renderer(client, settings),
output_surface_(output_surface),
resource_provider_(resource_provider) {}
@@ -151,7 +154,8 @@ void DirectRenderer::DecideRenderPassAllocationsForFrame(
render_passes_in_draw_order[i]->id, render_passes_in_draw_order[i]));
std::vector<RenderPass::Id> passes_to_delete;
- ScopedPtrHashMap<RenderPass::Id, CachedResource>::const_iterator pass_iter;
+ base::ScopedPtrHashMap<RenderPass::Id, CachedResource>::const_iterator
+ pass_iter;
for (pass_iter = render_pass_textures_.begin();
pass_iter != render_pass_textures_.end();
++pass_iter) {
@@ -164,12 +168,13 @@ void DirectRenderer::DecideRenderPassAllocationsForFrame(
const RenderPass* render_pass_in_frame = it->second;
gfx::Size required_size = RenderPassTextureSize(render_pass_in_frame);
- GLenum required_format = RenderPassTextureFormat(render_pass_in_frame);
+ ResourceFormat required_format =
+ RenderPassTextureFormat(render_pass_in_frame);
CachedResource* texture = pass_iter->second;
DCHECK(texture);
bool size_appropriate = texture->size().width() >= required_size.width() &&
- texture->size().height() >= required_size.height();
+ texture->size().height() >= required_size.height();
if (texture->id() &&
(!size_appropriate || texture->format() != required_format))
texture->Free();
@@ -190,7 +195,10 @@ void DirectRenderer::DecideRenderPassAllocationsForFrame(
}
}
-void DirectRenderer::DrawFrame(RenderPassList* render_passes_in_draw_order) {
+void DirectRenderer::DrawFrame(RenderPassList* render_passes_in_draw_order,
+ ContextProvider* offscreen_context_provider,
+ float device_scale_factor,
+ bool allow_partial_swap) {
TRACE_EVENT0("cc", "DirectRenderer::DrawFrame");
UMA_HISTOGRAM_COUNTS("Renderer4.renderPassCount",
render_passes_in_draw_order->size());
@@ -201,9 +209,11 @@ void DirectRenderer::DrawFrame(RenderPassList* render_passes_in_draw_order) {
DrawingFrame frame;
frame.root_render_pass = root_render_pass;
frame.root_damage_rect =
- Capabilities().using_partial_swap && client_->AllowPartialSwap() ?
- root_render_pass->damage_rect : root_render_pass->output_rect;
+ Capabilities().using_partial_swap && allow_partial_swap
+ ? root_render_pass->damage_rect
+ : root_render_pass->output_rect;
frame.root_damage_rect.Intersect(gfx::Rect(client_->DeviceViewport().size()));
+ frame.offscreen_context_provider = offscreen_context_provider;
EnsureBackbuffer();
@@ -211,12 +221,12 @@ void DirectRenderer::DrawFrame(RenderPassList* render_passes_in_draw_order) {
// can leave the window at the wrong size if we never draw and the proper
// viewport size is never set.
output_surface_->Reshape(client_->DeviceViewport().size(),
- client_->DeviceScaleFactor());
+ device_scale_factor);
BeginDrawingFrame(&frame);
for (size_t i = 0; i < render_passes_in_draw_order->size(); ++i) {
RenderPass* pass = render_passes_in_draw_order->at(i);
- DrawRenderPass(&frame, pass);
+ DrawRenderPass(&frame, pass, allow_partial_swap);
for (ScopedPtrVector<CopyOutputRequest>::iterator it =
pass->copy_requests.begin();
@@ -255,14 +265,33 @@ gfx::RectF DirectRenderer::ComputeScissorRectForRenderPass(
return render_pass_scissor;
}
+bool DirectRenderer::NeedDeviceClip(const DrawingFrame* frame) const {
+ if (frame->current_render_pass != frame->root_render_pass)
+ return false;
+
+ return !client_->DeviceClip().Contains(client_->DeviceViewport());
+}
+
+gfx::Rect DirectRenderer::DeviceClipRect(const DrawingFrame* frame) const {
+ gfx::Rect device_clip_rect = client_->DeviceClip();
+ if (FlippedFramebuffer())
+ device_clip_rect.set_y(current_surface_size_.height() -
+ device_clip_rect.bottom());
+ return device_clip_rect;
+}
+
void DirectRenderer::SetScissorStateForQuad(const DrawingFrame* frame,
const DrawQuad& quad) {
if (quad.isClipped()) {
- gfx::RectF quad_scissor_rect = quad.clipRect();
- SetScissorTestRect(MoveFromDrawToWindowSpace(quad_scissor_rect));
- } else {
- EnsureScissorTestDisabled();
+ SetScissorTestRectInDrawSpace(frame, quad.clipRect());
+ return;
}
+ if (NeedDeviceClip(frame)) {
+ SetScissorTestRect(DeviceClipRect(frame));
+ return;
+ }
+
+ EnsureScissorTestDisabled();
}
void DirectRenderer::SetScissorStateForQuadWithRenderPassScissor(
@@ -281,31 +310,57 @@ void DirectRenderer::SetScissorStateForQuadWithRenderPassScissor(
}
*should_skip_quad = false;
- SetScissorTestRect(MoveFromDrawToWindowSpace(quad_scissor_rect));
+ SetScissorTestRectInDrawSpace(frame, quad_scissor_rect);
+}
+
+void DirectRenderer::SetScissorTestRectInDrawSpace(const DrawingFrame* frame,
+ gfx::RectF draw_space_rect) {
+ gfx::Rect window_space_rect = MoveFromDrawToWindowSpace(draw_space_rect);
+ if (NeedDeviceClip(frame))
+ window_space_rect.Intersect(DeviceClipRect(frame));
+ SetScissorTestRect(window_space_rect);
}
void DirectRenderer::FinishDrawingQuadList() {}
void DirectRenderer::DrawRenderPass(DrawingFrame* frame,
- const RenderPass* render_pass) {
+ const RenderPass* render_pass,
+ bool allow_partial_swap) {
TRACE_EVENT0("cc", "DirectRenderer::DrawRenderPass");
if (!UseRenderPass(frame, render_pass))
return;
bool using_scissor_as_optimization =
- Capabilities().using_partial_swap && client_->AllowPartialSwap();
+ Capabilities().using_partial_swap && allow_partial_swap;
gfx::RectF render_pass_scissor;
+ bool draw_rect_covers_full_surface = true;
+ if (frame->current_render_pass == frame->root_render_pass &&
+ !client_->DeviceViewport().Contains(
+ gfx::Rect(output_surface_->SurfaceSize())))
+ draw_rect_covers_full_surface = false;
if (using_scissor_as_optimization) {
render_pass_scissor = ComputeScissorRectForRenderPass(frame);
- SetScissorTestRect(MoveFromDrawToWindowSpace(render_pass_scissor));
+ SetScissorTestRectInDrawSpace(frame, render_pass_scissor);
+ if (!render_pass_scissor.Contains(frame->current_render_pass->output_rect))
+ draw_rect_covers_full_surface = false;
}
if (frame->current_render_pass != frame->root_render_pass ||
- client_->ShouldClearRootRenderPass()) {
- if (!using_scissor_as_optimization)
+ settings_->should_clear_root_render_pass) {
+ if (NeedDeviceClip(frame)) {
+ SetScissorTestRect(DeviceClipRect(frame));
+ draw_rect_covers_full_surface = false;
+ } else if (!using_scissor_as_optimization) {
EnsureScissorTestDisabled();
- ClearFramebuffer(frame);
+ }
+
+ bool has_external_stencil_test =
+ output_surface_->HasExternalStencilTest() &&
+ frame->current_render_pass == frame->root_render_pass;
+
+ DiscardPixels(has_external_stencil_test, draw_rect_covers_full_surface);
+ ClearFramebuffer(frame, has_external_stencil_test);
}
const QuadList& quad_list = render_pass->quad_list;
@@ -359,8 +414,8 @@ bool DirectRenderer::UseRenderPass(DrawingFrame* frame,
enlarge_pass_texture_amount_.y());
if (!texture->id() &&
!texture->Allocate(size,
- RenderPassTextureFormat(render_pass),
- ResourceProvider::TextureUsageFramebuffer))
+ ResourceProvider::TextureUsageFramebuffer,
+ RenderPassTextureFormat(render_pass)))
return false;
return BindFramebufferToTexture(frame, texture, render_pass->output_rect);
@@ -368,7 +423,7 @@ bool DirectRenderer::UseRenderPass(DrawingFrame* frame,
bool DirectRenderer::HaveCachedResourcesForRenderPassId(RenderPass::Id id)
const {
- if (!Settings().cache_render_pass_contents)
+ if (!settings_->cache_render_pass_contents)
return false;
CachedResource* texture = render_pass_textures_.get(id);
@@ -381,8 +436,9 @@ gfx::Size DirectRenderer::RenderPassTextureSize(const RenderPass* render_pass) {
}
// static
-GLenum DirectRenderer::RenderPassTextureFormat(const RenderPass* render_pass) {
- return GL_RGBA;
+ResourceFormat DirectRenderer::RenderPassTextureFormat(
+ const RenderPass* render_pass) {
+ return RGBA_8888;
}
} // namespace cc
diff --git a/chromium/cc/output/direct_renderer.h b/chromium/cc/output/direct_renderer.h
index ffc2b67b8a7..e5ee83c87bd 100644
--- a/chromium/cc/output/direct_renderer.h
+++ b/chromium/cc/output/direct_renderer.h
@@ -7,6 +7,7 @@
#include "base/basictypes.h"
#include "base/callback.h"
+#include "base/containers/scoped_ptr_hash_map.h"
#include "cc/base/cc_export.h"
#include "cc/output/renderer.h"
#include "cc/resources/resource_provider.h"
@@ -30,7 +31,10 @@ class CC_EXPORT DirectRenderer : public Renderer {
const RenderPassList& render_passes_in_draw_order) OVERRIDE;
virtual bool HaveCachedResourcesForRenderPassId(RenderPass::Id id) const
OVERRIDE;
- virtual void DrawFrame(RenderPassList* render_passes_in_draw_order) OVERRIDE;
+ virtual void DrawFrame(RenderPassList* render_passes_in_draw_order,
+ ContextProvider* offscreen_context_provider,
+ float device_scale_factor,
+ bool allow_partial_swap) OVERRIDE;
struct CC_EXPORT DrawingFrame {
DrawingFrame();
@@ -44,12 +48,15 @@ class CC_EXPORT DirectRenderer : public Renderer {
gfx::Transform projection_matrix;
gfx::Transform window_matrix;
+
+ ContextProvider* offscreen_context_provider;
};
void SetEnlargePassTextureAmountForTesting(gfx::Vector2d amount);
protected:
DirectRenderer(RendererClient* client,
+ const LayerTreeSettings* settings,
OutputSurface* output_surface,
ResourceProvider* resource_provider);
@@ -85,6 +92,8 @@ class CC_EXPORT DirectRenderer : public Renderer {
gfx::Size surface_size);
gfx::Rect MoveFromDrawToWindowSpace(const gfx::RectF& draw_rect) const;
+ bool NeedDeviceClip(const DrawingFrame* frame) const;
+ gfx::Rect DeviceClipRect(const DrawingFrame* frame) const;
static gfx::RectF ComputeScissorRectForRenderPass(const DrawingFrame* frame);
void SetScissorStateForQuad(const DrawingFrame* frame, const DrawQuad& quad);
void SetScissorStateForQuadWithRenderPassScissor(
@@ -92,11 +101,15 @@ class CC_EXPORT DirectRenderer : public Renderer {
const DrawQuad& quad,
const gfx::RectF& render_pass_scissor,
bool* should_skip_quad);
+ void SetScissorTestRectInDrawSpace(const DrawingFrame* frame,
+ gfx::RectF draw_space_rect);
static gfx::Size RenderPassTextureSize(const RenderPass* render_pass);
- static GLenum RenderPassTextureFormat(const RenderPass* render_pass);
+ static ResourceFormat RenderPassTextureFormat(const RenderPass* render_pass);
- void DrawRenderPass(DrawingFrame* frame, const RenderPass* render_pass);
+ void DrawRenderPass(DrawingFrame* frame,
+ const RenderPass* render_pass,
+ bool allow_partial_swap);
bool UseRenderPass(DrawingFrame* frame, const RenderPass* render_pass);
virtual void BindFramebufferToOutputSurface(DrawingFrame* frame) = 0;
@@ -105,7 +118,10 @@ class CC_EXPORT DirectRenderer : public Renderer {
gfx::Rect target_rect) = 0;
virtual void SetDrawViewport(gfx::Rect window_space_viewport) = 0;
virtual void SetScissorTestRect(gfx::Rect scissor_rect) = 0;
- virtual void ClearFramebuffer(DrawingFrame* frame) = 0;
+ virtual void DiscardPixels(bool has_external_stencil_test,
+ bool draw_rect_covers_full_surface) = 0;
+ virtual void ClearFramebuffer(DrawingFrame* frame,
+ bool has_external_stencil_test) = 0;
virtual void DoDrawQuad(DrawingFrame* frame, const DrawQuad* quad) = 0;
virtual void BeginDrawingFrame(DrawingFrame* frame) = 0;
virtual void FinishDrawingFrame(DrawingFrame* frame) = 0;
@@ -120,7 +136,7 @@ class CC_EXPORT DirectRenderer : public Renderer {
DrawingFrame* frame,
scoped_ptr<CopyOutputRequest> request) = 0;
- ScopedPtrHashMap<RenderPass::Id, CachedResource> render_pass_textures_;
+ base::ScopedPtrHashMap<RenderPass::Id, CachedResource> render_pass_textures_;
OutputSurface* output_surface_;
ResourceProvider* resource_provider_;
diff --git a/chromium/cc/output/filter_operation.cc b/chromium/cc/output/filter_operation.cc
index 6a778388ae5..f4e3dde0ce5 100644
--- a/chromium/cc/output/filter_operation.cc
+++ b/chromium/cc/output/filter_operation.cc
@@ -68,7 +68,7 @@ FilterOperation::FilterOperation(FilterType type, float amount, int inset)
memset(matrix_, 0, sizeof(matrix_));
}
-// TODO(ajuma): Define a version of ui::Tween::ValueBetween for floats, and use
+// TODO(ajuma): Define a version of gfx::Tween::ValueBetween for floats, and use
// that instead.
static float BlendFloats(float from, float to, double progress) {
return from * (1.0 - progress) + to * progress;
@@ -147,10 +147,9 @@ static FilterOperation CreateNoOpFilter(FilterOperation::FilterType type) {
return FilterOperation::CreateZoomFilter(1.f, 0);
case FilterOperation::SATURATING_BRIGHTNESS:
return FilterOperation::CreateSaturatingBrightnessFilter(0.f);
- default:
- NOTREACHED();
- return FilterOperation::CreateEmptyFilter();
}
+ NOTREACHED();
+ return FilterOperation::CreateEmptyFilter();
}
static float ClampAmountForFilterType(float amount,
@@ -173,10 +172,11 @@ static float ClampAmountForFilterType(float amount,
case FilterOperation::SATURATING_BRIGHTNESS:
return amount;
case FilterOperation::COLOR_MATRIX:
- default:
NOTREACHED();
return amount;
}
+ NOTREACHED();
+ return amount;
}
// static
diff --git a/chromium/cc/output/filter_operations.cc b/chromium/cc/output/filter_operations.cc
index 24208418026..e526f5c419b 100644
--- a/chromium/cc/output/filter_operations.cc
+++ b/chromium/cc/output/filter_operations.cc
@@ -86,7 +86,16 @@ bool FilterOperations::HasFilterThatMovesPixels() const {
case FilterOperation::DROP_SHADOW:
case FilterOperation::ZOOM:
return true;
- default:
+ case FilterOperation::OPACITY:
+ case FilterOperation::COLOR_MATRIX:
+ case FilterOperation::GRAYSCALE:
+ case FilterOperation::SEPIA:
+ case FilterOperation::SATURATE:
+ case FilterOperation::HUE_ROTATE:
+ case FilterOperation::INVERT:
+ case FilterOperation::BRIGHTNESS:
+ case FilterOperation::CONTRAST:
+ case FilterOperation::SATURATING_BRIGHTNESS:
break;
}
}
@@ -104,10 +113,22 @@ bool FilterOperations::HasFilterThatAffectsOpacity() const {
return true;
case FilterOperation::COLOR_MATRIX: {
const SkScalar* matrix = op.matrix();
- return matrix[15] || matrix[16] || matrix[17] || matrix[18] != 1 ||
- matrix[19];
+ if (matrix[15] ||
+ matrix[16] ||
+ matrix[17] ||
+ matrix[18] != 1 ||
+ matrix[19])
+ return true;
+ break;
}
- default:
+ case FilterOperation::GRAYSCALE:
+ case FilterOperation::SEPIA:
+ case FilterOperation::SATURATE:
+ case FilterOperation::HUE_ROTATE:
+ case FilterOperation::INVERT:
+ case FilterOperation::BRIGHTNESS:
+ case FilterOperation::CONTRAST:
+ case FilterOperation::SATURATING_BRIGHTNESS:
break;
}
}
diff --git a/chromium/cc/output/gl_renderer.cc b/chromium/cc/output/gl_renderer.cc
index 2dc699104ab..f4b1e575d39 100644
--- a/chromium/cc/output/gl_renderer.cc
+++ b/chromium/cc/output/gl_renderer.cc
@@ -33,6 +33,7 @@
#include "cc/resources/layer_quad.h"
#include "cc/resources/scoped_resource.h"
#include "cc/resources/sync_point_helper.h"
+#include "cc/resources/texture_mailbox_deleter.h"
#include "cc/trees/damage_tracker.h"
#include "cc/trees/proxy.h"
#include "cc/trees/single_thread_proxy.h"
@@ -126,13 +127,20 @@ struct GLRenderer::PendingAsyncReadPixels {
DISALLOW_COPY_AND_ASSIGN(PendingAsyncReadPixels);
};
-scoped_ptr<GLRenderer> GLRenderer::Create(RendererClient* client,
- OutputSurface* output_surface,
- ResourceProvider* resource_provider,
- int highp_threshold_min,
- bool use_skia_gpu_backend) {
- scoped_ptr<GLRenderer> renderer(new GLRenderer(
- client, output_surface, resource_provider, highp_threshold_min));
+scoped_ptr<GLRenderer> GLRenderer::Create(
+ RendererClient* client,
+ const LayerTreeSettings* settings,
+ OutputSurface* output_surface,
+ ResourceProvider* resource_provider,
+ TextureMailboxDeleter* texture_mailbox_deleter,
+ int highp_threshold_min,
+ bool use_skia_gpu_backend) {
+ scoped_ptr<GLRenderer> renderer(new GLRenderer(client,
+ settings,
+ output_surface,
+ resource_provider,
+ texture_mailbox_deleter,
+ highp_threshold_min));
if (!renderer->Initialize())
return scoped_ptr<GLRenderer>();
if (use_skia_gpu_backend) {
@@ -145,13 +153,16 @@ scoped_ptr<GLRenderer> GLRenderer::Create(RendererClient* client,
}
GLRenderer::GLRenderer(RendererClient* client,
+ const LayerTreeSettings* settings,
OutputSurface* output_surface,
ResourceProvider* resource_provider,
+ TextureMailboxDeleter* texture_mailbox_deleter,
int highp_threshold_min)
- : DirectRenderer(client, output_surface, resource_provider),
+ : DirectRenderer(client, settings, output_surface, resource_provider),
offscreen_framebuffer_id_(0),
shared_geometry_quad_(gfx::RectF(-0.5f, -0.5f, 1.0f, 1.0f)),
- context_(output_surface->context3d()),
+ context_(output_surface->context_provider()->Context3d()),
+ texture_mailbox_deleter_(texture_mailbox_deleter),
is_backbuffer_discarded_(false),
discard_backbuffer_when_not_visible_(false),
is_using_bind_uniform_(false),
@@ -161,9 +172,7 @@ GLRenderer::GLRenderer(RendererClient* client,
blend_shadow_(false),
highp_threshold_min_(highp_threshold_min),
highp_threshold_cache_(0),
- offscreen_context_labelled_(false),
- on_demand_tile_raster_resource_id_(0),
- weak_factory_(this) {
+ on_demand_tile_raster_resource_id_(0) {
DCHECK(context_);
}
@@ -171,31 +180,17 @@ bool GLRenderer::Initialize() {
if (!context_->makeContextCurrent())
return false;
- std::string unique_context_name = base::StringPrintf(
- "%s-%p",
- Settings().compositor_name.c_str(),
- context_);
- context_->pushGroupMarkerEXT(unique_context_name.c_str());
-
- std::string extensions_string =
- UTF16ToASCII(context_->getString(GL_EXTENSIONS));
- std::vector<std::string> extensions_list;
- base::SplitString(extensions_string, ' ', &extensions_list);
- std::set<std::string> extensions(extensions_list.begin(),
- extensions_list.end());
+ ContextProvider::Capabilities context_caps =
+ output_surface_->context_provider()->ContextCapabilities();
capabilities_.using_partial_swap =
- Settings().partial_swap_enabled &&
- extensions.count("GL_CHROMIUM_post_sub_buffer");
+ settings_->partial_swap_enabled && context_caps.post_sub_buffer;
- capabilities_.using_set_visibility =
- extensions.count("GL_CHROMIUM_set_visibility") > 0;
+ capabilities_.using_set_visibility = context_caps.set_visibility;
- if (extensions.count("GL_CHROMIUM_iosurface") > 0)
- DCHECK_GT(extensions.count("GL_ARB_texture_rectangle"), 0u);
+ DCHECK(!context_caps.iosurface || context_caps.texture_rectangle);
- capabilities_.using_egl_image =
- extensions.count("GL_OES_EGL_image_external") > 0;
+ capabilities_.using_egl_image = context_caps.egl_image_external;
capabilities_.max_texture_size = resource_provider_->max_texture_size();
capabilities_.best_texture_format = resource_provider_->best_texture_format();
@@ -205,17 +200,17 @@ bool GLRenderer::Initialize() {
// Check for texture fast paths. Currently we always use MO8 textures,
// so we only need to avoid POT textures if we have an NPOT fast-path.
- capabilities_.avoid_pow2_textures =
- extensions.count("GL_CHROMIUM_fast_NPOT_MO8_textures") > 0;
+ capabilities_.avoid_pow2_textures = context_caps.fast_npot_mo8_textures;
capabilities_.using_offscreen_context3d = true;
capabilities_.using_map_image =
- extensions.count("GL_CHROMIUM_map_image") > 0 &&
- Settings().use_map_image;
+ settings_->use_map_image && context_caps.map_image;
+
+ capabilities_.using_discard_framebuffer =
+ context_caps.discard_framebuffer;
- is_using_bind_uniform_ =
- extensions.count("GL_CHROMIUM_bind_uniform_location") > 0;
+ is_using_bind_uniform_ = context_caps.bind_uniform_location;
if (!InitializeSharedObjects())
return false;
@@ -244,7 +239,6 @@ GLRenderer::~GLRenderer() {
pending_async_read_pixels_.pop_back();
}
- context_->setMemoryAllocationChangedCallbackCHROMIUM(NULL);
CleanupSharedObjects();
}
@@ -296,11 +290,25 @@ void GLRenderer::ViewportChanged() {
ReinitializeGrCanvas();
}
-void GLRenderer::ClearFramebuffer(DrawingFrame* frame) {
+void GLRenderer::DiscardPixels(bool has_external_stencil_test,
+ bool draw_rect_covers_full_surface) {
+ if (has_external_stencil_test || !draw_rect_covers_full_surface ||
+ !capabilities_.using_discard_framebuffer)
+ return;
+ bool using_default_framebuffer =
+ !current_framebuffer_lock_ &&
+ output_surface_->capabilities().uses_default_gl_framebuffer;
+ GLenum attachments[] = {static_cast<GLenum>(
+ using_default_framebuffer ? GL_COLOR_EXT : GL_COLOR_ATTACHMENT0_EXT)};
+ context_->discardFramebufferEXT(
+ GL_FRAMEBUFFER, arraysize(attachments), attachments);
+}
+
+void GLRenderer::ClearFramebuffer(DrawingFrame* frame,
+ bool has_external_stencil_test) {
// It's unsafe to clear when we have a stencil test because glClear ignores
// stencil.
- if (client_->ExternalStencilTestEnabled() &&
- frame->current_render_pass == frame->root_render_pass) {
+ if (has_external_stencil_test) {
DCHECK(!frame->current_render_pass->has_transparent_background);
return;
}
@@ -471,13 +479,12 @@ void GLRenderer::DrawDebugBorderQuad(const DrawingFrame* frame,
}
static inline SkBitmap ApplyFilters(GLRenderer* renderer,
+ ContextProvider* offscreen_contexts,
const FilterOperations& filters,
ScopedResource* source_texture_resource) {
if (filters.IsEmpty())
return SkBitmap();
- ContextProvider* offscreen_contexts =
- renderer->resource_provider()->offscreen_context_provider();
if (!offscreen_contexts || !offscreen_contexts->GrContext())
return SkBitmap();
@@ -492,9 +499,6 @@ static inline SkBitmap ApplyFilters(GLRenderer* renderer,
// Make sure skia uses the correct GL context.
offscreen_contexts->Context3d()->makeContextCurrent();
- // Lazily label this context.
- renderer->LazyLabelOffscreenContext();
-
SkBitmap source =
RenderSurfaceFilters::Apply(filters,
lock.texture_id(),
@@ -510,18 +514,18 @@ static inline SkBitmap ApplyFilters(GLRenderer* renderer,
offscreen_contexts->Context3d()->flush();
// Use the compositor's GL context again.
- renderer->resource_provider()->GraphicsContext3D()->makeContextCurrent();
+ renderer->Context()->makeContextCurrent();
return source;
}
static SkBitmap ApplyImageFilter(GLRenderer* renderer,
+ ContextProvider* offscreen_contexts,
+ gfx::Point origin,
SkImageFilter* filter,
ScopedResource* source_texture_resource) {
if (!filter)
return SkBitmap();
- ContextProvider* offscreen_contexts =
- renderer->resource_provider()->offscreen_context_provider();
if (!offscreen_contexts || !offscreen_contexts->GrContext())
return SkBitmap();
@@ -536,9 +540,6 @@ static SkBitmap ApplyImageFilter(GLRenderer* renderer,
// Make sure skia uses the correct GL context.
offscreen_contexts->Context3d()->makeContextCurrent();
- // Lazily label this context.
- renderer->LazyLabelOffscreenContext();
-
// Wrap the source texture in a Ganesh platform texture.
GrBackendTextureDesc backend_texture_description;
backend_texture_description.fWidth = source_texture_resource->size().width();
@@ -581,6 +582,11 @@ static SkBitmap ApplyImageFilter(GLRenderer* renderer,
SkPaint paint;
paint.setImageFilter(filter);
canvas.clear(SK_ColorTRANSPARENT);
+
+ // TODO(senorblanco): in addition to the origin translation here, the canvas
+ // should also be scaled to accomodate device pixel ratio and pinch zoom. See
+ // crbug.com/281516 and crbug.com/281518.
+ canvas.translate(SkIntToScalar(-origin.x()), SkIntToScalar(-origin.y()));
canvas.drawSprite(source, 0, 0, &paint);
// Flush skia context so that all the rendered stuff appears on the
@@ -592,7 +598,7 @@ static SkBitmap ApplyImageFilter(GLRenderer* renderer,
offscreen_contexts->Context3d()->flush();
// Use the compositor's GL context again.
- renderer->resource_provider()->GraphicsContext3D()->makeContextCurrent();
+ renderer->Context()->makeContextCurrent();
return device.accessBitmap(false);
}
@@ -651,8 +657,8 @@ scoped_ptr<ScopedResource> GLRenderer::DrawBackgroundFilters(
scoped_ptr<ScopedResource> device_background_texture =
ScopedResource::create(resource_provider_);
if (!device_background_texture->Allocate(window_rect.size(),
- GL_RGB,
- ResourceProvider::TextureUsageAny)) {
+ ResourceProvider::TextureUsageAny,
+ RGBA_8888)) {
return scoped_ptr<ScopedResource>();
} else {
ResourceProvider::ScopedWriteLockGL lock(resource_provider_,
@@ -663,7 +669,10 @@ scoped_ptr<ScopedResource> GLRenderer::DrawBackgroundFilters(
}
SkBitmap filtered_device_background =
- ApplyFilters(this, filters, device_background_texture.get());
+ ApplyFilters(this,
+ frame->offscreen_context_provider,
+ filters,
+ device_background_texture.get());
if (!filtered_device_background.getTexture())
return scoped_ptr<ScopedResource>();
@@ -674,8 +683,8 @@ scoped_ptr<ScopedResource> GLRenderer::DrawBackgroundFilters(
scoped_ptr<ScopedResource> background_texture =
ScopedResource::create(resource_provider_);
if (!background_texture->Allocate(quad->rect.size(),
- GL_RGBA,
- ResourceProvider::TextureUsageFramebuffer))
+ ResourceProvider::TextureUsageFramebuffer,
+ RGBA_8888))
return scoped_ptr<ScopedResource>();
const RenderPass* target_render_pass = frame->current_render_pass;
@@ -777,8 +786,11 @@ void GLRenderer::DrawRenderPassQuad(DrawingFrame* frame,
// in the compositor.
use_color_matrix = true;
} else {
- filter_bitmap =
- ApplyImageFilter(this, quad->filter.get(), contents_texture);
+ filter_bitmap = ApplyImageFilter(this,
+ frame->offscreen_context_provider,
+ quad->rect.origin(),
+ quad->filter.get(),
+ contents_texture);
}
} else if (!quad->filters.IsEmpty()) {
FilterOperations optimized_filters =
@@ -790,7 +802,10 @@ void GLRenderer::DrawRenderPassQuad(DrawingFrame* frame,
color_matrix, optimized_filters.at(0).matrix(), sizeof(color_matrix));
use_color_matrix = true;
} else {
- filter_bitmap = ApplyFilters(this, optimized_filters, contents_texture);
+ filter_bitmap = ApplyFilters(this,
+ frame->offscreen_context_provider,
+ optimized_filters,
+ contents_texture);
}
}
@@ -1130,11 +1145,12 @@ static void SolidColorUniformLocation(T program,
uniforms->color_location = program->fragment_shader().color_location();
}
+// static
bool GLRenderer::SetupQuadForAntialiasing(
const gfx::Transform& device_transform,
const DrawQuad* quad,
gfx::QuadF* local_quad,
- float edge[24]) const {
+ float edge[24]) {
gfx::Rect tile_rect = quad->visible_rect;
bool clipped = false;
@@ -1145,11 +1161,8 @@ bool GLRenderer::SetupQuadForAntialiasing(
bool is_nearest_rect_within_epsilon = is_axis_aligned_in_target &&
gfx::IsNearestRectWithinDistance(device_layer_quad.BoundingBox(),
kAntiAliasingEpsilon);
-
- bool use_aa = Settings().allow_antialiasing &&
- !clipped && // code can't handle clipped quads
- !is_nearest_rect_within_epsilon &&
- quad->IsEdge();
+ // AAing clipped quads is not supported by the code yet.
+ bool use_aa = !clipped && !is_nearest_rect_within_epsilon && quad->IsEdge();
if (!use_aa)
return false;
@@ -1238,8 +1251,9 @@ void GLRenderer::DrawSolidColorQuad(const DrawingFrame* frame,
gfx::QuadF local_quad = gfx::QuadF(gfx::RectF(tile_rect));
float edge[24];
- bool use_aa = !quad->force_anti_aliasing_off && SetupQuadForAntialiasing(
- device_transform, quad, &local_quad, edge);
+ bool use_aa =
+ settings_->allow_antialiasing && !quad->force_anti_aliasing_off &&
+ SetupQuadForAntialiasing(device_transform, quad, &local_quad, edge);
SolidColorProgramUniforms uniforms;
if (use_aa)
@@ -1324,20 +1338,11 @@ void GLRenderer::DrawContentQuad(const DrawingFrame* frame,
ResourceProvider::ResourceId resource_id) {
gfx::Rect tile_rect = quad->visible_rect;
- gfx::RectF tex_coord_rect = quad->tex_coord_rect;
- float tex_to_geom_scale_x = quad->rect.width() / tex_coord_rect.width();
- float tex_to_geom_scale_y = quad->rect.height() / tex_coord_rect.height();
-
- // tex_coord_rect corresponds to quad_rect, but quad_visible_rect may be
- // smaller than quad_rect due to occlusion or clipping. Adjust
- // tex_coord_rect to match.
- gfx::Vector2d top_left_diff = tile_rect.origin() - quad->rect.origin();
- gfx::Vector2d bottom_right_diff =
- tile_rect.bottom_right() - quad->rect.bottom_right();
- tex_coord_rect.Inset(top_left_diff.x() / tex_to_geom_scale_x,
- top_left_diff.y() / tex_to_geom_scale_y,
- -bottom_right_diff.x() / tex_to_geom_scale_x,
- -bottom_right_diff.y() / tex_to_geom_scale_y);
+ gfx::RectF tex_coord_rect = MathUtil::ScaleRectProportional(
+ quad->tex_coord_rect, quad->rect, tile_rect);
+ float tex_to_geom_scale_x = quad->rect.width() / quad->tex_coord_rect.width();
+ float tex_to_geom_scale_y =
+ quad->rect.height() / quad->tex_coord_rect.height();
gfx::RectF clamp_geom_rect(tile_rect);
gfx::RectF clamp_tex_rect(tex_coord_rect);
@@ -1386,7 +1391,7 @@ void GLRenderer::DrawContentQuad(const DrawingFrame* frame,
gfx::QuadF local_quad = gfx::QuadF(gfx::RectF(tile_rect));
float edge[24];
- bool use_aa = SetupQuadForAntialiasing(
+ bool use_aa = settings_->allow_antialiasing && SetupQuadForAntialiasing(
device_transform, quad, &local_quad, edge);
TileProgramUniforms uniforms;
@@ -1701,20 +1706,37 @@ void GLRenderer::DrawPictureQuad(const DrawingFrame* frame,
on_demand_tile_raster_resource_id_ = resource_provider_->CreateGLTexture(
quad->texture_size,
- GL_RGBA,
GL_TEXTURE_POOL_UNMANAGED_CHROMIUM,
- ResourceProvider::TextureUsageAny);
+ GL_CLAMP_TO_EDGE,
+ ResourceProvider::TextureUsageAny,
+ quad->texture_format);
}
- SkDevice device(on_demand_tile_raster_bitmap_);
+ SkBitmapDevice device(on_demand_tile_raster_bitmap_);
SkCanvas canvas(&device);
quad->picture_pile->RasterToBitmap(&canvas, quad->content_rect,
quad->contents_scale, NULL);
+ uint8_t* bitmap_pixels = NULL;
+ SkBitmap on_demand_tile_raster_bitmap_dest;
+ SkBitmap::Config config = SkBitmapConfigFromFormat(quad->texture_format);
+ if (on_demand_tile_raster_bitmap_.getConfig() != config) {
+ on_demand_tile_raster_bitmap_.copyTo(&on_demand_tile_raster_bitmap_dest,
+ config);
+ // TODO(kaanb): The GL pipeline assumes a 4-byte alignment for the
+ // bitmap data. This check will be removed once crbug.com/293728 is fixed.
+ CHECK_EQ(0u, on_demand_tile_raster_bitmap_dest.rowBytes() % 4);
+ bitmap_pixels = reinterpret_cast<uint8_t*>(
+ on_demand_tile_raster_bitmap_dest.getPixels());
+ } else {
+ bitmap_pixels = reinterpret_cast<uint8_t*>(
+ on_demand_tile_raster_bitmap_.getPixels());
+ }
+
resource_provider_->SetPixels(
on_demand_tile_raster_resource_id_,
- reinterpret_cast<uint8_t*>(on_demand_tile_raster_bitmap_.getPixels()),
+ bitmap_pixels,
gfx::Rect(quad->texture_size),
gfx::Rect(quad->texture_size),
gfx::Vector2d());
@@ -1965,11 +1987,8 @@ void GLRenderer::CopyCurrentRenderPassToBitmap(
DrawingFrame* frame,
scoped_ptr<CopyOutputRequest> request) {
gfx::Rect copy_rect = frame->current_render_pass->output_rect;
- if (request->has_area()) {
- // Intersect with the request's area, positioned with its origin at the
- // origin of the full copy_rect.
- copy_rect.Intersect(request->area() - copy_rect.OffsetFromOrigin());
- }
+ if (request->has_area())
+ copy_rect.Intersect(request->area());
GetFramebufferPixelsAsync(copy_rect, request.Pass());
}
@@ -2095,7 +2114,7 @@ void GLRenderer::SwapBuffers() {
compositor_frame.metadata = client_->MakeCompositorFrameMetadata();
compositor_frame.gl_frame_data = make_scoped_ptr(new GLFrameData);
compositor_frame.gl_frame_data->size = output_surface_->SurfaceSize();
- if (capabilities_.using_partial_swap && client_->AllowPartialSwap()) {
+ if (capabilities_.using_partial_swap) {
// If supported, we can save significant bandwidth by only swapping the
// damaged/scissored region (clamped to the viewport)
swap_buffer_rect_.Intersect(client_->DeviceViewport());
@@ -2178,30 +2197,6 @@ void GLRenderer::GetFramebufferPixels(void* pixels, gfx::Rect rect) {
AsyncGetFramebufferPixelsCleanupCallback());
}
-void GLRenderer::DeleteTextureReleaseCallbackOnImplThread(unsigned texture_id,
- unsigned sync_point,
- bool lost_resource) {
- if (sync_point)
- context_->waitSyncPoint(sync_point);
- context_->deleteTexture(texture_id);
-}
-
-// static
-void GLRenderer::DeleteTextureReleaseCallback(
- scoped_refptr<base::SingleThreadTaskRunner> task_runner,
- base::WeakPtr<GLRenderer> gl_renderer,
- unsigned texture_id,
- unsigned sync_point,
- bool lost_resource) {
- task_runner->PostTask(
- FROM_HERE,
- base::Bind(&GLRenderer::DeleteTextureReleaseCallbackOnImplThread,
- gl_renderer,
- texture_id,
- sync_point,
- lost_resource));
-}
-
void GLRenderer::GetFramebufferPixelsAsync(
gfx::Rect rect, scoped_ptr<CopyOutputRequest> request) {
DCHECK(!request->IsEmpty());
@@ -2210,10 +2205,6 @@ void GLRenderer::GetFramebufferPixelsAsync(
if (rect.IsEmpty())
return;
- DCHECK(gfx::Rect(current_surface_size_).Contains(rect)) <<
- "current_surface_size_: " << current_surface_size_.ToString() <<
- " rect: " << rect.ToString();
-
gfx::Rect window_rect = MoveFromDrawToWindowSpace(rect);
if (!request->force_bitmap_result()) {
@@ -2227,7 +2218,7 @@ void GLRenderer::GetFramebufferPixelsAsync(
GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
GLC(context_, context_->texParameteri(
GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
- GetFramebufferTexture(texture_id, GL_RGBA, window_rect);
+ GetFramebufferTexture(texture_id, RGBA_8888, window_rect);
gpu::Mailbox mailbox;
unsigned sync_point = 0;
@@ -2243,15 +2234,13 @@ void GLRenderer::GetFramebufferPixelsAsync(
GL_TEXTURE_2D, mailbox.name));
GLC(context_, context_->bindTexture(GL_TEXTURE_2D, 0));
sync_point = context_->insertSyncPoint();
- scoped_ptr<TextureMailbox> texture_mailbox = make_scoped_ptr(
- new TextureMailbox(mailbox,
- base::Bind(&GLRenderer::DeleteTextureReleaseCallback,
- base::MessageLoopProxy::current(),
- weak_factory_.GetWeakPtr(),
- texture_id),
- GL_TEXTURE_2D,
- sync_point));
- request->SendTextureResult(window_rect.size(), texture_mailbox.Pass());
+ TextureMailbox texture_mailbox(mailbox, GL_TEXTURE_2D, sync_point);
+ scoped_ptr<SingleReleaseCallback> release_callback =
+ texture_mailbox_deleter_->GetReleaseCallback(
+ output_surface_->context_provider(), texture_id);
+ request->SendTextureResult(window_rect.size(),
+ texture_mailbox,
+ release_callback.Pass());
return;
}
@@ -2321,7 +2310,7 @@ void GLRenderer::DoGetFramebufferPixels(
// Copy the contents of the current (IOSurface-backed) framebuffer into a
// temporary texture.
GetFramebufferTexture(temporary_texture,
- GL_RGBA,
+ RGBA_8888,
gfx::Rect(current_surface_size_));
temporary_fbo = context_->createFramebuffer();
// Attach this texture to an FBO, and perform the readback from that FBO.
@@ -2348,7 +2337,7 @@ void GLRenderer::DoGetFramebufferPixels(
if (is_async) {
query = context_->createQueryEXT();
GLC(context_, context_->beginQueryEXT(
- GL_ASYNC_READ_PIXELS_COMPLETED_CHROMIUM,
+ GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM,
query));
}
@@ -2389,7 +2378,7 @@ void GLRenderer::DoGetFramebufferPixels(
if (is_async) {
GLC(context_, context_->endQueryEXT(
- GL_ASYNC_READ_PIXELS_COMPLETED_CHROMIUM));
+ GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM));
SyncPointHelper::SignalQuery(
context_,
query,
@@ -2471,9 +2460,8 @@ void GLRenderer::PassOnSkBitmap(
request->SendBitmapResult(bitmap.Pass());
}
-void GLRenderer::GetFramebufferTexture(unsigned texture_id,
- unsigned texture_format,
- gfx::Rect window_rect) {
+void GLRenderer::GetFramebufferTexture(
+ unsigned texture_id, ResourceFormat texture_format, gfx::Rect window_rect) {
DCHECK(texture_id);
DCHECK_GE(window_rect.x(), 0);
DCHECK_GE(window_rect.y(), 0);
@@ -2482,14 +2470,15 @@ void GLRenderer::GetFramebufferTexture(unsigned texture_id,
GLC(context_, context_->bindTexture(GL_TEXTURE_2D, texture_id));
GLC(context_,
- context_->copyTexImage2D(GL_TEXTURE_2D,
- 0,
- texture_format,
- window_rect.x(),
- window_rect.y(),
- window_rect.width(),
- window_rect.height(),
- 0));
+ context_->copyTexImage2D(
+ GL_TEXTURE_2D,
+ 0,
+ ResourceProvider::GetGLDataFormat(texture_format),
+ window_rect.x(),
+ window_rect.y(),
+ window_rect.width(),
+ window_rect.height(),
+ 0));
GLC(context_, context_->bindTexture(GL_TEXTURE_2D, 0));
}
@@ -2507,7 +2496,7 @@ void GLRenderer::BindFramebufferToOutputSurface(DrawingFrame* frame) {
current_framebuffer_lock_.reset();
output_surface_->BindFramebuffer();
- if (client_->ExternalStencilTestEnabled()) {
+ if (output_surface_->HasExternalStencilTest()) {
SetStencilEnabled(true);
GLC(context_, context_->stencilFunc(GL_EQUAL, 1, 1));
} else {
@@ -3114,7 +3103,7 @@ void GLRenderer::ReinitializeGrCanvas() {
skia::RefPtr<GrSurface> surface(
skia::AdoptRef(gr_context_->wrapBackendRenderTarget(desc)));
- skia::RefPtr<SkDevice> device(
+ skia::RefPtr<SkBaseDevice> device(
skia::AdoptRef(SkGpuDevice::Create(surface.get())));
sk_canvas_ = skia::AdoptRef(new SkCanvas(device.get()));
}
@@ -3149,17 +3138,4 @@ bool GLRenderer::IsContextLost() {
return (context_->getGraphicsResetStatusARB() != GL_NO_ERROR);
}
-void GLRenderer::LazyLabelOffscreenContext() {
- if (offscreen_context_labelled_)
- return;
- offscreen_context_labelled_ = true;
- std::string unique_context_name = base::StringPrintf(
- "%s-Offscreen-%p",
- Settings().compositor_name.c_str(),
- context_);
- resource_provider()->offscreen_context_provider()->Context3d()->
- pushGroupMarkerEXT(unique_context_name.c_str());
-}
-
-
} // namespace cc
diff --git a/chromium/cc/output/gl_renderer.h b/chromium/cc/output/gl_renderer.h
index b1a3643b2ba..bc47870063d 100644
--- a/chromium/cc/output/gl_renderer.h
+++ b/chromium/cc/output/gl_renderer.h
@@ -33,17 +33,21 @@ class PictureDrawQuad;
class ScopedResource;
class StreamVideoDrawQuad;
class TextureDrawQuad;
+class TextureMailboxDeleter;
class GeometryBinding;
class ScopedEnsureFramebufferAllocation;
// Class that handles drawing of composited render layers using GL.
class CC_EXPORT GLRenderer : public DirectRenderer {
public:
- static scoped_ptr<GLRenderer> Create(RendererClient* client,
- OutputSurface* output_surface,
- ResourceProvider* resource_provider,
- int highp_threshold_min,
- bool use_skia_gpu_backend);
+ static scoped_ptr<GLRenderer> Create(
+ RendererClient* client,
+ const LayerTreeSettings* settings,
+ OutputSurface* output_surface,
+ ResourceProvider* resource_provider,
+ TextureMailboxDeleter* texture_mailbox_deleter,
+ int highp_threshold_min,
+ bool use_skia_gpu_backend);
virtual ~GLRenderer();
@@ -77,12 +81,13 @@ class CC_EXPORT GLRenderer : public DirectRenderer {
int line);
bool CanUseSkiaGPUBackend() const;
- void LazyLabelOffscreenContext();
protected:
GLRenderer(RendererClient* client,
+ const LayerTreeSettings* settings,
OutputSurface* output_surface,
ResourceProvider* resource_provider,
+ TextureMailboxDeleter* texture_mailbox_deleter,
int highp_threshold_min);
bool IsBackbufferDiscarded() const { return is_backbuffer_discarded_; }
@@ -97,7 +102,7 @@ class CC_EXPORT GLRenderer : public DirectRenderer {
void GetFramebufferPixelsAsync(gfx::Rect rect,
scoped_ptr<CopyOutputRequest> request);
void GetFramebufferTexture(unsigned texture_id,
- unsigned texture_format,
+ ResourceFormat texture_format,
gfx::Rect device_rect);
void ReleaseRenderPassTextures();
@@ -112,7 +117,10 @@ class CC_EXPORT GLRenderer : public DirectRenderer {
gfx::Rect target_rect) OVERRIDE;
virtual void SetDrawViewport(gfx::Rect window_space_viewport) OVERRIDE;
virtual void SetScissorTestRect(gfx::Rect scissor_rect) OVERRIDE;
- virtual void ClearFramebuffer(DrawingFrame* frame) OVERRIDE;
+ virtual void DiscardPixels(bool has_external_stencil_test,
+ bool draw_rect_covers_full_surface) OVERRIDE;
+ virtual void ClearFramebuffer(DrawingFrame* frame,
+ bool has_external_stencil_test) OVERRIDE;
virtual void DoDrawQuad(DrawingFrame* frame, const class DrawQuad*) OVERRIDE;
virtual void BeginDrawingFrame(DrawingFrame* frame) OVERRIDE;
virtual void FinishDrawingFrame(DrawingFrame* frame) OVERRIDE;
@@ -124,6 +132,17 @@ class CC_EXPORT GLRenderer : public DirectRenderer {
scoped_ptr<CopyOutputRequest> request) OVERRIDE;
virtual void FinishDrawingQuadList() OVERRIDE;
+ // Check if quad needs antialiasing and if so, inflate the quad and
+ // fill edge array for fragment shader. local_quad is set to
+ // inflated quad if antialiasing is required, otherwise it is left
+ // unchanged. edge array is filled with inflated quad's edge data
+ // if antialiasing is required, otherwise it is left unchanged.
+ // Returns true if quad requires antialiasing and false otherwise.
+ static bool SetupQuadForAntialiasing(const gfx::Transform& device_transform,
+ const DrawQuad* quad,
+ gfx::QuadF* local_quad,
+ float edge[24]);
+
private:
friend class GLRendererShaderPixelTest;
friend class GLRendererShaderTest;
@@ -174,17 +193,6 @@ class CC_EXPORT GLRenderer : public DirectRenderer {
const gfx::Transform& draw_matrix,
bool flip_vertically);
- // Check if quad needs antialiasing and if so, inflate the quad and
- // fill edge array for fragment shader. local_quad is set to
- // inflated quad if antialiasing is required, otherwise it is left
- // unchanged. edge array is filled with inflated quad's edge data
- // if antialiasing is required, otherwise it is left unchanged.
- // Returns true if quad requires antialiasing and false otherwise.
- bool SetupQuadForAntialiasing(const gfx::Transform& device_transform,
- const DrawQuad* quad,
- gfx::QuadF* local_quad,
- float edge[24]) const;
-
bool UseScopedTexture(DrawingFrame* frame,
const ScopedResource* resource,
gfx::Rect viewport_rect);
@@ -212,16 +220,6 @@ class CC_EXPORT GLRenderer : public DirectRenderer {
scoped_ptr<CopyOutputRequest> request,
bool success);
- static void DeleteTextureReleaseCallback(
- scoped_refptr<base::SingleThreadTaskRunner> task_runner,
- base::WeakPtr<GLRenderer> gl_renderer,
- unsigned texture_id,
- unsigned sync_point,
- bool lost_resource);
- void DeleteTextureReleaseCallbackOnImplThread(unsigned texture_id,
- unsigned sync_point,
- bool lost_resource);
-
void ReinitializeGrCanvas();
void ReinitializeGLState();
@@ -433,6 +431,8 @@ class CC_EXPORT GLRenderer : public DirectRenderer {
skia::RefPtr<GrContext> gr_context_;
skia::RefPtr<SkCanvas> sk_canvas_;
+ TextureMailboxDeleter* texture_mailbox_deleter_;
+
gfx::Rect swap_buffer_rect_;
gfx::Rect scissor_rect_;
gfx::Rect viewport_;
@@ -447,7 +447,6 @@ class CC_EXPORT GLRenderer : public DirectRenderer {
TexturedQuadDrawCache draw_cache_;
int highp_threshold_min_;
int highp_threshold_cache_;
- bool offscreen_context_labelled_;
struct PendingAsyncReadPixels;
ScopedPtrVector<PendingAsyncReadPixels> pending_async_read_pixels_;
@@ -459,8 +458,6 @@ class CC_EXPORT GLRenderer : public DirectRenderer {
SkBitmap on_demand_tile_raster_bitmap_;
ResourceProvider::ResourceId on_demand_tile_raster_resource_id_;
- base::WeakPtrFactory<GLRenderer> weak_factory_;
-
DISALLOW_COPY_AND_ASSIGN(GLRenderer);
};
diff --git a/chromium/cc/output/gl_renderer_unittest.cc b/chromium/cc/output/gl_renderer_unittest.cc
index e4213d65842..836287f4448 100644
--- a/chromium/cc/output/gl_renderer_unittest.cc
+++ b/chromium/cc/output/gl_renderer_unittest.cc
@@ -7,6 +7,7 @@
#include <set>
#include "cc/base/math_util.h"
+#include "cc/debug/test_web_graphics_context_3d.h"
#include "cc/output/compositor_frame_metadata.h"
#include "cc/resources/prioritized_resource_manager.h"
#include "cc/resources/resource_provider.h"
@@ -14,11 +15,11 @@
#include "cc/test/fake_impl_proxy.h"
#include "cc/test/fake_layer_tree_host_impl.h"
#include "cc/test/fake_output_surface.h"
+#include "cc/test/fake_output_surface_client.h"
#include "cc/test/mock_quad_culler.h"
#include "cc/test/pixel_test.h"
#include "cc/test/render_pass_test_common.h"
#include "cc/test/render_pass_test_utils.h"
-#include "cc/test/test_web_graphics_context_3d.h"
#include "gpu/GLES2/gl2extchromium.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -31,7 +32,9 @@
using testing::_;
using testing::AnyNumber;
+using testing::Args;
using testing::AtLeast;
+using testing::ElementsAre;
using testing::Expectation;
using testing::InSequence;
using testing::Mock;
@@ -116,19 +119,16 @@ TEST_F(GLRendererShaderPixelTest, AllShadersCompile) { TestShaders(); }
class FrameCountingContext : public TestWebGraphicsContext3D {
public:
- FrameCountingContext() : frame_(0) {}
+ FrameCountingContext()
+ : frame_(0) {
+ test_capabilities_.set_visibility = true;
+ test_capabilities_.discard_backbuffer = true;
+ }
// WebGraphicsContext3D methods.
// This method would normally do a glSwapBuffers under the hood.
virtual void prepareTexture() { frame_++; }
- virtual WebString getString(WebKit::WGC3Denum name) {
- if (name == GL_EXTENSIONS)
- return WebString(
- "GL_CHROMIUM_set_visibility GL_CHROMIUM_gpu_memory_manager "
- "GL_CHROMIUM_discard_backbuffer");
- return WebString();
- }
// Methods added for test.
int frame_count() { return frame_; }
@@ -143,9 +143,8 @@ class FakeRendererClient : public RendererClient {
: host_impl_(&proxy_),
set_full_root_layer_damage_count_(0),
root_layer_(LayerImpl::Create(host_impl_.active_tree(), 1)),
- viewport_size_(gfx::Size(1, 1)),
- scale_factor_(1.f),
- external_stencil_test_enabled_(false) {
+ viewport_(gfx::Rect(0, 0, 1, 1)),
+ clip_(gfx::Rect(0, 0, 1, 1)) {
root_layer_->CreateRenderSurface();
RenderPass::Id render_pass_id =
root_layer_->render_surface()->RenderPassId();
@@ -156,45 +155,21 @@ class FakeRendererClient : public RendererClient {
}
// RendererClient methods.
- virtual gfx::Rect DeviceViewport() const OVERRIDE {
- static gfx::Size fake_size(1, 1);
- return gfx::Rect(fake_size);
- }
- virtual float DeviceScaleFactor() const OVERRIDE {
- return scale_factor_;
- }
- virtual const LayerTreeSettings& Settings() const OVERRIDE {
- static LayerTreeSettings fake_settings;
- return fake_settings;
- }
+ virtual gfx::Rect DeviceViewport() const OVERRIDE { return viewport_; }
+ virtual gfx::Rect DeviceClip() const OVERRIDE { return clip_; }
virtual void SetFullRootLayerDamage() OVERRIDE {
set_full_root_layer_damage_count_++;
}
- virtual bool HasImplThread() const OVERRIDE { return false; }
- virtual bool ShouldClearRootRenderPass() const OVERRIDE { return true; }
virtual CompositorFrameMetadata MakeCompositorFrameMetadata() const OVERRIDE {
return CompositorFrameMetadata();
}
- virtual bool AllowPartialSwap() const OVERRIDE {
- return true;
- }
- virtual bool ExternalStencilTestEnabled() const OVERRIDE {
- return external_stencil_test_enabled_;
- }
-
- void EnableExternalStencilTest() {
- external_stencil_test_enabled_ = true;
- }
// Methods added for test.
int set_full_root_layer_damage_count() const {
return set_full_root_layer_damage_count_;
}
- void set_viewport_and_scale(
- gfx::Size viewport_size, float scale_factor) {
- viewport_size_ = viewport_size;
- scale_factor_ = scale_factor;
- }
+ void set_viewport(gfx::Rect viewport) { viewport_ = viewport; }
+ void set_clip(gfx::Rect clip) { clip_ = clip; }
RenderPass* root_render_pass() { return render_passes_in_draw_order_.back(); }
RenderPassList* render_passes_in_draw_order() {
@@ -207,17 +182,22 @@ class FakeRendererClient : public RendererClient {
int set_full_root_layer_damage_count_;
scoped_ptr<LayerImpl> root_layer_;
RenderPassList render_passes_in_draw_order_;
- gfx::Size viewport_size_;
- float scale_factor_;
- bool external_stencil_test_enabled_;
+ gfx::Rect viewport_;
+ gfx::Rect clip_;
};
class FakeRendererGL : public GLRenderer {
public:
FakeRendererGL(RendererClient* client,
+ const LayerTreeSettings* settings,
OutputSurface* output_surface,
ResourceProvider* resource_provider)
- : GLRenderer(client, output_surface, resource_provider, 0) {}
+ : GLRenderer(client,
+ settings,
+ output_surface,
+ resource_provider,
+ NULL,
+ 0) {}
// GLRenderer methods.
@@ -232,27 +212,33 @@ class FakeRendererGL : public GLRenderer {
class GLRendererTest : public testing::Test {
protected:
- GLRendererTest()
- : output_surface_(FakeOutputSurface::Create3d(
- scoped_ptr<WebKit::WebGraphicsContext3D>(
- new FrameCountingContext()))),
- resource_provider_(ResourceProvider::Create(output_surface_.get(), 0)),
- renderer_(&mock_client_,
- output_surface_.get(),
- resource_provider_.get()) {}
-
- virtual void SetUp() { renderer_.Initialize(); }
+ GLRendererTest() {
+ scoped_ptr<FrameCountingContext> context3d(new FrameCountingContext);
+ context3d_ = context3d.get();
- void SwapBuffers() { renderer_.SwapBuffers(); }
+ output_surface_ = FakeOutputSurface::Create3d(
+ context3d.PassAs<TestWebGraphicsContext3D>()).Pass();
+ CHECK(output_surface_->BindToClient(&output_surface_client_));
- FrameCountingContext* Context() {
- return static_cast<FrameCountingContext*>(output_surface_->context3d());
+ resource_provider_ =
+ ResourceProvider::Create(output_surface_.get(), 0, false).Pass();
+ renderer_ = make_scoped_ptr(new FakeRendererGL(&renderer_client_,
+ &settings_,
+ output_surface_.get(),
+ resource_provider_.get()));
}
- scoped_ptr<OutputSurface> output_surface_;
- FakeRendererClient mock_client_;
+ virtual void SetUp() { renderer_->Initialize(); }
+
+ void SwapBuffers() { renderer_->SwapBuffers(); }
+
+ LayerTreeSettings settings_;
+ FrameCountingContext* context3d_;
+ FakeOutputSurfaceClient output_surface_client_;
+ scoped_ptr<FakeOutputSurface> output_surface_;
+ FakeRendererClient renderer_client_;
scoped_ptr<ResourceProvider> resource_provider_;
- FakeRendererGL renderer_;
+ scoped_ptr<FakeRendererGL> renderer_;
};
// Closing the namespace here so that GLRendererShaderTest can take advantage
@@ -320,15 +306,18 @@ class ShaderCreatorMockGraphicsContext : public TestWebGraphicsContext3D {
class GLRendererShaderTest : public testing::Test {
protected:
- GLRendererShaderTest()
- : output_surface_(FakeOutputSurface::Create3d(
- scoped_ptr<WebKit::WebGraphicsContext3D>(
- new ShaderCreatorMockGraphicsContext()))),
- resource_provider_(ResourceProvider::Create(output_surface_.get(), 0)),
- renderer_(scoped_ptr<FakeRendererGL>(
- new FakeRendererGL(&mock_client_,
- output_surface_.get(),
- resource_provider_.get()))) {
+ GLRendererShaderTest() {
+ output_surface_ = FakeOutputSurface::Create3d(
+ scoped_ptr<TestWebGraphicsContext3D>(
+ new ShaderCreatorMockGraphicsContext())).Pass();
+ CHECK(output_surface_->BindToClient(&output_surface_client_));
+
+ resource_provider_ = ResourceProvider::Create(
+ output_surface_.get(), 0, false).Pass();
+ renderer_.reset(new FakeRendererGL(&renderer_client_,
+ &settings_,
+ output_surface_.get(),
+ resource_provider_.get()));
renderer_->Initialize();
}
@@ -386,8 +375,10 @@ class GLRendererShaderTest : public testing::Test {
renderer_->program_shadow_);
}
- scoped_ptr<OutputSurface> output_surface_;
- FakeRendererClient mock_client_;
+ LayerTreeSettings settings_;
+ FakeOutputSurfaceClient output_surface_client_;
+ scoped_ptr<FakeOutputSurface> output_surface_;
+ FakeRendererClient renderer_client_;
scoped_ptr<ResourceProvider> resource_provider_;
scoped_ptr<FakeRendererGL> renderer_;
};
@@ -398,12 +389,12 @@ namespace {
// Suggest recreating framebuffer when one already exists.
// Expected: it does nothing.
TEST_F(GLRendererTest, SuggestBackbufferYesWhenItAlreadyExistsShouldDoNothing) {
- renderer_.SetDiscardBackBufferWhenNotVisible(false);
- EXPECT_EQ(0, mock_client_.set_full_root_layer_damage_count());
- EXPECT_FALSE(renderer_.IsBackbufferDiscarded());
+ renderer_->SetDiscardBackBufferWhenNotVisible(false);
+ EXPECT_EQ(0, renderer_client_.set_full_root_layer_damage_count());
+ EXPECT_FALSE(renderer_->IsBackbufferDiscarded());
SwapBuffers();
- EXPECT_EQ(1, Context()->frame_count());
+ EXPECT_EQ(1, context3d_->frame_count());
}
// Test GLRenderer DiscardBackbuffer functionality:
@@ -413,76 +404,79 @@ TEST_F(GLRendererTest, SuggestBackbufferYesWhenItAlreadyExistsShouldDoNothing) {
TEST_F(
GLRendererTest,
SuggestBackbufferNoShouldDiscardBackbufferAndDamageRootLayerIfNotVisible) {
- renderer_.SetVisible(false);
- renderer_.SetDiscardBackBufferWhenNotVisible(true);
- EXPECT_EQ(1, mock_client_.set_full_root_layer_damage_count());
- EXPECT_TRUE(renderer_.IsBackbufferDiscarded());
+ renderer_->SetVisible(false);
+ renderer_->SetDiscardBackBufferWhenNotVisible(true);
+ EXPECT_EQ(1, renderer_client_.set_full_root_layer_damage_count());
+ EXPECT_TRUE(renderer_->IsBackbufferDiscarded());
}
// Test GLRenderer DiscardBackbuffer functionality:
// Suggest discarding framebuffer when one exists and the renderer is visible.
// Expected: the allocation is ignored.
TEST_F(GLRendererTest, SuggestBackbufferNoDoNothingWhenVisible) {
- renderer_.SetVisible(true);
- renderer_.SetDiscardBackBufferWhenNotVisible(true);
- EXPECT_EQ(0, mock_client_.set_full_root_layer_damage_count());
- EXPECT_FALSE(renderer_.IsBackbufferDiscarded());
+ renderer_->SetVisible(true);
+ renderer_->SetDiscardBackBufferWhenNotVisible(true);
+ EXPECT_EQ(0, renderer_client_.set_full_root_layer_damage_count());
+ EXPECT_FALSE(renderer_->IsBackbufferDiscarded());
}
// Test GLRenderer DiscardBackbuffer functionality:
// Suggest discarding framebuffer when one does not exist.
// Expected: it does nothing.
TEST_F(GLRendererTest, SuggestBackbufferNoWhenItDoesntExistShouldDoNothing) {
- renderer_.SetVisible(false);
- renderer_.SetDiscardBackBufferWhenNotVisible(true);
- EXPECT_EQ(1, mock_client_.set_full_root_layer_damage_count());
- EXPECT_TRUE(renderer_.IsBackbufferDiscarded());
-
- renderer_.SetDiscardBackBufferWhenNotVisible(true);
- EXPECT_EQ(1, mock_client_.set_full_root_layer_damage_count());
- EXPECT_TRUE(renderer_.IsBackbufferDiscarded());
+ renderer_->SetVisible(false);
+ renderer_->SetDiscardBackBufferWhenNotVisible(true);
+ EXPECT_EQ(1, renderer_client_.set_full_root_layer_damage_count());
+ EXPECT_TRUE(renderer_->IsBackbufferDiscarded());
+
+ renderer_->SetDiscardBackBufferWhenNotVisible(true);
+ EXPECT_EQ(1, renderer_client_.set_full_root_layer_damage_count());
+ EXPECT_TRUE(renderer_->IsBackbufferDiscarded());
}
// Test GLRenderer DiscardBackbuffer functionality:
// Begin drawing a frame while a framebuffer is discarded.
// Expected: will recreate framebuffer.
TEST_F(GLRendererTest, DiscardedBackbufferIsRecreatedForScopeDuration) {
- renderer_.SetVisible(false);
- renderer_.SetDiscardBackBufferWhenNotVisible(true);
- EXPECT_TRUE(renderer_.IsBackbufferDiscarded());
- EXPECT_EQ(1, mock_client_.set_full_root_layer_damage_count());
+ renderer_->SetVisible(false);
+ renderer_->SetDiscardBackBufferWhenNotVisible(true);
+ EXPECT_TRUE(renderer_->IsBackbufferDiscarded());
+ EXPECT_EQ(1, renderer_client_.set_full_root_layer_damage_count());
- renderer_.SetVisible(true);
- renderer_.DrawFrame(mock_client_.render_passes_in_draw_order());
- EXPECT_FALSE(renderer_.IsBackbufferDiscarded());
+ renderer_->SetVisible(true);
+ renderer_->DrawFrame(
+ renderer_client_.render_passes_in_draw_order(), NULL, 1.f, true);
+ EXPECT_FALSE(renderer_->IsBackbufferDiscarded());
SwapBuffers();
- EXPECT_EQ(1, Context()->frame_count());
+ EXPECT_EQ(1, context3d_->frame_count());
}
TEST_F(GLRendererTest, FramebufferDiscardedAfterReadbackWhenNotVisible) {
- renderer_.SetVisible(false);
- renderer_.SetDiscardBackBufferWhenNotVisible(true);
- EXPECT_TRUE(renderer_.IsBackbufferDiscarded());
- EXPECT_EQ(1, mock_client_.set_full_root_layer_damage_count());
+ renderer_->SetVisible(false);
+ renderer_->SetDiscardBackBufferWhenNotVisible(true);
+ EXPECT_TRUE(renderer_->IsBackbufferDiscarded());
+ EXPECT_EQ(1, renderer_client_.set_full_root_layer_damage_count());
char pixels[4];
- renderer_.DrawFrame(mock_client_.render_passes_in_draw_order());
- EXPECT_FALSE(renderer_.IsBackbufferDiscarded());
+ renderer_->DrawFrame(
+ renderer_client_.render_passes_in_draw_order(), NULL, 1.f, true);
+ EXPECT_FALSE(renderer_->IsBackbufferDiscarded());
- renderer_.GetFramebufferPixels(pixels, gfx::Rect(0, 0, 1, 1));
- EXPECT_TRUE(renderer_.IsBackbufferDiscarded());
- EXPECT_EQ(2, mock_client_.set_full_root_layer_damage_count());
+ renderer_->GetFramebufferPixels(pixels, gfx::Rect(0, 0, 1, 1));
+ EXPECT_TRUE(renderer_->IsBackbufferDiscarded());
+ EXPECT_EQ(2, renderer_client_.set_full_root_layer_damage_count());
}
TEST_F(GLRendererTest, ExternalStencil) {
- EXPECT_FALSE(renderer_.stencil_enabled());
+ EXPECT_FALSE(renderer_->stencil_enabled());
- mock_client_.EnableExternalStencilTest();
- mock_client_.root_render_pass()->has_transparent_background = false;
+ output_surface_->set_has_external_stencil_test(true);
+ renderer_client_.root_render_pass()->has_transparent_background = false;
- renderer_.DrawFrame(mock_client_.render_passes_in_draw_order());
- EXPECT_TRUE(renderer_.stencil_enabled());
+ renderer_->DrawFrame(
+ renderer_client_.render_passes_in_draw_order(), NULL, 1.f, true);
+ EXPECT_TRUE(renderer_->stencil_enabled());
}
class ForbidSynchronousCallContext : public TestWebGraphicsContext3D {
@@ -562,11 +556,7 @@ class ForbidSynchronousCallContext : public TestWebGraphicsContext3D {
}
virtual WebString getString(WGC3Denum name) {
- // We allow querying the extension string.
- // TODO(enne): It'd be better to check that we only do this before starting
- // any other expensive work (like starting a compilation)
- if (name != GL_EXTENSIONS)
- ADD_FAILURE();
+ ADD_FAILURE() << name;
return WebString();
}
@@ -638,14 +628,20 @@ class ForbidSynchronousCallContext : public TestWebGraphicsContext3D {
// This test isn't using the same fixture as GLRendererTest, and you can't mix
// TEST() and TEST_F() with the same name, Hence LRC2.
TEST(GLRendererTest2, InitializationDoesNotMakeSynchronousCalls) {
- FakeRendererClient mock_client;
- scoped_ptr<OutputSurface> output_surface(
- FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>(
- new ForbidSynchronousCallContext)));
+ FakeOutputSurfaceClient output_surface_client;
+ scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d(
+ scoped_ptr<TestWebGraphicsContext3D>(new ForbidSynchronousCallContext)));
+ CHECK(output_surface->BindToClient(&output_surface_client));
+
scoped_ptr<ResourceProvider> resource_provider(
- ResourceProvider::Create(output_surface.get(), 0));
- FakeRendererGL renderer(
- &mock_client, output_surface.get(), resource_provider.get());
+ ResourceProvider::Create(output_surface.get(), 0, false));
+
+ LayerTreeSettings settings;
+ FakeRendererClient renderer_client;
+ FakeRendererGL renderer(&renderer_client,
+ &settings,
+ output_surface.get(),
+ resource_provider.get());
EXPECT_TRUE(renderer.Initialize());
}
@@ -677,81 +673,145 @@ class LoseContextOnFirstGetContext : public TestWebGraphicsContext3D {
};
TEST(GLRendererTest2, InitializationWithQuicklyLostContextDoesNotAssert) {
- FakeRendererClient mock_client;
- scoped_ptr<OutputSurface> output_surface(
- FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>(
- new LoseContextOnFirstGetContext)));
+ FakeOutputSurfaceClient output_surface_client;
+ scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d(
+ scoped_ptr<TestWebGraphicsContext3D>(new LoseContextOnFirstGetContext)));
+ CHECK(output_surface->BindToClient(&output_surface_client));
+
scoped_ptr<ResourceProvider> resource_provider(
- ResourceProvider::Create(output_surface.get(), 0));
- FakeRendererGL renderer(
- &mock_client, output_surface.get(), resource_provider.get());
+ ResourceProvider::Create(output_surface.get(), 0, false));
+
+ LayerTreeSettings settings;
+ FakeRendererClient renderer_client;
+ FakeRendererGL renderer(&renderer_client,
+ &settings,
+ output_surface.get(),
+ resource_provider.get());
renderer.Initialize();
}
class ClearCountingContext : public TestWebGraphicsContext3D {
public:
- ClearCountingContext() : clear_(0) {}
-
- virtual void clear(WGC3Dbitfield) { clear_++; }
-
- int clear_count() const { return clear_; }
+ ClearCountingContext() {
+ test_capabilities_.discard_framebuffer = true;
+ }
- private:
- int clear_;
+ MOCK_METHOD3(discardFramebufferEXT,
+ void(WGC3Denum target,
+ WGC3Dsizei numAttachments,
+ const WGC3Denum* attachments));
+ MOCK_METHOD1(clear, void(WGC3Dbitfield mask));
};
TEST(GLRendererTest2, OpaqueBackground) {
- FakeRendererClient mock_client;
+ scoped_ptr<ClearCountingContext> context_owned(new ClearCountingContext);
+ ClearCountingContext* context = context_owned.get();
+
+ FakeOutputSurfaceClient output_surface_client;
scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d(
- scoped_ptr<WebKit::WebGraphicsContext3D>(new ClearCountingContext)));
- ClearCountingContext* context =
- static_cast<ClearCountingContext*>(output_surface->context3d());
+ context_owned.PassAs<TestWebGraphicsContext3D>()));
+ CHECK(output_surface->BindToClient(&output_surface_client));
+
scoped_ptr<ResourceProvider> resource_provider(
- ResourceProvider::Create(output_surface.get(), 0));
- FakeRendererGL renderer(
- &mock_client, output_surface.get(), resource_provider.get());
+ ResourceProvider::Create(output_surface.get(), 0, false));
- mock_client.root_render_pass()->has_transparent_background = false;
+ LayerTreeSettings settings;
+ FakeRendererClient renderer_client;
+ FakeRendererGL renderer(&renderer_client,
+ &settings,
+ output_surface.get(),
+ resource_provider.get());
- EXPECT_TRUE(renderer.Initialize());
+ renderer_client.root_render_pass()->has_transparent_background = false;
- renderer.DrawFrame(mock_client.render_passes_in_draw_order());
+ EXPECT_TRUE(renderer.Initialize());
-// On DEBUG builds, render passes with opaque background clear to blue to
-// easily see regions that were not drawn on the screen.
+ // On DEBUG builds, render passes with opaque background clear to blue to
+ // easily see regions that were not drawn on the screen.
+ EXPECT_CALL(*context, discardFramebufferEXT(GL_FRAMEBUFFER, _, _))
+ .With(Args<2, 1>(ElementsAre(GL_COLOR_EXT)))
+ .Times(1);
#ifdef NDEBUG
- EXPECT_EQ(0, context->clear_count());
+ EXPECT_CALL(*context, clear(_)).Times(0);
#else
- EXPECT_EQ(1, context->clear_count());
+ EXPECT_CALL(*context, clear(_)).Times(1);
#endif
+ renderer.DrawFrame(
+ renderer_client.render_passes_in_draw_order(), NULL, 1.f, true);
+ Mock::VerifyAndClearExpectations(context);
}
TEST(GLRendererTest2, TransparentBackground) {
- FakeRendererClient mock_client;
+ scoped_ptr<ClearCountingContext> context_owned(new ClearCountingContext);
+ ClearCountingContext* context = context_owned.get();
+
+ FakeOutputSurfaceClient output_surface_client;
scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d(
- scoped_ptr<WebKit::WebGraphicsContext3D>(new ClearCountingContext)));
- ClearCountingContext* context =
- static_cast<ClearCountingContext*>(output_surface->context3d());
+ context_owned.PassAs<TestWebGraphicsContext3D>()));
+ CHECK(output_surface->BindToClient(&output_surface_client));
+
scoped_ptr<ResourceProvider> resource_provider(
- ResourceProvider::Create(output_surface.get(), 0));
- FakeRendererGL renderer(
- &mock_client, output_surface.get(), resource_provider.get());
+ ResourceProvider::Create(output_surface.get(), 0, false));
- mock_client.root_render_pass()->has_transparent_background = true;
+ LayerTreeSettings settings;
+ FakeRendererClient renderer_client;
+ FakeRendererGL renderer(&renderer_client,
+ &settings,
+ output_surface.get(),
+ resource_provider.get());
+
+ renderer_client.root_render_pass()->has_transparent_background = true;
EXPECT_TRUE(renderer.Initialize());
- renderer.DrawFrame(mock_client.render_passes_in_draw_order());
+ EXPECT_CALL(*context, discardFramebufferEXT(GL_FRAMEBUFFER, 1, _))
+ .Times(1);
+ EXPECT_CALL(*context, clear(_)).Times(1);
+ renderer.DrawFrame(
+ renderer_client.render_passes_in_draw_order(), NULL, 1.f, true);
- EXPECT_EQ(1, context->clear_count());
+ Mock::VerifyAndClearExpectations(context);
+}
+
+TEST(GLRendererTest2, OffscreenOutputSurface) {
+ scoped_ptr<ClearCountingContext> context_owned(new ClearCountingContext);
+ ClearCountingContext* context = context_owned.get();
+
+ FakeOutputSurfaceClient output_surface_client;
+ scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::CreateOffscreen(
+ context_owned.PassAs<TestWebGraphicsContext3D>()));
+ CHECK(output_surface->BindToClient(&output_surface_client));
+
+ scoped_ptr<ResourceProvider> resource_provider(
+ ResourceProvider::Create(output_surface.get(), 0, false));
+
+ LayerTreeSettings settings;
+ FakeRendererClient renderer_client;
+ FakeRendererGL renderer(&renderer_client,
+ &settings,
+ output_surface.get(),
+ resource_provider.get());
+
+ EXPECT_TRUE(renderer.Initialize());
+
+ EXPECT_CALL(*context, discardFramebufferEXT(GL_FRAMEBUFFER, _, _))
+ .With(Args<2, 1>(ElementsAre(GL_COLOR_ATTACHMENT0)))
+ .Times(1);
+ EXPECT_CALL(*context, clear(_)).Times(AnyNumber());
+ renderer.DrawFrame(
+ renderer_client.render_passes_in_draw_order(), NULL, 1.f, true);
+ Mock::VerifyAndClearExpectations(context);
}
class VisibilityChangeIsLastCallTrackingContext
: public TestWebGraphicsContext3D {
public:
VisibilityChangeIsLastCallTrackingContext()
- : last_call_was_set_visibility_(false) {}
+ : last_call_was_set_visibility_(false) {
+ test_capabilities_.set_visibility = true;
+ test_capabilities_.discard_backbuffer = true;
+ }
// WebGraphicsContext3D methods.
virtual void setVisibilityCHROMIUM(bool visible) {
@@ -780,15 +840,6 @@ class VisibilityChangeIsLastCallTrackingContext
last_call_was_set_visibility_ = false;
}
- // This method would normally do a glSwapBuffers under the hood.
- virtual WebString getString(WebKit::WGC3Denum name) {
- if (name == GL_EXTENSIONS)
- return WebString(
- "GL_CHROMIUM_set_visibility GL_CHROMIUM_gpu_memory_manager "
- "GL_CHROMIUM_discard_backbuffer");
- return WebString();
- }
-
// Methods added for test.
bool last_call_was_set_visibility() const {
return last_call_was_set_visibility_;
@@ -799,17 +850,24 @@ class VisibilityChangeIsLastCallTrackingContext
};
TEST(GLRendererTest2, VisibilityChangeIsLastCall) {
- FakeRendererClient mock_client;
- scoped_ptr<OutputSurface> output_surface(
- FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>(
- new VisibilityChangeIsLastCallTrackingContext)));
- VisibilityChangeIsLastCallTrackingContext* context =
- static_cast<VisibilityChangeIsLastCallTrackingContext*>(
- output_surface->context3d());
+ scoped_ptr<VisibilityChangeIsLastCallTrackingContext> context_owned(
+ new VisibilityChangeIsLastCallTrackingContext);
+ VisibilityChangeIsLastCallTrackingContext* context = context_owned.get();
+
+ FakeOutputSurfaceClient output_surface_client;
+ scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d(
+ context_owned.PassAs<TestWebGraphicsContext3D>()));
+ CHECK(output_surface->BindToClient(&output_surface_client));
+
scoped_ptr<ResourceProvider> resource_provider(
- ResourceProvider::Create(output_surface.get(), 0));
- FakeRendererGL renderer(
- &mock_client, output_surface.get(), resource_provider.get());
+ ResourceProvider::Create(output_surface.get(), 0, false));
+
+ LayerTreeSettings settings;
+ FakeRendererClient renderer_client;
+ FakeRendererGL renderer(&renderer_client,
+ &settings,
+ output_surface.get(),
+ resource_provider.get());
EXPECT_TRUE(renderer.Initialize());
@@ -819,19 +877,17 @@ TEST(GLRendererTest2, VisibilityChangeIsLastCall) {
// RenderClient and the Context by giving them both a pointer to a variable on
// the stack.
renderer.SetVisible(true);
- renderer.DrawFrame(mock_client.render_passes_in_draw_order());
+ renderer.DrawFrame(
+ renderer_client.render_passes_in_draw_order(), NULL, 1.f, true);
renderer.SetVisible(false);
EXPECT_TRUE(context->last_call_was_set_visibility());
}
class TextureStateTrackingContext : public TestWebGraphicsContext3D {
public:
- TextureStateTrackingContext() : active_texture_(GL_INVALID_ENUM) {}
-
- virtual WebString getString(WGC3Denum name) {
- if (name == GL_EXTENSIONS)
- return WebString("GL_OES_EGL_image_external");
- return WebString();
+ TextureStateTrackingContext()
+ : active_texture_(GL_INVALID_ENUM) {
+ test_capabilities_.egl_image_external = true;
}
MOCK_METHOD3(texParameteri,
@@ -854,16 +910,24 @@ class TextureStateTrackingContext : public TestWebGraphicsContext3D {
};
TEST(GLRendererTest2, ActiveTextureState) {
- FakeRendererClient fake_client;
- scoped_ptr<OutputSurface> output_surface(
- FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>(
- new TextureStateTrackingContext)));
- TextureStateTrackingContext* context =
- static_cast<TextureStateTrackingContext*>(output_surface->context3d());
+ scoped_ptr<TextureStateTrackingContext> context_owned(
+ new TextureStateTrackingContext);
+ TextureStateTrackingContext* context = context_owned.get();
+
+ FakeOutputSurfaceClient output_surface_client;
+ scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d(
+ context_owned.PassAs<TestWebGraphicsContext3D>()));
+ CHECK(output_surface->BindToClient(&output_surface_client));
+
scoped_ptr<ResourceProvider> resource_provider(
- ResourceProvider::Create(output_surface.get(), 0));
- FakeRendererGL renderer(
- &fake_client, output_surface.get(), resource_provider.get());
+ ResourceProvider::Create(output_surface.get(), 0, false));
+
+ LayerTreeSettings settings;
+ FakeRendererClient renderer_client;
+ FakeRendererGL renderer(&renderer_client,
+ &settings,
+ output_surface.get(),
+ resource_provider.get());
// During initialization we are allowed to set any texture parameters.
EXPECT_CALL(*context, texParameteri(_, _, _)).Times(AnyNumber());
@@ -922,11 +986,6 @@ TEST(GLRendererTest2, ActiveTextureState) {
Mock::VerifyAndClearExpectations(context);
}
-class NoClearRootRenderPassFakeClient : public FakeRendererClient {
- public:
- virtual bool ShouldClearRootRenderPass() const OVERRIDE { return false; }
-};
-
class NoClearRootRenderPassMockContext : public TestWebGraphicsContext3D {
public:
MOCK_METHOD1(clear, void(WGC3Dbitfield mask));
@@ -938,22 +997,31 @@ class NoClearRootRenderPassMockContext : public TestWebGraphicsContext3D {
};
TEST(GLRendererTest2, ShouldClearRootRenderPass) {
- NoClearRootRenderPassFakeClient mock_client;
- scoped_ptr<OutputSurface> output_surface(
- FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>(
- new NoClearRootRenderPassMockContext)));
- NoClearRootRenderPassMockContext* mock_context =
- static_cast<NoClearRootRenderPassMockContext*>(
- output_surface->context3d());
+ scoped_ptr<NoClearRootRenderPassMockContext> mock_context_owned(
+ new NoClearRootRenderPassMockContext);
+ NoClearRootRenderPassMockContext* mock_context = mock_context_owned.get();
+
+ FakeOutputSurfaceClient output_surface_client;
+ scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d(
+ mock_context_owned.PassAs<TestWebGraphicsContext3D>()));
+ CHECK(output_surface->BindToClient(&output_surface_client));
+
scoped_ptr<ResourceProvider> resource_provider(
- ResourceProvider::Create(output_surface.get(), 0));
- FakeRendererGL renderer(
- &mock_client, output_surface.get(), resource_provider.get());
+ ResourceProvider::Create(output_surface.get(), 0, false));
+
+ LayerTreeSettings settings;
+ settings.should_clear_root_render_pass = false;
+
+ FakeRendererClient renderer_client;
+ FakeRendererGL renderer(&renderer_client,
+ &settings,
+ output_surface.get(),
+ resource_provider.get());
EXPECT_TRUE(renderer.Initialize());
- gfx::Rect viewport_rect(mock_client.DeviceViewport());
+ gfx::Rect viewport_rect(renderer_client.DeviceViewport());
ScopedPtrVector<RenderPass>& render_passes =
- *mock_client.render_passes_in_draw_order();
+ *renderer_client.render_passes_in_draw_order();
render_passes.clear();
RenderPass::Id root_pass_id(1, 0);
@@ -988,8 +1056,9 @@ TEST(GLRendererTest2, ShouldClearRootRenderPass) {
.After(first_render_pass);
renderer.DecideRenderPassAllocationsForFrame(
- *mock_client.render_passes_in_draw_order());
- renderer.DrawFrame(mock_client.render_passes_in_draw_order());
+ *renderer_client.render_passes_in_draw_order());
+ renderer.DrawFrame(
+ renderer_client.render_passes_in_draw_order(), NULL, 1.f, true);
// In multiple render passes all but the root pass should clear the
// framebuffer.
@@ -1017,20 +1086,29 @@ class ScissorTestOnClearCheckingContext : public TestWebGraphicsContext3D {
};
TEST(GLRendererTest2, ScissorTestWhenClearing) {
- FakeRendererClient mock_client;
- scoped_ptr<OutputSurface> output_surface(
- FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>(
- new ScissorTestOnClearCheckingContext)));
+ scoped_ptr<ScissorTestOnClearCheckingContext> context_owned(
+ new ScissorTestOnClearCheckingContext);
+
+ FakeOutputSurfaceClient output_surface_client;
+ scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d(
+ context_owned.PassAs<TestWebGraphicsContext3D>()));
+ CHECK(output_surface->BindToClient(&output_surface_client));
+
scoped_ptr<ResourceProvider> resource_provider(
- ResourceProvider::Create(output_surface.get(), 0));
- FakeRendererGL renderer(
- &mock_client, output_surface.get(), resource_provider.get());
+ ResourceProvider::Create(output_surface.get(), 0, false));
+
+ LayerTreeSettings settings;
+ FakeRendererClient renderer_client;
+ FakeRendererGL renderer(&renderer_client,
+ &settings,
+ output_surface.get(),
+ resource_provider.get());
EXPECT_TRUE(renderer.Initialize());
EXPECT_FALSE(renderer.Capabilities().using_partial_swap);
- gfx::Rect viewport_rect(mock_client.DeviceViewport());
+ gfx::Rect viewport_rect(renderer_client.DeviceViewport());
ScopedPtrVector<RenderPass>& render_passes =
- *mock_client.render_passes_in_draw_order();
+ *renderer_client.render_passes_in_draw_order();
render_passes.clear();
gfx::Rect grand_child_rect(25, 25);
@@ -1054,25 +1132,190 @@ TEST(GLRendererTest2, ScissorTestWhenClearing) {
AddRenderPassQuad(child_pass, grand_child_pass);
renderer.DecideRenderPassAllocationsForFrame(
- *mock_client.render_passes_in_draw_order());
- renderer.DrawFrame(mock_client.render_passes_in_draw_order());
+ *renderer_client.render_passes_in_draw_order());
+ renderer.DrawFrame(
+ renderer_client.render_passes_in_draw_order(), NULL, 1.f, true);
}
-class NonReshapableOutputSurface : public FakeOutputSurface {
+class DiscardCheckingContext : public TestWebGraphicsContext3D {
public:
- explicit NonReshapableOutputSurface(
- scoped_ptr<WebKit::WebGraphicsContext3D> context3d)
- : FakeOutputSurface(context3d.Pass(), false) {}
- virtual gfx::Size SurfaceSize() const OVERRIDE { return gfx::Size(500, 500); }
+ DiscardCheckingContext() : discarded_(0) {
+ set_have_post_sub_buffer(true);
+ set_have_discard_framebuffer(true);
+ }
+
+ virtual void discardFramebufferEXT(WGC3Denum target,
+ WGC3Dsizei numAttachments,
+ const WGC3Denum* attachments) {
+ ++discarded_;
+ }
+
+ int discarded() const { return discarded_; }
+ void reset() { discarded_ = 0; }
+
+ private:
+ int discarded_;
};
-class OffsetViewportRendererClient : public FakeRendererClient {
+class NonReshapableOutputSurface : public FakeOutputSurface {
public:
- virtual gfx::Rect DeviceViewport() const OVERRIDE {
- return gfx::Rect(10, 10, 100, 100);
+ explicit NonReshapableOutputSurface(
+ scoped_ptr<TestWebGraphicsContext3D> context3d)
+ : FakeOutputSurface(TestContextProvider::Create(context3d.Pass()),
+ false) {
+ surface_size_ = gfx::Size(500, 500);
}
+ virtual void Reshape(gfx::Size size, float scale_factor) OVERRIDE {}
+ void set_fixed_size(gfx::Size size) { surface_size_ = size; }
};
+TEST(GLRendererTest2, NoDiscardOnPartialUpdates) {
+ scoped_ptr<DiscardCheckingContext> context_owned(new DiscardCheckingContext);
+ DiscardCheckingContext* context = context_owned.get();
+
+ FakeOutputSurfaceClient output_surface_client;
+ scoped_ptr<NonReshapableOutputSurface> output_surface(
+ new NonReshapableOutputSurface(
+ context_owned.PassAs<TestWebGraphicsContext3D>()));
+ CHECK(output_surface->BindToClient(&output_surface_client));
+ output_surface->set_fixed_size(gfx::Size(100, 100));
+
+ scoped_ptr<ResourceProvider> resource_provider(
+ ResourceProvider::Create(output_surface.get(), 0, false));
+
+ LayerTreeSettings settings;
+ settings.partial_swap_enabled = true;
+ FakeRendererClient renderer_client;
+ renderer_client.set_viewport(gfx::Rect(0, 0, 100, 100));
+ renderer_client.set_clip(gfx::Rect(0, 0, 100, 100));
+ FakeRendererGL renderer(&renderer_client,
+ &settings,
+ output_surface.get(),
+ resource_provider.get());
+ EXPECT_TRUE(renderer.Initialize());
+ EXPECT_TRUE(renderer.Capabilities().using_partial_swap);
+
+ gfx::Rect viewport_rect(renderer_client.DeviceViewport());
+ ScopedPtrVector<RenderPass>& render_passes =
+ *renderer_client.render_passes_in_draw_order();
+ render_passes.clear();
+
+ {
+ // Partial frame, should not discard.
+ RenderPass::Id root_pass_id(1, 0);
+ TestRenderPass* root_pass = AddRenderPass(
+ &render_passes, root_pass_id, viewport_rect, gfx::Transform());
+ AddQuad(root_pass, viewport_rect, SK_ColorGREEN);
+ root_pass->damage_rect = gfx::RectF(2.f, 2.f, 3.f, 3.f);
+
+ renderer.DecideRenderPassAllocationsForFrame(
+ *renderer_client.render_passes_in_draw_order());
+ renderer.DrawFrame(
+ renderer_client.render_passes_in_draw_order(), NULL, 1.f, true);
+ EXPECT_EQ(0, context->discarded());
+ context->reset();
+ }
+ {
+ // Full frame, should discard.
+ RenderPass::Id root_pass_id(1, 0);
+ TestRenderPass* root_pass = AddRenderPass(
+ &render_passes, root_pass_id, viewport_rect, gfx::Transform());
+ AddQuad(root_pass, viewport_rect, SK_ColorGREEN);
+ root_pass->damage_rect = gfx::RectF(root_pass->output_rect);
+
+ renderer.DecideRenderPassAllocationsForFrame(
+ *renderer_client.render_passes_in_draw_order());
+ renderer.DrawFrame(
+ renderer_client.render_passes_in_draw_order(), NULL, 1.f, true);
+ EXPECT_EQ(1, context->discarded());
+ context->reset();
+ }
+ {
+ // Partial frame, disallow partial swap, should discard.
+ RenderPass::Id root_pass_id(1, 0);
+ TestRenderPass* root_pass = AddRenderPass(
+ &render_passes, root_pass_id, viewport_rect, gfx::Transform());
+ AddQuad(root_pass, viewport_rect, SK_ColorGREEN);
+ root_pass->damage_rect = gfx::RectF(2.f, 2.f, 3.f, 3.f);
+
+ renderer.DecideRenderPassAllocationsForFrame(
+ *renderer_client.render_passes_in_draw_order());
+ renderer.DrawFrame(
+ renderer_client.render_passes_in_draw_order(), NULL, 1.f, false);
+ EXPECT_EQ(1, context->discarded());
+ context->reset();
+ }
+ {
+ // Full frame, external scissor is set, should not discard.
+ output_surface->set_has_external_stencil_test(true);
+ RenderPass::Id root_pass_id(1, 0);
+ TestRenderPass* root_pass = AddRenderPass(
+ &render_passes, root_pass_id, viewport_rect, gfx::Transform());
+ AddQuad(root_pass, viewport_rect, SK_ColorGREEN);
+ root_pass->damage_rect = gfx::RectF(root_pass->output_rect);
+ root_pass->has_transparent_background = false;
+
+ renderer.DecideRenderPassAllocationsForFrame(
+ *renderer_client.render_passes_in_draw_order());
+ renderer.DrawFrame(
+ renderer_client.render_passes_in_draw_order(), NULL, 1.f, true);
+ EXPECT_EQ(0, context->discarded());
+ context->reset();
+ output_surface->set_has_external_stencil_test(false);
+ }
+ {
+ // Full frame, clipped, should not discard.
+ renderer_client.set_clip(gfx::Rect(10, 10, 10, 10));
+ RenderPass::Id root_pass_id(1, 0);
+ TestRenderPass* root_pass = AddRenderPass(
+ &render_passes, root_pass_id, viewport_rect, gfx::Transform());
+ AddQuad(root_pass, viewport_rect, SK_ColorGREEN);
+ root_pass->damage_rect = gfx::RectF(root_pass->output_rect);
+
+ renderer.DecideRenderPassAllocationsForFrame(
+ *renderer_client.render_passes_in_draw_order());
+ renderer.DrawFrame(
+ renderer_client.render_passes_in_draw_order(), NULL, 1.f, true);
+ EXPECT_EQ(0, context->discarded());
+ context->reset();
+ }
+ {
+ // Full frame, doesn't cover the surface, should not discard.
+ renderer_client.set_viewport(gfx::Rect(10, 10, 10, 10));
+ viewport_rect = renderer_client.DeviceViewport();
+ RenderPass::Id root_pass_id(1, 0);
+ TestRenderPass* root_pass = AddRenderPass(
+ &render_passes, root_pass_id, viewport_rect, gfx::Transform());
+ AddQuad(root_pass, viewport_rect, SK_ColorGREEN);
+ root_pass->damage_rect = gfx::RectF(root_pass->output_rect);
+
+ renderer.DecideRenderPassAllocationsForFrame(
+ *renderer_client.render_passes_in_draw_order());
+ renderer.DrawFrame(
+ renderer_client.render_passes_in_draw_order(), NULL, 1.f, true);
+ EXPECT_EQ(0, context->discarded());
+ context->reset();
+ }
+ {
+ // Full frame, doesn't cover the surface (no offset), should not discard.
+ renderer_client.set_viewport(gfx::Rect(0, 0, 50, 50));
+ renderer_client.set_clip(gfx::Rect(0, 0, 100, 100));
+ viewport_rect = renderer_client.DeviceViewport();
+ RenderPass::Id root_pass_id(1, 0);
+ TestRenderPass* root_pass = AddRenderPass(
+ &render_passes, root_pass_id, viewport_rect, gfx::Transform());
+ AddQuad(root_pass, viewport_rect, SK_ColorGREEN);
+ root_pass->damage_rect = gfx::RectF(root_pass->output_rect);
+
+ renderer.DecideRenderPassAllocationsForFrame(
+ *renderer_client.render_passes_in_draw_order());
+ renderer.DrawFrame(
+ renderer_client.render_passes_in_draw_order(), NULL, 1.f, true);
+ EXPECT_EQ(0, context->discarded());
+ context->reset();
+ }
+}
+
class FlippedScissorAndViewportContext : public TestWebGraphicsContext3D {
public:
FlippedScissorAndViewportContext()
@@ -1108,21 +1351,32 @@ TEST(GLRendererTest2, ScissorAndViewportWithinNonreshapableSurface) {
// and maintains a fixed size. This test verifies that glViewport and
// glScissor's Y coordinate is flipped correctly in this environment, and that
// the glViewport can be at a nonzero origin within the surface.
- OffsetViewportRendererClient mock_client;
- scoped_ptr<OutputSurface> output_surface(make_scoped_ptr(
- new NonReshapableOutputSurface(scoped_ptr<WebKit::WebGraphicsContext3D>(
- new FlippedScissorAndViewportContext))));
+ scoped_ptr<FlippedScissorAndViewportContext> context_owned(
+ new FlippedScissorAndViewportContext);
+
+ FakeOutputSurfaceClient output_surface_client;
+ scoped_ptr<OutputSurface> output_surface(new NonReshapableOutputSurface(
+ context_owned.PassAs<TestWebGraphicsContext3D>()));
+ CHECK(output_surface->BindToClient(&output_surface_client));
+
scoped_ptr<ResourceProvider> resource_provider(
- ResourceProvider::Create(output_surface.get(), 0));
- FakeRendererGL renderer(
- &mock_client, output_surface.get(), resource_provider.get());
+ ResourceProvider::Create(output_surface.get(), 0, false));
+
+ LayerTreeSettings settings;
+ FakeRendererClient renderer_client;
+ renderer_client.set_viewport(gfx::Rect(10, 10, 100, 100));
+ renderer_client.set_clip(gfx::Rect(10, 10, 100, 100));
+ FakeRendererGL renderer(&renderer_client,
+ &settings,
+ output_surface.get(),
+ resource_provider.get());
EXPECT_TRUE(renderer.Initialize());
EXPECT_FALSE(renderer.Capabilities().using_partial_swap);
- gfx::Rect viewport_rect(mock_client.DeviceViewport().size());
+ gfx::Rect viewport_rect(renderer_client.DeviceViewport().size());
gfx::Rect quad_rect = gfx::Rect(20, 20, 20, 20);
ScopedPtrVector<RenderPass>& render_passes =
- *mock_client.render_passes_in_draw_order();
+ *renderer_client.render_passes_in_draw_order();
render_passes.clear();
RenderPass::Id root_pass_id(1, 0);
@@ -1131,14 +1385,15 @@ TEST(GLRendererTest2, ScissorAndViewportWithinNonreshapableSurface) {
AddClippedQuad(root_pass, quad_rect, SK_ColorGREEN);
renderer.DecideRenderPassAllocationsForFrame(
- *mock_client.render_passes_in_draw_order());
- renderer.DrawFrame(mock_client.render_passes_in_draw_order());
+ *renderer_client.render_passes_in_draw_order());
+ renderer.DrawFrame(
+ renderer_client.render_passes_in_draw_order(), NULL, 1.f, true);
}
TEST_F(GLRendererShaderTest, DrawRenderPassQuadShaderPermutations) {
- gfx::Rect viewport_rect(mock_client_.DeviceViewport());
+ gfx::Rect viewport_rect(renderer_client_.DeviceViewport());
ScopedPtrVector<RenderPass>* render_passes =
- mock_client_.render_passes_in_draw_order();
+ renderer_client_.render_passes_in_draw_order();
gfx::Rect child_rect(50, 50);
RenderPass::Id child_pass_id(2, 0);
@@ -1149,8 +1404,9 @@ TEST_F(GLRendererShaderTest, DrawRenderPassQuadShaderPermutations) {
cc::ResourceProvider::ResourceId mask =
resource_provider_->CreateResource(gfx::Size(20, 12),
- resource_provider_->best_texture_format(),
- ResourceProvider::TextureUsageAny);
+ GL_CLAMP_TO_EDGE,
+ ResourceProvider::TextureUsageAny,
+ resource_provider_->best_texture_format());
resource_provider_->AllocateForTesting(mask);
SkScalar matrix[20];
@@ -1193,8 +1449,9 @@ TEST_F(GLRendererShaderTest, DrawRenderPassQuadShaderPermutations) {
gfx::Transform());
renderer_->DecideRenderPassAllocationsForFrame(
- *mock_client_.render_passes_in_draw_order());
- renderer_->DrawFrame(mock_client_.render_passes_in_draw_order());
+ *renderer_client_.render_passes_in_draw_order());
+ renderer_->DrawFrame(
+ renderer_client_.render_passes_in_draw_order(), NULL, 1.f, true);
TestRenderPassProgram();
// RenderPassColorMatrixProgram
@@ -1209,8 +1466,9 @@ TEST_F(GLRendererShaderTest, DrawRenderPassQuadShaderPermutations) {
AddRenderPassQuad(root_pass, child_pass, 0, filter, gfx::Transform());
renderer_->DecideRenderPassAllocationsForFrame(
- *mock_client_.render_passes_in_draw_order());
- renderer_->DrawFrame(mock_client_.render_passes_in_draw_order());
+ *renderer_client_.render_passes_in_draw_order());
+ renderer_->DrawFrame(
+ renderer_client_.render_passes_in_draw_order(), NULL, 1.f, true);
TestRenderPassColorMatrixProgram();
// RenderPassMaskProgram
@@ -1229,8 +1487,9 @@ TEST_F(GLRendererShaderTest, DrawRenderPassQuadShaderPermutations) {
gfx::Transform());
renderer_->DecideRenderPassAllocationsForFrame(
- *mock_client_.render_passes_in_draw_order());
- renderer_->DrawFrame(mock_client_.render_passes_in_draw_order());
+ *renderer_client_.render_passes_in_draw_order());
+ renderer_->DrawFrame(
+ renderer_client_.render_passes_in_draw_order(), NULL, 1.f, true);
TestRenderPassMaskProgram();
// RenderPassMaskColorMatrixProgram
@@ -1245,8 +1504,9 @@ TEST_F(GLRendererShaderTest, DrawRenderPassQuadShaderPermutations) {
AddRenderPassQuad(root_pass, child_pass, mask, filter, gfx::Transform());
renderer_->DecideRenderPassAllocationsForFrame(
- *mock_client_.render_passes_in_draw_order());
- renderer_->DrawFrame(mock_client_.render_passes_in_draw_order());
+ *renderer_client_.render_passes_in_draw_order());
+ renderer_->DrawFrame(
+ renderer_client_.render_passes_in_draw_order(), NULL, 1.f, true);
TestRenderPassMaskColorMatrixProgram();
// RenderPassProgramAA
@@ -1265,8 +1525,9 @@ TEST_F(GLRendererShaderTest, DrawRenderPassQuadShaderPermutations) {
transform_causing_aa);
renderer_->DecideRenderPassAllocationsForFrame(
- *mock_client_.render_passes_in_draw_order());
- renderer_->DrawFrame(mock_client_.render_passes_in_draw_order());
+ *renderer_client_.render_passes_in_draw_order());
+ renderer_->DrawFrame(
+ renderer_client_.render_passes_in_draw_order(), NULL, 1.f, true);
TestRenderPassProgramAA();
// RenderPassColorMatrixProgramAA
@@ -1281,8 +1542,9 @@ TEST_F(GLRendererShaderTest, DrawRenderPassQuadShaderPermutations) {
AddRenderPassQuad(root_pass, child_pass, 0, filter, transform_causing_aa);
renderer_->DecideRenderPassAllocationsForFrame(
- *mock_client_.render_passes_in_draw_order());
- renderer_->DrawFrame(mock_client_.render_passes_in_draw_order());
+ *renderer_client_.render_passes_in_draw_order());
+ renderer_->DrawFrame(
+ renderer_client_.render_passes_in_draw_order(), NULL, 1.f, true);
TestRenderPassColorMatrixProgramAA();
// RenderPassMaskProgramAA
@@ -1298,8 +1560,9 @@ TEST_F(GLRendererShaderTest, DrawRenderPassQuadShaderPermutations) {
transform_causing_aa);
renderer_->DecideRenderPassAllocationsForFrame(
- *mock_client_.render_passes_in_draw_order());
- renderer_->DrawFrame(mock_client_.render_passes_in_draw_order());
+ *renderer_client_.render_passes_in_draw_order());
+ renderer_->DrawFrame(
+ renderer_client_.render_passes_in_draw_order(), NULL, 1.f, true);
TestRenderPassMaskProgramAA();
// RenderPassMaskColorMatrixProgramAA
@@ -1314,8 +1577,9 @@ TEST_F(GLRendererShaderTest, DrawRenderPassQuadShaderPermutations) {
AddRenderPassQuad(root_pass, child_pass, mask, filter, transform_causing_aa);
renderer_->DecideRenderPassAllocationsForFrame(
- *mock_client_.render_passes_in_draw_order());
- renderer_->DrawFrame(mock_client_.render_passes_in_draw_order());
+ *renderer_client_.render_passes_in_draw_order());
+ renderer_->DrawFrame(
+ renderer_client_.render_passes_in_draw_order(), NULL, 1.f, true);
TestRenderPassMaskColorMatrixProgramAA();
}
@@ -1326,7 +1590,7 @@ TEST_F(GLRendererShaderTest, DrawRenderPassQuadSkipsAAForClippingTransform) {
RenderPass::Id child_pass_id(2, 0);
TestRenderPass* child_pass;
- gfx::Rect viewport_rect(mock_client_.DeviceViewport());
+ gfx::Rect viewport_rect(renderer_client_.DeviceViewport());
RenderPass::Id root_pass_id(1, 0);
TestRenderPass* root_pass;
@@ -1345,7 +1609,7 @@ TEST_F(GLRendererShaderTest, DrawRenderPassQuadSkipsAAForClippingTransform) {
// Set up the render pass quad to be drawn
ScopedPtrVector<RenderPass>* render_passes =
- mock_client_.render_passes_in_draw_order();
+ renderer_client_.render_passes_in_draw_order();
render_passes->clear();
@@ -1362,8 +1626,9 @@ TEST_F(GLRendererShaderTest, DrawRenderPassQuadSkipsAAForClippingTransform) {
transform_preventing_aa);
renderer_->DecideRenderPassAllocationsForFrame(
- *mock_client_.render_passes_in_draw_order());
- renderer_->DrawFrame(mock_client_.render_passes_in_draw_order());
+ *renderer_client_.render_passes_in_draw_order());
+ renderer_->DrawFrame(
+ renderer_client_.render_passes_in_draw_order(), NULL, 1.f, true);
// If use_aa incorrectly ignores clipping, it will use the
// RenderPassProgramAA shader instead of the RenderPassProgram.
@@ -1371,9 +1636,9 @@ TEST_F(GLRendererShaderTest, DrawRenderPassQuadSkipsAAForClippingTransform) {
}
TEST_F(GLRendererShaderTest, DrawSolidColorShader) {
- gfx::Rect viewport_rect(mock_client_.DeviceViewport());
+ gfx::Rect viewport_rect(renderer_client_.DeviceViewport());
ScopedPtrVector<RenderPass>* render_passes =
- mock_client_.render_passes_in_draw_order();
+ renderer_client_.render_passes_in_draw_order();
RenderPass::Id root_pass_id(1, 0);
TestRenderPass* root_pass;
@@ -1392,14 +1657,20 @@ TEST_F(GLRendererShaderTest, DrawSolidColorShader) {
pixel_aligned_transform_causing_aa);
renderer_->DecideRenderPassAllocationsForFrame(
- *mock_client_.render_passes_in_draw_order());
- renderer_->DrawFrame(mock_client_.render_passes_in_draw_order());
+ *renderer_client_.render_passes_in_draw_order());
+ renderer_->DrawFrame(
+ renderer_client_.render_passes_in_draw_order(), NULL, 1.f, true);
TestSolidColorProgramAA();
}
class OutputSurfaceMockContext : public TestWebGraphicsContext3D {
public:
+ OutputSurfaceMockContext() {
+ test_capabilities_.discard_backbuffer = true;
+ test_capabilities_.post_sub_buffer = true;
+ }
+
// Specifically override methods even if they are unused (used in conjunction
// with StrictMock). We need to make sure that GLRenderer does not issue
// framebuffer-related GL calls directly. Instead these are supposed to go
@@ -1415,20 +1686,14 @@ class OutputSurfaceMockContext : public TestWebGraphicsContext3D {
WGC3Dsizei count,
WGC3Denum type,
WGC3Dintptr offset));
-
- virtual WebString getString(WebKit::WGC3Denum name) {
- if (name == GL_EXTENSIONS)
- return WebString(
- "GL_CHROMIUM_post_sub_buffer GL_CHROMIUM_discard_backbuffer");
- return WebString();
- }
};
class MockOutputSurface : public OutputSurface {
public:
MockOutputSurface()
- : OutputSurface(scoped_ptr<WebKit::WebGraphicsContext3D>(
- new StrictMock<OutputSurfaceMockContext>)) {
+ : OutputSurface(TestContextProvider::Create(
+ scoped_ptr<TestWebGraphicsContext3D>(
+ new StrictMock<OutputSurfaceMockContext>))) {
surface_size_ = gfx::Size(100, 100);
}
virtual ~MockOutputSurface() {}
@@ -1442,15 +1707,21 @@ class MockOutputSurface : public OutputSurface {
class MockOutputSurfaceTest : public testing::Test, public FakeRendererClient {
protected:
- MockOutputSurfaceTest()
- : resource_provider_(ResourceProvider::Create(&output_surface_, 0)),
- renderer_(this, &output_surface_, resource_provider_.get()) {}
+ virtual void SetUp() {
+ FakeOutputSurfaceClient output_surface_client_;
+ CHECK(output_surface_.BindToClient(&output_surface_client_));
- virtual void SetUp() { EXPECT_TRUE(renderer_.Initialize()); }
+ resource_provider_ =
+ ResourceProvider::Create(&output_surface_, 0, false).Pass();
- void SwapBuffers() { renderer_.SwapBuffers(); }
+ renderer_.reset(new FakeRendererGL(
+ this, &settings_, &output_surface_, resource_provider_.get()));
+ EXPECT_TRUE(renderer_->Initialize());
+ }
+
+ void SwapBuffers() { renderer_->SwapBuffers(); }
- void DrawFrame() {
+ void DrawFrame(float device_scale_factor) {
gfx::Rect viewport_rect(DeviceViewport());
ScopedPtrVector<RenderPass>* render_passes = render_passes_in_draw_order();
render_passes->clear();
@@ -1463,55 +1734,59 @@ class MockOutputSurfaceTest : public testing::Test, public FakeRendererClient {
EXPECT_CALL(output_surface_, EnsureBackbuffer()).WillRepeatedly(Return());
EXPECT_CALL(output_surface_,
- Reshape(DeviceViewport().size(), DeviceScaleFactor())).Times(1);
+ Reshape(DeviceViewport().size(), device_scale_factor)).Times(1);
EXPECT_CALL(output_surface_, BindFramebuffer()).Times(1);
EXPECT_CALL(*Context(), drawElements(_, _, _, _)).Times(1);
- renderer_.DecideRenderPassAllocationsForFrame(
+ renderer_->DecideRenderPassAllocationsForFrame(
*render_passes_in_draw_order());
- renderer_.DrawFrame(render_passes_in_draw_order());
+ renderer_->DrawFrame(
+ render_passes_in_draw_order(), NULL, device_scale_factor, true);
}
OutputSurfaceMockContext* Context() {
- return static_cast<OutputSurfaceMockContext*>(output_surface_.context3d());
+ return static_cast<OutputSurfaceMockContext*>(
+ output_surface_.context_provider()->Context3d());
}
+ LayerTreeSettings settings_;
+ FakeOutputSurfaceClient output_surface_client_;
StrictMock<MockOutputSurface> output_surface_;
scoped_ptr<ResourceProvider> resource_provider_;
- FakeRendererGL renderer_;
+ scoped_ptr<FakeRendererGL> renderer_;
};
TEST_F(MockOutputSurfaceTest, DrawFrameAndSwap) {
- DrawFrame();
+ DrawFrame(1.f);
EXPECT_CALL(output_surface_, SwapBuffers(_)).Times(1);
- renderer_.SwapBuffers();
+ renderer_->SwapBuffers();
}
TEST_F(MockOutputSurfaceTest, DrawFrameAndResizeAndSwap) {
- DrawFrame();
+ DrawFrame(1.f);
EXPECT_CALL(output_surface_, SwapBuffers(_)).Times(1);
- renderer_.SwapBuffers();
+ renderer_->SwapBuffers();
- set_viewport_and_scale(gfx::Size(2, 2), 2.f);
- renderer_.ViewportChanged();
+ set_viewport(gfx::Rect(0, 0, 2, 2));
+ renderer_->ViewportChanged();
- DrawFrame();
+ DrawFrame(2.f);
EXPECT_CALL(output_surface_, SwapBuffers(_)).Times(1);
- renderer_.SwapBuffers();
+ renderer_->SwapBuffers();
- DrawFrame();
+ DrawFrame(2.f);
EXPECT_CALL(output_surface_, SwapBuffers(_)).Times(1);
- renderer_.SwapBuffers();
+ renderer_->SwapBuffers();
- set_viewport_and_scale(gfx::Size(1, 1), 1.f);
- renderer_.ViewportChanged();
+ set_viewport(gfx::Rect(0, 0, 1, 1));
+ renderer_->ViewportChanged();
- DrawFrame();
+ DrawFrame(1.f);
EXPECT_CALL(output_surface_, SwapBuffers(_)).Times(1);
- renderer_.SwapBuffers();
+ renderer_->SwapBuffers();
}
class GLRendererTestSyncPoint : public GLRendererPixelTest {
@@ -1531,20 +1806,21 @@ class GLRendererTestSyncPoint : public GLRendererPixelTest {
TEST_F(GLRendererTestSyncPoint, SignalSyncPointOnLostContext) {
int sync_point_callback_count = 0;
int other_callback_count = 0;
- unsigned sync_point = output_surface_->context3d()->insertSyncPoint();
+ unsigned sync_point =
+ output_surface_->context_provider()->Context3d()->insertSyncPoint();
- output_surface_->context3d()->loseContextCHROMIUM(
+ output_surface_->context_provider()->Context3d()->loseContextCHROMIUM(
GL_GUILTY_CONTEXT_RESET_ARB, GL_INNOCENT_CONTEXT_RESET_ARB);
SyncPointHelper::SignalSyncPoint(
- output_surface_->context3d(),
+ output_surface_->context_provider()->Context3d(),
sync_point,
base::Bind(&SyncPointCallback, &sync_point_callback_count));
EXPECT_EQ(0, sync_point_callback_count);
EXPECT_EQ(0, other_callback_count);
// Make the sync point happen.
- output_surface_->context3d()->finish();
+ output_surface_->context_provider()->Context3d()->finish();
// Post a task after the sync point.
base::MessageLoop::current()->PostTask(
FROM_HERE,
@@ -1560,17 +1836,18 @@ TEST_F(GLRendererTestSyncPoint, SignalSyncPointOnLostContext) {
TEST_F(GLRendererTestSyncPoint, SignalSyncPoint) {
int sync_point_callback_count = 0;
int other_callback_count = 0;
- unsigned sync_point = output_surface_->context3d()->insertSyncPoint();
+ unsigned sync_point =
+ output_surface_->context_provider()->Context3d()->insertSyncPoint();
SyncPointHelper::SignalSyncPoint(
- output_surface_->context3d(),
+ output_surface_->context_provider()->Context3d(),
sync_point,
base::Bind(&SyncPointCallback, &sync_point_callback_count));
EXPECT_EQ(0, sync_point_callback_count);
EXPECT_EQ(0, other_callback_count);
// Make the sync point happen.
- output_surface_->context3d()->finish();
+ output_surface_->context_provider()->Context3d()->finish();
// Post a task after the sync point.
base::MessageLoop::current()->PostTask(
FROM_HERE,
diff --git a/chromium/cc/output/output_surface.cc b/chromium/cc/output/output_surface.cc
index be990cc0cb7..555fea27911 100644
--- a/chromium/cc/output/output_surface.cc
+++ b/chromium/cc/output/output_surface.cc
@@ -4,6 +4,7 @@
#include "cc/output/output_surface.h"
+#include <algorithm>
#include <set>
#include <string>
#include <vector>
@@ -15,11 +16,11 @@
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "cc/output/compositor_frame.h"
+#include "cc/output/compositor_frame_ack.h"
#include "cc/output/managed_memory_policy.h"
#include "cc/output/output_surface_client.h"
#include "cc/scheduler/delay_based_time_source.h"
#include "third_party/WebKit/public/platform/WebGraphicsContext3D.h"
-#include "third_party/WebKit/public/platform/WebGraphicsMemoryAllocation.h"
#include "third_party/khronos/GLES2/gl2.h"
#include "third_party/khronos/GLES2/gl2ext.h"
#include "ui/gfx/rect.h"
@@ -30,67 +31,9 @@ using std::string;
using std::vector;
namespace cc {
-namespace {
-
-ManagedMemoryPolicy::PriorityCutoff ConvertPriorityCutoff(
- WebKit::WebGraphicsMemoryAllocation::PriorityCutoff priority_cutoff) {
- // This is simple a 1:1 map, the names differ only because the WebKit names
- // should be to match the cc names.
- switch (priority_cutoff) {
- case WebKit::WebGraphicsMemoryAllocation::PriorityCutoffAllowNothing:
- return ManagedMemoryPolicy::CUTOFF_ALLOW_NOTHING;
- case WebKit::WebGraphicsMemoryAllocation::PriorityCutoffAllowVisibleOnly:
- return ManagedMemoryPolicy::CUTOFF_ALLOW_REQUIRED_ONLY;
- case WebKit::WebGraphicsMemoryAllocation::
- PriorityCutoffAllowVisibleAndNearby:
- return ManagedMemoryPolicy::CUTOFF_ALLOW_NICE_TO_HAVE;
- case WebKit::WebGraphicsMemoryAllocation::PriorityCutoffAllowEverything:
- return ManagedMemoryPolicy::CUTOFF_ALLOW_EVERYTHING;
- }
- NOTREACHED();
- return ManagedMemoryPolicy::CUTOFF_ALLOW_NOTHING;
-}
-
-} // anonymous namespace
-
-class OutputSurfaceCallbacks
- : public WebKit::WebGraphicsContext3D::
- WebGraphicsSwapBuffersCompleteCallbackCHROMIUM,
- public WebKit::WebGraphicsContext3D::WebGraphicsContextLostCallback,
- public WebKit::WebGraphicsContext3D::
- WebGraphicsMemoryAllocationChangedCallbackCHROMIUM {
- public:
- explicit OutputSurfaceCallbacks(OutputSurface* client)
- : client_(client) {
- DCHECK(client_);
- }
-
- // WK:WGC3D::WGSwapBuffersCompleteCallbackCHROMIUM implementation.
- virtual void onSwapBuffersComplete() { client_->OnSwapBuffersComplete(NULL); }
-
- // WK:WGC3D::WGContextLostCallback implementation.
- virtual void onContextLost() { client_->DidLoseOutputSurface(); }
-
- // WK:WGC3D::WGMemoryAllocationChangedCallbackCHROMIUM implementation.
- virtual void onMemoryAllocationChanged(
- WebKit::WebGraphicsMemoryAllocation allocation) {
- ManagedMemoryPolicy policy(
- allocation.bytesLimitWhenVisible,
- ConvertPriorityCutoff(allocation.priorityCutoffWhenVisible),
- allocation.bytesLimitWhenNotVisible,
- ConvertPriorityCutoff(allocation.priorityCutoffWhenNotVisible),
- ManagedMemoryPolicy::kDefaultNumResourcesLimit);
- bool discard_backbuffer = !allocation.suggestHaveBackbuffer;
- client_->SetMemoryPolicy(policy, discard_backbuffer);
- }
-
- private:
- OutputSurface* client_;
-};
-OutputSurface::OutputSurface(
- scoped_ptr<WebKit::WebGraphicsContext3D> context3d)
- : context3d_(context3d.Pass()),
+OutputSurface::OutputSurface(scoped_refptr<ContextProvider> context_provider)
+ : context_provider_(context_provider),
has_gl_discard_backbuffer_(false),
has_swap_buffers_complete_callback_(false),
device_scale_factor_(-1),
@@ -98,10 +41,10 @@ OutputSurface::OutputSurface(
max_frames_pending_(0),
pending_swap_buffers_(0),
needs_begin_frame_(false),
- begin_frame_pending_(false),
+ client_ready_for_begin_frame_(true),
client_(NULL),
- check_for_retroactive_begin_frame_pending_(false) {
-}
+ check_for_retroactive_begin_frame_pending_(false),
+ external_stencil_test_enabled_(false) {}
OutputSurface::OutputSurface(
scoped_ptr<cc::SoftwareOutputDevice> software_device)
@@ -113,15 +56,15 @@ OutputSurface::OutputSurface(
max_frames_pending_(0),
pending_swap_buffers_(0),
needs_begin_frame_(false),
- begin_frame_pending_(false),
+ client_ready_for_begin_frame_(true),
client_(NULL),
- check_for_retroactive_begin_frame_pending_(false) {
-}
+ check_for_retroactive_begin_frame_pending_(false),
+ external_stencil_test_enabled_(false) {}
OutputSurface::OutputSurface(
- scoped_ptr<WebKit::WebGraphicsContext3D> context3d,
+ scoped_refptr<ContextProvider> context_provider,
scoped_ptr<cc::SoftwareOutputDevice> software_device)
- : context3d_(context3d.Pass()),
+ : context_provider_(context_provider),
software_device_(software_device.Pass()),
has_gl_discard_backbuffer_(false),
has_swap_buffers_complete_callback_(false),
@@ -130,10 +73,10 @@ OutputSurface::OutputSurface(
max_frames_pending_(0),
pending_swap_buffers_(0),
needs_begin_frame_(false),
- begin_frame_pending_(false),
+ client_ready_for_begin_frame_(true),
client_(NULL),
- check_for_retroactive_begin_frame_pending_(false) {
-}
+ check_for_retroactive_begin_frame_pending_(false),
+ external_stencil_test_enabled_(false) {}
void OutputSurface::InitializeBeginFrameEmulation(
base::SingleThreadTaskRunner* task_runner,
@@ -192,7 +135,7 @@ void OutputSurface::SetNeedsRedrawRect(gfx::Rect damage_rect) {
void OutputSurface::SetNeedsBeginFrame(bool enable) {
TRACE_EVENT1("cc", "OutputSurface::SetNeedsBeginFrame", "enable", enable);
needs_begin_frame_ = enable;
- begin_frame_pending_ = false;
+ client_ready_for_begin_frame_ = true;
if (frame_rate_controller_) {
BeginFrameArgs skipped = frame_rate_controller_->SetActive(enable);
if (skipped.IsValid())
@@ -204,14 +147,14 @@ void OutputSurface::SetNeedsBeginFrame(bool enable) {
void OutputSurface::BeginFrame(const BeginFrameArgs& args) {
TRACE_EVENT2("cc", "OutputSurface::BeginFrame",
- "begin_frame_pending_", begin_frame_pending_,
+ "client_ready_for_begin_frame_", client_ready_for_begin_frame_,
"pending_swap_buffers_", pending_swap_buffers_);
- if (!needs_begin_frame_ || begin_frame_pending_ ||
+ if (!needs_begin_frame_ || !client_ready_for_begin_frame_ ||
(pending_swap_buffers_ >= max_frames_pending_ &&
max_frames_pending_ > 0)) {
skipped_begin_frame_args_ = args;
} else {
- begin_frame_pending_ = true;
+ client_ready_for_begin_frame_ = false;
client_->BeginFrame(args);
// args might be an alias for skipped_begin_frame_args_.
// Do not reset it before calling BeginFrame!
@@ -219,8 +162,13 @@ void OutputSurface::BeginFrame(const BeginFrameArgs& args) {
}
}
-base::TimeDelta OutputSurface::RetroactiveBeginFramePeriod() {
- return BeginFrameArgs::DefaultRetroactiveBeginFramePeriod();
+base::TimeTicks OutputSurface::RetroactiveBeginFrameDeadline() {
+ // TODO(brianderson): Remove the alternative deadline once we have better
+ // deadline estimations.
+ base::TimeTicks alternative_deadline =
+ skipped_begin_frame_args_.frame_time +
+ BeginFrameArgs::DefaultRetroactiveBeginFramePeriod();
+ return std::max(skipped_begin_frame_args_.deadline, alternative_deadline);
}
void OutputSurface::PostCheckForRetroactiveBeginFrame() {
@@ -238,18 +186,11 @@ void OutputSurface::PostCheckForRetroactiveBeginFrame() {
void OutputSurface::CheckForRetroactiveBeginFrame() {
TRACE_EVENT0("cc", "OutputSurface::CheckForRetroactiveBeginFrame");
check_for_retroactive_begin_frame_pending_ = false;
- base::TimeTicks now = base::TimeTicks::Now();
- base::TimeTicks alternative_deadline =
- skipped_begin_frame_args_.frame_time +
- RetroactiveBeginFramePeriod();
- if (now < skipped_begin_frame_args_.deadline ||
- now < alternative_deadline) {
+ if (base::TimeTicks::Now() < RetroactiveBeginFrameDeadline())
BeginFrame(skipped_begin_frame_args_);
- }
}
void OutputSurface::DidSwapBuffers() {
- begin_frame_pending_ = false;
pending_swap_buffers_++;
TRACE_EVENT1("cc", "OutputSurface::DidSwapBuffers",
"pending_swap_buffers_", pending_swap_buffers_);
@@ -258,56 +199,63 @@ void OutputSurface::DidSwapBuffers() {
PostCheckForRetroactiveBeginFrame();
}
-void OutputSurface::OnSwapBuffersComplete(const CompositorFrameAck* ack) {
+void OutputSurface::OnSwapBuffersComplete() {
pending_swap_buffers_--;
TRACE_EVENT1("cc", "OutputSurface::OnSwapBuffersComplete",
"pending_swap_buffers_", pending_swap_buffers_);
- client_->OnSwapBuffersComplete(ack);
+ client_->OnSwapBuffersComplete();
if (frame_rate_controller_)
frame_rate_controller_->DidSwapBuffersComplete();
PostCheckForRetroactiveBeginFrame();
}
+void OutputSurface::ReclaimResources(const CompositorFrameAck* ack) {
+ client_->ReclaimResources(ack);
+}
+
void OutputSurface::DidLoseOutputSurface() {
TRACE_EVENT0("cc", "OutputSurface::DidLoseOutputSurface");
- begin_frame_pending_ = false;
+ client_ready_for_begin_frame_ = true;
pending_swap_buffers_ = 0;
+ skipped_begin_frame_args_ = BeginFrameArgs();
+ if (frame_rate_controller_)
+ frame_rate_controller_->SetActive(false);
client_->DidLoseOutputSurface();
}
void OutputSurface::SetExternalStencilTest(bool enabled) {
- client_->SetExternalStencilTest(enabled);
+ external_stencil_test_enabled_ = enabled;
}
void OutputSurface::SetExternalDrawConstraints(const gfx::Transform& transform,
- gfx::Rect viewport) {
- client_->SetExternalDrawConstraints(transform, viewport);
+ gfx::Rect viewport,
+ gfx::Rect clip,
+ bool valid_for_tile_management) {
+ client_->SetExternalDrawConstraints(
+ transform, viewport, clip, valid_for_tile_management);
}
OutputSurface::~OutputSurface() {
if (frame_rate_controller_)
frame_rate_controller_->SetActive(false);
-
- if (context3d_) {
- context3d_->setSwapBuffersCompleteCallbackCHROMIUM(NULL);
- context3d_->setContextLostCallback(NULL);
- context3d_->setMemoryAllocationChangedCallbackCHROMIUM(NULL);
- }
+ ResetContext3d();
}
-bool OutputSurface::ForcedDrawToSoftwareDevice() const {
- return false;
+bool OutputSurface::HasExternalStencilTest() const {
+ return external_stencil_test_enabled_;
}
+bool OutputSurface::ForcedDrawToSoftwareDevice() const { return false; }
+
bool OutputSurface::BindToClient(cc::OutputSurfaceClient* client) {
DCHECK(client);
client_ = client;
bool success = true;
- if (context3d_) {
- success = context3d_->makeContextCurrent();
+ if (context_provider_) {
+ success = context_provider_->BindToCurrentThread();
if (success)
- SetContext3D(context3d_.Pass());
+ SetUpContext3d();
}
if (!success)
@@ -316,71 +264,78 @@ bool OutputSurface::BindToClient(cc::OutputSurfaceClient* client) {
return success;
}
-bool OutputSurface::InitializeAndSetContext3D(
- scoped_ptr<WebKit::WebGraphicsContext3D> context3d,
+bool OutputSurface::InitializeAndSetContext3d(
+ scoped_refptr<ContextProvider> context_provider,
scoped_refptr<ContextProvider> offscreen_context_provider) {
- DCHECK(!context3d_);
- DCHECK(context3d);
+ DCHECK(!context_provider_);
+ DCHECK(context_provider);
DCHECK(client_);
bool success = false;
- if (context3d->makeContextCurrent()) {
- SetContext3D(context3d.Pass());
+ if (context_provider->BindToCurrentThread()) {
+ context_provider_ = context_provider;
+ SetUpContext3d();
if (client_->DeferredInitialize(offscreen_context_provider))
success = true;
}
if (!success)
- ResetContext3D();
+ ResetContext3d();
return success;
}
void OutputSurface::ReleaseGL() {
DCHECK(client_);
- DCHECK(context3d_);
+ DCHECK(context_provider_);
client_->ReleaseGL();
- ResetContext3D();
+ ResetContext3d();
}
-void OutputSurface::SetContext3D(
- scoped_ptr<WebKit::WebGraphicsContext3D> context3d) {
- DCHECK(!context3d_);
- DCHECK(context3d);
+void OutputSurface::SetUpContext3d() {
+ DCHECK(context_provider_);
DCHECK(client_);
- string extensions_string = UTF16ToASCII(context3d->getString(GL_EXTENSIONS));
- vector<string> extensions_list;
- base::SplitString(extensions_string, ' ', &extensions_list);
- set<string> extensions(extensions_list.begin(), extensions_list.end());
- has_gl_discard_backbuffer_ =
- extensions.count("GL_CHROMIUM_discard_backbuffer") > 0;
- has_swap_buffers_complete_callback_ =
- extensions.count("GL_CHROMIUM_swapbuffers_complete_callback") > 0;
-
-
- context3d_ = context3d.Pass();
- callbacks_.reset(new OutputSurfaceCallbacks(this));
- context3d_->setSwapBuffersCompleteCallbackCHROMIUM(callbacks_.get());
- context3d_->setContextLostCallback(callbacks_.get());
- context3d_->setMemoryAllocationChangedCallbackCHROMIUM(callbacks_.get());
-}
-
-void OutputSurface::ResetContext3D() {
- context3d_.reset();
- callbacks_.reset();
+ const ContextProvider::Capabilities& caps =
+ context_provider_->ContextCapabilities();
+
+ has_gl_discard_backbuffer_ = caps.discard_backbuffer;
+ has_swap_buffers_complete_callback_ = caps.swapbuffers_complete_callback;
+
+ context_provider_->SetLostContextCallback(
+ base::Bind(&OutputSurface::DidLoseOutputSurface,
+ base::Unretained(this)));
+ context_provider_->SetSwapBuffersCompleteCallback(base::Bind(
+ &OutputSurface::OnSwapBuffersComplete, base::Unretained(this)));
+ context_provider_->SetMemoryPolicyChangedCallback(
+ base::Bind(&OutputSurface::SetMemoryPolicy,
+ base::Unretained(this)));
+}
+
+void OutputSurface::ResetContext3d() {
+ if (context_provider_.get()) {
+ context_provider_->SetLostContextCallback(
+ ContextProvider::LostContextCallback());
+ context_provider_->SetSwapBuffersCompleteCallback(
+ ContextProvider::SwapBuffersCompleteCallback());
+ context_provider_->SetMemoryPolicyChangedCallback(
+ ContextProvider::MemoryPolicyChangedCallback());
+ }
+ context_provider_ = NULL;
}
void OutputSurface::EnsureBackbuffer() {
- DCHECK(context3d_);
- if (has_gl_discard_backbuffer_)
- context3d_->ensureBackbufferCHROMIUM();
+ if (context_provider_ && has_gl_discard_backbuffer_)
+ context_provider_->Context3d()->ensureBackbufferCHROMIUM();
+ if (software_device_)
+ software_device_->EnsureBackbuffer();
}
void OutputSurface::DiscardBackbuffer() {
- DCHECK(context3d_);
- if (has_gl_discard_backbuffer_)
- context3d_->discardBackbufferCHROMIUM();
+ if (context_provider_ && has_gl_discard_backbuffer_)
+ context_provider_->Context3d()->discardBackbufferCHROMIUM();
+ if (software_device_)
+ software_device_->DiscardBackbuffer();
}
void OutputSurface::Reshape(gfx::Size size, float scale_factor) {
@@ -389,8 +344,8 @@ void OutputSurface::Reshape(gfx::Size size, float scale_factor) {
surface_size_ = size;
device_scale_factor_ = scale_factor;
- if (context3d_) {
- context3d_->reshapeWithScaleFactor(
+ if (context_provider_) {
+ context_provider_->Context3d()->reshapeWithScaleFactor(
size.width(), size.height(), scale_factor);
}
if (software_device_)
@@ -402,8 +357,8 @@ gfx::Size OutputSurface::SurfaceSize() const {
}
void OutputSurface::BindFramebuffer() {
- DCHECK(context3d_);
- context3d_->bindFramebuffer(GL_FRAMEBUFFER, 0);
+ DCHECK(context_provider_);
+ context_provider_->Context3d()->bindFramebuffer(GL_FRAMEBUFFER, 0);
}
void OutputSurface::SwapBuffers(cc::CompositorFrame* frame) {
@@ -413,20 +368,21 @@ void OutputSurface::SwapBuffers(cc::CompositorFrame* frame) {
return;
}
- DCHECK(context3d_);
+ DCHECK(context_provider_);
DCHECK(frame->gl_frame_data);
if (frame->gl_frame_data->sub_buffer_rect ==
gfx::Rect(frame->gl_frame_data->size)) {
// Note that currently this has the same effect as SwapBuffers; we should
// consider exposing a different entry point on WebGraphicsContext3D.
- context3d()->prepareTexture();
+ context_provider_->Context3d()->prepareTexture();
} else {
gfx::Rect sub_buffer_rect = frame->gl_frame_data->sub_buffer_rect;
- context3d()->postSubBufferCHROMIUM(sub_buffer_rect.x(),
- sub_buffer_rect.y(),
- sub_buffer_rect.width(),
- sub_buffer_rect.height());
+ context_provider_->Context3d()->postSubBufferCHROMIUM(
+ sub_buffer_rect.x(),
+ sub_buffer_rect.y(),
+ sub_buffer_rect.width(),
+ sub_buffer_rect.height());
}
if (!has_swap_buffers_complete_callback_)
@@ -437,10 +393,9 @@ void OutputSurface::SwapBuffers(cc::CompositorFrame* frame) {
void OutputSurface::PostSwapBuffersComplete() {
base::MessageLoop::current()->PostTask(
- FROM_HERE,
- base::Bind(&OutputSurface::OnSwapBuffersComplete,
- weak_ptr_factory_.GetWeakPtr(),
- static_cast<CompositorFrameAck*>(NULL)));
+ FROM_HERE,
+ base::Bind(&OutputSurface::OnSwapBuffersComplete,
+ weak_ptr_factory_.GetWeakPtr()));
}
void OutputSurface::SetMemoryPolicy(const ManagedMemoryPolicy& policy,
diff --git a/chromium/cc/output/output_surface.h b/chromium/cc/output/output_surface.h
index 86d4dc81dd3..13db5faa06b 100644
--- a/chromium/cc/output/output_surface.h
+++ b/chromium/cc/output/output_surface.h
@@ -13,7 +13,6 @@
#include "cc/output/context_provider.h"
#include "cc/output/software_output_device.h"
#include "cc/scheduler/frame_rate_controller.h"
-#include "third_party/WebKit/public/platform/WebGraphicsContext3D.h"
namespace base { class SingleThreadTaskRunner; }
@@ -31,7 +30,6 @@ class CompositorFrame;
class CompositorFrameAck;
struct ManagedMemoryPolicy;
class OutputSurfaceClient;
-class OutputSurfaceCallbacks;
// Represents the output surface for a compositor. The compositor owns
// and manages its destruction. Its lifetime is:
@@ -46,11 +44,11 @@ class CC_EXPORT OutputSurface : public FrameRateControllerClient {
DEFAULT_MAX_FRAMES_PENDING = 2
};
- explicit OutputSurface(scoped_ptr<WebKit::WebGraphicsContext3D> context3d);
+ explicit OutputSurface(scoped_refptr<ContextProvider> context_provider);
explicit OutputSurface(scoped_ptr<cc::SoftwareOutputDevice> software_device);
- OutputSurface(scoped_ptr<WebKit::WebGraphicsContext3D> context3d,
+ OutputSurface(scoped_refptr<ContextProvider> context_provider,
scoped_ptr<cc::SoftwareOutputDevice> software_device);
virtual ~OutputSurface();
@@ -61,7 +59,8 @@ class CC_EXPORT OutputSurface : public FrameRateControllerClient {
max_frames_pending(0),
deferred_gl_initialization(false),
draw_and_swap_full_viewport_every_frame(false),
- adjust_deadline_for_parent(true) {}
+ adjust_deadline_for_parent(true),
+ uses_default_gl_framebuffer(true) {}
bool delegated_rendering;
int max_frames_pending;
bool deferred_gl_initialization;
@@ -69,20 +68,24 @@ class CC_EXPORT OutputSurface : public FrameRateControllerClient {
// This doesn't handle the <webview> case, but once BeginFrame is
// supported natively, we shouldn't need adjust_deadline_for_parent.
bool adjust_deadline_for_parent;
+ // Whether this output surface renders to the default OpenGL zero
+ // framebuffer or to an offscreen framebuffer.
+ bool uses_default_gl_framebuffer;
};
const Capabilities& capabilities() const {
return capabilities_;
}
+ virtual bool HasExternalStencilTest() const;
+
// Obtain the 3d context or the software device associated with this output
// surface. Either of these may return a null pointer, but not both.
// In the event of a lost context, the entire output surface should be
// recreated.
- WebKit::WebGraphicsContext3D* context3d() const {
- return context3d_.get();
+ scoped_refptr<ContextProvider> context_provider() const {
+ return context_provider_.get();
}
-
SoftwareOutputDevice* software_device() const {
return software_device_.get();
}
@@ -127,21 +130,22 @@ class CC_EXPORT OutputSurface : public FrameRateControllerClient {
// OutputSurfaceClient::BeginFrame until the callback is disabled.
virtual void SetNeedsBeginFrame(bool enable);
+ bool HasClient() { return !!client_; }
+
protected:
// Synchronously initialize context3d and enter hardware mode.
// This can only supported in threaded compositing mode.
// |offscreen_context_provider| should match what is returned by
// LayerTreeClient::OffscreenContextProviderForCompositorThread.
- bool InitializeAndSetContext3D(
- scoped_ptr<WebKit::WebGraphicsContext3D> context3d,
+ bool InitializeAndSetContext3d(
+ scoped_refptr<ContextProvider> context_provider,
scoped_refptr<ContextProvider> offscreen_context_provider);
void ReleaseGL();
void PostSwapBuffersComplete();
struct cc::OutputSurface::Capabilities capabilities_;
- scoped_ptr<OutputSurfaceCallbacks> callbacks_;
- scoped_ptr<WebKit::WebGraphicsContext3D> context3d_;
+ scoped_refptr<ContextProvider> context_provider_;
scoped_ptr<cc::SoftwareOutputDevice> software_device_;
bool has_gl_discard_backbuffer_;
bool has_swap_buffers_complete_callback_;
@@ -159,22 +163,28 @@ class CC_EXPORT OutputSurface : public FrameRateControllerClient {
int max_frames_pending_;
int pending_swap_buffers_;
bool needs_begin_frame_;
- bool begin_frame_pending_;
+ bool client_ready_for_begin_frame_;
+
+ // This stores a BeginFrame that we couldn't process immediately, but might
+ // process retroactively in the near future.
+ BeginFrameArgs skipped_begin_frame_args_;
// Forwarded to OutputSurfaceClient but threaded through OutputSurface
// first so OutputSurface has a chance to update the FrameRateController
- bool HasClient() { return !!client_; }
void SetNeedsRedrawRect(gfx::Rect damage_rect);
void BeginFrame(const BeginFrameArgs& args);
void DidSwapBuffers();
- void OnSwapBuffersComplete(const CompositorFrameAck* ack);
+ void OnSwapBuffersComplete();
+ void ReclaimResources(const CompositorFrameAck* ack);
void DidLoseOutputSurface();
void SetExternalStencilTest(bool enabled);
void SetExternalDrawConstraints(const gfx::Transform& transform,
- gfx::Rect viewport);
+ gfx::Rect viewport,
+ gfx::Rect clip,
+ bool valid_for_tile_management);
// virtual for testing.
- virtual base::TimeDelta RetroactiveBeginFramePeriod();
+ virtual base::TimeTicks RetroactiveBeginFrameDeadline();
virtual void PostCheckForRetroactiveBeginFrame();
void CheckForRetroactiveBeginFrame();
@@ -182,19 +192,17 @@ class CC_EXPORT OutputSurface : public FrameRateControllerClient {
OutputSurfaceClient* client_;
friend class OutputSurfaceCallbacks;
- void SetContext3D(scoped_ptr<WebKit::WebGraphicsContext3D> context3d);
- void ResetContext3D();
+ void SetUpContext3d();
+ void ResetContext3d();
void SetMemoryPolicy(const ManagedMemoryPolicy& policy,
bool discard_backbuffer_when_not_visible);
- // This stores a BeginFrame that we couldn't process immediately, but might
- // process retroactively in the near future.
- BeginFrameArgs skipped_begin_frame_args_;
-
// check_for_retroactive_begin_frame_pending_ is used to avoid posting
// redundant checks for a retroactive BeginFrame.
bool check_for_retroactive_begin_frame_pending_;
+ bool external_stencil_test_enabled_;
+
DISALLOW_COPY_AND_ASSIGN(OutputSurface);
};
diff --git a/chromium/cc/output/output_surface_client.h b/chromium/cc/output/output_surface_client.h
index 4d1762211bb..c0e2e4554b5 100644
--- a/chromium/cc/output/output_surface_client.h
+++ b/chromium/cc/output/output_surface_client.h
@@ -32,11 +32,13 @@ class CC_EXPORT OutputSurfaceClient {
virtual void ReleaseGL() = 0;
virtual void SetNeedsRedrawRect(gfx::Rect damage_rect) = 0;
virtual void BeginFrame(const BeginFrameArgs& args) = 0;
- virtual void OnSwapBuffersComplete(const CompositorFrameAck* ack) = 0;
+ virtual void OnSwapBuffersComplete() = 0;
+ virtual void ReclaimResources(const CompositorFrameAck* ack) = 0;
virtual void DidLoseOutputSurface() = 0;
- virtual void SetExternalStencilTest(bool enabled) = 0;
virtual void SetExternalDrawConstraints(const gfx::Transform& transform,
- gfx::Rect viewport) = 0;
+ gfx::Rect viewport,
+ gfx::Rect clip,
+ bool valid_for_tile_management) = 0;
virtual void SetDiscardBackBufferWhenNotVisible(bool discard) = 0;
virtual void SetMemoryPolicy(const ManagedMemoryPolicy& policy) = 0;
// If set, |callback| will be called subsequent to each new tree activation,
diff --git a/chromium/cc/output/output_surface_unittest.cc b/chromium/cc/output/output_surface_unittest.cc
index 276828c04aa..91e7c39d0b1 100644
--- a/chromium/cc/output/output_surface_unittest.cc
+++ b/chromium/cc/output/output_surface_unittest.cc
@@ -5,47 +5,47 @@
#include "cc/output/output_surface.h"
#include "base/test/test_simple_task_runner.h"
+#include "cc/debug/test_context_provider.h"
+#include "cc/debug/test_web_graphics_context_3d.h"
#include "cc/output/managed_memory_policy.h"
#include "cc/output/output_surface_client.h"
#include "cc/output/software_output_device.h"
#include "cc/test/fake_output_surface.h"
#include "cc/test/fake_output_surface_client.h"
#include "cc/test/scheduler_test_common.h"
-#include "cc/test/test_web_graphics_context_3d.h"
#include "gpu/GLES2/gl2extchromium.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/WebKit/public/platform/WebGraphicsMemoryAllocation.h"
-
-using WebKit::WebGraphicsMemoryAllocation;
namespace cc {
namespace {
class TestOutputSurface : public OutputSurface {
public:
- explicit TestOutputSurface(scoped_ptr<WebKit::WebGraphicsContext3D> context3d)
- : OutputSurface(context3d.Pass()) {}
+ explicit TestOutputSurface(scoped_refptr<ContextProvider> context_provider)
+ : OutputSurface(context_provider),
+ retroactive_begin_frame_deadline_enabled_(false),
+ override_retroactive_period_(false) {}
explicit TestOutputSurface(
scoped_ptr<cc::SoftwareOutputDevice> software_device)
- : OutputSurface(software_device.Pass()) {}
+ : OutputSurface(software_device.Pass()),
+ retroactive_begin_frame_deadline_enabled_(false),
+ override_retroactive_period_(false) {}
- TestOutputSurface(scoped_ptr<WebKit::WebGraphicsContext3D> context3d,
+ TestOutputSurface(scoped_refptr<ContextProvider> context_provider,
scoped_ptr<cc::SoftwareOutputDevice> software_device)
- : OutputSurface(context3d.Pass(), software_device.Pass()) {}
+ : OutputSurface(context_provider, software_device.Pass()),
+ retroactive_begin_frame_deadline_enabled_(false),
+ override_retroactive_period_(false) {}
- bool InitializeNewContext3D(
- scoped_ptr<WebKit::WebGraphicsContext3D> new_context3d) {
- return InitializeAndSetContext3D(new_context3d.Pass(),
+ bool InitializeNewContext3d(
+ scoped_refptr<ContextProvider> new_context_provider) {
+ return InitializeAndSetContext3d(new_context_provider,
scoped_refptr<ContextProvider>());
}
using OutputSurface::ReleaseGL;
- bool HasClientForTesting() {
- return HasClient();
- }
-
void OnVSyncParametersChangedForTesting(base::TimeTicks timebase,
base::TimeDelta interval) {
OnVSyncParametersChanged(timebase, interval);
@@ -64,11 +64,15 @@ class TestOutputSurface : public OutputSurface {
}
void OnSwapBuffersCompleteForTesting() {
- OnSwapBuffersComplete(NULL);
+ OnSwapBuffersComplete();
}
- void SetRetroactiveBeginFramePeriod(base::TimeDelta period) {
- retroactive_begin_frame_period_ = period;
+ void EnableRetroactiveBeginFrameDeadline(bool enable,
+ bool override_retroactive_period,
+ base::TimeDelta period_override) {
+ retroactive_begin_frame_deadline_enabled_ = enable;
+ override_retroactive_period_ = override_retroactive_period;
+ retroactive_period_override_ = period_override;
}
protected:
@@ -77,116 +81,150 @@ class TestOutputSurface : public OutputSurface {
CheckForRetroactiveBeginFrame();
}
- virtual base::TimeDelta RetroactiveBeginFramePeriod() OVERRIDE {
- return retroactive_begin_frame_period_;
+ virtual base::TimeTicks RetroactiveBeginFrameDeadline() OVERRIDE {
+ if (retroactive_begin_frame_deadline_enabled_) {
+ if (override_retroactive_period_) {
+ return skipped_begin_frame_args_.frame_time +
+ retroactive_period_override_;
+ } else {
+ return OutputSurface::RetroactiveBeginFrameDeadline();
+ }
+ }
+ return base::TimeTicks();
}
- base::TimeDelta retroactive_begin_frame_period_;
+ bool retroactive_begin_frame_deadline_enabled_;
+ bool override_retroactive_period_;
+ base::TimeDelta retroactive_period_override_;
};
-TEST(OutputSurfaceTest, ClientPointerIndicatesBindToClientSuccess) {
- scoped_ptr<TestWebGraphicsContext3D> context3d =
- TestWebGraphicsContext3D::Create();
+class TestSoftwareOutputDevice : public SoftwareOutputDevice {
+ public:
+ TestSoftwareOutputDevice();
+ virtual ~TestSoftwareOutputDevice();
+
+ // Overriden from cc:SoftwareOutputDevice
+ virtual void DiscardBackbuffer() OVERRIDE;
+ virtual void EnsureBackbuffer() OVERRIDE;
+
+ int discard_backbuffer_count() { return discard_backbuffer_count_; }
+ int ensure_backbuffer_count() { return ensure_backbuffer_count_; }
+
+ private:
+ int discard_backbuffer_count_;
+ int ensure_backbuffer_count_;
+};
- TestOutputSurface output_surface(
- context3d.PassAs<WebKit::WebGraphicsContext3D>());
- EXPECT_FALSE(output_surface.HasClientForTesting());
+TestSoftwareOutputDevice::TestSoftwareOutputDevice()
+ : discard_backbuffer_count_(0), ensure_backbuffer_count_(0) {}
+
+TestSoftwareOutputDevice::~TestSoftwareOutputDevice() {}
+
+void TestSoftwareOutputDevice::DiscardBackbuffer() {
+ SoftwareOutputDevice::DiscardBackbuffer();
+ discard_backbuffer_count_++;
+}
+
+void TestSoftwareOutputDevice::EnsureBackbuffer() {
+ SoftwareOutputDevice::EnsureBackbuffer();
+ ensure_backbuffer_count_++;
+}
+
+TEST(OutputSurfaceTest, ClientPointerIndicatesBindToClientSuccess) {
+ TestOutputSurface output_surface(TestContextProvider::Create());
+ EXPECT_FALSE(output_surface.HasClient());
FakeOutputSurfaceClient client;
EXPECT_TRUE(output_surface.BindToClient(&client));
- EXPECT_TRUE(output_surface.HasClientForTesting());
+ EXPECT_TRUE(output_surface.HasClient());
EXPECT_FALSE(client.deferred_initialize_called());
// Verify DidLoseOutputSurface callback is hooked up correctly.
EXPECT_FALSE(client.did_lose_output_surface_called());
- output_surface.context3d()->loseContextCHROMIUM(
+ output_surface.context_provider()->Context3d()->loseContextCHROMIUM(
GL_GUILTY_CONTEXT_RESET_ARB, GL_INNOCENT_CONTEXT_RESET_ARB);
EXPECT_TRUE(client.did_lose_output_surface_called());
}
TEST(OutputSurfaceTest, ClientPointerIndicatesBindToClientFailure) {
- scoped_ptr<TestWebGraphicsContext3D> context3d =
- TestWebGraphicsContext3D::Create();
+ scoped_refptr<TestContextProvider> context_provider =
+ TestContextProvider::Create();
// Lose the context so BindToClient fails.
- context3d->set_times_make_current_succeeds(0);
+ context_provider->UnboundTestContext3d()->set_times_make_current_succeeds(0);
- TestOutputSurface output_surface(
- context3d.PassAs<WebKit::WebGraphicsContext3D>());
- EXPECT_FALSE(output_surface.HasClientForTesting());
+ TestOutputSurface output_surface(context_provider);
+ EXPECT_FALSE(output_surface.HasClient());
FakeOutputSurfaceClient client;
EXPECT_FALSE(output_surface.BindToClient(&client));
- EXPECT_FALSE(output_surface.HasClientForTesting());
+ EXPECT_FALSE(output_surface.HasClient());
}
-class InitializeNewContext3D : public ::testing::Test {
+class OutputSurfaceTestInitializeNewContext3d : public ::testing::Test {
public:
- InitializeNewContext3D()
- : context3d_(TestWebGraphicsContext3D::Create()),
+ OutputSurfaceTestInitializeNewContext3d()
+ : context_provider_(TestContextProvider::Create()),
output_surface_(
scoped_ptr<SoftwareOutputDevice>(new SoftwareOutputDevice)) {}
protected:
void BindOutputSurface() {
EXPECT_TRUE(output_surface_.BindToClient(&client_));
- EXPECT_TRUE(output_surface_.HasClientForTesting());
+ EXPECT_TRUE(output_surface_.HasClient());
}
void InitializeNewContextExpectFail() {
- EXPECT_FALSE(output_surface_.InitializeNewContext3D(
- context3d_.PassAs<WebKit::WebGraphicsContext3D>()));
- EXPECT_TRUE(output_surface_.HasClientForTesting());
+ EXPECT_FALSE(output_surface_.InitializeNewContext3d(context_provider_));
+ EXPECT_TRUE(output_surface_.HasClient());
- EXPECT_FALSE(output_surface_.context3d());
+ EXPECT_FALSE(output_surface_.context_provider());
EXPECT_TRUE(output_surface_.software_device());
}
- scoped_ptr<TestWebGraphicsContext3D> context3d_;
+ scoped_refptr<TestContextProvider> context_provider_;
TestOutputSurface output_surface_;
FakeOutputSurfaceClient client_;
};
-TEST_F(InitializeNewContext3D, Success) {
+TEST_F(OutputSurfaceTestInitializeNewContext3d, Success) {
BindOutputSurface();
EXPECT_FALSE(client_.deferred_initialize_called());
- EXPECT_TRUE(output_surface_.InitializeNewContext3D(
- context3d_.PassAs<WebKit::WebGraphicsContext3D>()));
+ EXPECT_TRUE(output_surface_.InitializeNewContext3d(context_provider_));
EXPECT_TRUE(client_.deferred_initialize_called());
+ EXPECT_EQ(context_provider_, output_surface_.context_provider());
EXPECT_FALSE(client_.did_lose_output_surface_called());
- output_surface_.context3d()->loseContextCHROMIUM(
+ context_provider_->Context3d()->loseContextCHROMIUM(
GL_GUILTY_CONTEXT_RESET_ARB, GL_INNOCENT_CONTEXT_RESET_ARB);
EXPECT_TRUE(client_.did_lose_output_surface_called());
output_surface_.ReleaseGL();
- EXPECT_FALSE(output_surface_.context3d());
+ EXPECT_FALSE(output_surface_.context_provider());
}
-TEST_F(InitializeNewContext3D, Context3dMakeCurrentFails) {
+TEST_F(OutputSurfaceTestInitializeNewContext3d, Context3dMakeCurrentFails) {
BindOutputSurface();
- context3d_->set_times_make_current_succeeds(0);
+
+ context_provider_->UnboundTestContext3d()
+ ->set_times_make_current_succeeds(0);
InitializeNewContextExpectFail();
}
-TEST_F(InitializeNewContext3D, ClientDeferredInitializeFails) {
+TEST_F(OutputSurfaceTestInitializeNewContext3d, ClientDeferredInitializeFails) {
BindOutputSurface();
client_.set_deferred_initialize_result(false);
InitializeNewContextExpectFail();
}
TEST(OutputSurfaceTest, BeginFrameEmulation) {
- scoped_ptr<TestWebGraphicsContext3D> context3d =
- TestWebGraphicsContext3D::Create();
-
- TestOutputSurface output_surface(
- context3d.PassAs<WebKit::WebGraphicsContext3D>());
- EXPECT_FALSE(output_surface.HasClientForTesting());
+ TestOutputSurface output_surface(TestContextProvider::Create());
+ EXPECT_FALSE(output_surface.HasClient());
FakeOutputSurfaceClient client;
EXPECT_TRUE(output_surface.BindToClient(&client));
- EXPECT_TRUE(output_surface.HasClientForTesting());
+ EXPECT_TRUE(output_surface.HasClient());
EXPECT_FALSE(client.deferred_initialize_called());
// Initialize BeginFrame emulation
@@ -202,8 +240,8 @@ TEST(OutputSurfaceTest, BeginFrameEmulation) {
display_refresh_interval);
output_surface.SetMaxFramesPending(2);
- output_surface.SetRetroactiveBeginFramePeriod(
- base::TimeDelta::FromSeconds(-1));
+ output_surface.EnableRetroactiveBeginFrameDeadline(
+ false, false, base::TimeDelta());
// We should start off with 0 BeginFrames
EXPECT_EQ(client.begin_frame_count(), 0);
@@ -224,8 +262,10 @@ TEST(OutputSurfaceTest, BeginFrameEmulation) {
EXPECT_EQ(client.begin_frame_count(), 1);
EXPECT_EQ(output_surface.pending_swap_buffers(), 0);
- // DidSwapBuffers should clear the pending BeginFrame.
+ // SetNeedsBeginFrame should clear the pending BeginFrame after
+ // a SwapBuffers.
output_surface.DidSwapBuffersForTesting();
+ output_surface.SetNeedsBeginFrame(true);
EXPECT_EQ(client.begin_frame_count(), 1);
EXPECT_EQ(output_surface.pending_swap_buffers(), 1);
task_runner->RunPendingTasks();
@@ -234,6 +274,7 @@ TEST(OutputSurfaceTest, BeginFrameEmulation) {
// BeginFrame should be throttled by pending swap buffers.
output_surface.DidSwapBuffersForTesting();
+ output_surface.SetNeedsBeginFrame(true);
EXPECT_EQ(client.begin_frame_count(), 2);
EXPECT_EQ(output_surface.pending_swap_buffers(), 2);
task_runner->RunPendingTasks();
@@ -264,23 +305,17 @@ TEST(OutputSurfaceTest, BeginFrameEmulation) {
}
TEST(OutputSurfaceTest, OptimisticAndRetroactiveBeginFrames) {
- scoped_ptr<TestWebGraphicsContext3D> context3d =
- TestWebGraphicsContext3D::Create();
-
- TestOutputSurface output_surface(
- context3d.PassAs<WebKit::WebGraphicsContext3D>());
- EXPECT_FALSE(output_surface.HasClientForTesting());
+ TestOutputSurface output_surface(TestContextProvider::Create());
+ EXPECT_FALSE(output_surface.HasClient());
FakeOutputSurfaceClient client;
EXPECT_TRUE(output_surface.BindToClient(&client));
- EXPECT_TRUE(output_surface.HasClientForTesting());
+ EXPECT_TRUE(output_surface.HasClient());
EXPECT_FALSE(client.deferred_initialize_called());
output_surface.SetMaxFramesPending(2);
-
- // Enable retroactive BeginFrames.
- output_surface.SetRetroactiveBeginFramePeriod(
- base::TimeDelta::FromSeconds(100000));
+ output_surface.EnableRetroactiveBeginFrameDeadline(
+ true, false, base::TimeDelta());
// Optimistically injected BeginFrames should be throttled if
// SetNeedsBeginFrame is false...
@@ -302,12 +337,14 @@ TEST(OutputSurfaceTest, OptimisticAndRetroactiveBeginFrames) {
output_surface.BeginFrameForTesting();
EXPECT_EQ(client.begin_frame_count(), 2);
output_surface.DidSwapBuffersForTesting();
+ output_surface.SetNeedsBeginFrame(true);
EXPECT_EQ(client.begin_frame_count(), 3);
EXPECT_EQ(output_surface.pending_swap_buffers(), 1);
// Optimistically injected BeginFrames should be by throttled by pending
// swap buffers...
output_surface.DidSwapBuffersForTesting();
+ output_surface.SetNeedsBeginFrame(true);
EXPECT_EQ(client.begin_frame_count(), 3);
EXPECT_EQ(output_surface.pending_swap_buffers(), 2);
output_surface.BeginFrameForTesting();
@@ -317,28 +354,82 @@ TEST(OutputSurfaceTest, OptimisticAndRetroactiveBeginFrames) {
EXPECT_EQ(client.begin_frame_count(), 4);
}
+TEST(OutputSurfaceTest, RetroactiveBeginFrameDoesNotDoubleTickWhenEmulating) {
+ scoped_refptr<TestContextProvider> context_provider =
+ TestContextProvider::Create();
+
+ TestOutputSurface output_surface(context_provider);
+ EXPECT_FALSE(output_surface.HasClient());
+
+ FakeOutputSurfaceClient client;
+ EXPECT_TRUE(output_surface.BindToClient(&client));
+ EXPECT_TRUE(output_surface.HasClient());
+ EXPECT_FALSE(client.deferred_initialize_called());
+
+ base::TimeDelta big_interval = base::TimeDelta::FromSeconds(10);
+
+ // Initialize BeginFrame emulation
+ scoped_refptr<base::TestSimpleTaskRunner> task_runner =
+ new base::TestSimpleTaskRunner;
+ bool throttle_frame_production = true;
+ const base::TimeDelta display_refresh_interval = big_interval;
+
+ output_surface.InitializeBeginFrameEmulation(
+ task_runner.get(),
+ throttle_frame_production,
+ display_refresh_interval);
+
+ // We need to subtract an epsilon from Now() because some platforms have
+ // a slow clock.
+ output_surface.OnVSyncParametersChangedForTesting(
+ base::TimeTicks::Now() - base::TimeDelta::FromSeconds(1), big_interval);
+
+ output_surface.SetMaxFramesPending(2);
+ output_surface.EnableRetroactiveBeginFrameDeadline(true, true, big_interval);
+
+ // We should start off with 0 BeginFrames
+ EXPECT_EQ(client.begin_frame_count(), 0);
+ EXPECT_EQ(output_surface.pending_swap_buffers(), 0);
+
+ // The first SetNeedsBeginFrame(true) should start a retroactive BeginFrame.
+ EXPECT_FALSE(task_runner->HasPendingTask());
+ output_surface.SetNeedsBeginFrame(true);
+ EXPECT_TRUE(task_runner->HasPendingTask());
+ EXPECT_GT(task_runner->NextPendingTaskDelay(), big_interval / 2);
+ EXPECT_EQ(client.begin_frame_count(), 1);
+
+ output_surface.SetNeedsBeginFrame(false);
+ EXPECT_TRUE(task_runner->HasPendingTask());
+ EXPECT_EQ(client.begin_frame_count(), 1);
+
+ // The second SetNeedBeginFrame(true) should not retroactively start a
+ // BeginFrame if the timestamp would be the same as the previous BeginFrame.
+ output_surface.SetNeedsBeginFrame(true);
+ EXPECT_TRUE(task_runner->HasPendingTask());
+ EXPECT_EQ(client.begin_frame_count(), 1);
+}
+
TEST(OutputSurfaceTest, MemoryAllocation) {
- scoped_ptr<TestWebGraphicsContext3D> scoped_context =
- TestWebGraphicsContext3D::Create();
- TestWebGraphicsContext3D* context = scoped_context.get();
+ scoped_refptr<TestContextProvider> context_provider =
+ TestContextProvider::Create();
- TestOutputSurface output_surface(
- scoped_context.PassAs<WebKit::WebGraphicsContext3D>());
+ TestOutputSurface output_surface(context_provider);
FakeOutputSurfaceClient client;
EXPECT_TRUE(output_surface.BindToClient(&client));
- WebGraphicsMemoryAllocation allocation;
- allocation.suggestHaveBackbuffer = true;
- allocation.bytesLimitWhenVisible = 1234;
- allocation.priorityCutoffWhenVisible =
- WebGraphicsMemoryAllocation::PriorityCutoffAllowVisibleOnly;
- allocation.bytesLimitWhenNotVisible = 4567;
- allocation.priorityCutoffWhenNotVisible =
- WebGraphicsMemoryAllocation::PriorityCutoffAllowNothing;
+ ManagedMemoryPolicy policy(0);
+ policy.bytes_limit_when_visible = 1234;
+ policy.priority_cutoff_when_visible =
+ ManagedMemoryPolicy::CUTOFF_ALLOW_REQUIRED_ONLY;
+ policy.bytes_limit_when_not_visible = 4567;
+ policy.priority_cutoff_when_not_visible =
+ ManagedMemoryPolicy::CUTOFF_ALLOW_NOTHING;
- context->SetMemoryAllocation(allocation);
+ bool discard_backbuffer_when_not_visible = false;
+ context_provider->SetMemoryAllocation(policy,
+ discard_backbuffer_when_not_visible);
EXPECT_EQ(1234u, client.memory_policy().bytes_limit_when_visible);
EXPECT_EQ(ManagedMemoryPolicy::CUTOFF_ALLOW_REQUIRED_ONLY,
client.memory_policy().priority_cutoff_when_visible);
@@ -347,25 +438,49 @@ TEST(OutputSurfaceTest, MemoryAllocation) {
client.memory_policy().priority_cutoff_when_not_visible);
EXPECT_FALSE(client.discard_backbuffer_when_not_visible());
- allocation.suggestHaveBackbuffer = false;
- context->SetMemoryAllocation(allocation);
+ discard_backbuffer_when_not_visible = true;
+ context_provider->SetMemoryAllocation(policy,
+ discard_backbuffer_when_not_visible);
EXPECT_TRUE(client.discard_backbuffer_when_not_visible());
- allocation.priorityCutoffWhenVisible =
- WebGraphicsMemoryAllocation::PriorityCutoffAllowEverything;
- allocation.priorityCutoffWhenNotVisible =
- WebGraphicsMemoryAllocation::PriorityCutoffAllowVisibleAndNearby;
- context->SetMemoryAllocation(allocation);
+ policy.priority_cutoff_when_visible =
+ ManagedMemoryPolicy::CUTOFF_ALLOW_EVERYTHING;
+ policy.priority_cutoff_when_not_visible =
+ ManagedMemoryPolicy::CUTOFF_ALLOW_NICE_TO_HAVE;
+ context_provider->SetMemoryAllocation(policy,
+ discard_backbuffer_when_not_visible);
EXPECT_EQ(ManagedMemoryPolicy::CUTOFF_ALLOW_EVERYTHING,
client.memory_policy().priority_cutoff_when_visible);
EXPECT_EQ(ManagedMemoryPolicy::CUTOFF_ALLOW_NICE_TO_HAVE,
client.memory_policy().priority_cutoff_when_not_visible);
// 0 bytes limit should be ignored.
- allocation.bytesLimitWhenVisible = 0;
- context->SetMemoryAllocation(allocation);
+ policy.bytes_limit_when_visible = 0;
+ context_provider->SetMemoryAllocation(policy,
+ discard_backbuffer_when_not_visible);
EXPECT_EQ(1234u, client.memory_policy().bytes_limit_when_visible);
}
+TEST(OutputSurfaceTest, SoftwareOutputDeviceBackbufferManagement) {
+ TestSoftwareOutputDevice* software_output_device =
+ new TestSoftwareOutputDevice();
+
+ // TestOutputSurface now owns software_output_device and has responsibility to
+ // free it.
+ scoped_ptr<TestSoftwareOutputDevice> p(software_output_device);
+ TestOutputSurface output_surface(p.PassAs<SoftwareOutputDevice>());
+
+ EXPECT_EQ(0, software_output_device->ensure_backbuffer_count());
+ EXPECT_EQ(0, software_output_device->discard_backbuffer_count());
+
+ output_surface.EnsureBackbuffer();
+ EXPECT_EQ(1, software_output_device->ensure_backbuffer_count());
+ EXPECT_EQ(0, software_output_device->discard_backbuffer_count());
+ output_surface.DiscardBackbuffer();
+
+ EXPECT_EQ(1, software_output_device->ensure_backbuffer_count());
+ EXPECT_EQ(1, software_output_device->discard_backbuffer_count());
+}
+
} // namespace
} // namespace cc
diff --git a/chromium/cc/output/render_surface_filters.cc b/chromium/cc/output/render_surface_filters.cc
index fa84e67f4d3..04d8c5ab9f7 100644
--- a/chromium/cc/output/render_surface_filters.cc
+++ b/chromium/cc/output/render_surface_filters.cc
@@ -226,9 +226,13 @@ bool GetColorMatrix(const FilterOperation& op, SkScalar matrix[20]) {
memcpy(matrix, op.matrix(), sizeof(SkScalar[20]));
return true;
}
- default:
+ case FilterOperation::BLUR:
+ case FilterOperation::DROP_SHADOW:
+ case FilterOperation::ZOOM:
return false;
}
+ NOTREACHED();
+ return false;
}
class FilterBufferState {
diff --git a/chromium/cc/output/renderer.h b/chromium/cc/output/renderer.h
index a9a800b6a0e..32a109fac85 100644
--- a/chromium/cc/output/renderer.h
+++ b/chromium/cc/output/renderer.h
@@ -18,19 +18,13 @@ class ScopedResource;
class CC_EXPORT RendererClient {
public:
- // Draw viewport in non-y-flipped window space. Note that while a draw is in
- // progress, this is guaranteed to be contained within the output surface
- // size.
+ // These return the draw viewport and clip in non-y-flipped window space.
+ // Note that while a draw is in progress, these are guaranteed to be
+ // contained within the output surface size.
virtual gfx::Rect DeviceViewport() const = 0;
-
- virtual float DeviceScaleFactor() const = 0;
- virtual const LayerTreeSettings& Settings() const = 0;
+ virtual gfx::Rect DeviceClip() const = 0;
virtual void SetFullRootLayerDamage() = 0;
- virtual bool HasImplThread() const = 0;
- virtual bool ShouldClearRootRenderPass() const = 0;
virtual CompositorFrameMetadata MakeCompositorFrameMetadata() const = 0;
- virtual bool AllowPartialSwap() const = 0;
- virtual bool ExternalStencilTestEnabled() const = 0;
protected:
virtual ~RendererClient() {}
@@ -42,8 +36,6 @@ class CC_EXPORT Renderer {
virtual const RendererCapabilities& Capabilities() const = 0;
- const LayerTreeSettings& Settings() const { return client_->Settings(); }
-
virtual void ViewportChanged() {}
virtual bool CanReadPixels() const = 0;
@@ -53,8 +45,12 @@ class CC_EXPORT Renderer {
virtual bool HaveCachedResourcesForRenderPassId(RenderPass::Id id) const;
// This passes ownership of the render passes to the renderer. It should
- // consume them, and empty the list.
- virtual void DrawFrame(RenderPassList* render_passes_in_draw_order) = 0;
+ // consume them, and empty the list. The parameters here may change from frame
+ // to frame and should not be cached.
+ virtual void DrawFrame(RenderPassList* render_passes_in_draw_order,
+ ContextProvider* offscreen_context_provider,
+ float device_scale_factor,
+ bool allow_partial_swap) = 0;
// Waits for rendering to finish.
virtual void Finish() = 0;
@@ -78,10 +74,11 @@ class CC_EXPORT Renderer {
virtual void SetDiscardBackBufferWhenNotVisible(bool discard) = 0;
protected:
- explicit Renderer(RendererClient* client)
- : client_(client) {}
+ explicit Renderer(RendererClient* client, const LayerTreeSettings* settings)
+ : client_(client), settings_(settings) {}
RendererClient* client_;
+ const LayerTreeSettings* settings_;
private:
DISALLOW_COPY_AND_ASSIGN(Renderer);
diff --git a/chromium/cc/output/renderer_pixeltest.cc b/chromium/cc/output/renderer_pixeltest.cc
index 9508aa29166..f0187aacdb3 100644
--- a/chromium/cc/output/renderer_pixeltest.cc
+++ b/chromium/cc/output/renderer_pixeltest.cc
@@ -8,11 +8,11 @@
#include "cc/quads/draw_quad.h"
#include "cc/quads/picture_draw_quad.h"
#include "cc/quads/texture_draw_quad.h"
-#include "cc/resources/platform_color.h"
#include "cc/resources/sync_point_helper.h"
#include "cc/test/fake_picture_pile_impl.h"
#include "cc/test/pixel_test.h"
#include "gpu/GLES2/gl2extchromium.h"
+#include "third_party/WebKit/public/platform/WebGraphicsContext3D.h"
#include "third_party/skia/include/core/SkImageFilter.h"
#include "third_party/skia/include/core/SkMatrix.h"
#include "third_party/skia/include/effects/SkColorFilterImageFilter.h"
@@ -110,8 +110,11 @@ scoped_ptr<TextureDrawQuad> CreateTestTextureDrawQuad(
SkColorGetB(texel_color));
std::vector<uint32_t> pixels(rect.size().GetArea(), pixel_color);
- ResourceProvider::ResourceId resource = resource_provider->CreateResource(
- rect.size(), GL_RGBA, ResourceProvider::TextureUsageAny);
+ ResourceProvider::ResourceId resource =
+ resource_provider->CreateResource(rect.size(),
+ GL_CLAMP_TO_EDGE,
+ ResourceProvider::TextureUsageAny,
+ RGBA_8888);
resource_provider->SetPixels(
resource,
reinterpret_cast<uint8_t*>(&pixels.front()),
@@ -214,6 +217,7 @@ TYPED_TEST(RendererPixelTest, SimpleGreenRect) {
EXPECT_TRUE(this->RunPixelTest(
&pass_list,
+ PixelTest::NoOffscreenContext,
base::FilePath(FILE_PATH_LITERAL("green.png")),
ExactPixelComparator(true)));
}
@@ -255,6 +259,7 @@ TYPED_TEST(RendererPixelTest, SimpleGreenRect_NonRootRenderPass) {
EXPECT_TRUE(this->RunPixelTestWithReadbackTarget(
&pass_list,
child_pass_ptr,
+ PixelTest::NoOffscreenContext,
base::FilePath(FILE_PATH_LITERAL("green_small.png")),
ExactPixelComparator(true)));
}
@@ -286,6 +291,7 @@ TYPED_TEST(RendererPixelTest, PremultipliedTextureWithoutBackground) {
EXPECT_TRUE(this->RunPixelTest(
&pass_list,
+ PixelTest::NoOffscreenContext,
base::FilePath(FILE_PATH_LITERAL("green_alpha.png")),
FuzzyPixelOffByOneComparator(true)));
}
@@ -320,6 +326,7 @@ TYPED_TEST(RendererPixelTest, PremultipliedTextureWithBackground) {
EXPECT_TRUE(this->RunPixelTest(
&pass_list,
+ PixelTest::NoOffscreenContext,
base::FilePath(FILE_PATH_LITERAL("green_alpha.png")),
FuzzyPixelOffByOneComparator(true)));
}
@@ -352,6 +359,7 @@ TEST_F(GLRendererPixelTest, NonPremultipliedTextureWithoutBackground) {
EXPECT_TRUE(this->RunPixelTest(
&pass_list,
+ PixelTest::NoOffscreenContext,
base::FilePath(FILE_PATH_LITERAL("green_alpha.png")),
FuzzyPixelOffByOneComparator(true)));
}
@@ -387,6 +395,7 @@ TEST_F(GLRendererPixelTest, NonPremultipliedTextureWithBackground) {
EXPECT_TRUE(this->RunPixelTest(
&pass_list,
+ PixelTest::NoOffscreenContext,
base::FilePath(FILE_PATH_LITERAL("green_alpha.png")),
FuzzyPixelOffByOneComparator(true)));
}
@@ -401,24 +410,28 @@ class VideoGLRendererPixelTest : public GLRendererPixelTest {
ResourceProvider::ResourceId y_resource =
resource_provider_->CreateResource(
this->device_viewport_size_,
- GL_LUMINANCE,
- ResourceProvider::TextureUsageAny);
+ GL_CLAMP_TO_EDGE,
+ ResourceProvider::TextureUsageAny,
+ LUMINANCE_8);
ResourceProvider::ResourceId u_resource =
resource_provider_->CreateResource(
this->device_viewport_size_,
- GL_LUMINANCE,
- ResourceProvider::TextureUsageAny);
+ GL_CLAMP_TO_EDGE,
+ ResourceProvider::TextureUsageAny,
+ LUMINANCE_8);
ResourceProvider::ResourceId v_resource =
resource_provider_->CreateResource(
this->device_viewport_size_,
- GL_LUMINANCE,
- ResourceProvider::TextureUsageAny);
+ GL_CLAMP_TO_EDGE,
+ ResourceProvider::TextureUsageAny,
+ LUMINANCE_8);
ResourceProvider::ResourceId a_resource = 0;
if (with_alpha) {
a_resource = resource_provider_->CreateResource(
this->device_viewport_size_,
- GL_LUMINANCE,
- ResourceProvider::TextureUsageAny);
+ GL_CLAMP_TO_EDGE,
+ ResourceProvider::TextureUsageAny,
+ LUMINANCE_8);
}
int w = this->device_viewport_size_.width();
@@ -476,6 +489,7 @@ TEST_F(VideoGLRendererPixelTest, SimpleYUVRect) {
EXPECT_TRUE(this->RunPixelTest(
&pass_list,
+ PixelTest::NoOffscreenContext,
base::FilePath(FILE_PATH_LITERAL("green.png")),
ExactPixelComparator(true)));
}
@@ -504,6 +518,7 @@ TEST_F(VideoGLRendererPixelTest, SimpleYUVARect) {
EXPECT_TRUE(this->RunPixelTest(
&pass_list,
+ PixelTest::NoOffscreenContext,
base::FilePath(FILE_PATH_LITERAL("green_alpha.png")),
ExactPixelComparator(true)));
}
@@ -532,6 +547,7 @@ TEST_F(VideoGLRendererPixelTest, FullyTransparentYUVARect) {
EXPECT_TRUE(this->RunPixelTest(
&pass_list,
+ PixelTest::NoOffscreenContext,
base::FilePath(FILE_PATH_LITERAL("black.png")),
ExactPixelComparator(true)));
}
@@ -631,6 +647,7 @@ TYPED_TEST(RendererPixelTest, FastPassColorFilterAlpha) {
// renderer so use a fuzzy comparator.
EXPECT_TRUE(this->RunPixelTest(
&pass_list,
+ PixelTest::NoOffscreenContext,
base::FilePath(FILE_PATH_LITERAL("blue_yellow_alpha.png")),
FuzzyForSoftwareOnlyPixelComparator<TypeParam>(false)));
}
@@ -733,6 +750,7 @@ TYPED_TEST(RendererPixelTest, FastPassColorFilterAlphaTranslation) {
// renderer so use a fuzzy comparator.
EXPECT_TRUE(this->RunPixelTest(
&pass_list,
+ PixelTest::NoOffscreenContext,
base::FilePath(FILE_PATH_LITERAL("blue_yellow_alpha_translate.png")),
FuzzyForSoftwareOnlyPixelComparator<TypeParam>(false)));
}
@@ -789,6 +807,7 @@ TYPED_TEST(RendererPixelTest, EnlargedRenderPassTexture) {
EXPECT_TRUE(this->RunPixelTest(
&pass_list,
+ PixelTest::NoOffscreenContext,
base::FilePath(FILE_PATH_LITERAL("blue_yellow.png")),
ExactPixelComparator(true)));
}
@@ -857,6 +876,7 @@ TYPED_TEST(RendererPixelTest, EnlargedRenderPassTextureWithAntiAliasing) {
EXPECT_TRUE(this->RunPixelTest(
&pass_list,
+ PixelTest::NoOffscreenContext,
base::FilePath(FILE_PATH_LITERAL("blue_yellow_anti_aliasing.png")),
FuzzyPixelOffByOneComparator(true)));
}
@@ -996,6 +1016,7 @@ TEST_F(GLRendererPixelTestWithBackgroundFilter, InvertFilter) {
this->SetUpRenderPassList();
EXPECT_TRUE(this->RunPixelTest(
&this->pass_list_,
+ PixelTest::WithOffscreenContext,
base::FilePath(FILE_PATH_LITERAL("background_filter.png")),
ExactPixelComparator(true)));
}
@@ -1003,7 +1024,8 @@ TEST_F(GLRendererPixelTestWithBackgroundFilter, InvertFilter) {
class ExternalStencilPixelTest : public GLRendererPixelTest {
protected:
void ClearBackgroundToGreen() {
- WebKit::WebGraphicsContext3D* context3d = output_surface_->context3d();
+ WebKit::WebGraphicsContext3D* context3d =
+ output_surface_->context_provider()->Context3d();
output_surface_->EnsureBackbuffer();
output_surface_->Reshape(device_viewport_size_, 1);
context3d->clearColor(0.f, 1.f, 0.f, 1.f);
@@ -1012,7 +1034,8 @@ class ExternalStencilPixelTest : public GLRendererPixelTest {
void PopulateStencilBuffer() {
// Set two quadrants of the stencil buffer to 1.
- WebKit::WebGraphicsContext3D* context3d = output_surface_->context3d();
+ WebKit::WebGraphicsContext3D* context3d =
+ output_surface_->context_provider()->Context3d();
ASSERT_TRUE(context3d->getContextAttributes().stencil);
output_surface_->EnsureBackbuffer();
output_surface_->Reshape(device_viewport_size_, 1);
@@ -1054,6 +1077,7 @@ TEST_F(ExternalStencilPixelTest, StencilTestEnabled) {
EXPECT_TRUE(this->RunPixelTest(
&pass_list,
+ PixelTest::NoOffscreenContext,
base::FilePath(FILE_PATH_LITERAL("four_blue_green_checkers.png")),
ExactPixelComparator(true)));
}
@@ -1076,6 +1100,7 @@ TEST_F(ExternalStencilPixelTest, StencilTestDisabled) {
EXPECT_TRUE(this->RunPixelTest(
&pass_list,
+ PixelTest::NoOffscreenContext,
base::FilePath(FILE_PATH_LITERAL("green.png")),
ExactPixelComparator(true)));
}
@@ -1125,10 +1150,36 @@ TEST_F(ExternalStencilPixelTest, RenderSurfacesIgnoreStencil) {
EXPECT_TRUE(this->RunPixelTest(
&pass_list,
+ PixelTest::NoOffscreenContext,
base::FilePath(FILE_PATH_LITERAL("four_blue_green_checkers.png")),
ExactPixelComparator(true)));
}
+TEST_F(ExternalStencilPixelTest, DeviceClip) {
+ ClearBackgroundToGreen();
+ gfx::Rect clip_rect(gfx::Point(150, 150), gfx::Size(50, 50));
+ this->ForceDeviceClip(clip_rect);
+
+ // Draw a blue quad that covers the entire device viewport. It should be
+ // clipped to the bottom right corner by the device clip.
+ gfx::Rect rect(this->device_viewport_size_);
+ RenderPass::Id id(1, 1);
+ scoped_ptr<RenderPass> pass = CreateTestRootRenderPass(id, rect);
+ scoped_ptr<SharedQuadState> blue_shared_state =
+ CreateTestSharedQuadState(gfx::Transform(), rect);
+ scoped_ptr<SolidColorDrawQuad> blue = SolidColorDrawQuad::Create();
+ blue->SetNew(blue_shared_state.get(), rect, SK_ColorBLUE, false);
+ pass->quad_list.push_back(blue.PassAs<DrawQuad>());
+ RenderPassList pass_list;
+ pass_list.push_back(pass.Pass());
+
+ EXPECT_TRUE(this->RunPixelTest(
+ &pass_list,
+ PixelTest::NoOffscreenContext,
+ base::FilePath(FILE_PATH_LITERAL("green_with_blue_corner.png")),
+ ExactPixelComparator(true)));
+}
+
// Software renderer does not support anti-aliased edges.
TEST_F(GLRendererPixelTest, AntiAliasing) {
gfx::Rect rect(this->device_viewport_size_);
@@ -1170,6 +1221,7 @@ TEST_F(GLRendererPixelTest, AntiAliasing) {
EXPECT_TRUE(this->RunPixelTest(
&pass_list,
+ PixelTest::NoOffscreenContext,
base::FilePath(FILE_PATH_LITERAL("anti_aliasing.png")),
FuzzyPixelOffByOneComparator(true)));
}
@@ -1222,6 +1274,7 @@ TEST_F(GLRendererPixelTest, AxisAligned) {
EXPECT_TRUE(this->RunPixelTest(
&pass_list,
+ PixelTest::NoOffscreenContext,
base::FilePath(FILE_PATH_LITERAL("axis_aligned.png")),
ExactPixelComparator(true)));
}
@@ -1263,6 +1316,7 @@ TEST_F(GLRendererPixelTest, ForceAntiAliasingOff) {
EXPECT_TRUE(this->RunPixelTest(
&pass_list,
+ PixelTest::NoOffscreenContext,
base::FilePath(FILE_PATH_LITERAL("force_anti_aliasing_off.png")),
ExactPixelComparator(false)));
}
@@ -1303,6 +1357,7 @@ TEST_F(GLRendererPixelTest, AntiAliasingPerspective) {
EXPECT_TRUE(this->RunPixelTest(
&pass_list,
+ PixelTest::NoOffscreenContext,
base::FilePath(FILE_PATH_LITERAL("anti_aliasing_perspective.png")),
FuzzyPixelOffByOneComparator(true)));
}
@@ -1312,7 +1367,7 @@ TYPED_TEST(RendererPixelTestWithSkiaGPUBackend, PictureDrawQuadIdentityScale) {
gfx::Rect viewport(this->device_viewport_size_);
bool use_skia_gpu_backend = this->UseSkiaGPUBackend();
// TODO(enne): the renderer should figure this out on its own.
- bool contents_swizzled = !PlatformColor::SameComponentOrder(GL_RGBA);
+ ResourceFormat texture_format = RGBA_8888;
RenderPass::Id id(1, 1);
gfx::Transform transform_to_root;
@@ -1350,7 +1405,7 @@ TYPED_TEST(RendererPixelTestWithSkiaGPUBackend, PictureDrawQuadIdentityScale) {
gfx::Rect(),
viewport,
viewport.size(),
- contents_swizzled,
+ texture_format,
viewport,
1.f,
use_skia_gpu_backend,
@@ -1375,7 +1430,7 @@ TYPED_TEST(RendererPixelTestWithSkiaGPUBackend, PictureDrawQuadIdentityScale) {
gfx::Rect(),
gfx::RectF(0.f, 0.f, 1.f, 1.f),
viewport.size(),
- contents_swizzled,
+ texture_format,
viewport,
1.f,
use_skia_gpu_backend,
@@ -1387,17 +1442,91 @@ TYPED_TEST(RendererPixelTestWithSkiaGPUBackend, PictureDrawQuadIdentityScale) {
EXPECT_TRUE(this->RunPixelTest(
&pass_list,
+ PixelTest::NoOffscreenContext,
base::FilePath(FILE_PATH_LITERAL("green_with_blue_corner.png")),
ExactPixelComparator(true)));
}
+// Not WithSkiaGPUBackend since that path currently requires tiles for opacity.
+TYPED_TEST(RendererPixelTest, PictureDrawQuadOpacity) {
+ gfx::Size pile_tile_size(1000, 1000);
+ gfx::Rect viewport(this->device_viewport_size_);
+ bool use_skia_gpu_backend = this->UseSkiaGPUBackend();
+ ResourceFormat texture_format = RGBA_8888;
+
+ RenderPass::Id id(1, 1);
+ gfx::Transform transform_to_root;
+ scoped_ptr<RenderPass> pass =
+ CreateTestRenderPass(id, viewport, transform_to_root);
+
+ // One viewport-filling 0.5-opacity green quad.
+ scoped_refptr<FakePicturePileImpl> green_pile =
+ FakePicturePileImpl::CreateFilledPile(pile_tile_size, viewport.size());
+ SkPaint green_paint;
+ green_paint.setColor(SK_ColorGREEN);
+ green_pile->add_draw_rect_with_paint(viewport, green_paint);
+ green_pile->RerecordPile();
+
+ gfx::Transform green_content_to_target_transform;
+ scoped_ptr<SharedQuadState> green_shared_state =
+ CreateTestSharedQuadState(green_content_to_target_transform, viewport);
+ green_shared_state->opacity = 0.5f;
+
+ scoped_ptr<PictureDrawQuad> green_quad = PictureDrawQuad::Create();
+ green_quad->SetNew(green_shared_state.get(),
+ viewport,
+ gfx::Rect(),
+ gfx::RectF(0, 0, 1, 1),
+ viewport.size(),
+ texture_format,
+ viewport,
+ 1.f,
+ use_skia_gpu_backend,
+ green_pile);
+ pass->quad_list.push_back(green_quad.PassAs<DrawQuad>());
+
+ // One viewport-filling white quad.
+ scoped_refptr<FakePicturePileImpl> white_pile =
+ FakePicturePileImpl::CreateFilledPile(pile_tile_size, viewport.size());
+ SkPaint white_paint;
+ white_paint.setColor(SK_ColorWHITE);
+ white_pile->add_draw_rect_with_paint(viewport, white_paint);
+ white_pile->RerecordPile();
+
+ gfx::Transform white_content_to_target_transform;
+ scoped_ptr<SharedQuadState> white_shared_state =
+ CreateTestSharedQuadState(white_content_to_target_transform, viewport);
+
+ scoped_ptr<PictureDrawQuad> white_quad = PictureDrawQuad::Create();
+ white_quad->SetNew(white_shared_state.get(),
+ viewport,
+ gfx::Rect(),
+ gfx::RectF(0, 0, 1, 1),
+ viewport.size(),
+ texture_format,
+ viewport,
+ 1.f,
+ use_skia_gpu_backend,
+ white_pile);
+ pass->quad_list.push_back(white_quad.PassAs<DrawQuad>());
+
+ RenderPassList pass_list;
+ pass_list.push_back(pass.Pass());
+
+ EXPECT_TRUE(this->RunPixelTest(
+ &pass_list,
+ PixelTest::NoOffscreenContext,
+ base::FilePath(FILE_PATH_LITERAL("green_alpha.png")),
+ FuzzyPixelOffByOneComparator(true)));
+}
+
TYPED_TEST(RendererPixelTestWithSkiaGPUBackend,
PictureDrawQuadNonIdentityScale) {
gfx::Size pile_tile_size(1000, 1000);
gfx::Rect viewport(this->device_viewport_size_);
bool use_skia_gpu_backend = this->UseSkiaGPUBackend();
// TODO(enne): the renderer should figure this out on its own.
- bool contents_swizzled = !PlatformColor::SameComponentOrder(GL_RGBA);
+ ResourceFormat texture_format = RGBA_8888;
RenderPass::Id id(1, 1);
gfx::Transform transform_to_root;
@@ -1431,7 +1560,7 @@ TYPED_TEST(RendererPixelTestWithSkiaGPUBackend,
gfx::Rect(),
gfx::RectF(green_rect1.size()),
green_rect1.size(),
- contents_swizzled,
+ texture_format,
green_rect1,
1.f,
use_skia_gpu_backend,
@@ -1444,7 +1573,7 @@ TYPED_TEST(RendererPixelTestWithSkiaGPUBackend,
gfx::Rect(),
gfx::RectF(green_rect2.size()),
green_rect2.size(),
- contents_swizzled,
+ texture_format,
green_rect2,
1.f,
use_skia_gpu_backend,
@@ -1516,7 +1645,7 @@ TYPED_TEST(RendererPixelTestWithSkiaGPUBackend,
gfx::Rect(),
quad_content_rect,
content_union_rect.size(),
- contents_swizzled,
+ texture_format,
content_union_rect,
contents_scale,
use_skia_gpu_backend,
@@ -1539,6 +1668,7 @@ TYPED_TEST(RendererPixelTestWithSkiaGPUBackend,
EXPECT_TRUE(this->RunPixelTest(
&pass_list,
+ PixelTest::NoOffscreenContext,
base::FilePath(FILE_PATH_LITERAL("four_blue_green_checkers.png")),
ExactPixelComparator(true)));
}
diff --git a/chromium/cc/output/shader.cc b/chromium/cc/output/shader.cc
index 8ab2114555f..beabf8db755 100644
--- a/chromium/cc/output/shader.cc
+++ b/chromium/cc/output/shader.cc
@@ -1597,7 +1597,7 @@ std::string FragmentShaderCheckerboard::GetShaderString(
vec2 texCoord =
clamp(v_texCoord, 0.0, 1.0) * texTransform.zw + texTransform.xy;
vec2 coord = mod(floor(texCoord * frequency * 2.0), 2.0);
- float picker = abs(coord.x - coord.y);
+ float picker = abs(coord.x - coord.y); // NOLINT
gl_FragColor = mix(color1, color2, picker) * alpha;
}
); // NOLINT(whitespace/parens)
diff --git a/chromium/cc/output/software_output_device.cc b/chromium/cc/output/software_output_device.cc
index fc77eb4c701..03f977e7426 100644
--- a/chromium/cc/output/software_output_device.cc
+++ b/chromium/cc/output/software_output_device.cc
@@ -6,8 +6,8 @@
#include "base/logging.h"
#include "cc/output/software_frame_data.h"
+#include "third_party/skia/include/core/SkBitmapDevice.h"
#include "third_party/skia/include/core/SkCanvas.h"
-#include "third_party/skia/include/core/SkDevice.h"
#include "ui/gfx/skia_util.h"
namespace cc {
@@ -21,7 +21,7 @@ void SoftwareOutputDevice::Resize(gfx::Size viewport_size) {
return;
viewport_size_ = viewport_size;
- device_ = skia::AdoptRef(new SkDevice(SkBitmap::kARGB_8888_Config,
+ device_ = skia::AdoptRef(new SkBitmapDevice(SkBitmap::kARGB_8888_Config,
viewport_size.width(), viewport_size.height(), true));
canvas_ = skia::AdoptRef(new SkCanvas(device_.get()));
}
diff --git a/chromium/cc/output/software_output_device.h b/chromium/cc/output/software_output_device.h
index fbada745a0a..9e2de88b048 100644
--- a/chromium/cc/output/software_output_device.h
+++ b/chromium/cc/output/software_output_device.h
@@ -8,12 +8,13 @@
#include "base/basictypes.h"
#include "cc/base/cc_export.h"
#include "skia/ext/refptr.h"
+// TODO(robertphillips): change this to "class SkBaseDevice;"
+#include "third_party/skia/include/core/SkDevice.h"
#include "ui/gfx/rect.h"
#include "ui/gfx/size.h"
#include "ui/gfx/vector2d.h"
class SkBitmap;
-class SkDevice;
class SkCanvas;
namespace cc {
@@ -27,23 +28,47 @@ class CC_EXPORT SoftwareOutputDevice {
SoftwareOutputDevice();
virtual ~SoftwareOutputDevice();
- // SoftwareOutputDevice implementation.
+ // Discards any pre-existing backing buffers and allocates memory for a
+ // software device of |size|. This must be called before the
+ // |SoftwareOutputDevice| can be used in other ways.
virtual void Resize(gfx::Size size);
+ // Called on BeginDrawingFrame. The compositor will draw into the returned
+ // SkCanvas. The |SoftwareOutputDevice| implementation needs to provide a
+ // valid SkCanvas of at least size |damage_rect|. This class retains ownership
+ // of the SkCanvas.
virtual SkCanvas* BeginPaint(gfx::Rect damage_rect);
+
+ // Called on FinishDrawingFrame. The compositor will no longer mutate the the
+ // SkCanvas instance returned by |BeginPaint| and should discard any reference
+ // that it holds to it.
virtual void EndPaint(SoftwareFrameData* frame_data);
+ // Copies pixels inside |rect| from the current software framebuffer to
+ // |output|. Fails if there is no current softwareframebuffer.
virtual void CopyToBitmap(gfx::Rect rect, SkBitmap* output);
+
+ // Blit the pixel content of the SoftwareOutputDevice by |delta| with the
+ // write clipped to |clip_rect|.
virtual void Scroll(gfx::Vector2d delta,
gfx::Rect clip_rect);
+ // Discard the backing buffer in the surface provided by this instance.
+ virtual void DiscardBackbuffer() {}
+
+ // Ensures that there is a backing buffer available on this instance.
+ virtual void EnsureBackbuffer() {}
+
// TODO(skaslev) Remove this after UberCompositor lands.
+ // Called in response to receiving a SwapBuffersAck. At this point, software
+ // frame identified by id can be reused or discarded as it is no longer being
+ // displayed.
virtual void ReclaimSoftwareFrame(unsigned id);
protected:
gfx::Size viewport_size_;
gfx::Rect damage_rect_;
- skia::RefPtr<SkDevice> device_;
+ skia::RefPtr<SkBaseDevice> device_;
skia::RefPtr<SkCanvas> canvas_;
private:
diff --git a/chromium/cc/output/software_renderer.cc b/chromium/cc/output/software_renderer.cc
index 9bf2efef39f..fa8c178df35 100644
--- a/chromium/cc/output/software_renderer.cc
+++ b/chromium/cc/output/software_renderer.cc
@@ -22,7 +22,7 @@
#include "skia/ext/opacity_draw_filter.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkColor.h"
-#include "third_party/skia/include/core/SkDevice.h"
+#include "third_party/skia/include/core/SkImageFilter.h"
#include "third_party/skia/include/core/SkMatrix.h"
#include "third_party/skia/include/core/SkShader.h"
#include "third_party/skia/include/effects/SkLayerRasterizer.h"
@@ -52,20 +52,23 @@ bool IsScaleAndIntegerTranslate(const SkMatrix& matrix) {
scoped_ptr<SoftwareRenderer> SoftwareRenderer::Create(
RendererClient* client,
+ const LayerTreeSettings* settings,
OutputSurface* output_surface,
ResourceProvider* resource_provider) {
- return make_scoped_ptr(
- new SoftwareRenderer(client, output_surface, resource_provider));
+ return make_scoped_ptr(new SoftwareRenderer(
+ client, settings, output_surface, resource_provider));
}
SoftwareRenderer::SoftwareRenderer(RendererClient* client,
+ const LayerTreeSettings* settings,
OutputSurface* output_surface,
ResourceProvider* resource_provider)
- : DirectRenderer(client, output_surface, resource_provider),
- visible_(true),
- is_scissor_enabled_(false),
- output_device_(output_surface->software_device()),
- current_canvas_(NULL) {
+ : DirectRenderer(client, settings, output_surface, resource_provider),
+ visible_(true),
+ is_scissor_enabled_(false),
+ is_backbuffer_discarded_(false),
+ output_device_(output_surface->software_device()),
+ current_canvas_(NULL) {
if (resource_provider_) {
capabilities_.max_texture_size = resource_provider_->max_texture_size();
capabilities_.best_texture_format =
@@ -76,7 +79,7 @@ SoftwareRenderer::SoftwareRenderer(RendererClient* client,
capabilities_.allow_partial_texture_updates = true;
capabilities_.using_partial_swap = true;
- capabilities_.using_map_image = Settings().use_map_image;
+ capabilities_.using_map_image = settings_->use_map_image;
capabilities_.using_shared_memory_resources = true;
}
@@ -128,14 +131,14 @@ void SoftwareRenderer::EnsureScissorTestDisabled() {
// clipRect on the current SkCanvas. This is done by setting clipRect to
// the viewport's dimensions.
is_scissor_enabled_ = false;
- SkDevice* device = current_canvas_->getDevice();
+ SkBaseDevice* device = current_canvas_->getDevice();
SetClipRect(gfx::Rect(device->width(), device->height()));
}
void SoftwareRenderer::Finish() {}
void SoftwareRenderer::BindFramebufferToOutputSurface(DrawingFrame* frame) {
- DCHECK(!client_->ExternalStencilTestEnabled());
+ DCHECK(!output_surface_->HasExternalStencilTest());
current_framebuffer_lock_.reset();
current_canvas_ = root_canvas_;
}
@@ -179,7 +182,11 @@ void SoftwareRenderer::ClearCanvas(SkColor color) {
current_canvas_->clear(color);
}
-void SoftwareRenderer::ClearFramebuffer(DrawingFrame* frame) {
+void SoftwareRenderer::DiscardPixels(bool has_external_stencil_test,
+ bool draw_rect_covers_full_surface) {}
+
+void SoftwareRenderer::ClearFramebuffer(DrawingFrame* frame,
+ bool has_external_stencil_test) {
if (frame->current_render_pass->has_transparent_background) {
ClearCanvas(SkColorSetARGB(0, 0, 0, 0));
} else {
@@ -228,8 +235,7 @@ void SoftwareRenderer::DoDrawQuad(DrawingFrame* frame, const DrawQuad* quad) {
quad->IsLeftEdge() &&
quad->IsBottomEdge() &&
quad->IsRightEdge();
- if (Settings().allow_antialiasing &&
- all_four_edges_are_exterior)
+ if (settings_->allow_antialiasing && all_four_edges_are_exterior)
current_paint_.setAntiAlias(true);
current_paint_.setFilterBitmap(true);
}
@@ -277,9 +283,11 @@ void SoftwareRenderer::DoDrawQuad(DrawingFrame* frame, const DrawQuad* quad) {
void SoftwareRenderer::DrawCheckerboardQuad(const DrawingFrame* frame,
const CheckerboardDrawQuad* quad) {
+ gfx::RectF visible_quad_vertex_rect = MathUtil::ScaleRectProportional(
+ QuadVertexRect(), quad->rect, quad->visible_rect);
current_paint_.setColor(quad->color);
current_paint_.setAlpha(quad->opacity() * SkColorGetA(quad->color));
- current_canvas_->drawRect(gfx::RectFToSkRect(QuadVertexRect()),
+ current_canvas_->drawRect(gfx::RectFToSkRect(visible_quad_vertex_rect),
current_paint_);
}
@@ -329,9 +337,11 @@ void SoftwareRenderer::DrawPictureQuad(const DrawingFrame* frame,
void SoftwareRenderer::DrawSolidColorQuad(const DrawingFrame* frame,
const SolidColorDrawQuad* quad) {
+ gfx::RectF visible_quad_vertex_rect = MathUtil::ScaleRectProportional(
+ QuadVertexRect(), quad->rect, quad->visible_rect);
current_paint_.setColor(quad->color);
current_paint_.setAlpha(quad->opacity() * SkColorGetA(quad->color));
- current_canvas_->drawRect(gfx::RectFToSkRect(QuadVertexRect()),
+ current_canvas_->drawRect(gfx::RectFToSkRect(visible_quad_vertex_rect),
current_paint_);
}
@@ -350,8 +360,12 @@ void SoftwareRenderer::DrawTextureQuad(const DrawingFrame* frame,
quad->uv_bottom_right),
bitmap->width(),
bitmap->height());
- SkRect sk_uv_rect = gfx::RectFToSkRect(uv_rect);
- SkRect quad_rect = gfx::RectFToSkRect(QuadVertexRect());
+ gfx::RectF visible_uv_rect =
+ MathUtil::ScaleRectProportional(uv_rect, quad->rect, quad->visible_rect);
+ SkRect sk_uv_rect = gfx::RectFToSkRect(visible_uv_rect);
+ gfx::RectF visible_quad_vertex_rect = MathUtil::ScaleRectProportional(
+ QuadVertexRect(), quad->rect, quad->visible_rect);
+ SkRect quad_rect = gfx::RectFToSkRect(visible_quad_vertex_rect);
if (quad->flipped)
current_canvas_->scale(1, -1);
@@ -384,12 +398,18 @@ void SoftwareRenderer::DrawTileQuad(const DrawingFrame* frame,
DCHECK(IsSoftwareResource(quad->resource_id));
ResourceProvider::ScopedReadLockSoftware lock(resource_provider_,
quad->resource_id);
+ gfx::RectF visible_tex_coord_rect = MathUtil::ScaleRectProportional(
+ quad->tex_coord_rect, quad->rect, quad->visible_rect);
+ gfx::RectF visible_quad_vertex_rect = MathUtil::ScaleRectProportional(
+ QuadVertexRect(), quad->rect, quad->visible_rect);
- SkRect uv_rect = gfx::RectFToSkRect(quad->tex_coord_rect);
+ SkRect uv_rect = gfx::RectFToSkRect(visible_tex_coord_rect);
current_paint_.setFilterBitmap(true);
- current_canvas_->drawBitmapRectToRect(*lock.sk_bitmap(), &uv_rect,
- gfx::RectFToSkRect(QuadVertexRect()),
- &current_paint_);
+ current_canvas_->drawBitmapRectToRect(
+ *lock.sk_bitmap(),
+ &uv_rect,
+ gfx::RectFToSkRect(visible_quad_vertex_rect),
+ &current_paint_);
}
void SoftwareRenderer::DrawRenderPassQuad(const DrawingFrame* frame,
@@ -404,6 +424,8 @@ void SoftwareRenderer::DrawRenderPassQuad(const DrawingFrame* frame,
content_texture->id());
SkRect dest_rect = gfx::RectFToSkRect(QuadVertexRect());
+ SkRect dest_visible_rect = gfx::RectFToSkRect(MathUtil::ScaleRectProportional(
+ QuadVertexRect(), quad->rect, quad->visible_rect));
SkRect content_rect = SkRect::MakeWH(quad->rect.width(), quad->rect.height());
SkMatrix content_mat;
@@ -451,10 +473,10 @@ void SoftwareRenderer::DrawRenderPassQuad(const DrawingFrame* frame,
mask_rasterizer->addLayer(mask_paint);
current_paint_.setRasterizer(mask_rasterizer.get());
- current_canvas_->drawRect(dest_rect, current_paint_);
+ current_canvas_->drawRect(dest_visible_rect, current_paint_);
} else {
// TODO(skaslev): Apply background filters and blend with content
- current_canvas_->drawRect(dest_rect, current_paint_);
+ current_canvas_->drawRect(dest_visible_rect, current_paint_);
}
}
@@ -474,11 +496,8 @@ void SoftwareRenderer::CopyCurrentRenderPassToBitmap(
DrawingFrame* frame,
scoped_ptr<CopyOutputRequest> request) {
gfx::Rect copy_rect = frame->current_render_pass->output_rect;
- if (request->has_area()) {
- // Intersect with the request's area, positioned with its origin at the
- // origin of the full copy_rect.
- copy_rect.Intersect(request->area() - copy_rect.OffsetFromOrigin());
- }
+ if (request->has_area())
+ copy_rect.Intersect(request->area());
gfx::Rect window_copy_rect = MoveFromDrawToWindowSpace(copy_rect);
scoped_ptr<SkBitmap> bitmap(new SkBitmap);
@@ -491,6 +510,26 @@ void SoftwareRenderer::CopyCurrentRenderPassToBitmap(
request->SendBitmapResult(bitmap.Pass());
}
+void SoftwareRenderer::DiscardBackbuffer() {
+ if (is_backbuffer_discarded_)
+ return;
+
+ output_surface_->DiscardBackbuffer();
+
+ is_backbuffer_discarded_ = true;
+
+ // Damage tracker needs a full reset every time framebuffer is discarded.
+ client_->SetFullRootLayerDamage();
+}
+
+void SoftwareRenderer::EnsureBackbuffer() {
+ if (!is_backbuffer_discarded_)
+ return;
+
+ output_surface_->EnsureBackbuffer();
+ is_backbuffer_discarded_ = false;
+}
+
void SoftwareRenderer::GetFramebufferPixels(void* pixels, gfx::Rect rect) {
TRACE_EVENT0("cc", "SoftwareRenderer::GetFramebufferPixels");
SkBitmap subset_bitmap;
@@ -505,12 +544,15 @@ void SoftwareRenderer::SetVisible(bool visible) {
if (visible_ == visible)
return;
visible_ = visible;
+
+ if (visible_)
+ EnsureBackbuffer();
+ else
+ DiscardBackbuffer();
}
void SoftwareRenderer::SetDiscardBackBufferWhenNotVisible(bool discard) {
- // TODO(piman, skaslev): Can we release the backbuffer? We don't currently
- // receive memory policy yet anyway.
- NOTIMPLEMENTED();
+ // The software renderer always discards the backbuffer when not visible.
}
} // namespace cc
diff --git a/chromium/cc/output/software_renderer.h b/chromium/cc/output/software_renderer.h
index f810b68274d..5a8780e043c 100644
--- a/chromium/cc/output/software_renderer.h
+++ b/chromium/cc/output/software_renderer.h
@@ -29,6 +29,7 @@ class CC_EXPORT SoftwareRenderer : public DirectRenderer {
public:
static scoped_ptr<SoftwareRenderer> Create(
RendererClient* client,
+ const LayerTreeSettings* settings,
OutputSurface* output_surface,
ResourceProvider* resource_provider);
@@ -45,6 +46,8 @@ class CC_EXPORT SoftwareRenderer : public DirectRenderer {
virtual void ReceiveSwapBuffersAck(
const CompositorFrameAck& ack) OVERRIDE;
virtual void SetDiscardBackBufferWhenNotVisible(bool discard) OVERRIDE;
+ virtual void DiscardBackbuffer() OVERRIDE;
+ virtual void EnsureBackbuffer() OVERRIDE;
protected:
virtual void BindFramebufferToOutputSurface(DrawingFrame* frame) OVERRIDE;
@@ -54,7 +57,10 @@ class CC_EXPORT SoftwareRenderer : public DirectRenderer {
gfx::Rect target_rect) OVERRIDE;
virtual void SetDrawViewport(gfx::Rect window_space_viewport) OVERRIDE;
virtual void SetScissorTestRect(gfx::Rect scissor_rect) OVERRIDE;
- virtual void ClearFramebuffer(DrawingFrame* frame) OVERRIDE;
+ virtual void DiscardPixels(bool has_external_stencil_test,
+ bool draw_rect_covers_full_surface) OVERRIDE;
+ virtual void ClearFramebuffer(DrawingFrame* frame,
+ bool has_external_stencil_test) OVERRIDE;
virtual void DoDrawQuad(DrawingFrame* frame, const DrawQuad* quad) OVERRIDE;
virtual void BeginDrawingFrame(DrawingFrame* frame) OVERRIDE;
virtual void FinishDrawingFrame(DrawingFrame* frame) OVERRIDE;
@@ -65,10 +71,10 @@ class CC_EXPORT SoftwareRenderer : public DirectRenderer {
DrawingFrame* frame,
scoped_ptr<CopyOutputRequest> request) OVERRIDE;
- SoftwareRenderer(
- RendererClient* client,
- OutputSurface* output_surface,
- ResourceProvider* resource_provider);
+ SoftwareRenderer(RendererClient* client,
+ const LayerTreeSettings* settings,
+ OutputSurface* output_surface,
+ ResourceProvider* resource_provider);
private:
void ClearCanvas(SkColor color);
@@ -95,6 +101,7 @@ class CC_EXPORT SoftwareRenderer : public DirectRenderer {
RendererCapabilities capabilities_;
bool visible_;
bool is_scissor_enabled_;
+ bool is_backbuffer_discarded_;
gfx::Rect scissor_rect_;
SoftwareOutputDevice* output_device_;
diff --git a/chromium/cc/output/software_renderer_unittest.cc b/chromium/cc/output/software_renderer_unittest.cc
index 238d654ac19..03f1635084b 100644
--- a/chromium/cc/output/software_renderer_unittest.cc
+++ b/chromium/cc/output/software_renderer_unittest.cc
@@ -13,6 +13,7 @@
#include "cc/quads/tile_draw_quad.h"
#include "cc/test/animation_test_common.h"
#include "cc/test/fake_output_surface.h"
+#include "cc/test/fake_output_surface_client.h"
#include "cc/test/geometry_test_utils.h"
#include "cc/test/render_pass_test_common.h"
#include "cc/test/render_pass_test_utils.h"
@@ -26,15 +27,16 @@ namespace {
class SoftwareRendererTest : public testing::Test, public RendererClient {
public:
- SoftwareRendererTest() : should_clear_root_render_pass_(true) {}
-
void InitializeRenderer(
scoped_ptr<SoftwareOutputDevice> software_output_device) {
output_surface_ = FakeOutputSurface::CreateSoftware(
software_output_device.Pass());
- resource_provider_ = ResourceProvider::Create(output_surface_.get(), 0);
+ CHECK(output_surface_->BindToClient(&output_surface_client_));
+
+ resource_provider_ =
+ ResourceProvider::Create(output_surface_.get(), 0, false);
renderer_ = SoftwareRenderer::Create(
- this, output_surface_.get(), resource_provider());
+ this, &settings_, output_surface_.get(), resource_provider());
}
ResourceProvider* resource_provider() const {
@@ -47,40 +49,23 @@ class SoftwareRendererTest : public testing::Test, public RendererClient {
viewport_ = viewport;
}
- void set_should_clear_root_render_pass(bool clear_root_render_pass) {
- should_clear_root_render_pass_ = clear_root_render_pass;
- }
-
// RendererClient implementation.
virtual gfx::Rect DeviceViewport() const OVERRIDE {
return viewport_;
}
- virtual float DeviceScaleFactor() const OVERRIDE {
- return 1.f;
- }
- virtual const LayerTreeSettings& Settings() const OVERRIDE {
- return settings_;
- }
+ virtual gfx::Rect DeviceClip() const OVERRIDE { return DeviceViewport(); }
virtual void SetFullRootLayerDamage() OVERRIDE {}
- virtual bool HasImplThread() const OVERRIDE { return false; }
- virtual bool ShouldClearRootRenderPass() const OVERRIDE {
- return should_clear_root_render_pass_;
- }
virtual CompositorFrameMetadata MakeCompositorFrameMetadata() const OVERRIDE {
return CompositorFrameMetadata();
}
- virtual bool AllowPartialSwap() const OVERRIDE {
- return true;
- }
- virtual bool ExternalStencilTestEnabled() const OVERRIDE { return false; }
protected:
+ LayerTreeSettings settings_;
+ FakeOutputSurfaceClient output_surface_client_;
scoped_ptr<FakeOutputSurface> output_surface_;
scoped_ptr<ResourceProvider> resource_provider_;
scoped_ptr<SoftwareRenderer> renderer_;
gfx::Rect viewport_;
- LayerTreeSettings settings_;
- bool should_clear_root_render_pass_;
};
TEST_F(SoftwareRendererTest, SolidColorQuad) {
@@ -88,6 +73,7 @@ TEST_F(SoftwareRendererTest, SolidColorQuad) {
gfx::Size inner_size(98, 98);
gfx::Rect outer_rect(outer_size);
gfx::Rect inner_rect(gfx::Point(1, 1), inner_size);
+ gfx::Rect visible_rect(gfx::Point(1, 2), gfx::Size(98, 97));
set_viewport(gfx::Rect(outer_size));
InitializeRenderer(make_scoped_ptr(new SoftwareOutputDevice));
@@ -104,12 +90,15 @@ TEST_F(SoftwareRendererTest, SolidColorQuad) {
shared_quad_state.get(), outer_rect, SK_ColorYELLOW, false);
scoped_ptr<SolidColorDrawQuad> inner_quad = SolidColorDrawQuad::Create();
inner_quad->SetNew(shared_quad_state.get(), inner_rect, SK_ColorCYAN, false);
+ inner_quad->visible_rect = visible_rect;
root_render_pass->AppendQuad(inner_quad.PassAs<DrawQuad>());
root_render_pass->AppendQuad(outer_quad.PassAs<DrawQuad>());
RenderPassList list;
list.push_back(root_render_pass.PassAs<RenderPass>());
- renderer()->DrawFrame(&list);
+
+ float device_scale_factor = 1.f;
+ renderer()->DrawFrame(&list, NULL, device_scale_factor, true);
SkBitmap output;
output.setConfig(SkBitmap::kARGB_8888_Config,
@@ -121,7 +110,8 @@ TEST_F(SoftwareRendererTest, SolidColorQuad) {
EXPECT_EQ(SK_ColorYELLOW, output.getColor(0, 0));
EXPECT_EQ(SK_ColorYELLOW,
output.getColor(outer_size.width() - 1, outer_size.height() - 1));
- EXPECT_EQ(SK_ColorCYAN, output.getColor(1, 1));
+ EXPECT_EQ(SK_ColorYELLOW, output.getColor(1, 1));
+ EXPECT_EQ(SK_ColorCYAN, output.getColor(1, 2));
EXPECT_EQ(SK_ColorCYAN,
output.getColor(inner_size.width() - 1, inner_size.height() - 1));
}
@@ -135,11 +125,15 @@ TEST_F(SoftwareRendererTest, TileQuad) {
InitializeRenderer(make_scoped_ptr(new SoftwareOutputDevice));
ResourceProvider::ResourceId resource_yellow =
- resource_provider()->CreateResource(
- outer_size, GL_RGBA, ResourceProvider::TextureUsageAny);
+ resource_provider()->CreateResource(outer_size,
+ GL_CLAMP_TO_EDGE,
+ ResourceProvider::TextureUsageAny,
+ RGBA_8888);
ResourceProvider::ResourceId resource_cyan =
- resource_provider()->CreateResource(
- inner_size, GL_RGBA, ResourceProvider::TextureUsageAny);
+ resource_provider()->CreateResource(inner_size,
+ GL_CLAMP_TO_EDGE,
+ ResourceProvider::TextureUsageAny,
+ RGBA_8888);
SkBitmap yellow_tile;
yellow_tile.setConfig(
@@ -195,7 +189,9 @@ TEST_F(SoftwareRendererTest, TileQuad) {
RenderPassList list;
list.push_back(root_render_pass.PassAs<RenderPass>());
- renderer()->DrawFrame(&list);
+
+ float device_scale_factor = 1.f;
+ renderer()->DrawFrame(&list, NULL, device_scale_factor, true);
SkBitmap output;
output.setConfig(SkBitmap::kARGB_8888_Config,
@@ -212,10 +208,95 @@ TEST_F(SoftwareRendererTest, TileQuad) {
output.getColor(inner_size.width() - 1, inner_size.height() - 1));
}
+TEST_F(SoftwareRendererTest, TileQuadVisibleRect) {
+ gfx::Size tile_size(100, 100);
+ gfx::Rect tile_rect(tile_size);
+ gfx::Rect visible_rect = tile_rect;
+ visible_rect.Inset(1, 2, 3, 4);
+ set_viewport(gfx::Rect(tile_size));
+ InitializeRenderer(make_scoped_ptr(new SoftwareOutputDevice));
+
+ ResourceProvider::ResourceId resource_cyan =
+ resource_provider()->CreateResource(tile_size,
+ GL_CLAMP_TO_EDGE,
+ ResourceProvider::TextureUsageAny,
+ RGBA_8888);
+
+ SkBitmap cyan_tile; // The lowest five rows are yellow.
+ cyan_tile.setConfig(
+ SkBitmap::kARGB_8888_Config, tile_size.width(), tile_size.height());
+ cyan_tile.allocPixels();
+ cyan_tile.eraseColor(SK_ColorCYAN);
+ cyan_tile.eraseArea(
+ SkIRect::MakeLTRB(
+ 0, visible_rect.bottom() - 1, tile_rect.width(), tile_rect.bottom()),
+ SK_ColorYELLOW);
+
+ resource_provider()->SetPixels(resource_cyan,
+ static_cast<uint8_t*>(cyan_tile.getPixels()),
+ gfx::Rect(tile_size),
+ gfx::Rect(tile_size),
+ gfx::Vector2d());
+
+ gfx::Rect root_rect = DeviceViewport();
+
+ scoped_ptr<SharedQuadState> shared_quad_state = SharedQuadState::Create();
+ shared_quad_state->SetAll(
+ gfx::Transform(), tile_size, tile_rect, tile_rect, false, 1.0);
+ RenderPass::Id root_render_pass_id = RenderPass::Id(1, 1);
+ scoped_ptr<TestRenderPass> root_render_pass = TestRenderPass::Create();
+ root_render_pass->SetNew(
+ root_render_pass_id, root_rect, root_rect, gfx::Transform());
+ scoped_ptr<TileDrawQuad> quad = TileDrawQuad::Create();
+ quad->SetNew(shared_quad_state.get(),
+ tile_rect,
+ tile_rect,
+ resource_cyan,
+ gfx::RectF(tile_size),
+ tile_size,
+ false);
+ quad->visible_rect = visible_rect;
+ root_render_pass->AppendQuad(quad.PassAs<DrawQuad>());
+
+ RenderPassList list;
+ list.push_back(root_render_pass.PassAs<RenderPass>());
+
+ float device_scale_factor = 1.f;
+ renderer()->DrawFrame(&list, NULL, device_scale_factor, true);
+
+ SkBitmap output;
+ output.setConfig(SkBitmap::kARGB_8888_Config,
+ DeviceViewport().width(),
+ DeviceViewport().height());
+ output.allocPixels();
+ renderer()->GetFramebufferPixels(output.getPixels(), tile_rect);
+
+ // Check portion of tile not in visible rect isn't drawn.
+ const unsigned int kTransparent = SK_ColorTRANSPARENT;
+ EXPECT_EQ(kTransparent, output.getColor(0, 0));
+ EXPECT_EQ(kTransparent,
+ output.getColor(tile_rect.width() - 1, tile_rect.height() - 1));
+ EXPECT_EQ(kTransparent,
+ output.getColor(visible_rect.x() - 1, visible_rect.y() - 1));
+ EXPECT_EQ(kTransparent,
+ output.getColor(visible_rect.right(), visible_rect.bottom()));
+ // Ensure visible part is drawn correctly.
+ EXPECT_EQ(SK_ColorCYAN, output.getColor(visible_rect.x(), visible_rect.y()));
+ EXPECT_EQ(
+ SK_ColorCYAN,
+ output.getColor(visible_rect.right() - 2, visible_rect.bottom() - 2));
+ // Ensure last visible line is correct.
+ EXPECT_EQ(
+ SK_ColorYELLOW,
+ output.getColor(visible_rect.right() - 1, visible_rect.bottom() - 1));
+}
+
TEST_F(SoftwareRendererTest, ShouldClearRootRenderPass) {
+ float device_scale_factor = 1.f;
gfx::Rect viewport_rect(0, 0, 100, 100);
set_viewport(viewport_rect);
- set_should_clear_root_render_pass(false);
+
+ settings_.should_clear_root_render_pass = false;
InitializeRenderer(make_scoped_ptr(new SoftwareOutputDevice));
RenderPassList list;
@@ -233,7 +314,7 @@ TEST_F(SoftwareRendererTest, ShouldClearRootRenderPass) {
AddQuad(root_clear_pass, viewport_rect, SK_ColorGREEN);
renderer()->DecideRenderPassAllocationsForFrame(list);
- renderer()->DrawFrame(&list);
+ renderer()->DrawFrame(&list, NULL, device_scale_factor, true);
renderer()->GetFramebufferPixels(output.getPixels(), viewport_rect);
EXPECT_EQ(SK_ColorGREEN, output.getColor(0, 0));
@@ -252,7 +333,7 @@ TEST_F(SoftwareRendererTest, ShouldClearRootRenderPass) {
AddQuad(root_smaller_pass, smaller_rect, SK_ColorMAGENTA);
renderer()->DecideRenderPassAllocationsForFrame(list);
- renderer()->DrawFrame(&list);
+ renderer()->DrawFrame(&list, NULL, device_scale_factor, true);
renderer()->GetFramebufferPixels(output.getPixels(), viewport_rect);
// If we didn't clear, the borders should still be green.
@@ -266,5 +347,60 @@ TEST_F(SoftwareRendererTest, ShouldClearRootRenderPass) {
output.getColor(smaller_rect.right() - 1, smaller_rect.bottom() - 1));
}
+TEST_F(SoftwareRendererTest, RenderPassVisibleRect) {
+ float device_scale_factor = 1.f;
+ gfx::Rect viewport_rect(0, 0, 100, 100);
+ set_viewport(viewport_rect);
+ InitializeRenderer(make_scoped_ptr(new SoftwareOutputDevice));
+
+ RenderPassList list;
+
+ SkBitmap output;
+ output.setConfig(SkBitmap::kARGB_8888_Config,
+ viewport_rect.width(),
+ viewport_rect.height());
+ output.allocPixels();
+
+ // Pass drawn as inner quad is magenta.
+ gfx::Rect smaller_rect(20, 20, 60, 60);
+ RenderPass::Id smaller_pass_id(2, 1);
+ TestRenderPass* smaller_pass =
+ AddRenderPass(&list, smaller_pass_id, smaller_rect, gfx::Transform());
+ AddQuad(smaller_pass, smaller_rect, SK_ColorMAGENTA);
+
+ // Root pass is green.
+ RenderPass::Id root_clear_pass_id(1, 0);
+ TestRenderPass* root_clear_pass =
+ AddRenderPass(&list, root_clear_pass_id, viewport_rect, gfx::Transform());
+ AddRenderPassQuad(root_clear_pass, smaller_pass);
+ AddQuad(root_clear_pass, viewport_rect, SK_ColorGREEN);
+
+ // Interior pass quad has smaller visible rect.
+ gfx::Rect interior_visible_rect(30, 30, 40, 40);
+ root_clear_pass->quad_list[0]->visible_rect = interior_visible_rect;
+
+ renderer()->DecideRenderPassAllocationsForFrame(list);
+ renderer()->DrawFrame(&list, NULL, device_scale_factor, true);
+ renderer()->GetFramebufferPixels(output.getPixels(), viewport_rect);
+
+ EXPECT_EQ(SK_ColorGREEN, output.getColor(0, 0));
+ EXPECT_EQ(
+ SK_ColorGREEN,
+ output.getColor(viewport_rect.width() - 1, viewport_rect.height() - 1));
+
+ // Part outside visible rect should remain green.
+ EXPECT_EQ(SK_ColorGREEN, output.getColor(smaller_rect.x(), smaller_rect.y()));
+ EXPECT_EQ(
+ SK_ColorGREEN,
+ output.getColor(smaller_rect.right() - 1, smaller_rect.bottom() - 1));
+
+ EXPECT_EQ(
+ SK_ColorMAGENTA,
+ output.getColor(interior_visible_rect.x(), interior_visible_rect.y()));
+ EXPECT_EQ(SK_ColorMAGENTA,
+ output.getColor(interior_visible_rect.right() - 1,
+ interior_visible_rect.bottom() - 1));
+}
+
} // namespace
} // namespace cc
diff --git a/chromium/cc/quads/draw_quad_unittest.cc b/chromium/cc/quads/draw_quad_unittest.cc
index 0ab0513ef01..5705ce7bf24 100644
--- a/chromium/cc/quads/draw_quad_unittest.cc
+++ b/chromium/cc/quads/draw_quad_unittest.cc
@@ -665,7 +665,7 @@ TEST(DrawQuadTest, CopyPictureDrawQuad) {
gfx::Rect opaque_rect(33, 44, 22, 33);
gfx::RectF tex_coord_rect(31.f, 12.f, 54.f, 20.f);
gfx::Size texture_size(85, 32);
- bool swizzle_contents = true;
+ ResourceFormat texture_format = RGBA_8888;
gfx::Rect content_rect(30, 40, 20, 30);
float contents_scale = 3.141592f;
bool can_draw_direct_to_backbuffer = true;
@@ -676,7 +676,7 @@ TEST(DrawQuadTest, CopyPictureDrawQuad) {
opaque_rect,
tex_coord_rect,
texture_size,
- swizzle_contents,
+ texture_format,
content_rect,
contents_scale,
can_draw_direct_to_backbuffer,
@@ -685,7 +685,7 @@ TEST(DrawQuadTest, CopyPictureDrawQuad) {
EXPECT_RECT_EQ(opaque_rect, copy_quad->opaque_rect);
EXPECT_EQ(tex_coord_rect, copy_quad->tex_coord_rect);
EXPECT_EQ(texture_size, copy_quad->texture_size);
- EXPECT_EQ(swizzle_contents, copy_quad->swizzle_contents);
+ EXPECT_EQ(texture_format, copy_quad->texture_format);
EXPECT_RECT_EQ(content_rect, copy_quad->content_rect);
EXPECT_EQ(contents_scale, copy_quad->contents_scale);
EXPECT_EQ(can_draw_direct_to_backbuffer,
@@ -695,7 +695,7 @@ TEST(DrawQuadTest, CopyPictureDrawQuad) {
CREATE_QUAD_7_ALL(PictureDrawQuad,
tex_coord_rect,
texture_size,
- swizzle_contents,
+ texture_format,
content_rect,
contents_scale,
can_draw_direct_to_backbuffer,
@@ -703,7 +703,7 @@ TEST(DrawQuadTest, CopyPictureDrawQuad) {
EXPECT_EQ(DrawQuad::PICTURE_CONTENT, copy_quad->material);
EXPECT_EQ(tex_coord_rect, copy_quad->tex_coord_rect);
EXPECT_EQ(texture_size, copy_quad->texture_size);
- EXPECT_EQ(swizzle_contents, copy_quad->swizzle_contents);
+ EXPECT_EQ(texture_format, copy_quad->texture_format);
EXPECT_RECT_EQ(content_rect, copy_quad->content_rect);
EXPECT_EQ(contents_scale, copy_quad->contents_scale);
EXPECT_EQ(can_draw_direct_to_backbuffer,
@@ -893,7 +893,7 @@ TEST_F(DrawQuadIteratorTest, DISABLED_PictureDrawQuad) {
gfx::Rect opaque_rect(33, 44, 22, 33);
gfx::RectF tex_coord_rect(31.f, 12.f, 54.f, 20.f);
gfx::Size texture_size(85, 32);
- bool swizzle_contents = true;
+ ResourceFormat texture_format = RGBA_8888;
gfx::Rect content_rect(30, 40, 20, 30);
float contents_scale = 3.141592f;
bool can_draw_direct_to_backbuffer = true;
@@ -904,7 +904,7 @@ TEST_F(DrawQuadIteratorTest, DISABLED_PictureDrawQuad) {
opaque_rect,
tex_coord_rect,
texture_size,
- swizzle_contents,
+ texture_format,
content_rect,
contents_scale,
can_draw_direct_to_backbuffer,
diff --git a/chromium/cc/quads/picture_draw_quad.cc b/chromium/cc/quads/picture_draw_quad.cc
index 0494764d8aa..e566b247f72 100644
--- a/chromium/cc/quads/picture_draw_quad.cc
+++ b/chromium/cc/quads/picture_draw_quad.cc
@@ -6,6 +6,7 @@
#include "base/values.h"
#include "cc/base/math_util.h"
+#include "cc/resources/platform_color.h"
namespace cc {
@@ -24,18 +25,24 @@ void PictureDrawQuad::SetNew(const SharedQuadState* shared_quad_state,
gfx::Rect opaque_rect,
const gfx::RectF& tex_coord_rect,
gfx::Size texture_size,
- bool swizzle_contents,
+ ResourceFormat texture_format,
gfx::Rect content_rect,
float contents_scale,
bool can_draw_direct_to_backbuffer,
scoped_refptr<PicturePileImpl> picture_pile) {
- ContentDrawQuadBase::SetNew(shared_quad_state, DrawQuad::PICTURE_CONTENT,
- rect, opaque_rect, tex_coord_rect, texture_size,
- swizzle_contents);
+ ContentDrawQuadBase::SetNew(shared_quad_state,
+ DrawQuad::PICTURE_CONTENT,
+ rect,
+ opaque_rect,
+ tex_coord_rect,
+ texture_size,
+ !PlatformColor::SameComponentOrder(
+ texture_format));
this->content_rect = content_rect;
this->contents_scale = contents_scale;
this->can_draw_direct_to_backbuffer = can_draw_direct_to_backbuffer;
this->picture_pile = picture_pile;
+ this->texture_format = texture_format;
}
void PictureDrawQuad::SetAll(const SharedQuadState* shared_quad_state,
@@ -45,19 +52,26 @@ void PictureDrawQuad::SetAll(const SharedQuadState* shared_quad_state,
bool needs_blending,
const gfx::RectF& tex_coord_rect,
gfx::Size texture_size,
- bool swizzle_contents,
+ ResourceFormat texture_format,
gfx::Rect content_rect,
float contents_scale,
bool can_draw_direct_to_backbuffer,
scoped_refptr<PicturePileImpl> picture_pile) {
ContentDrawQuadBase::SetAll(shared_quad_state,
- DrawQuad::PICTURE_CONTENT, rect, opaque_rect,
- visible_rect, needs_blending, tex_coord_rect,
- texture_size, swizzle_contents);
+ DrawQuad::PICTURE_CONTENT,
+ rect,
+ opaque_rect,
+ visible_rect,
+ needs_blending,
+ tex_coord_rect,
+ texture_size,
+ !PlatformColor::SameComponentOrder(
+ texture_format));
this->content_rect = content_rect;
this->contents_scale = contents_scale;
this->can_draw_direct_to_backbuffer = can_draw_direct_to_backbuffer;
this->picture_pile = picture_pile;
+ this->texture_format = texture_format;
}
void PictureDrawQuad::IterateResources(
@@ -77,6 +91,7 @@ void PictureDrawQuad::ExtendValue(base::DictionaryValue* value) const {
value->SetDouble("contents_scale", contents_scale);
value->SetBoolean("can_draw_direct_to_backbuffer",
can_draw_direct_to_backbuffer);
+ value->SetInteger("texture_format", texture_format);
// TODO(piman): picture_pile?
}
diff --git a/chromium/cc/quads/picture_draw_quad.h b/chromium/cc/quads/picture_draw_quad.h
index beec88ce3f3..756482a610b 100644
--- a/chromium/cc/quads/picture_draw_quad.h
+++ b/chromium/cc/quads/picture_draw_quad.h
@@ -27,7 +27,7 @@ class CC_EXPORT PictureDrawQuad : public ContentDrawQuadBase {
gfx::Rect opaque_rect,
const gfx::RectF& tex_coord_rect,
gfx::Size texture_size,
- bool swizzle_contents,
+ ResourceFormat texture_format,
gfx::Rect content_rect,
float contents_scale,
bool can_draw_direct_to_backbuffer,
@@ -40,7 +40,7 @@ class CC_EXPORT PictureDrawQuad : public ContentDrawQuadBase {
bool needs_blending,
const gfx::RectF& tex_coord_rect,
gfx::Size texture_size,
- bool swizzle_contents,
+ ResourceFormat texture_format,
gfx::Rect content_rect,
float contents_scale,
bool can_draw_direct_to_backbuffer,
@@ -50,6 +50,7 @@ class CC_EXPORT PictureDrawQuad : public ContentDrawQuadBase {
float contents_scale;
bool can_draw_direct_to_backbuffer;
scoped_refptr<PicturePileImpl> picture_pile;
+ ResourceFormat texture_format;
virtual void IterateResources(const ResourceIteratorCallback& callback)
OVERRIDE;
diff --git a/chromium/cc/quads/render_pass.h b/chromium/cc/quads/render_pass.h
index 224a0506f03..cf2a02d4a3c 100644
--- a/chromium/cc/quads/render_pass.h
+++ b/chromium/cc/quads/render_pass.h
@@ -11,11 +11,8 @@
#include "base/callback.h"
#include "base/containers/hash_tables.h"
#include "cc/base/cc_export.h"
-#include "cc/base/scoped_ptr_hash_map.h"
#include "cc/base/scoped_ptr_vector.h"
#include "skia/ext/refptr.h"
-#include "third_party/skia/include/core/SkColor.h"
-#include "third_party/skia/include/core/SkImageFilter.h"
#include "ui/gfx/rect.h"
#include "ui/gfx/rect_f.h"
#include "ui/gfx/transform.h"
diff --git a/chromium/cc/quads/render_pass_draw_quad.cc b/chromium/cc/quads/render_pass_draw_quad.cc
index 0528cd5e34f..73b2553e209 100644
--- a/chromium/cc/quads/render_pass_draw_quad.cc
+++ b/chromium/cc/quads/render_pass_draw_quad.cc
@@ -7,6 +7,7 @@
#include "base/values.h"
#include "cc/base/math_util.h"
#include "cc/debug/traced_value.h"
+#include "third_party/skia/include/core/SkImageFilter.h"
namespace cc {
diff --git a/chromium/cc/resources/bitmap_content_layer_updater.cc b/chromium/cc/resources/bitmap_content_layer_updater.cc
index 3d90eb0a4d5..75b99c54b81 100644
--- a/chromium/cc/resources/bitmap_content_layer_updater.cc
+++ b/chromium/cc/resources/bitmap_content_layer_updater.cc
@@ -43,8 +43,7 @@ BitmapContentLayerUpdater::BitmapContentLayerUpdater(
scoped_ptr<LayerPainter> painter,
RenderingStatsInstrumentation* stats_instrumentation,
int layer_id)
- : ContentLayerUpdater(painter.Pass(), stats_instrumentation, layer_id),
- opaque_(false) {}
+ : ContentLayerUpdater(painter.Pass(), stats_instrumentation, layer_id) {}
BitmapContentLayerUpdater::~BitmapContentLayerUpdater() {}
@@ -67,13 +66,13 @@ void BitmapContentLayerUpdater::PrepareToUpdate(
devtools_instrumentation::kPaintSetup, layer_id_);
canvas_size_ = content_rect.size();
canvas_ = skia::AdoptRef(skia::CreateBitmapCanvas(
- canvas_size_.width(), canvas_size_.height(), opaque_));
+ canvas_size_.width(), canvas_size_.height(), layer_is_opaque_));
}
base::TimeTicks start_time =
rendering_stats_instrumentation_->StartRecording();
PaintContents(canvas_.get(),
- content_rect,
+ content_rect.origin(),
contents_width_scale,
contents_height_scale,
resulting_opaque_rect);
@@ -108,11 +107,12 @@ void BitmapContentLayerUpdater::ReduceMemoryUsage() {
}
void BitmapContentLayerUpdater::SetOpaque(bool opaque) {
- if (opaque != opaque_) {
+ if (opaque != layer_is_opaque_) {
canvas_.clear();
canvas_size_ = gfx::Size();
}
- opaque_ = opaque;
+
+ ContentLayerUpdater::SetOpaque(opaque);
}
} // namespace cc
diff --git a/chromium/cc/resources/bitmap_skpicture_content_layer_updater.cc b/chromium/cc/resources/bitmap_skpicture_content_layer_updater.cc
index 2c00099de1c..2db3f417a97 100644
--- a/chromium/cc/resources/bitmap_skpicture_content_layer_updater.cc
+++ b/chromium/cc/resources/bitmap_skpicture_content_layer_updater.cc
@@ -9,8 +9,8 @@
#include "cc/resources/layer_painter.h"
#include "cc/resources/prioritized_resource.h"
#include "cc/resources/resource_update_queue.h"
+#include "third_party/skia/include/core/SkBitmapDevice.h"
#include "third_party/skia/include/core/SkCanvas.h"
-#include "third_party/skia/include/core/SkDevice.h"
namespace cc {
@@ -28,7 +28,7 @@ void BitmapSkPictureContentLayerUpdater::Resource::Update(
SkBitmap::kARGB_8888_Config, source_rect.width(), source_rect.height());
bitmap_.allocPixels();
bitmap_.setIsOpaque(updater_->layer_is_opaque());
- SkDevice device(bitmap_);
+ SkBitmapDevice device(bitmap_);
SkCanvas canvas(&device);
updater_->PaintContentsRect(&canvas, source_rect);
diff --git a/chromium/cc/resources/content_layer_updater.cc b/chromium/cc/resources/content_layer_updater.cc
index c9c2eccf31c..2e73c4bb904 100644
--- a/chromium/cc/resources/content_layer_updater.cc
+++ b/chromium/cc/resources/content_layer_updater.cc
@@ -9,6 +9,7 @@
#include "cc/debug/rendering_stats_instrumentation.h"
#include "cc/resources/layer_painter.h"
#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkDevice.h"
#include "third_party/skia/include/core/SkPaint.h"
#include "third_party/skia/include/core/SkRect.h"
#include "third_party/skia/include/core/SkScalar.h"
@@ -23,7 +24,8 @@ ContentLayerUpdater::ContentLayerUpdater(
int layer_id)
: rendering_stats_instrumentation_(stats_instrumentation),
layer_id_(layer_id),
- painter_(painter.Pass()) {}
+ painter_(painter.Pass()),
+ layer_is_opaque_(false) {}
ContentLayerUpdater::~ContentLayerUpdater() {}
@@ -33,14 +35,17 @@ void ContentLayerUpdater::set_rendering_stats_instrumentation(
}
void ContentLayerUpdater::PaintContents(SkCanvas* canvas,
- gfx::Rect content_rect,
+ gfx::Point origin,
float contents_width_scale,
float contents_height_scale,
gfx::Rect* resulting_opaque_rect) {
TRACE_EVENT0("cc", "ContentLayerUpdater::PaintContents");
canvas->save();
- canvas->translate(SkFloatToScalar(-content_rect.x()),
- SkFloatToScalar(-content_rect.y()));
+ canvas->translate(SkFloatToScalar(-origin.x()),
+ SkFloatToScalar(-origin.y()));
+
+ SkBaseDevice* device = canvas->getDevice();
+ gfx::Rect content_rect(origin, gfx::Size(device->width(), device->height()));
gfx::Rect layer_rect = content_rect;
@@ -52,12 +57,14 @@ void ContentLayerUpdater::PaintContents(SkCanvas* canvas,
content_rect, 1.f / contents_width_scale, 1.f / contents_height_scale);
}
- SkPaint paint;
- paint.setAntiAlias(false);
- paint.setXfermodeMode(SkXfermode::kClear_Mode);
SkRect layer_sk_rect = SkRect::MakeXYWH(
layer_rect.x(), layer_rect.y(), layer_rect.width(), layer_rect.height());
- canvas->drawRect(layer_sk_rect, paint);
+
+ // If the layer has opaque contents then there is no need to
+ // clear the canvas before painting.
+ if (!layer_is_opaque_)
+ canvas->clear(SK_ColorTRANSPARENT);
+
canvas->clipRect(layer_sk_rect);
gfx::RectF opaque_layer_rect;
@@ -71,4 +78,8 @@ void ContentLayerUpdater::PaintContents(SkCanvas* canvas,
content_rect_ = content_rect;
}
+void ContentLayerUpdater::SetOpaque(bool opaque) {
+ layer_is_opaque_ = opaque;
+}
+
} // namespace cc
diff --git a/chromium/cc/resources/content_layer_updater.h b/chromium/cc/resources/content_layer_updater.h
index 6c8dee3bba7..6959526ef1c 100644
--- a/chromium/cc/resources/content_layer_updater.h
+++ b/chromium/cc/resources/content_layer_updater.h
@@ -22,6 +22,7 @@ class RenderingStatsInstrumentation;
class CC_EXPORT ContentLayerUpdater : public LayerUpdater {
public:
void set_rendering_stats_instrumentation(RenderingStatsInstrumentation* rsi);
+ virtual void SetOpaque(bool) OVERRIDE;
protected:
ContentLayerUpdater(scoped_ptr<LayerPainter> painter,
@@ -30,12 +31,14 @@ class CC_EXPORT ContentLayerUpdater : public LayerUpdater {
virtual ~ContentLayerUpdater();
void PaintContents(SkCanvas* canvas,
- gfx::Rect content_rect,
+ gfx::Point origin,
float contents_width_scale,
float contents_height_scale,
gfx::Rect* resulting_opaque_rect);
gfx::Rect content_rect() const { return content_rect_; }
+ bool layer_is_opaque() const { return layer_is_opaque_; }
+
RenderingStatsInstrumentation* rendering_stats_instrumentation_;
int layer_id_;
@@ -43,6 +46,10 @@ class CC_EXPORT ContentLayerUpdater : public LayerUpdater {
gfx::Rect content_rect_;
scoped_ptr<LayerPainter> painter_;
+ protected:
+ // True when it is known that all output pixels will be opaque.
+ bool layer_is_opaque_;
+
DISALLOW_COPY_AND_ASSIGN(ContentLayerUpdater);
};
diff --git a/chromium/cc/resources/image_raster_worker_pool.cc b/chromium/cc/resources/image_raster_worker_pool.cc
index 408459c8f87..30add2ad2ea 100644
--- a/chromium/cc/resources/image_raster_worker_pool.cc
+++ b/chromium/cc/resources/image_raster_worker_pool.cc
@@ -8,7 +8,7 @@
#include "base/values.h"
#include "cc/debug/traced_value.h"
#include "cc/resources/resource.h"
-#include "third_party/skia/include/core/SkDevice.h"
+#include "third_party/skia/include/core/SkBitmapDevice.h"
namespace cc {
@@ -34,14 +34,10 @@ class ImageWorkerPoolTaskImpl : public internal::WorkerPoolTask {
if (!buffer_)
return;
- SkBitmap bitmap;
- bitmap.setConfig(SkBitmap::kARGB_8888_Config,
- task_->resource()->size().width(),
- task_->resource()->size().height(),
- stride_);
- bitmap.setPixels(buffer_);
- SkDevice device(bitmap);
- task_->RunOnWorkerThread(&device, thread_index);
+ task_->RunOnWorkerThread(thread_index,
+ buffer_,
+ task_->resource()->size(),
+ stride_);
}
virtual void CompleteOnOriginThread() OVERRIDE {
reply_.Run(!HasFinishedRunning());
@@ -104,6 +100,8 @@ void ImageRasterWorkerPool::ScheduleTasks(RasterTask::Queue* queue) {
for (RasterTaskVector::const_iterator it = raster_tasks().begin();
it != raster_tasks().end(); ++it) {
internal::RasterWorkerPoolTask* task = it->get();
+ DCHECK(!task->HasCompleted());
+ DCHECK(!task->WasCanceled());
TaskMap::iterator image_it = image_tasks_.find(task);
if (image_it != image_tasks_.end()) {
@@ -156,6 +154,11 @@ void ImageRasterWorkerPool::ScheduleTasks(RasterTask::Queue* queue) {
"state", TracedValue::FromValue(StateAsValue().release()));
}
+ResourceFormat ImageRasterWorkerPool::GetResourceFormat() const {
+ // Only format supported by CHROMIUM_map_image
+ return RGBA_8888;
+}
+
void ImageRasterWorkerPool::OnRasterTasksFinished() {
DCHECK(raster_tasks_pending_);
raster_tasks_pending_ = false;
diff --git a/chromium/cc/resources/image_raster_worker_pool.h b/chromium/cc/resources/image_raster_worker_pool.h
index d4f4d74d120..63526493350 100644
--- a/chromium/cc/resources/image_raster_worker_pool.h
+++ b/chromium/cc/resources/image_raster_worker_pool.h
@@ -21,6 +21,7 @@ class CC_EXPORT ImageRasterWorkerPool : public RasterWorkerPool {
// Overridden from RasterWorkerPool:
virtual void ScheduleTasks(RasterTask::Queue* queue) OVERRIDE;
+ virtual ResourceFormat GetResourceFormat() const OVERRIDE;
virtual void OnRasterTasksFinished() OVERRIDE;
virtual void OnRasterTasksRequiredForActivationFinished() OVERRIDE;
diff --git a/chromium/cc/resources/layer_tiling_data.h b/chromium/cc/resources/layer_tiling_data.h
index 51565eba3b2..c5c9a745871 100644
--- a/chromium/cc/resources/layer_tiling_data.h
+++ b/chromium/cc/resources/layer_tiling_data.h
@@ -9,10 +9,10 @@
#include "base/basictypes.h"
#include "base/containers/hash_tables.h"
+#include "base/containers/scoped_ptr_hash_map.h"
#include "base/memory/scoped_ptr.h"
#include "cc/base/cc_export.h"
#include "cc/base/region.h"
-#include "cc/base/scoped_ptr_hash_map.h"
#include "cc/base/tiling_data.h"
#include "ui/gfx/rect.h"
@@ -72,7 +72,7 @@ class CC_EXPORT LayerTilingData {
DISALLOW_COPY_AND_ASSIGN(Tile);
};
typedef std::pair<int, int> TileMapKey;
- typedef ScopedPtrHashMap<TileMapKey, Tile> TileMap;
+ typedef base::ScopedPtrHashMap<TileMapKey, Tile> TileMap;
void AddTile(scoped_ptr<Tile> tile, int i, int j);
scoped_ptr<Tile> TakeTile(int i, int j);
diff --git a/chromium/cc/resources/managed_tile_state.cc b/chromium/cc/resources/managed_tile_state.cc
index 6f85331dbb2..8856d4f5168 100644
--- a/chromium/cc/resources/managed_tile_state.cc
+++ b/chromium/cc/resources/managed_tile_state.cc
@@ -12,63 +12,50 @@ namespace cc {
scoped_ptr<base::Value> ManagedTileBinAsValue(ManagedTileBin bin) {
switch (bin) {
- case NOW_AND_READY_TO_DRAW_BIN:
- return scoped_ptr<base::Value>(base::Value::CreateStringValue(
- "NOW_AND_READY_TO_DRAW_BIN"));
- case NOW_BIN:
- return scoped_ptr<base::Value>(base::Value::CreateStringValue(
- "NOW_BIN"));
- case SOON_BIN:
- return scoped_ptr<base::Value>(base::Value::CreateStringValue(
- "SOON_BIN"));
- case EVENTUALLY_AND_ACTIVE_BIN:
- return scoped_ptr<base::Value>(base::Value::CreateStringValue(
- "EVENTUALLY_AND_ACTIVE_BIN"));
- case EVENTUALLY_BIN:
- return scoped_ptr<base::Value>(base::Value::CreateStringValue(
- "EVENTUALLY_BIN"));
- case NEVER_AND_ACTIVE_BIN:
- return scoped_ptr<base::Value>(base::Value::CreateStringValue(
- "NEVER_AND_ACTIVE_BIN"));
- case NEVER_BIN:
- return scoped_ptr<base::Value>(base::Value::CreateStringValue(
- "NEVER_BIN"));
- default:
- DCHECK(false) << "Unrecognized ManagedTileBin value " << bin;
- return scoped_ptr<base::Value>(base::Value::CreateStringValue(
- "<unknown ManagedTileBin value>"));
- }
-}
-
-scoped_ptr<base::Value> ManagedTileBinPriorityAsValue(
- ManagedTileBinPriority bin_priority) {
- switch (bin_priority) {
- case HIGH_PRIORITY_BIN:
- return scoped_ptr<base::Value>(base::Value::CreateStringValue(
- "HIGH_PRIORITY_BIN"));
- case LOW_PRIORITY_BIN:
- return scoped_ptr<base::Value>(base::Value::CreateStringValue(
- "LOW_PRIORITY_BIN"));
- default:
- DCHECK(false) << "Unrecognized ManagedTileBinPriority value";
- return scoped_ptr<base::Value>(base::Value::CreateStringValue(
- "<unknown ManagedTileBinPriority value>"));
+ case NOW_AND_READY_TO_DRAW_BIN:
+ return scoped_ptr<base::Value>(
+ base::Value::CreateStringValue("NOW_AND_READY_TO_DRAW_BIN"));
+ case NOW_BIN:
+ return scoped_ptr<base::Value>(
+ base::Value::CreateStringValue("NOW_BIN"));
+ case SOON_BIN:
+ return scoped_ptr<base::Value>(
+ base::Value::CreateStringValue("SOON_BIN"));
+ case EVENTUALLY_AND_ACTIVE_BIN:
+ return scoped_ptr<base::Value>(
+ base::Value::CreateStringValue("EVENTUALLY_AND_ACTIVE_BIN"));
+ case EVENTUALLY_BIN:
+ return scoped_ptr<base::Value>(
+ base::Value::CreateStringValue("EVENTUALLY_BIN"));
+ case AT_LAST_AND_ACTIVE_BIN:
+ return scoped_ptr<base::Value>(
+ base::Value::CreateStringValue("AT_LAST_AND_ACTIVE_BIN"));
+ case AT_LAST_BIN:
+ return scoped_ptr<base::Value>(
+ base::Value::CreateStringValue("AT_LAST_BIN"));
+ case NEVER_BIN:
+ return scoped_ptr<base::Value>(
+ base::Value::CreateStringValue("NEVER_BIN"));
+ case NUM_BINS:
+ NOTREACHED();
+ return scoped_ptr<base::Value>(
+ base::Value::CreateStringValue("Invalid Bin (NUM_BINS)"));
}
+ return scoped_ptr<base::Value>(
+ base::Value::CreateStringValue("Invalid Bin (UNKNOWN)"));
}
ManagedTileState::ManagedTileState()
: raster_mode(LOW_QUALITY_RASTER_MODE),
- gpu_memmgr_stats_bin(NEVER_BIN),
+ bin(NEVER_BIN),
resolution(NON_IDEAL_RESOLUTION),
required_for_activation(false),
time_to_needed_in_seconds(std::numeric_limits<float>::infinity()),
distance_to_visible_in_pixels(std::numeric_limits<float>::infinity()),
visible_and_ready_to_draw(false),
scheduled_priority(0) {
- for (int i = 0; i < NUM_TREES; ++i) {
+ for (int i = 0; i < NUM_TREES; ++i)
tree_bin[i] = NEVER_BIN;
- bin[i] = NEVER_BIN;
- }
}
ManagedTileState::TileVersion::TileVersion()
@@ -87,10 +74,9 @@ bool ManagedTileState::TileVersion::IsReadyToDraw() const {
case SOLID_COLOR_MODE:
case PICTURE_PILE_MODE:
return true;
- default:
- NOTREACHED();
- return false;
}
+ NOTREACHED();
+ return false;
}
size_t ManagedTileState::TileVersion::GPUMemoryUsageInBytes() const {
@@ -106,10 +92,10 @@ scoped_ptr<base::Value> ManagedTileState::AsValue() const {
scoped_ptr<base::DictionaryValue> state(new base::DictionaryValue());
state->SetBoolean("has_resource",
tile_versions[raster_mode].resource_.get() != 0);
- state->Set("bin.0", ManagedTileBinAsValue(bin[ACTIVE_TREE]).release());
- state->Set("bin.1", ManagedTileBinAsValue(bin[PENDING_TREE]).release());
- state->Set("gpu_memmgr_stats_bin",
- ManagedTileBinAsValue(bin[ACTIVE_TREE]).release());
+ state->Set("tree_bin.0",
+ ManagedTileBinAsValue(tree_bin[ACTIVE_TREE]).release());
+ state->Set("tree_bin.1",
+ ManagedTileBinAsValue(tree_bin[PENDING_TREE]).release());
state->Set("resolution", TileResolutionAsValue(resolution).release());
state->Set("time_to_needed_in_seconds",
MathUtil::AsValueSafely(time_to_needed_in_seconds).release());
diff --git a/chromium/cc/resources/managed_tile_state.h b/chromium/cc/resources/managed_tile_state.h
index c6065f97895..35e4415dd9e 100644
--- a/chromium/cc/resources/managed_tile_state.h
+++ b/chromium/cc/resources/managed_tile_state.h
@@ -22,23 +22,16 @@ enum ManagedTileBin {
SOON_BIN = 2, // Impl-side version of prepainting.
EVENTUALLY_AND_ACTIVE_BIN = 3, // Nice to have, and has a task or resource.
EVENTUALLY_BIN = 4, // Nice to have, if we've got memory and time.
- NEVER_AND_ACTIVE_BIN = 5, // Dont bother, but has a task or resource.
- NEVER_BIN = 6, // Dont bother.
- NUM_BINS = 7
+ AT_LAST_AND_ACTIVE_BIN = 5, // Only do this after all other bins.
+ AT_LAST_BIN = 6, // Only do this after all other bins.
+ NEVER_BIN = 7, // Dont bother.
+ NUM_BINS = 8
// NOTE: Be sure to update ManagedTileBinAsValue and kBinPolicyMap when adding
// or reordering fields.
};
scoped_ptr<base::Value> ManagedTileBinAsValue(
ManagedTileBin bin);
-enum ManagedTileBinPriority {
- HIGH_PRIORITY_BIN = 0,
- LOW_PRIORITY_BIN = 1,
- NUM_BIN_PRIORITIES = 2
-};
-scoped_ptr<base::Value> ManagedTileBinPriorityAsValue(
- ManagedTileBinPriority bin);
-
// This is state that is specific to a tile that is
// managed by the TileManager.
class CC_EXPORT ManagedTileState {
@@ -48,8 +41,7 @@ class CC_EXPORT ManagedTileState {
enum Mode {
RESOURCE_MODE,
SOLID_COLOR_MODE,
- PICTURE_PILE_MODE,
- NUM_MODES
+ PICTURE_PILE_MODE
};
TileVersion();
@@ -86,12 +78,6 @@ class CC_EXPORT ManagedTileState {
size_t GPUMemoryUsageInBytes() const;
- void SetResourceForTesting(scoped_ptr<ResourcePool::Resource> resource) {
- resource_ = resource.Pass();
- }
- const ResourcePool::Resource* GetResourceForTesting() const {
- return resource_.get();
- }
void SetSolidColorForTesting(SkColor color) {
set_solid_color(color);
}
@@ -138,21 +124,9 @@ class CC_EXPORT ManagedTileState {
TileVersion tile_versions[NUM_RASTER_MODES];
RasterMode raster_mode;
- // Ephemeral state, valid only during TileManager::ManageTiles.
- bool is_in_never_bin_on_both_trees() const {
- return (bin[HIGH_PRIORITY_BIN] == NEVER_BIN ||
- bin[HIGH_PRIORITY_BIN] == NEVER_AND_ACTIVE_BIN) &&
- (bin[LOW_PRIORITY_BIN] == NEVER_BIN ||
- bin[LOW_PRIORITY_BIN] == NEVER_AND_ACTIVE_BIN);
- }
-
- ManagedTileBin bin[NUM_BIN_PRIORITIES];
+ ManagedTileBin bin;
ManagedTileBin tree_bin[NUM_TREES];
- // The bin that the tile would have if the GPU memory manager had
- // a maximally permissive policy, send to the GPU memory manager
- // to determine policy.
- ManagedTileBin gpu_memmgr_stats_bin;
TileResolution resolution;
bool required_for_activation;
float time_to_needed_in_seconds;
diff --git a/chromium/cc/resources/picture.cc b/chromium/cc/resources/picture.cc
index 8c5d8512ae3..f9afca9ee3d 100644
--- a/chromium/cc/resources/picture.cc
+++ b/chromium/cc/resources/picture.cc
@@ -93,6 +93,27 @@ Picture::Picture(gfx::Rect layer_rect)
// the picture to be recorded in Picture::Record.
}
+scoped_refptr<Picture> Picture::CreateFromSkpValue(const base::Value* value) {
+ // Decode the picture from base64.
+ std::string encoded;
+ if (!value->GetAsString(&encoded))
+ return NULL;
+
+ std::string decoded;
+ base::Base64Decode(encoded, &decoded);
+ SkMemoryStream stream(decoded.data(), decoded.size());
+
+ // Read the picture. This creates an empty picture on failure.
+ SkPicture* skpicture = SkPicture::CreateFromStream(&stream, &DecodeBitmap);
+ if (skpicture == NULL)
+ return NULL;
+
+ gfx::Rect layer_rect(skpicture->width(), skpicture->height());
+ gfx::Rect opaque_rect(skpicture->width(), skpicture->height());
+
+ return make_scoped_refptr(new Picture(skpicture, layer_rect, opaque_rect));
+}
+
scoped_refptr<Picture> Picture::CreateFromValue(const base::Value* raw_value) {
const base::DictionaryValue* value = NULL;
if (!raw_value->GetAsDictionary(&value))
@@ -178,7 +199,7 @@ void Picture::CloneForDrawing(int num_threads) {
pixel_refs_));
clones_.push_back(clone);
- clone->EmitTraceSnapshot();
+ clone->EmitTraceSnapshotAlias(this);
}
}
@@ -348,6 +369,14 @@ void Picture::EmitTraceSnapshot() {
"cc::Picture", this, TracedPicture::AsTraceablePicture(this));
}
+void Picture::EmitTraceSnapshotAlias(Picture* original) {
+ TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
+ TRACE_DISABLED_BY_DEFAULT("cc.debug"),
+ "cc::Picture",
+ this,
+ TracedPicture::AsTraceablePictureAlias(original));
+}
+
base::LazyInstance<Picture::PixelRefs>
Picture::PixelRefIterator::empty_pixel_refs_;
diff --git a/chromium/cc/resources/picture.h b/chromium/cc/resources/picture.h
index 15a6c9d0ab4..238647772fc 100644
--- a/chromium/cc/resources/picture.h
+++ b/chromium/cc/resources/picture.h
@@ -45,6 +45,7 @@ class CC_EXPORT Picture
static scoped_refptr<Picture> Create(gfx::Rect layer_rect);
static scoped_refptr<Picture> CreateFromValue(const base::Value* value);
+ static scoped_refptr<Picture> CreateFromSkpValue(const base::Value* value);
gfx::Rect LayerRect() const { return layer_rect_; }
gfx::Rect OpaqueRect() const { return opaque_rect_; }
@@ -115,6 +116,7 @@ class CC_EXPORT Picture
};
void EmitTraceSnapshot();
+ void EmitTraceSnapshotAlias(Picture* original);
private:
explicit Picture(gfx::Rect layer_rect);
diff --git a/chromium/cc/resources/picture_layer_tiling_perftest.cc b/chromium/cc/resources/picture_layer_tiling_perftest.cc
new file mode 100644
index 00000000000..655603d2e85
--- /dev/null
+++ b/chromium/cc/resources/picture_layer_tiling_perftest.cc
@@ -0,0 +1,172 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/resources/picture_layer_tiling.h"
+#include "cc/test/fake_picture_layer_tiling_client.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/perf/perf_test.h"
+
+namespace cc {
+
+namespace {
+
+static const int kTimeLimitMillis = 2000;
+static const int kWarmupRuns = 5;
+static const int kTimeCheckInterval = 10;
+
+class PictureLayerTilingPerfTest : public testing::Test {
+ public:
+ PictureLayerTilingPerfTest() : num_runs_(0) {}
+
+ virtual void SetUp() OVERRIDE {
+ picture_layer_tiling_client_.SetTileSize(gfx::Size(256, 256));
+ picture_layer_tiling_ = PictureLayerTiling::Create(
+ 1, gfx::Size(256 * 50, 256 * 50), &picture_layer_tiling_client_);
+ picture_layer_tiling_->CreateAllTilesForTesting();
+ }
+
+ virtual void TearDown() OVERRIDE {
+ picture_layer_tiling_.reset(NULL);
+ }
+
+ void EndTest() {
+ elapsed_ = base::TimeTicks::HighResNow() - start_time_;
+ }
+
+ bool DidRun() {
+ ++num_runs_;
+ if (num_runs_ == kWarmupRuns)
+ start_time_ = base::TimeTicks::HighResNow();
+
+ if (!start_time_.is_null() && (num_runs_ % kTimeCheckInterval) == 0) {
+ base::TimeDelta elapsed = base::TimeTicks::HighResNow() - start_time_;
+ if (elapsed >= base::TimeDelta::FromMilliseconds(kTimeLimitMillis)) {
+ elapsed_ = elapsed;
+ return false;
+ }
+ }
+ return true;
+ }
+
+ void RunInvalidateTest(const std::string& test_name, const Region& region) {
+ start_time_ = base::TimeTicks();
+ num_runs_ = 0;
+ do {
+ picture_layer_tiling_->Invalidate(region);
+ } while (DidRun());
+
+ perf_test::PrintResult("invalidation", "", test_name,
+ num_runs_ / elapsed_.InSecondsF(), "runs/s", true);
+ }
+
+ void RunUpdateTilePrioritiesStationaryTest(
+ const std::string& test_name,
+ const gfx::Transform& transform) {
+ start_time_ = base::TimeTicks();
+ num_runs_ = 0;
+
+ gfx::Size layer_bounds(50 * 256, 50 * 256);
+ do {
+ picture_layer_tiling_->UpdateTilePriorities(
+ ACTIVE_TREE,
+ layer_bounds,
+ gfx::Rect(layer_bounds),
+ gfx::Rect(layer_bounds),
+ layer_bounds,
+ layer_bounds,
+ 1.f,
+ 1.f,
+ transform,
+ transform,
+ num_runs_ + 1,
+ 250);
+ } while (DidRun());
+
+ perf_test::PrintResult("update_tile_priorities_stationary", "", test_name,
+ num_runs_ / elapsed_.InSecondsF(), "runs/s", true);
+ }
+
+ void RunUpdateTilePrioritiesScrollingTest(
+ const std::string& test_name,
+ const gfx::Transform& transform) {
+ start_time_ = base::TimeTicks();
+ num_runs_ = 0;
+
+ gfx::Size layer_bounds(50 * 256, 50 * 256);
+ gfx::Size viewport_size(1024, 768);
+ gfx::Rect viewport_rect(viewport_size);
+ int xoffsets[] = {10, 0, -10, 0};
+ int yoffsets[] = {0, 10, 0, -10};
+ int offsetIndex = 0;
+ int offsetCount = 0;
+ const int maxOffsetCount = 1000;
+ do {
+ picture_layer_tiling_->UpdateTilePriorities(
+ ACTIVE_TREE,
+ viewport_size,
+ viewport_rect,
+ gfx::Rect(layer_bounds),
+ layer_bounds,
+ layer_bounds,
+ 1.f,
+ 1.f,
+ transform,
+ transform,
+ num_runs_ + 1,
+ 250);
+
+ viewport_rect = gfx::Rect(
+ viewport_rect.x() + xoffsets[offsetIndex],
+ viewport_rect.y() + yoffsets[offsetIndex],
+ viewport_rect.width(),
+ viewport_rect.height());
+
+ if (++offsetCount > maxOffsetCount) {
+ offsetCount = 0;
+ offsetIndex = (offsetIndex + 1) % 4;
+ }
+ } while (DidRun());
+
+ perf_test::PrintResult("update_tile_priorities_scrolling", "", test_name,
+ num_runs_ / elapsed_.InSecondsF(), "runs/s", true);
+ }
+
+ private:
+ FakePictureLayerTilingClient picture_layer_tiling_client_;
+ scoped_ptr<PictureLayerTiling> picture_layer_tiling_;
+
+ base::TimeTicks start_time_;
+ base::TimeDelta elapsed_;
+ int num_runs_;
+};
+
+TEST_F(PictureLayerTilingPerfTest, Invalidate) {
+ Region one_tile(gfx::Rect(256, 256));
+ RunInvalidateTest("1x1", one_tile);
+
+ Region half_region(gfx::Rect(25 * 256, 50 * 256));
+ RunInvalidateTest("25x50", half_region);
+
+ Region full_region(gfx::Rect(50 * 256, 50 * 256));
+ RunInvalidateTest("50x50", full_region);
+}
+
+TEST_F(PictureLayerTilingPerfTest, UpdateTilePriorities) {
+ gfx::Transform transform;
+ RunUpdateTilePrioritiesStationaryTest("no_transform", transform);
+ RunUpdateTilePrioritiesScrollingTest("no_transform", transform);
+
+ transform.Rotate(10);
+ RunUpdateTilePrioritiesStationaryTest("rotation", transform);
+ RunUpdateTilePrioritiesScrollingTest("rotation", transform);
+
+ transform.ApplyPerspectiveDepth(10);
+ RunUpdateTilePrioritiesStationaryTest("perspective", transform);
+ RunUpdateTilePrioritiesScrollingTest("perspective", transform);
+}
+
+} // namespace
+
+} // namespace cc
diff --git a/chromium/cc/resources/picture_layer_tiling_set.cc b/chromium/cc/resources/picture_layer_tiling_set.cc
index 1b0cb5f7335..14c9cc87a30 100644
--- a/chromium/cc/resources/picture_layer_tiling_set.cc
+++ b/chromium/cc/resources/picture_layer_tiling_set.cc
@@ -110,6 +110,15 @@ PictureLayerTiling* PictureLayerTilingSet::AddTiling(float contents_scale) {
return appended;
}
+int PictureLayerTilingSet::NumHighResTilings() const {
+ int num_high_res = 0;
+ for (size_t i = 0; i < tilings_.size(); ++i) {
+ if (tilings_[i]->resolution() == HIGH_RESOLUTION)
+ num_high_res++;
+ }
+ return num_high_res;
+}
+
PictureLayerTiling* PictureLayerTilingSet::TilingAtScale(float scale) const {
for (size_t i = 0; i < tilings_.size(); ++i) {
if (tilings_[i]->contents_scale() == scale)
diff --git a/chromium/cc/resources/picture_layer_tiling_set.h b/chromium/cc/resources/picture_layer_tiling_set.h
index e498cb964b6..9a3f00db61f 100644
--- a/chromium/cc/resources/picture_layer_tiling_set.h
+++ b/chromium/cc/resources/picture_layer_tiling_set.h
@@ -37,6 +37,7 @@ class CC_EXPORT PictureLayerTilingSet {
PictureLayerTiling* AddTiling(float contents_scale);
size_t num_tilings() const { return tilings_.size(); }
+ int NumHighResTilings() const;
PictureLayerTiling* tiling_at(size_t idx) { return tilings_[idx]; }
const PictureLayerTiling* tiling_at(size_t idx) const {
return tilings_[idx];
diff --git a/chromium/cc/resources/picture_layer_tiling_set_unittest.cc b/chromium/cc/resources/picture_layer_tiling_set_unittest.cc
index c47cf4b5a90..231e378fbd0 100644
--- a/chromium/cc/resources/picture_layer_tiling_set_unittest.cc
+++ b/chromium/cc/resources/picture_layer_tiling_set_unittest.cc
@@ -10,6 +10,7 @@
#include "cc/resources/resource_pool.h"
#include "cc/resources/resource_provider.h"
#include "cc/test/fake_output_surface.h"
+#include "cc/test/fake_output_surface_client.h"
#include "cc/test/fake_picture_layer_tiling_client.h"
#include "cc/test/fake_tile_manager_client.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -59,10 +60,13 @@ class PictureLayerTilingSetTestWithResources : public testing::Test {
float scale_increment,
float ideal_contents_scale,
float expected_scale) {
+ FakeOutputSurfaceClient output_surface_client;
scoped_ptr<FakeOutputSurface> output_surface =
FakeOutputSurface::Create3d();
+ CHECK(output_surface->BindToClient(&output_surface_client));
+
scoped_ptr<ResourceProvider> resource_provider =
- ResourceProvider::Create(output_surface.get(), 0);
+ ResourceProvider::Create(output_surface.get(), 0, false);
FakePictureLayerTilingClient client;
client.SetTileSize(gfx::Size(256, 256));
@@ -74,17 +78,8 @@ class PictureLayerTilingSetTestWithResources : public testing::Test {
PictureLayerTiling* tiling = set.AddTiling(scale);
tiling->CreateAllTilesForTesting();
std::vector<Tile*> tiles = tiling->AllTilesForTesting();
- for (size_t i = 0; i < tiles.size(); ++i) {
- ManagedTileState::TileVersion& tile_version =
- tiles[i]->GetTileVersionForTesting(HIGH_QUALITY_NO_LCD_RASTER_MODE);
- EXPECT_FALSE(tile_version.GetResourceForTesting());
-
- tile_version.SetResourceForTesting(
- make_scoped_ptr(new ResourcePool::Resource(
- resource_provider.get(),
- gfx::Size(1, 1),
- resource_provider->best_texture_format())));
- }
+ client.tile_manager()->InitializeTilesWithResourcesForTesting(
+ tiles, resource_provider.get());
}
float max_contents_scale = scale;
diff --git a/chromium/cc/resources/picture_pile_base.cc b/chromium/cc/resources/picture_pile_base.cc
index d982f9fc9b8..0352d30816c 100644
--- a/chromium/cc/resources/picture_pile_base.cc
+++ b/chromium/cc/resources/picture_pile_base.cc
@@ -152,12 +152,10 @@ void PicturePileBase::Clear() {
void PicturePileBase::UpdateRecordedRegion() {
recorded_region_.Clear();
- for (int x = 0; x < num_tiles_x(); ++x) {
- for (int y = 0; y < num_tiles_y(); ++y) {
- if (!HasRecordingAt(x, y))
- continue;
- recorded_region_.Union(tile_bounds(x, y));
- }
+ for (PictureListMap::iterator it = picture_list_map_.begin();
+ it != picture_list_map_.end(); ++it) {
+ const PictureListMapKey& key = it->first;
+ recorded_region_.Union(tile_bounds(key.first, key.second));
}
}
diff --git a/chromium/cc/resources/picture_pile_impl.cc b/chromium/cc/resources/picture_pile_impl.cc
index 42f3dabd4cf..3a4020e072b 100644
--- a/chromium/cc/resources/picture_pile_impl.cc
+++ b/chromium/cc/resources/picture_pile_impl.cc
@@ -186,9 +186,13 @@ void PicturePileImpl::RasterCommon(
// encompasses all invalidated pixels at any larger scale level.
gfx::Rect content_clip = gfx::ScaleToEnclosedRect(
(*i)->LayerRect(), contents_scale);
+
DCHECK(!content_clip.IsEmpty()) <<
"Layer rect: " << (*i)->LayerRect().ToString() <<
"Contents scale: " << contents_scale;
+
+ content_clip.Intersect(canvas_rect);
+
if (!unclipped.Intersects(content_clip))
continue;
@@ -215,16 +219,15 @@ void PicturePileImpl::RasterCommon(
}
if (raster_stats) {
- gfx::Rect raster_rect = canvas_rect;
- raster_rect.Intersect(content_clip);
raster_stats->total_pixels_rasterized +=
- repeat_count * raster_rect.width() * raster_rect.height();
+ repeat_count * content_clip.width() * content_clip.height();
raster_stats->total_rasterize_time += total_duration;
raster_stats->best_rasterize_time += best_duration;
}
if (show_debug_picture_borders_) {
- gfx::Rect border = content_clip;
+ gfx::Rect border = gfx::ScaleToEnclosedRect(
+ (*i)->LayerRect(), contents_scale);
border.Inset(0, 0, 1, 1);
SkPaint picture_border_paint;
diff --git a/chromium/cc/resources/picture_unittest.cc b/chromium/cc/resources/picture_unittest.cc
index ee5e07dbdd6..aa20ebb6381 100644
--- a/chromium/cc/resources/picture_unittest.cc
+++ b/chromium/cc/resources/picture_unittest.cc
@@ -377,5 +377,58 @@ TEST(PictureTest, PixelRefIteratorOnePixelQuery) {
}
}
}
+
+TEST(PictureTest, CreateFromSkpValue) {
+ SkGraphics::Init();
+
+ gfx::Rect layer_rect(100, 200);
+
+ SkTileGridPicture::TileGridInfo tile_grid_info;
+ tile_grid_info.fTileInterval = SkISize::Make(100, 200);
+ tile_grid_info.fMargin.setEmpty();
+ tile_grid_info.fOffset.setZero();
+
+ FakeContentLayerClient content_layer_client;
+ FakeRenderingStatsInstrumentation stats_instrumentation;
+
+ scoped_ptr<base::Value> tmp;
+
+ SkPaint red_paint;
+ red_paint.setColor(SkColorSetARGB(255, 255, 0, 0));
+ SkPaint green_paint;
+ green_paint.setColor(SkColorSetARGB(255, 0, 255, 0));
+
+ // Invalid picture (not a dict).
+ tmp.reset(new base::StringValue("abc!@#$%"));
+ scoped_refptr<Picture> invalid_picture =
+ Picture::CreateFromSkpValue(tmp.get());
+ EXPECT_TRUE(!invalid_picture.get());
+
+ // Single full-size rect picture.
+ content_layer_client.add_draw_rect(layer_rect, red_paint);
+ scoped_refptr<Picture> one_rect_picture = Picture::Create(layer_rect);
+ one_rect_picture->Record(&content_layer_client,
+ tile_grid_info,
+ &stats_instrumentation);
+ scoped_ptr<base::Value> serialized_one_rect(
+ one_rect_picture->AsValue());
+
+ const base::DictionaryValue* value = NULL;
+ EXPECT_TRUE(serialized_one_rect->GetAsDictionary(&value));
+
+ // Decode the picture from base64.
+ const base::Value* skp_value;
+ EXPECT_TRUE(value->Get("skp64", &skp_value));
+
+ // Reconstruct the picture.
+ scoped_refptr<Picture> one_rect_picture_check =
+ Picture::CreateFromSkpValue(skp_value);
+ EXPECT_TRUE(!!one_rect_picture_check.get());
+
+ EXPECT_EQ(100, one_rect_picture_check->LayerRect().width());
+ EXPECT_EQ(200, one_rect_picture_check->LayerRect().height());
+ EXPECT_EQ(100, one_rect_picture_check->OpaqueRect().width());
+ EXPECT_EQ(200, one_rect_picture_check->OpaqueRect().height());
+}
} // namespace
} // namespace cc
diff --git a/chromium/cc/resources/pixel_buffer_raster_worker_pool.cc b/chromium/cc/resources/pixel_buffer_raster_worker_pool.cc
index a03ed81f4e1..a555e581540 100644
--- a/chromium/cc/resources/pixel_buffer_raster_worker_pool.cc
+++ b/chromium/cc/resources/pixel_buffer_raster_worker_pool.cc
@@ -9,7 +9,11 @@
#include "base/values.h"
#include "cc/debug/traced_value.h"
#include "cc/resources/resource.h"
-#include "third_party/skia/include/core/SkDevice.h"
+#include "third_party/skia/include/core/SkBitmapDevice.h"
+
+#if defined(OS_ANDROID)
+#include "base/android/sys_utils.h"
+#endif
namespace cc {
@@ -37,13 +41,10 @@ class PixelBufferWorkerPoolTaskImpl : public internal::WorkerPoolTask {
needs_upload_ = true;
return;
}
- SkBitmap bitmap;
- bitmap.setConfig(SkBitmap::kARGB_8888_Config,
- task_->resource()->size().width(),
- task_->resource()->size().height());
- bitmap.setPixels(buffer_);
- SkDevice device(bitmap);
- needs_upload_ = task_->RunOnWorkerThread(&device, thread_index);
+ needs_upload_ = task_->RunOnWorkerThread(thread_index,
+ buffer_,
+ task_->resource()->size(),
+ 0);
}
virtual void CompleteOnOriginThread() OVERRIDE {
// |needs_upload_| must be be false if task didn't run.
@@ -62,22 +63,6 @@ class PixelBufferWorkerPoolTaskImpl : public internal::WorkerPoolTask {
DISALLOW_COPY_AND_ASSIGN(PixelBufferWorkerPoolTaskImpl);
};
-// If we raster too fast we become upload bound, and pending
-// uploads consume memory. For maximum upload throughput, we would
-// want to allow for upload_throughput * pipeline_time of pending
-// uploads, after which we are just wasting memory. Since we don't
-// know our upload throughput yet, this just caps our memory usage.
-#if defined(OS_ANDROID)
-// For reference Nexus10 can upload 1MB in about 2.5ms.
-const size_t kMaxBytesUploadedPerMs = (2 * 1024 * 1024) / 5;
-#else
-// For reference Chromebook Pixel can upload 1MB in about 0.5ms.
-const size_t kMaxBytesUploadedPerMs = 1024 * 1024 * 2;
-#endif
-
-// Assuming a two frame deep pipeline.
-const size_t kMaxPendingUploadBytes = 16 * 2 * kMaxBytesUploadedPerMs;
-
const int kCheckForCompletedRasterTasksDelayMs = 6;
const size_t kMaxScheduledRasterTasks = 48;
@@ -106,11 +91,13 @@ bool WasCanceled(const internal::RasterWorkerPoolTask* task) {
PixelBufferRasterWorkerPool::PixelBufferRasterWorkerPool(
ResourceProvider* resource_provider,
- size_t num_threads)
+ size_t num_threads,
+ size_t max_transfer_buffer_usage_bytes)
: RasterWorkerPool(resource_provider, num_threads),
shutdown_(false),
scheduled_raster_task_count_(0),
bytes_pending_upload_(0),
+ max_bytes_pending_upload_(max_transfer_buffer_usage_bytes),
has_performed_uploads_since_last_flush_(false),
check_for_completed_raster_tasks_pending_(false),
should_notify_client_if_no_tasks_are_pending_(false),
@@ -158,6 +145,8 @@ void PixelBufferRasterWorkerPool::ScheduleTasks(RasterTask::Queue* queue) {
should_notify_client_if_no_tasks_are_pending_ = true;
should_notify_client_if_no_tasks_required_for_activation_are_pending_ = true;
+ tasks_required_for_activation_.clear();
+
// Build new pixel buffer task set.
TaskMap new_pixel_buffer_tasks;
for (RasterTaskVector::const_iterator it = raster_tasks().begin();
@@ -165,16 +154,13 @@ void PixelBufferRasterWorkerPool::ScheduleTasks(RasterTask::Queue* queue) {
internal::RasterWorkerPoolTask* task = it->get();
DCHECK(new_pixel_buffer_tasks.find(task) == new_pixel_buffer_tasks.end());
DCHECK(!task->HasCompleted());
+ DCHECK(!task->WasCanceled());
- // Use existing pixel buffer task if available.
- TaskMap::iterator pixel_buffer_it = pixel_buffer_tasks_.find(task);
- if (pixel_buffer_it == pixel_buffer_tasks_.end()) {
- new_pixel_buffer_tasks[task] = NULL;
- continue;
- }
-
- new_pixel_buffer_tasks[task] = pixel_buffer_it->second;
+ new_pixel_buffer_tasks[task] = pixel_buffer_tasks_[task];
pixel_buffer_tasks_.erase(task);
+
+ if (IsRasterTaskRequiredForActivation(task))
+ tasks_required_for_activation_.insert(task);
}
// Transfer remaining pixel buffer tasks to |new_pixel_buffer_tasks|
@@ -194,22 +180,17 @@ void PixelBufferRasterWorkerPool::ScheduleTasks(RasterTask::Queue* queue) {
completed_tasks_.end(),
task) == completed_tasks_.end());
completed_tasks_.push_back(task);
- }
- }
-
- tasks_required_for_activation_.clear();
- for (TaskMap::iterator it = new_pixel_buffer_tasks.begin();
- it != new_pixel_buffer_tasks.end(); ++it) {
- internal::RasterWorkerPoolTask* task = it->first;
- if (IsRasterTaskRequiredForActivation(task))
+ } else if (IsRasterTaskRequiredForActivation(task)) {
tasks_required_for_activation_.insert(task);
+ }
}
// |tasks_required_for_activation_| contains all tasks that need to
// complete before we can send a "ready to activate" signal. Tasks
// that have already completed should not be part of this set.
for (TaskDeque::const_iterator it = completed_tasks_.begin();
- it != completed_tasks_.end(); ++it) {
+ it != completed_tasks_.end() && !tasks_required_for_activation_.empty();
+ ++it) {
tasks_required_for_activation_.erase(*it);
}
@@ -236,6 +217,10 @@ void PixelBufferRasterWorkerPool::ScheduleTasks(RasterTask::Queue* queue) {
"state", TracedValue::FromValue(StateAsValue().release()));
}
+ResourceFormat PixelBufferRasterWorkerPool::GetResourceFormat() const {
+ return resource_provider()->memory_efficient_texture_format();
+}
+
void PixelBufferRasterWorkerPool::CheckForCompletedTasks() {
TRACE_EVENT0("cc", "PixelBufferRasterWorkerPool::CheckForCompletedTasks");
@@ -470,7 +455,7 @@ void PixelBufferRasterWorkerPool::ScheduleMoreTasks() {
// All raster tasks need to be throttled by bytes of pending uploads.
size_t new_bytes_pending_upload = bytes_pending_upload;
new_bytes_pending_upload += task->resource()->bytes();
- if (new_bytes_pending_upload > kMaxPendingUploadBytes)
+ if (new_bytes_pending_upload > max_bytes_pending_upload_)
break;
internal::WorkerPoolTask* pixel_buffer_task = pixel_buffer_it->second.get();
@@ -590,6 +575,11 @@ void PixelBufferRasterWorkerPool::OnRasterTaskCompleted(
scoped_refptr<internal::RasterWorkerPoolTask> task,
bool was_canceled,
bool needs_upload) {
+ TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("cc"),
+ "PixelBufferRasterWorkerPool::OnRasterTaskCompleted",
+ "was_canceled", was_canceled,
+ "needs_upload", needs_upload);
+
DCHECK(pixel_buffer_tasks_.find(task.get()) != pixel_buffer_tasks_.end());
// Balanced with MapPixelBuffer() call in ScheduleMoreTasks().
@@ -673,7 +663,7 @@ scoped_ptr<base::Value> PixelBufferRasterWorkerPool::ThrottleStateAsValue()
scoped_ptr<base::DictionaryValue> throttle_state(new base::DictionaryValue);
throttle_state->SetInteger("bytes_available_for_upload",
- kMaxPendingUploadBytes - bytes_pending_upload_);
+ max_bytes_pending_upload_ - bytes_pending_upload_);
throttle_state->SetInteger("bytes_pending_upload", bytes_pending_upload_);
throttle_state->SetInteger("scheduled_raster_task_count",
scheduled_raster_task_count_);
diff --git a/chromium/cc/resources/pixel_buffer_raster_worker_pool.h b/chromium/cc/resources/pixel_buffer_raster_worker_pool.h
index d9613f7634d..a856d8ae5a9 100644
--- a/chromium/cc/resources/pixel_buffer_raster_worker_pool.h
+++ b/chromium/cc/resources/pixel_buffer_raster_worker_pool.h
@@ -9,6 +9,7 @@
#include <set>
#include <vector>
+#include "base/containers/hash_tables.h"
#include "cc/resources/raster_worker_pool.h"
namespace cc {
@@ -18,9 +19,13 @@ class CC_EXPORT PixelBufferRasterWorkerPool : public RasterWorkerPool {
virtual ~PixelBufferRasterWorkerPool();
static scoped_ptr<RasterWorkerPool> Create(
- ResourceProvider* resource_provider, size_t num_threads) {
+ ResourceProvider* resource_provider,
+ size_t num_threads,
+ size_t max_transfer_buffer_usage_bytes) {
return make_scoped_ptr<RasterWorkerPool>(
- new PixelBufferRasterWorkerPool(resource_provider, num_threads));
+ new PixelBufferRasterWorkerPool(resource_provider,
+ num_threads,
+ max_transfer_buffer_usage_bytes));
}
// Overridden from WorkerPool:
@@ -29,12 +34,14 @@ class CC_EXPORT PixelBufferRasterWorkerPool : public RasterWorkerPool {
// Overridden from RasterWorkerPool:
virtual void ScheduleTasks(RasterTask::Queue* queue) OVERRIDE;
+ virtual ResourceFormat GetResourceFormat() const OVERRIDE;
virtual void OnRasterTasksFinished() OVERRIDE;
virtual void OnRasterTasksRequiredForActivationFinished() OVERRIDE;
private:
PixelBufferRasterWorkerPool(ResourceProvider* resource_provider,
- size_t num_threads);
+ size_t num_threads,
+ size_t max_transfer_buffer_usage_bytes);
void FlushUploads();
void CheckForCompletedUploads();
@@ -62,17 +69,19 @@ class CC_EXPORT PixelBufferRasterWorkerPool : public RasterWorkerPool {
TaskDeque tasks_with_pending_upload_;
TaskDeque completed_tasks_;
- typedef std::set<internal::RasterWorkerPoolTask*> TaskSet;
+ typedef base::hash_set<internal::RasterWorkerPoolTask*> TaskSet;
TaskSet tasks_required_for_activation_;
size_t scheduled_raster_task_count_;
size_t bytes_pending_upload_;
+ size_t max_bytes_pending_upload_;
bool has_performed_uploads_since_last_flush_;
base::CancelableClosure check_for_completed_raster_tasks_callback_;
bool check_for_completed_raster_tasks_pending_;
bool should_notify_client_if_no_tasks_are_pending_;
bool should_notify_client_if_no_tasks_required_for_activation_are_pending_;
+ ResourceFormat format_;
DISALLOW_COPY_AND_ASSIGN(PixelBufferRasterWorkerPool);
};
diff --git a/chromium/cc/resources/platform_color.h b/chromium/cc/resources/platform_color.h
index 09a606ae5a5..ecdf7c166f2 100644
--- a/chromium/cc/resources/platform_color.h
+++ b/chromium/cc/resources/platform_color.h
@@ -7,6 +7,7 @@
#include "base/basictypes.h"
#include "base/logging.h"
+#include "cc/resources/resource_format.h"
#include "third_party/khronos/GLES2/gl2.h"
#include "third_party/khronos/GLES2/gl2ext.h"
#include "third_party/skia/include/core/SkTypes.h"
@@ -25,34 +26,28 @@ class PlatformColor {
}
// Returns the most efficient texture format for this platform.
- static GLenum BestTextureFormat(bool supports_bgra8888) {
- GLenum texture_format = GL_RGBA;
+ static ResourceFormat BestTextureFormat(bool supports_bgra8888) {
switch (Format()) {
- case SOURCE_FORMAT_RGBA8:
- break;
case SOURCE_FORMAT_BGRA8:
- if (supports_bgra8888)
- texture_format = GL_BGRA_EXT;
- break;
- default:
- NOTREACHED();
- break;
+ return (supports_bgra8888) ? BGRA_8888 : RGBA_8888;
+ case SOURCE_FORMAT_RGBA8:
+ return RGBA_8888;
}
- return texture_format;
+ NOTREACHED();
+ return RGBA_8888;
}
// Return true if the given texture format has the same component order
// as the color on this platform.
- static bool SameComponentOrder(GLenum texture_format) {
+ static bool SameComponentOrder(ResourceFormat format) {
switch (Format()) {
case SOURCE_FORMAT_RGBA8:
- return texture_format == GL_RGBA;
+ return format == RGBA_8888 || format == RGBA_4444;
case SOURCE_FORMAT_BGRA8:
- return texture_format == GL_BGRA_EXT;
- default:
- NOTREACHED();
- return false;
+ return format == BGRA_8888;
}
+ NOTREACHED();
+ return false;
}
private:
diff --git a/chromium/cc/resources/prioritized_resource.cc b/chromium/cc/resources/prioritized_resource.cc
index 78fd90055a9..313b275a3ab 100644
--- a/chromium/cc/resources/prioritized_resource.cc
+++ b/chromium/cc/resources/prioritized_resource.cc
@@ -15,7 +15,7 @@ namespace cc {
PrioritizedResource::PrioritizedResource(PrioritizedResourceManager* manager,
gfx::Size size,
- GLenum format)
+ ResourceFormat format)
: size_(size),
format_(format),
bytes_(0),
@@ -25,10 +25,7 @@ PrioritizedResource::PrioritizedResource(PrioritizedResourceManager* manager,
is_self_managed_(false),
backing_(NULL),
manager_(NULL) {
- // manager_ is set in RegisterTexture() so validity can be checked.
- DCHECK(format || size.IsEmpty());
- if (format)
- bytes_ = Resource::MemorySizeBytes(size, format);
+ bytes_ = Resource::MemorySizeBytes(size, format);
if (manager)
manager->RegisterTexture(this);
}
@@ -48,7 +45,7 @@ void PrioritizedResource::SetTextureManager(
manager->RegisterTexture(this);
}
-void PrioritizedResource::SetDimensions(gfx::Size size, GLenum format) {
+void PrioritizedResource::SetDimensions(gfx::Size size, ResourceFormat format) {
if (format_ != format || size_ != size) {
is_above_priority_cutoff_ = false;
format_ = format;
@@ -113,7 +110,7 @@ void PrioritizedResource::Unlink() {
}
void PrioritizedResource::SetToSelfManagedMemoryPlaceholder(size_t bytes) {
- SetDimensions(gfx::Size(), GL_RGBA);
+ SetDimensions(gfx::Size(), RGBA_8888);
set_is_self_managed(true);
bytes_ = bytes;
}
@@ -121,7 +118,7 @@ void PrioritizedResource::SetToSelfManagedMemoryPlaceholder(size_t bytes) {
PrioritizedResource::Backing::Backing(unsigned id,
ResourceProvider* resource_provider,
gfx::Size size,
- GLenum format)
+ ResourceFormat format)
: Resource(id, size, format),
owner_(NULL),
priority_at_last_priority_update_(PriorityCalculator::LowestPriority()),
diff --git a/chromium/cc/resources/prioritized_resource.h b/chromium/cc/resources/prioritized_resource.h
index 07e07872205..a3d5d89cf64 100644
--- a/chromium/cc/resources/prioritized_resource.h
+++ b/chromium/cc/resources/prioritized_resource.h
@@ -12,7 +12,6 @@
#include "cc/resources/priority_calculator.h"
#include "cc/resources/resource.h"
#include "cc/resources/resource_provider.h"
-#include "third_party/khronos/GLES2/gl2.h"
#include "ui/gfx/rect.h"
#include "ui/gfx/size.h"
#include "ui/gfx/vector2d.h"
@@ -24,13 +23,16 @@ class Proxy;
class CC_EXPORT PrioritizedResource {
public:
- static scoped_ptr<PrioritizedResource>
- Create(PrioritizedResourceManager* manager, gfx::Size size, GLenum format) {
+ static scoped_ptr<PrioritizedResource> Create(
+ PrioritizedResourceManager* manager,
+ gfx::Size size,
+ ResourceFormat format) {
return make_scoped_ptr(new PrioritizedResource(manager, size, format));
}
static scoped_ptr<PrioritizedResource> Create(
PrioritizedResourceManager* manager) {
- return make_scoped_ptr(new PrioritizedResource(manager, gfx::Size(), 0));
+ return make_scoped_ptr(
+ new PrioritizedResource(manager, gfx::Size(), RGBA_8888));
}
~PrioritizedResource();
@@ -38,8 +40,8 @@ class CC_EXPORT PrioritizedResource {
// Setting these to the same value is a no-op.
void SetTextureManager(PrioritizedResourceManager* manager);
PrioritizedResourceManager* resource_manager() { return manager_; }
- void SetDimensions(gfx::Size size, GLenum format);
- GLenum format() const { return format_; }
+ void SetDimensions(gfx::Size size, ResourceFormat format);
+ ResourceFormat format() const { return format_; }
gfx::Size size() const { return size_; }
size_t bytes() const { return bytes_; }
bool contents_swizzled() const { return contents_swizzled_; }
@@ -106,7 +108,7 @@ class CC_EXPORT PrioritizedResource {
Backing(unsigned id,
ResourceProvider* resource_provider,
gfx::Size size,
- GLenum format);
+ ResourceFormat format);
~Backing();
void UpdatePriority();
void UpdateInDrawingImplTree();
@@ -146,7 +148,7 @@ class CC_EXPORT PrioritizedResource {
PrioritizedResource(PrioritizedResourceManager* resource_manager,
gfx::Size size,
- GLenum format);
+ ResourceFormat format);
bool is_above_priority_cutoff() { return is_above_priority_cutoff_; }
void set_above_priority_cutoff(bool is_above_priority_cutoff) {
@@ -161,7 +163,7 @@ class CC_EXPORT PrioritizedResource {
void Unlink();
gfx::Size size_;
- GLenum format_;
+ ResourceFormat format_;
size_t bytes_;
bool contents_swizzled_;
diff --git a/chromium/cc/resources/prioritized_resource_manager.cc b/chromium/cc/resources/prioritized_resource_manager.cc
index 81188851901..1a6e545a1a5 100644
--- a/chromium/cc/resources/prioritized_resource_manager.cc
+++ b/chromium/cc/resources/prioritized_resource_manager.cc
@@ -449,13 +449,16 @@ void PrioritizedResourceManager::ReturnBackingTexture(
PrioritizedResource::Backing* PrioritizedResourceManager::CreateBacking(
gfx::Size size,
- GLenum format,
+ ResourceFormat format,
ResourceProvider* resource_provider) {
DCHECK(proxy_->IsImplThread() && proxy_->IsMainThreadBlocked());
DCHECK(resource_provider);
ResourceProvider::ResourceId resource_id =
resource_provider->CreateManagedResource(
- size, format, ResourceProvider::TextureUsageAny);
+ size,
+ GL_CLAMP_TO_EDGE,
+ ResourceProvider::TextureUsageAny,
+ format);
PrioritizedResource::Backing* backing = new PrioritizedResource::Backing(
resource_id, resource_provider, size, format);
memory_use_bytes_ += backing->bytes();
diff --git a/chromium/cc/resources/prioritized_resource_manager.h b/chromium/cc/resources/prioritized_resource_manager.h
index 73967727c2c..07cc7cf39a6 100644
--- a/chromium/cc/resources/prioritized_resource_manager.h
+++ b/chromium/cc/resources/prioritized_resource_manager.h
@@ -17,7 +17,6 @@
#include "cc/resources/priority_calculator.h"
#include "cc/resources/resource.h"
#include "cc/trees/proxy.h"
-#include "third_party/khronos/GLES2/gl2.h"
#include "ui/gfx/size.h"
#if defined(COMPILER_GCC)
@@ -40,7 +39,8 @@ class CC_EXPORT PrioritizedResourceManager {
static scoped_ptr<PrioritizedResourceManager> Create(const Proxy* proxy) {
return make_scoped_ptr(new PrioritizedResourceManager(proxy));
}
- scoped_ptr<PrioritizedResource> CreateTexture(gfx::Size size, GLenum format) {
+ scoped_ptr<PrioritizedResource> CreateTexture(
+ gfx::Size size, ResourceFormat format) {
return make_scoped_ptr(new PrioritizedResource(this, size, format));
}
~PrioritizedResourceManager();
@@ -77,6 +77,9 @@ class CC_EXPORT PrioritizedResourceManager {
void SetExternalPriorityCutoff(int priority_cutoff) {
external_priority_cutoff_ = priority_cutoff;
}
+ int ExternalPriorityCutoff() const {
+ return external_priority_cutoff_;
+ }
// Return the amount of texture memory required at particular cutoffs.
size_t MemoryVisibleBytes() const;
@@ -184,7 +187,7 @@ class CC_EXPORT PrioritizedResourceManager {
ResourceProvider* resource_provider);
PrioritizedResource::Backing* CreateBacking(
gfx::Size size,
- GLenum format,
+ ResourceFormat format,
ResourceProvider* resource_provider);
void EvictFirstBackingResource(ResourceProvider* resource_provider);
void SortBackings();
diff --git a/chromium/cc/resources/prioritized_resource_unittest.cc b/chromium/cc/resources/prioritized_resource_unittest.cc
index 92ce9ff7239..181498c5a34 100644
--- a/chromium/cc/resources/prioritized_resource_unittest.cc
+++ b/chromium/cc/resources/prioritized_resource_unittest.cc
@@ -7,6 +7,7 @@
#include "cc/resources/prioritized_resource_manager.h"
#include "cc/resources/resource.h"
#include "cc/test/fake_output_surface.h"
+#include "cc/test/fake_output_surface_client.h"
#include "cc/test/fake_proxy.h"
#include "cc/test/tiled_layer_test_common.h"
#include "cc/trees/single_thread_proxy.h" // For DebugScopedSetImplThread
@@ -18,10 +19,12 @@ class PrioritizedResourceTest : public testing::Test {
public:
PrioritizedResourceTest()
: texture_size_(256, 256),
- texture_format_(GL_RGBA),
- output_surface_(CreateFakeOutputSurface()) {
+ texture_format_(RGBA_8888),
+ output_surface_(FakeOutputSurface::Create3d()) {
DebugScopedSetImplThread impl_thread(&proxy_);
- resource_provider_ = cc::ResourceProvider::Create(output_surface_.get(), 0);
+ CHECK(output_surface_->BindToClient(&output_surface_client_));
+ resource_provider_ =
+ cc::ResourceProvider::Create(output_surface_.get(), 0, false);
}
virtual ~PrioritizedResourceTest() {
@@ -91,7 +94,8 @@ class PrioritizedResourceTest : public testing::Test {
protected:
FakeProxy proxy_;
const gfx::Size texture_size_;
- const GLenum texture_format_;
+ const ResourceFormat texture_format_;
+ FakeOutputSurfaceClient output_surface_client_;
scoped_ptr<OutputSurface> output_surface_;
scoped_ptr<cc::ResourceProvider> resource_provider_;
};
diff --git a/chromium/cc/resources/prioritized_tile_set.cc b/chromium/cc/resources/prioritized_tile_set.cc
index 5b40945e559..6c5c4729d7e 100644
--- a/chromium/cc/resources/prioritized_tile_set.cc
+++ b/chromium/cc/resources/prioritized_tile_set.cc
@@ -13,14 +13,11 @@ namespace cc {
class BinComparator {
public:
- bool operator()(const scoped_refptr<Tile>& a,
- const scoped_refptr<Tile>& b) const {
+ bool operator()(const Tile* a,
+ const Tile* b) const {
const ManagedTileState& ams = a->managed_state();
const ManagedTileState& bms = b->managed_state();
- if (ams.bin[LOW_PRIORITY_BIN] != bms.bin[LOW_PRIORITY_BIN])
- return ams.bin[LOW_PRIORITY_BIN] < bms.bin[LOW_PRIORITY_BIN];
-
if (ams.required_for_activation != bms.required_for_activation)
return ams.required_for_activation;
@@ -46,18 +43,19 @@ class BinComparator {
namespace {
-typedef std::vector<scoped_refptr<Tile> > TileVector;
+typedef std::vector<Tile*> TileVector;
void SortBinTiles(ManagedTileBin bin, TileVector* tiles) {
switch (bin) {
case NOW_AND_READY_TO_DRAW_BIN:
+ case NEVER_BIN:
break;
case NOW_BIN:
case SOON_BIN:
case EVENTUALLY_AND_ACTIVE_BIN:
case EVENTUALLY_BIN:
- case NEVER_AND_ACTIVE_BIN:
- case NEVER_BIN:
+ case AT_LAST_AND_ACTIVE_BIN:
+ case AT_LAST_BIN:
std::sort(tiles->begin(), tiles->end(), BinComparator());
break;
default:
@@ -67,37 +65,52 @@ void SortBinTiles(ManagedTileBin bin, TileVector* tiles) {
} // namespace
-PrioritizedTileSet::PrioritizedTileSet() {}
+PrioritizedTileSet::PrioritizedTileSet() {
+ for (int bin = 0; bin < NUM_BINS; ++bin)
+ bin_sorted_[bin] = true;
+}
PrioritizedTileSet::~PrioritizedTileSet() {}
void PrioritizedTileSet::InsertTile(Tile* tile, ManagedTileBin bin) {
- tiles_[bin].push_back(make_scoped_refptr(tile));
+ tiles_[bin].push_back(tile);
+ bin_sorted_[bin] = false;
}
void PrioritizedTileSet::Clear() {
- for (int bin = 0; bin < NUM_BINS; ++bin)
+ for (int bin = 0; bin < NUM_BINS; ++bin) {
tiles_[bin].clear();
+ bin_sorted_[bin] = true;
+ }
}
-void PrioritizedTileSet::Sort() {
- for (int bin = 0; bin < NUM_BINS; ++bin)
- SortBinTiles(static_cast<ManagedTileBin>(bin), &tiles_[bin]);
+void PrioritizedTileSet::SortBinIfNeeded(ManagedTileBin bin) {
+ if (!bin_sorted_[bin]) {
+ SortBinTiles(bin, &tiles_[bin]);
+ bin_sorted_[bin] = true;
+ }
}
-PrioritizedTileSet::PriorityIterator::PriorityIterator(
- PrioritizedTileSet* tile_set)
+PrioritizedTileSet::Iterator::Iterator(
+ PrioritizedTileSet* tile_set, bool use_priority_ordering)
: tile_set_(tile_set),
current_bin_(NOW_AND_READY_TO_DRAW_BIN),
- iterator_(tile_set->tiles_[current_bin_].begin()) {
+ use_priority_ordering_(use_priority_ordering) {
+ if (use_priority_ordering_)
+ tile_set_->SortBinIfNeeded(current_bin_);
+ iterator_ = tile_set->tiles_[current_bin_].begin();
if (iterator_ == tile_set_->tiles_[current_bin_].end())
AdvanceList();
}
-PrioritizedTileSet::PriorityIterator::~PriorityIterator() {}
+PrioritizedTileSet::Iterator::~Iterator() {}
+
+void PrioritizedTileSet::Iterator::DisablePriorityOrdering() {
+ use_priority_ordering_ = false;
+}
-PrioritizedTileSet::PriorityIterator&
-PrioritizedTileSet::PriorityIterator::operator++() {
+PrioritizedTileSet::Iterator&
+PrioritizedTileSet::Iterator::operator++() {
// We can't increment past the end of the tiles.
DCHECK(iterator_ != tile_set_->tiles_[current_bin_].end());
@@ -107,16 +120,20 @@ PrioritizedTileSet::PriorityIterator::operator++() {
return *this;
}
-Tile* PrioritizedTileSet::PriorityIterator::operator*() {
+Tile* PrioritizedTileSet::Iterator::operator*() {
DCHECK(iterator_ != tile_set_->tiles_[current_bin_].end());
- return iterator_->get();
+ return *iterator_;
}
-void PrioritizedTileSet::PriorityIterator::AdvanceList() {
+void PrioritizedTileSet::Iterator::AdvanceList() {
DCHECK(iterator_ == tile_set_->tiles_[current_bin_].end());
while (current_bin_ != NEVER_BIN) {
current_bin_ = static_cast<ManagedTileBin>(current_bin_ + 1);
+
+ if (use_priority_ordering_)
+ tile_set_->SortBinIfNeeded(current_bin_);
+
iterator_ = tile_set_->tiles_[current_bin_].begin();
if (iterator_ != tile_set_->tiles_[current_bin_].end())
break;
diff --git a/chromium/cc/resources/prioritized_tile_set.h b/chromium/cc/resources/prioritized_tile_set.h
index fe1b2a0a941..15d0e4f8b48 100644
--- a/chromium/cc/resources/prioritized_tile_set.h
+++ b/chromium/cc/resources/prioritized_tile_set.h
@@ -7,8 +7,6 @@
#include <vector>
-#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_ptr.h"
#include "cc/base/cc_export.h"
#include "cc/resources/managed_tile_state.h"
@@ -22,14 +20,16 @@ class CC_EXPORT PrioritizedTileSet {
void InsertTile(Tile* tile, ManagedTileBin bin);
void Clear();
- void Sort();
- class CC_EXPORT PriorityIterator {
+ class CC_EXPORT Iterator {
public:
- explicit PriorityIterator(PrioritizedTileSet* set);
- ~PriorityIterator();
+ Iterator(PrioritizedTileSet* set, bool use_priority_ordering);
- PriorityIterator& operator++();
+ ~Iterator();
+
+ void DisablePriorityOrdering();
+
+ Iterator& operator++();
Tile* operator->() { return *(*this); }
Tile* operator*();
operator bool() const {
@@ -41,14 +41,17 @@ class CC_EXPORT PrioritizedTileSet {
PrioritizedTileSet* tile_set_;
ManagedTileBin current_bin_;
- std::vector<scoped_refptr<Tile> >::iterator iterator_;
+ std::vector<Tile*>::iterator iterator_;
+ bool use_priority_ordering_;
};
private:
- friend class PriorityIterator;
+ friend class Iterator;
+
+ void SortBinIfNeeded(ManagedTileBin bin);
- typedef scoped_refptr<Tile> TileRef;
- std::vector<TileRef> tiles_[NUM_BINS];
+ std::vector<Tile*> tiles_[NUM_BINS];
+ bool bin_sorted_[NUM_BINS];
};
} // namespace cc
diff --git a/chromium/cc/resources/prioritized_tile_set_unittest.cc b/chromium/cc/resources/prioritized_tile_set_unittest.cc
index 645fa04d497..7de1032523c 100644
--- a/chromium/cc/resources/prioritized_tile_set_unittest.cc
+++ b/chromium/cc/resources/prioritized_tile_set_unittest.cc
@@ -9,6 +9,7 @@
#include "cc/resources/prioritized_tile_set.h"
#include "cc/resources/tile.h"
#include "cc/test/fake_output_surface.h"
+#include "cc/test/fake_output_surface_client.h"
#include "cc/test/fake_picture_pile_impl.h"
#include "cc/test/fake_tile_manager.h"
#include "cc/test/fake_tile_manager_client.h"
@@ -24,9 +25,6 @@ class BinComparator {
const ManagedTileState& ams = a->managed_state();
const ManagedTileState& bms = b->managed_state();
- if (ams.bin[LOW_PRIORITY_BIN] != bms.bin[LOW_PRIORITY_BIN])
- return ams.bin[LOW_PRIORITY_BIN] < bms.bin[LOW_PRIORITY_BIN];
-
if (ams.required_for_activation != bms.required_for_activation)
return ams.required_for_activation;
@@ -54,12 +52,16 @@ namespace {
class PrioritizedTileSetTest : public testing::Test {
public:
- PrioritizedTileSetTest()
- : output_surface_(FakeOutputSurface::Create3d()),
- resource_provider_(ResourceProvider::Create(output_surface_.get(), 0)),
- tile_manager_(new FakeTileManager(&tile_manager_client_,
- resource_provider_.get())),
- picture_pile_(FakePicturePileImpl::CreatePile()) {}
+ PrioritizedTileSetTest() {
+ output_surface_ = FakeOutputSurface::Create3d().Pass();
+ CHECK(output_surface_->BindToClient(&output_surface_client_));
+
+ resource_provider_ =
+ ResourceProvider::Create(output_surface_.get(), 0, false).Pass();
+ tile_manager_.reset(new FakeTileManager(&tile_manager_client_,
+ resource_provider_.get()));
+ picture_pile_ = FakePicturePileImpl::CreatePile();
+ }
scoped_refptr<Tile> CreateTile() {
return make_scoped_refptr(new Tile(tile_manager_.get(),
@@ -74,19 +76,22 @@ class PrioritizedTileSetTest : public testing::Test {
}
private:
- FakeTileManagerClient tile_manager_client_;
LayerTreeSettings settings_;
+ FakeOutputSurfaceClient output_surface_client_;
scoped_ptr<FakeOutputSurface> output_surface_;
scoped_ptr<ResourceProvider> resource_provider_;
+ FakeTileManagerClient tile_manager_client_;
scoped_ptr<FakeTileManager> tile_manager_;
scoped_refptr<FakePicturePileImpl> picture_pile_;
};
TEST_F(PrioritizedTileSetTest, EmptyIterator) {
+ // Creating an iterator to an empty set should work (but create iterator that
+ // isn't valid).
+
PrioritizedTileSet set;
- set.Sort();
- PrioritizedTileSet::PriorityIterator it(&set);
+ PrioritizedTileSet::Iterator it(&set, true);
EXPECT_FALSE(it);
}
@@ -94,9 +99,8 @@ TEST_F(PrioritizedTileSetTest, NonEmptyIterator) {
PrioritizedTileSet set;
scoped_refptr<Tile> tile = CreateTile();
set.InsertTile(tile, NOW_BIN);
- set.Sort();
- PrioritizedTileSet::PriorityIterator it(&set);
+ PrioritizedTileSet::Iterator it(&set, true);
EXPECT_TRUE(it);
EXPECT_TRUE(*it == tile.get());
++it;
@@ -104,6 +108,8 @@ TEST_F(PrioritizedTileSetTest, NonEmptyIterator) {
}
TEST_F(PrioritizedTileSetTest, NowAndReadyToDrawBin) {
+ // Ensure that tiles in NOW_AND_READY_TO_DRAW_BIN aren't sorted.
+
PrioritizedTileSet set;
TilePriority priorities[4] = {
TilePriorityForEventualBin(),
@@ -122,11 +128,9 @@ TEST_F(PrioritizedTileSetTest, NowAndReadyToDrawBin) {
}
}
- set.Sort();
-
// Tiles should appear in the same order as inserted.
int i = 0;
- for (PrioritizedTileSet::PriorityIterator it(&set);
+ for (PrioritizedTileSet::Iterator it(&set, true);
it;
++it) {
EXPECT_TRUE(*it == tiles[i].get());
@@ -136,6 +140,8 @@ TEST_F(PrioritizedTileSetTest, NowAndReadyToDrawBin) {
}
TEST_F(PrioritizedTileSetTest, NowBin) {
+ // Ensure that tiles in NOW_BIN are sorted according to BinComparator.
+
PrioritizedTileSet set;
TilePriority priorities[4] = {
TilePriorityForEventualBin(),
@@ -154,13 +160,11 @@ TEST_F(PrioritizedTileSetTest, NowBin) {
}
}
- set.Sort();
-
// Tiles should appear in BinComparator order.
std::sort(tiles.begin(), tiles.end(), BinComparator());
int i = 0;
- for (PrioritizedTileSet::PriorityIterator it(&set);
+ for (PrioritizedTileSet::Iterator it(&set, true);
it;
++it) {
EXPECT_TRUE(*it == tiles[i].get());
@@ -170,6 +174,8 @@ TEST_F(PrioritizedTileSetTest, NowBin) {
}
TEST_F(PrioritizedTileSetTest, SoonBin) {
+ // Ensure that tiles in SOON_BIN are sorted according to BinComparator.
+
PrioritizedTileSet set;
TilePriority priorities[4] = {
TilePriorityForEventualBin(),
@@ -188,13 +194,43 @@ TEST_F(PrioritizedTileSetTest, SoonBin) {
}
}
- set.Sort();
-
// Tiles should appear in BinComparator order.
std::sort(tiles.begin(), tiles.end(), BinComparator());
int i = 0;
- for (PrioritizedTileSet::PriorityIterator it(&set);
+ for (PrioritizedTileSet::Iterator it(&set, true);
+ it;
+ ++it) {
+ EXPECT_TRUE(*it == tiles[i].get());
+ ++i;
+ }
+ EXPECT_EQ(20, i);
+}
+
+TEST_F(PrioritizedTileSetTest, SoonBinNoPriority) {
+ // Ensure that when not using priority iterator, SOON_BIN tiles
+ // are not sorted.
+
+ PrioritizedTileSet set;
+ TilePriority priorities[4] = {
+ TilePriorityForEventualBin(),
+ TilePriorityForNowBin(),
+ TilePriority(),
+ TilePriorityForSoonBin()};
+
+ std::vector<scoped_refptr<Tile> > tiles;
+ for (int priority = 0; priority < 4; ++priority) {
+ for (int i = 0; i < 5; ++i) {
+ scoped_refptr<Tile> tile = CreateTile();
+ tile->SetPriority(ACTIVE_TREE, priorities[priority]);
+ tile->SetPriority(PENDING_TREE, priorities[priority]);
+ tiles.push_back(tile);
+ set.InsertTile(tile, SOON_BIN);
+ }
+ }
+
+ int i = 0;
+ for (PrioritizedTileSet::Iterator it(&set, false);
it;
++it) {
EXPECT_TRUE(*it == tiles[i].get());
@@ -204,6 +240,8 @@ TEST_F(PrioritizedTileSetTest, SoonBin) {
}
TEST_F(PrioritizedTileSetTest, EventuallyAndActiveBin) {
+ // Ensure that EVENTUALLY_AND_ACTIVE_BIN tiles are sorted.
+
PrioritizedTileSet set;
TilePriority priorities[4] = {
TilePriorityForEventualBin(),
@@ -222,13 +260,11 @@ TEST_F(PrioritizedTileSetTest, EventuallyAndActiveBin) {
}
}
- set.Sort();
-
// Tiles should appear in BinComparator order.
std::sort(tiles.begin(), tiles.end(), BinComparator());
int i = 0;
- for (PrioritizedTileSet::PriorityIterator it(&set);
+ for (PrioritizedTileSet::Iterator it(&set, true);
it;
++it) {
EXPECT_TRUE(*it == tiles[i].get());
@@ -238,6 +274,8 @@ TEST_F(PrioritizedTileSetTest, EventuallyAndActiveBin) {
}
TEST_F(PrioritizedTileSetTest, EventuallyBin) {
+ // Ensure that EVENTUALLY_BIN tiles are sorted.
+
PrioritizedTileSet set;
TilePriority priorities[4] = {
TilePriorityForEventualBin(),
@@ -256,13 +294,11 @@ TEST_F(PrioritizedTileSetTest, EventuallyBin) {
}
}
- set.Sort();
-
// Tiles should appear in BinComparator order.
std::sort(tiles.begin(), tiles.end(), BinComparator());
int i = 0;
- for (PrioritizedTileSet::PriorityIterator it(&set);
+ for (PrioritizedTileSet::Iterator it(&set, true);
it;
++it) {
EXPECT_TRUE(*it == tiles[i].get());
@@ -271,7 +307,9 @@ TEST_F(PrioritizedTileSetTest, EventuallyBin) {
EXPECT_EQ(20, i);
}
-TEST_F(PrioritizedTileSetTest, NeverAndActiveBin) {
+TEST_F(PrioritizedTileSetTest, AtLastAndActiveBin) {
+ // Ensure that AT_LAST_AND_ACTIVE_BIN tiles are sorted.
+
PrioritizedTileSet set;
TilePriority priorities[4] = {
TilePriorityForEventualBin(),
@@ -286,17 +324,15 @@ TEST_F(PrioritizedTileSetTest, NeverAndActiveBin) {
tile->SetPriority(ACTIVE_TREE, priorities[priority]);
tile->SetPriority(PENDING_TREE, priorities[priority]);
tiles.push_back(tile);
- set.InsertTile(tile, NEVER_AND_ACTIVE_BIN);
+ set.InsertTile(tile, AT_LAST_AND_ACTIVE_BIN);
}
}
- set.Sort();
-
// Tiles should appear in BinComparator order.
std::sort(tiles.begin(), tiles.end(), BinComparator());
int i = 0;
- for (PrioritizedTileSet::PriorityIterator it(&set);
+ for (PrioritizedTileSet::Iterator it(&set, true);
it;
++it) {
EXPECT_TRUE(*it == tiles[i].get());
@@ -305,7 +341,9 @@ TEST_F(PrioritizedTileSetTest, NeverAndActiveBin) {
EXPECT_EQ(20, i);
}
-TEST_F(PrioritizedTileSetTest, NeverBin) {
+TEST_F(PrioritizedTileSetTest, AtLastBin) {
+ // Ensure that AT_LAST_BIN tiles are sorted.
+
PrioritizedTileSet set;
TilePriority priorities[4] = {
TilePriorityForEventualBin(),
@@ -320,17 +358,15 @@ TEST_F(PrioritizedTileSetTest, NeverBin) {
tile->SetPriority(ACTIVE_TREE, priorities[priority]);
tile->SetPriority(PENDING_TREE, priorities[priority]);
tiles.push_back(tile);
- set.InsertTile(tile, NEVER_BIN);
+ set.InsertTile(tile, AT_LAST_BIN);
}
}
- set.Sort();
-
// Tiles should appear in BinComparator order.
std::sort(tiles.begin(), tiles.end(), BinComparator());
int i = 0;
- for (PrioritizedTileSet::PriorityIterator it(&set);
+ for (PrioritizedTileSet::Iterator it(&set, true);
it;
++it) {
EXPECT_TRUE(*it == tiles[i].get());
@@ -340,27 +376,28 @@ TEST_F(PrioritizedTileSetTest, NeverBin) {
}
TEST_F(PrioritizedTileSetTest, TilesForEachBin) {
+ // Aggregate test with one tile for each of the bins, which
+ // should appear in order of the bins.
+
scoped_refptr<Tile> now_and_ready_to_draw_bin = CreateTile();
scoped_refptr<Tile> now_bin = CreateTile();
scoped_refptr<Tile> soon_bin = CreateTile();
scoped_refptr<Tile> eventually_and_active_bin = CreateTile();
scoped_refptr<Tile> eventually_bin = CreateTile();
- scoped_refptr<Tile> never_bin = CreateTile();
- scoped_refptr<Tile> never_and_active_bin = CreateTile();
+ scoped_refptr<Tile> at_last_bin = CreateTile();
+ scoped_refptr<Tile> at_last_and_active_bin = CreateTile();
PrioritizedTileSet set;
set.InsertTile(soon_bin, SOON_BIN);
- set.InsertTile(never_and_active_bin, NEVER_AND_ACTIVE_BIN);
+ set.InsertTile(at_last_and_active_bin, AT_LAST_AND_ACTIVE_BIN);
set.InsertTile(eventually_bin, EVENTUALLY_BIN);
set.InsertTile(now_bin, NOW_BIN);
set.InsertTile(eventually_and_active_bin, EVENTUALLY_AND_ACTIVE_BIN);
- set.InsertTile(never_bin, NEVER_BIN);
+ set.InsertTile(at_last_bin, AT_LAST_BIN);
set.InsertTile(now_and_ready_to_draw_bin, NOW_AND_READY_TO_DRAW_BIN);
- set.Sort();
-
// Tiles should appear in order.
- PrioritizedTileSet::PriorityIterator it(&set);
+ PrioritizedTileSet::Iterator it(&set, true);
EXPECT_TRUE(*it == now_and_ready_to_draw_bin.get());
++it;
EXPECT_TRUE(*it == now_bin.get());
@@ -371,50 +408,270 @@ TEST_F(PrioritizedTileSetTest, TilesForEachBin) {
++it;
EXPECT_TRUE(*it == eventually_bin.get());
++it;
- EXPECT_TRUE(*it == never_and_active_bin.get());
+ EXPECT_TRUE(*it == at_last_and_active_bin.get());
++it;
- EXPECT_TRUE(*it == never_bin.get());
+ EXPECT_TRUE(*it == at_last_bin.get());
++it;
EXPECT_FALSE(it);
}
+TEST_F(PrioritizedTileSetTest, ManyTilesForEachBin) {
+ // Aggregate test with many tiles in each of the bins of various
+ // priorities. Ensure that they are all returned in a sorted order.
+
+ std::vector<scoped_refptr<Tile> > now_and_ready_to_draw_bins;
+ std::vector<scoped_refptr<Tile> > now_bins;
+ std::vector<scoped_refptr<Tile> > soon_bins;
+ std::vector<scoped_refptr<Tile> > eventually_and_active_bins;
+ std::vector<scoped_refptr<Tile> > eventually_bins;
+ std::vector<scoped_refptr<Tile> > at_last_bins;
+ std::vector<scoped_refptr<Tile> > at_last_and_active_bins;
+
+ TilePriority priorities[4] = {
+ TilePriorityForEventualBin(),
+ TilePriorityForNowBin(),
+ TilePriority(),
+ TilePriorityForSoonBin()};
+
+ PrioritizedTileSet set;
+ for (int priority = 0; priority < 4; ++priority) {
+ for (int i = 0; i < 5; ++i) {
+ scoped_refptr<Tile> tile = CreateTile();
+ tile->SetPriority(ACTIVE_TREE, priorities[priority]);
+ tile->SetPriority(PENDING_TREE, priorities[priority]);
+
+ now_and_ready_to_draw_bins.push_back(tile);
+ now_bins.push_back(tile);
+ soon_bins.push_back(tile);
+ eventually_and_active_bins.push_back(tile);
+ eventually_bins.push_back(tile);
+ at_last_bins.push_back(tile);
+ at_last_and_active_bins.push_back(tile);
+
+ set.InsertTile(tile, NOW_AND_READY_TO_DRAW_BIN);
+ set.InsertTile(tile, NOW_BIN);
+ set.InsertTile(tile, SOON_BIN);
+ set.InsertTile(tile, EVENTUALLY_AND_ACTIVE_BIN);
+ set.InsertTile(tile, EVENTUALLY_BIN);
+ set.InsertTile(tile, AT_LAST_BIN);
+ set.InsertTile(tile, AT_LAST_AND_ACTIVE_BIN);
+ }
+ }
+
+ PrioritizedTileSet::Iterator it(&set, true);
+ std::vector<scoped_refptr<Tile> >::iterator vector_it;
+
+ // Now and ready are not sorted.
+ for (vector_it = now_and_ready_to_draw_bins.begin();
+ vector_it != now_and_ready_to_draw_bins.end();
+ ++vector_it) {
+ EXPECT_TRUE(*vector_it == *it);
+ ++it;
+ }
+
+ // Now bins are sorted.
+ std::sort(now_bins.begin(), now_bins.end(), BinComparator());
+ for (vector_it = now_bins.begin(); vector_it != now_bins.end(); ++vector_it) {
+ EXPECT_TRUE(*vector_it == *it);
+ ++it;
+ }
+
+ // Soon bins are sorted.
+ std::sort(soon_bins.begin(), soon_bins.end(), BinComparator());
+ for (vector_it = soon_bins.begin(); vector_it != soon_bins.end();
+ ++vector_it) {
+ EXPECT_TRUE(*vector_it == *it);
+ ++it;
+ }
+
+ // Eventually and active bins are sorted.
+ std::sort(eventually_and_active_bins.begin(),
+ eventually_and_active_bins.end(),
+ BinComparator());
+ for (vector_it = eventually_and_active_bins.begin();
+ vector_it != eventually_and_active_bins.end();
+ ++vector_it) {
+ EXPECT_TRUE(*vector_it == *it);
+ ++it;
+ }
+
+ // Eventually bins are sorted.
+ std::sort(eventually_bins.begin(), eventually_bins.end(), BinComparator());
+ for (vector_it = eventually_bins.begin(); vector_it != eventually_bins.end();
+ ++vector_it) {
+ EXPECT_TRUE(*vector_it == *it);
+ ++it;
+ }
+
+ // At last and active bins are sorted.
+ std::sort(at_last_and_active_bins.begin(),
+ at_last_and_active_bins.end(),
+ BinComparator());
+ for (vector_it = at_last_and_active_bins.begin();
+ vector_it != at_last_and_active_bins.end();
+ ++vector_it) {
+ EXPECT_TRUE(*vector_it == *it);
+ ++it;
+ }
+
+ // At last bins are sorted.
+ std::sort(at_last_bins.begin(), at_last_bins.end(), BinComparator());
+ for (vector_it = at_last_bins.begin(); vector_it != at_last_bins.end();
+ ++vector_it) {
+ EXPECT_TRUE(*vector_it == *it);
+ ++it;
+ }
+
+ EXPECT_FALSE(it);
+}
+
+TEST_F(PrioritizedTileSetTest, ManyTilesForEachBinDisablePriority) {
+ // Aggregate test with many tiles for each of the bins. Tiles should
+ // appear in order, until DisablePriorityOrdering is called. After that
+ // tiles should appear in the order they were inserted.
+
+ std::vector<scoped_refptr<Tile> > now_and_ready_to_draw_bins;
+ std::vector<scoped_refptr<Tile> > now_bins;
+ std::vector<scoped_refptr<Tile> > soon_bins;
+ std::vector<scoped_refptr<Tile> > eventually_and_active_bins;
+ std::vector<scoped_refptr<Tile> > eventually_bins;
+ std::vector<scoped_refptr<Tile> > at_last_bins;
+ std::vector<scoped_refptr<Tile> > at_last_and_active_bins;
+
+ TilePriority priorities[4] = {
+ TilePriorityForEventualBin(),
+ TilePriorityForNowBin(),
+ TilePriority(),
+ TilePriorityForSoonBin()};
+
+ PrioritizedTileSet set;
+ for (int priority = 0; priority < 4; ++priority) {
+ for (int i = 0; i < 5; ++i) {
+ scoped_refptr<Tile> tile = CreateTile();
+ tile->SetPriority(ACTIVE_TREE, priorities[priority]);
+ tile->SetPriority(PENDING_TREE, priorities[priority]);
+
+ now_and_ready_to_draw_bins.push_back(tile);
+ now_bins.push_back(tile);
+ soon_bins.push_back(tile);
+ eventually_and_active_bins.push_back(tile);
+ eventually_bins.push_back(tile);
+ at_last_bins.push_back(tile);
+ at_last_and_active_bins.push_back(tile);
+
+ set.InsertTile(tile, NOW_AND_READY_TO_DRAW_BIN);
+ set.InsertTile(tile, NOW_BIN);
+ set.InsertTile(tile, SOON_BIN);
+ set.InsertTile(tile, EVENTUALLY_AND_ACTIVE_BIN);
+ set.InsertTile(tile, EVENTUALLY_BIN);
+ set.InsertTile(tile, AT_LAST_BIN);
+ set.InsertTile(tile, AT_LAST_AND_ACTIVE_BIN);
+ }
+ }
+
+ PrioritizedTileSet::Iterator it(&set, true);
+ std::vector<scoped_refptr<Tile> >::iterator vector_it;
+
+ // Now and ready are not sorted.
+ for (vector_it = now_and_ready_to_draw_bins.begin();
+ vector_it != now_and_ready_to_draw_bins.end();
+ ++vector_it) {
+ EXPECT_TRUE(*vector_it == *it);
+ ++it;
+ }
+
+ // Now bins are sorted.
+ std::sort(now_bins.begin(), now_bins.end(), BinComparator());
+ for (vector_it = now_bins.begin(); vector_it != now_bins.end(); ++vector_it) {
+ EXPECT_TRUE(*vector_it == *it);
+ ++it;
+ }
+
+ // Soon bins are sorted.
+ std::sort(soon_bins.begin(), soon_bins.end(), BinComparator());
+ for (vector_it = soon_bins.begin(); vector_it != soon_bins.end();
+ ++vector_it) {
+ EXPECT_TRUE(*vector_it == *it);
+ ++it;
+ }
+
+ // After we disable priority ordering, we already have sorted the next vector.
+ it.DisablePriorityOrdering();
+
+ // Eventually and active bins are sorted.
+ std::sort(eventually_and_active_bins.begin(),
+ eventually_and_active_bins.end(),
+ BinComparator());
+ for (vector_it = eventually_and_active_bins.begin();
+ vector_it != eventually_and_active_bins.end();
+ ++vector_it) {
+ EXPECT_TRUE(*vector_it == *it);
+ ++it;
+ }
+
+ // Eventually bins are not sorted.
+ for (vector_it = eventually_bins.begin(); vector_it != eventually_bins.end();
+ ++vector_it) {
+ EXPECT_TRUE(*vector_it == *it);
+ ++it;
+ }
+
+ // At last and active bins are not sorted.
+ for (vector_it = at_last_and_active_bins.begin();
+ vector_it != at_last_and_active_bins.end();
+ ++vector_it) {
+ EXPECT_TRUE(*vector_it == *it);
+ ++it;
+ }
+
+ // At last bins are not sorted.
+ for (vector_it = at_last_bins.begin(); vector_it != at_last_bins.end();
+ ++vector_it) {
+ EXPECT_TRUE(*vector_it == *it);
+ ++it;
+ }
+
+ EXPECT_FALSE(it);
+}
+
TEST_F(PrioritizedTileSetTest, TilesForFirstAndLastBins) {
+ // Make sure that if we have empty lists between two non-empty lists,
+ // we just get two tiles from the iterator.
+
scoped_refptr<Tile> now_and_ready_to_draw_bin = CreateTile();
- scoped_refptr<Tile> never_bin = CreateTile();
+ scoped_refptr<Tile> at_last_bin = CreateTile();
PrioritizedTileSet set;
- set.InsertTile(never_bin, NEVER_BIN);
+ set.InsertTile(at_last_bin, AT_LAST_BIN);
set.InsertTile(now_and_ready_to_draw_bin, NOW_AND_READY_TO_DRAW_BIN);
- set.Sort();
-
// Only two tiles should appear and they should appear in order.
- PrioritizedTileSet::PriorityIterator it(&set);
+ PrioritizedTileSet::Iterator it(&set, true);
EXPECT_TRUE(*it == now_and_ready_to_draw_bin.get());
++it;
- EXPECT_TRUE(*it == never_bin.get());
+ EXPECT_TRUE(*it == at_last_bin.get());
++it;
EXPECT_FALSE(it);
}
TEST_F(PrioritizedTileSetTest, MultipleIterators) {
+ // Ensure that multiple iterators don't interfere with each other.
+
scoped_refptr<Tile> now_and_ready_to_draw_bin = CreateTile();
scoped_refptr<Tile> now_bin = CreateTile();
scoped_refptr<Tile> soon_bin = CreateTile();
scoped_refptr<Tile> eventually_bin = CreateTile();
- scoped_refptr<Tile> never_bin = CreateTile();
+ scoped_refptr<Tile> at_last_bin = CreateTile();
PrioritizedTileSet set;
set.InsertTile(soon_bin, SOON_BIN);
set.InsertTile(eventually_bin, EVENTUALLY_BIN);
set.InsertTile(now_bin, NOW_BIN);
- set.InsertTile(never_bin, NEVER_BIN);
+ set.InsertTile(at_last_bin, AT_LAST_BIN);
set.InsertTile(now_and_ready_to_draw_bin, NOW_AND_READY_TO_DRAW_BIN);
- set.Sort();
-
// Tiles should appear in order.
- PrioritizedTileSet::PriorityIterator it(&set);
+ PrioritizedTileSet::Iterator it(&set, true);
EXPECT_TRUE(*it == now_and_ready_to_draw_bin.get());
++it;
EXPECT_TRUE(*it == now_bin.get());
@@ -423,12 +680,12 @@ TEST_F(PrioritizedTileSetTest, MultipleIterators) {
++it;
EXPECT_TRUE(*it == eventually_bin.get());
++it;
- EXPECT_TRUE(*it == never_bin.get());
+ EXPECT_TRUE(*it == at_last_bin.get());
++it;
EXPECT_FALSE(it);
// Creating multiple iterators shouldn't affect old iterators.
- PrioritizedTileSet::PriorityIterator second_it(&set);
+ PrioritizedTileSet::Iterator second_it(&set, true);
EXPECT_TRUE(second_it);
EXPECT_FALSE(it);
@@ -438,7 +695,7 @@ TEST_F(PrioritizedTileSetTest, MultipleIterators) {
EXPECT_TRUE(second_it);
EXPECT_FALSE(it);
- PrioritizedTileSet::PriorityIterator third_it(&set);
+ PrioritizedTileSet::Iterator third_it(&set, true);
EXPECT_TRUE(third_it);
++second_it;
++second_it;
@@ -451,7 +708,7 @@ TEST_F(PrioritizedTileSetTest, MultipleIterators) {
EXPECT_TRUE(third_it);
EXPECT_TRUE(*third_it == soon_bin.get());
EXPECT_TRUE(second_it);
- EXPECT_TRUE(*second_it == never_bin.get());
+ EXPECT_TRUE(*second_it == at_last_bin.get());
EXPECT_FALSE(it);
++second_it;
@@ -461,7 +718,7 @@ TEST_F(PrioritizedTileSetTest, MultipleIterators) {
set.Clear();
- PrioritizedTileSet::PriorityIterator empty_it(&set);
+ PrioritizedTileSet::Iterator empty_it(&set, true);
EXPECT_FALSE(empty_it);
}
diff --git a/chromium/cc/resources/raster_mode.cc b/chromium/cc/resources/raster_mode.cc
index b35bc0be42d..47d6344c97d 100644
--- a/chromium/cc/resources/raster_mode.cc
+++ b/chromium/cc/resources/raster_mode.cc
@@ -21,7 +21,6 @@ scoped_ptr<base::Value> RasterModeAsValue(RasterMode raster_mode) {
case LOW_QUALITY_RASTER_MODE:
return scoped_ptr<base::Value>(
base::Value::CreateStringValue("LOW_QUALITY_RASTER_MODE"));
- case NUM_RASTER_MODES:
default:
NOTREACHED() << "Unrecognized RasterMode value " << raster_mode;
return scoped_ptr<base::Value>(
diff --git a/chromium/cc/resources/raster_worker_pool.cc b/chromium/cc/resources/raster_worker_pool.cc
index aced03fcec6..5aaf0b66540 100644
--- a/chromium/cc/resources/raster_worker_pool.cc
+++ b/chromium/cc/resources/raster_worker_pool.cc
@@ -13,11 +13,25 @@
#include "cc/resources/picture_pile_impl.h"
#include "skia/ext/lazy_pixel_ref.h"
#include "skia/ext/paint_simplifier.h"
+#include "third_party/skia/include/core/SkBitmap.h"
namespace cc {
namespace {
+// Subclass of Allocator that takes a suitably allocated pointer and uses
+// it as the pixel memory for the bitmap.
+class IdentityAllocator : public SkBitmap::Allocator {
+ public:
+ explicit IdentityAllocator(void* buffer) : buffer_(buffer) {}
+ virtual bool allocPixelRef(SkBitmap* dst, SkColorTable*) OVERRIDE {
+ dst->setPixels(buffer_);
+ return true;
+ }
+ private:
+ void* buffer_;
+};
+
// Flag to indicate whether we should try and detect that
// a tile is of solid color.
const bool kUseColorEstimator = true;
@@ -89,7 +103,10 @@ class RasterWorkerPoolTaskImpl : public internal::RasterWorkerPoolTask {
analysis_.is_solid_color &= kUseColorEstimator;
}
- bool RunRasterOnThread(SkDevice* device, unsigned thread_index) {
+ bool RunRasterOnThread(unsigned thread_index,
+ void* buffer,
+ gfx::Size size,
+ int stride) {
TRACE_EVENT2(
benchmark_instrumentation::kCategory,
benchmark_instrumentation::kRunRasterOnThread,
@@ -102,7 +119,7 @@ class RasterWorkerPoolTaskImpl : public internal::RasterWorkerPoolTask {
devtools_instrumentation::kRasterTask, layer_id_);
DCHECK(picture_pile_.get());
- DCHECK(device);
+ DCHECK(buffer);
if (analysis_.is_solid_color)
return false;
@@ -110,8 +127,32 @@ class RasterWorkerPoolTaskImpl : public internal::RasterWorkerPoolTask {
PicturePileImpl* picture_clone =
picture_pile_->GetCloneForDrawingOnThread(thread_index);
- SkCanvas canvas(device);
+ SkBitmap bitmap;
+ switch (resource()->format()) {
+ case RGBA_4444:
+ // Use the default stride if we will eventually convert this
+ // bitmap to 4444.
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config,
+ size.width(),
+ size.height());
+ bitmap.allocPixels();
+ break;
+ case RGBA_8888:
+ case BGRA_8888:
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config,
+ size.width(),
+ size.height(),
+ stride);
+ bitmap.setPixels(buffer);
+ break;
+ case LUMINANCE_8:
+ case RGB_565:
+ NOTREACHED();
+ break;
+ }
+ SkBitmapDevice device(bitmap);
+ SkCanvas canvas(&device);
skia::RefPtr<SkDrawFilter> draw_filter;
switch (raster_mode_) {
case LOW_QUALITY_RASTER_MODE:
@@ -149,14 +190,20 @@ class RasterWorkerPoolTaskImpl : public internal::RasterWorkerPoolTask {
picture_clone->RasterToBitmap(
&canvas, content_rect_, contents_scale_, NULL);
}
+
+ ChangeBitmapConfigIfNeeded(bitmap, buffer);
+
return true;
}
// Overridden from internal::RasterWorkerPoolTask:
- virtual bool RunOnWorkerThread(SkDevice* device, unsigned thread_index)
+ virtual bool RunOnWorkerThread(unsigned thread_index,
+ void* buffer,
+ gfx::Size size,
+ int stride)
OVERRIDE {
RunAnalysisOnThread(thread_index);
- return RunRasterOnThread(device, thread_index);
+ return RunRasterOnThread(thread_index, buffer, size, stride);
}
virtual void CompleteOnOriginThread() OVERRIDE {
reply_.Run(analysis_, !HasFinishedRunning() || WasCanceled());
@@ -177,6 +224,21 @@ class RasterWorkerPoolTaskImpl : public internal::RasterWorkerPoolTask {
return res.PassAs<base::Value>();
}
+ void ChangeBitmapConfigIfNeeded(const SkBitmap& bitmap,
+ void* buffer) {
+ TRACE_EVENT0("cc", "RasterWorkerPoolTaskImpl::ChangeBitmapConfigIfNeeded");
+ SkBitmap::Config config = SkBitmapConfigFromFormat(
+ resource()->format());
+ if (bitmap.getConfig() != config) {
+ SkBitmap bitmap_dest;
+ IdentityAllocator allocator(buffer);
+ bitmap.copyTo(&bitmap_dest, config, &allocator);
+ // TODO(kaanb): The GL pipeline assumes a 4-byte alignment for the
+ // bitmap data. This check will be removed once crbug.com/293728 is fixed.
+ CHECK_EQ(0u, bitmap_dest.rowBytes() % 4);
+ }
+ }
+
PicturePileImpl::Analysis analysis_;
scoped_refptr<PicturePileImpl> picture_pile_;
gfx::Rect content_rect_;
@@ -199,7 +261,7 @@ class ImageDecodeWorkerPoolTaskImpl : public internal::WorkerPoolTask {
int layer_id,
RenderingStatsInstrumentation* rendering_stats,
const RasterWorkerPool::Task::Reply& reply)
- : pixel_ref_(pixel_ref),
+ : pixel_ref_(skia::SharePtr(pixel_ref)),
layer_id_(layer_id),
rendering_stats_(rendering_stats),
reply_(reply) {}
@@ -207,8 +269,8 @@ class ImageDecodeWorkerPoolTaskImpl : public internal::WorkerPoolTask {
// Overridden from internal::WorkerPoolTask:
virtual void RunOnWorkerThread(unsigned thread_index) OVERRIDE {
TRACE_EVENT0("cc", "ImageDecodeWorkerPoolTaskImpl::RunOnWorkerThread");
- devtools_instrumentation::ScopedLayerTask image_decode_task(
- devtools_instrumentation::kImageDecodeTask, layer_id_);
+ devtools_instrumentation::ScopedImageDecodeTask image_decode_task(
+ pixel_ref_.get());
base::TimeTicks start_time = rendering_stats_->StartRecording();
pixel_ref_->Decode();
base::TimeDelta duration = rendering_stats_->EndRecording(start_time);
@@ -222,7 +284,7 @@ class ImageDecodeWorkerPoolTaskImpl : public internal::WorkerPoolTask {
virtual ~ImageDecodeWorkerPoolTaskImpl() {}
private:
- skia::LazyPixelRef* pixel_ref_;
+ skia::RefPtr<skia::LazyPixelRef> pixel_ref_;
int layer_id_;
RenderingStatsInstrumentation* rendering_stats_;
const RasterWorkerPool::Task::Reply reply_;
diff --git a/chromium/cc/resources/raster_worker_pool.h b/chromium/cc/resources/raster_worker_pool.h
index d0e1e7c0de0..b12a1b43c23 100644
--- a/chromium/cc/resources/raster_worker_pool.h
+++ b/chromium/cc/resources/raster_worker_pool.h
@@ -11,10 +11,11 @@
#include "cc/debug/rendering_stats_instrumentation.h"
#include "cc/resources/picture_pile_impl.h"
#include "cc/resources/raster_mode.h"
+#include "cc/resources/resource.h"
+#include "cc/resources/resource_provider.h"
#include "cc/resources/tile_priority.h"
#include "cc/resources/worker_pool.h"
-
-class SkDevice;
+#include "third_party/khronos/GLES2/gl2.h"
namespace skia {
class LazyPixelRef;
@@ -23,7 +24,6 @@ class LazyPixelRef;
namespace cc {
class PicturePileImpl;
class PixelBufferRasterWorkerPool;
-class Resource;
class ResourceProvider;
namespace internal {
@@ -33,10 +33,13 @@ class CC_EXPORT RasterWorkerPoolTask
public:
typedef std::vector<scoped_refptr<WorkerPoolTask> > TaskVector;
- // Returns true if |device| was written to. False indicate that
- // the content of |device| is undefined and the resource doesn't
+ // Returns true if |buffer| was written to. False indicate that
+ // the content of |buffer| is undefined and the resource doesn't
// need to be initialized.
- virtual bool RunOnWorkerThread(SkDevice* device, unsigned thread_index) = 0;
+ virtual bool RunOnWorkerThread(unsigned thread_index,
+ void* buffer,
+ gfx::Size size,
+ int stride) = 0;
virtual void CompleteOnOriginThread() = 0;
void DidRun(bool was_canceled);
@@ -183,6 +186,9 @@ class CC_EXPORT RasterWorkerPool : public WorkerPool {
// even if they later get canceled by another call to ScheduleTasks().
virtual void ScheduleTasks(RasterTask::Queue* queue) = 0;
+ // Returns the format that needs to be used for raster task resources.
+ virtual ResourceFormat GetResourceFormat() const = 0;
+
// TODO(vmpstr): Figure out an elegant way to not pass this many parameters.
static RasterTask CreateRasterTask(
const Resource* resource,
diff --git a/chromium/cc/resources/raster_worker_pool_perftest.cc b/chromium/cc/resources/raster_worker_pool_perftest.cc
index a4a258b16d3..cec9c4820d1 100644
--- a/chromium/cc/resources/raster_worker_pool_perftest.cc
+++ b/chromium/cc/resources/raster_worker_pool_perftest.cc
@@ -5,7 +5,9 @@
#include "cc/resources/raster_worker_pool.h"
#include "base/time/time.h"
+#include "cc/test/lap_timer.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/perf/perf_test.h"
namespace cc {
@@ -38,6 +40,10 @@ class PerfRasterWorkerPool : public RasterWorkerPool {
virtual void ScheduleTasks(RasterTask::Queue* queue) OVERRIDE {
NOTREACHED();
}
+ virtual ResourceFormat GetResourceFormat() const OVERRIDE {
+ NOTREACHED();
+ return RGBA_8888;
+ }
virtual void OnRasterTasksFinished() OVERRIDE {
NOTREACHED();
}
@@ -116,7 +122,10 @@ class PerfRasterWorkerPool : public RasterWorkerPool {
class RasterWorkerPoolPerfTest : public testing::Test {
public:
- RasterWorkerPoolPerfTest() : num_runs_(0) {}
+ RasterWorkerPoolPerfTest()
+ : timer_(kWarmupRuns,
+ base::TimeDelta::FromMilliseconds(kTimeLimitMillis),
+ kTimeCheckInterval) {}
// Overridden from testing::Test:
virtual void SetUp() OVERRIDE {
@@ -126,33 +135,6 @@ class RasterWorkerPoolPerfTest : public testing::Test {
raster_worker_pool_->Shutdown();
}
- void EndTest() {
- elapsed_ = base::TimeTicks::HighResNow() - start_time_;
- }
-
- void AfterTest(const std::string test_name) {
- // Format matches chrome/test/perf/perf_test.h:PrintResult
- printf("*RESULT %s: %.2f runs/s\n",
- test_name.c_str(),
- num_runs_ / elapsed_.InSecondsF());
- }
-
- bool DidRun() {
- ++num_runs_;
- if (num_runs_ == kWarmupRuns)
- start_time_ = base::TimeTicks::HighResNow();
-
- if (!start_time_.is_null() && (num_runs_ % kTimeCheckInterval) == 0) {
- base::TimeDelta elapsed = base::TimeTicks::HighResNow() - start_time_;
- if (elapsed >= base::TimeDelta::FromMilliseconds(kTimeLimitMillis)) {
- elapsed_ = elapsed;
- return false;
- }
- }
-
- return true;
- }
-
void CreateTasks(RasterWorkerPool::RasterTask::Queue* tasks,
unsigned num_raster_tasks,
unsigned num_image_decode_tasks) {
@@ -194,19 +176,20 @@ class RasterWorkerPoolPerfTest : public testing::Test {
}
}
- void RunBuildTaskGraphTest(const std::string test_name,
+ void RunBuildTaskGraphTest(const std::string& test_name,
unsigned num_raster_tasks,
unsigned num_image_decode_tasks) {
- start_time_ = base::TimeTicks();
- num_runs_ = 0;
+ timer_.Reset();
RasterWorkerPool::RasterTask::Queue tasks;
CreateTasks(&tasks, num_raster_tasks, num_image_decode_tasks);
raster_worker_pool_->SetRasterTasks(&tasks);
do {
raster_worker_pool_->BuildTaskGraph();
- } while (DidRun());
+ timer_.NextLap();
+ } while (!timer_.HasTimeLimitExpired());
- AfterTest(test_name);
+ perf_test::PrintResult("build_task_graph", "", test_name,
+ timer_.LapsPerSecond(), "runs/s", true);
}
protected:
@@ -215,24 +198,22 @@ class RasterWorkerPoolPerfTest : public testing::Test {
static void OnImageDecodeTaskCompleted(bool was_canceled) {}
scoped_ptr<PerfRasterWorkerPool> raster_worker_pool_;
- base::TimeTicks start_time_;
- base::TimeDelta elapsed_;
- int num_runs_;
+ LapTimer timer_;
};
TEST_F(RasterWorkerPoolPerfTest, BuildTaskGraph) {
- RunBuildTaskGraphTest("build_task_graph_10_0", 10, 0);
- RunBuildTaskGraphTest("build_task_graph_100_0", 100, 0);
- RunBuildTaskGraphTest("build_task_graph_1000_0", 1000, 0);
- RunBuildTaskGraphTest("build_task_graph_10_1", 10, 1);
- RunBuildTaskGraphTest("build_task_graph_100_1", 100, 1);
- RunBuildTaskGraphTest("build_task_graph_1000_1", 1000, 1);
- RunBuildTaskGraphTest("build_task_graph_10_4", 10, 4);
- RunBuildTaskGraphTest("build_task_graph_100_4", 100, 4);
- RunBuildTaskGraphTest("build_task_graph_1000_4", 1000, 4);
- RunBuildTaskGraphTest("build_task_graph_10_16", 10, 16);
- RunBuildTaskGraphTest("build_task_graph_100_16", 100, 16);
- RunBuildTaskGraphTest("build_task_graph_1000_16", 1000, 16);
+ RunBuildTaskGraphTest("10_0", 10, 0);
+ RunBuildTaskGraphTest("100_0", 100, 0);
+ RunBuildTaskGraphTest("1000_0", 1000, 0);
+ RunBuildTaskGraphTest("10_1", 10, 1);
+ RunBuildTaskGraphTest("100_1", 100, 1);
+ RunBuildTaskGraphTest("1000_1", 1000, 1);
+ RunBuildTaskGraphTest("10_4", 10, 4);
+ RunBuildTaskGraphTest("100_4", 100, 4);
+ RunBuildTaskGraphTest("1000_4", 1000, 4);
+ RunBuildTaskGraphTest("10_16", 10, 16);
+ RunBuildTaskGraphTest("100_16", 100, 16);
+ RunBuildTaskGraphTest("1000_16", 1000, 16);
}
} // namespace
diff --git a/chromium/cc/resources/raster_worker_pool_unittest.cc b/chromium/cc/resources/raster_worker_pool_unittest.cc
index 3d27c41b353..61cb324f772 100644
--- a/chromium/cc/resources/raster_worker_pool_unittest.cc
+++ b/chromium/cc/resources/raster_worker_pool_unittest.cc
@@ -4,8 +4,10 @@
#include "cc/resources/raster_worker_pool.h"
+#include <limits>
#include <vector>
+#include "cc/debug/test_web_graphics_context_3d.h"
#include "cc/resources/image_raster_worker_pool.h"
#include "cc/resources/picture_pile.h"
#include "cc/resources/picture_pile_impl.h"
@@ -13,6 +15,7 @@
#include "cc/resources/resource_provider.h"
#include "cc/resources/scoped_resource.h"
#include "cc/test/fake_output_surface.h"
+#include "cc/test/fake_output_surface_client.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace cc {
@@ -32,8 +35,10 @@ class TestRasterWorkerPoolTaskImpl : public internal::RasterWorkerPoolTask {
did_raster_(false) {}
// Overridden from internal::WorkerPoolTask:
- virtual bool RunOnWorkerThread(SkDevice* device, unsigned thread_index)
- OVERRIDE {
+ virtual bool RunOnWorkerThread(unsigned thread_index,
+ void* buffer,
+ gfx::Size size,
+ int stride) OVERRIDE {
did_raster_ = true;
return true;
}
@@ -55,12 +60,15 @@ class RasterWorkerPoolTest : public testing::Test,
public RasterWorkerPoolClient {
public:
RasterWorkerPoolTest()
- : output_surface_(FakeOutputSurface::Create3d()),
- resource_provider_(
- ResourceProvider::Create(output_surface_.get(), 0)),
+ : context_provider_(TestContextProvider::Create()),
check_interval_milliseconds_(1),
timeout_seconds_(5),
timed_out_(false) {
+ output_surface_ = FakeOutputSurface::Create3d(context_provider_).Pass();
+ CHECK(output_surface_->BindToClient(&output_surface_client_));
+
+ resource_provider_ =
+ ResourceProvider::Create(output_surface_.get(), 0, false).Pass();
}
virtual ~RasterWorkerPoolTest() {
resource_provider_.reset();
@@ -98,8 +106,11 @@ class RasterWorkerPoolTest : public testing::Test,
raster_worker_pool_ = ImageRasterWorkerPool::Create(
resource_provider(), 1);
} else {
- raster_worker_pool_ = PixelBufferRasterWorkerPool::Create(
- resource_provider(), 1);
+ raster_worker_pool_ =
+ PixelBufferRasterWorkerPool::Create(
+ resource_provider(),
+ 1,
+ std::numeric_limits<size_t>::max());
}
raster_worker_pool_->SetClient(this);
@@ -149,7 +160,7 @@ class RasterWorkerPoolTest : public testing::Test,
scoped_ptr<ScopedResource> resource(
ScopedResource::create(resource_provider()));
- resource->Allocate(size, GL_RGBA, ResourceProvider::TextureUsageAny);
+ resource->Allocate(size, ResourceProvider::TextureUsageAny, RGBA_8888);
const Resource* const_resource = resource.get();
RasterWorkerPool::Task::Set empty;
@@ -190,6 +201,8 @@ class RasterWorkerPoolTest : public testing::Test,
}
protected:
+ scoped_refptr<TestContextProvider> context_provider_;
+ FakeOutputSurfaceClient output_surface_client_;
scoped_ptr<FakeOutputSurface> output_surface_;
scoped_ptr<ResourceProvider> resource_provider_;
scoped_ptr<RasterWorkerPool> raster_worker_pool_;
@@ -259,8 +272,7 @@ class RasterWorkerPoolTestFailedMapResource : public RasterWorkerPoolTest {
// Overridden from RasterWorkerPoolTest:
virtual void BeginTest() OVERRIDE {
- TestWebGraphicsContext3D* context3d =
- static_cast<TestWebGraphicsContext3D*>(output_surface_->context3d());
+ TestWebGraphicsContext3D* context3d = context_provider_->TestContext3d();
context3d->set_times_map_image_chromium_succeeds(0);
context3d->set_times_map_buffer_chromium_succeeds(0);
AppendTask(0u);
diff --git a/chromium/cc/resources/release_callback.h b/chromium/cc/resources/release_callback.h
new file mode 100644
index 00000000000..9433f7f6eb9
--- /dev/null
+++ b/chromium/cc/resources/release_callback.h
@@ -0,0 +1,17 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_RESOURCES_RELEASE_CALLBACK_H_
+#define CC_RESOURCES_RELEASE_CALLBACK_H_
+
+#include "base/callback.h"
+
+namespace cc {
+
+typedef base::Callback<void(unsigned sync_point, bool is_lost)>
+ ReleaseCallback;
+
+} // namespace cc
+
+#endif // CC_RESOURCES_RELEASE_CALLBACK_H_
diff --git a/chromium/cc/resources/resource.cc b/chromium/cc/resources/resource.cc
index 192eaebddc6..c97db7fb872 100644
--- a/chromium/cc/resources/resource.cc
+++ b/chromium/cc/resources/resource.cc
@@ -3,7 +3,6 @@
// found in the LICENSE file.
#include "cc/resources/resource.h"
-#include "third_party/khronos/GLES2/gl2ext.h"
namespace cc {
@@ -14,26 +13,8 @@ size_t Resource::bytes() const {
return MemorySizeBytes(size_, format_);
}
-size_t Resource::BytesPerPixel(GLenum format) {
- size_t components_per_pixel = 0;
- size_t bytes_per_component = 1;
- switch (format) {
- case GL_RGBA:
- case GL_BGRA_EXT:
- components_per_pixel = 4;
- break;
- case GL_LUMINANCE:
- components_per_pixel = 1;
- break;
- default:
- NOTREACHED();
- }
- return components_per_pixel * bytes_per_component;
+size_t Resource::MemorySizeBytes(gfx::Size size, ResourceFormat format) {
+ return ResourceProvider::BytesPerPixel(format) * size.width() * size.height();
}
-size_t Resource::MemorySizeBytes(gfx::Size size, GLenum format) {
- return BytesPerPixel(format) * size.width() * size.height();
-}
-
-
} // namespace cc
diff --git a/chromium/cc/resources/resource.h b/chromium/cc/resources/resource.h
index 1c658223649..d2d104c1370 100644
--- a/chromium/cc/resources/resource.h
+++ b/chromium/cc/resources/resource.h
@@ -15,23 +15,21 @@ namespace cc {
class CC_EXPORT Resource {
public:
Resource() : id_(0) {}
- Resource(unsigned id, gfx::Size size, GLenum format)
+ Resource(unsigned id, gfx::Size size, ResourceFormat format)
: id_(id),
size_(size),
format_(format) {}
ResourceProvider::ResourceId id() const { return id_; }
gfx::Size size() const { return size_; }
- GLenum format() const { return format_; }
-
+ ResourceFormat format() const { return format_; }
size_t bytes() const;
- static size_t BytesPerPixel(GLenum format);
- static size_t MemorySizeBytes(gfx::Size size, GLenum format);
+ static size_t MemorySizeBytes(gfx::Size size, ResourceFormat format);
protected:
void set_id(ResourceProvider::ResourceId id) { id_ = id; }
- void set_dimensions(gfx::Size size, GLenum format) {
+ void set_dimensions(gfx::Size size, ResourceFormat format) {
size_ = size;
format_ = format;
}
@@ -39,7 +37,7 @@ class CC_EXPORT Resource {
private:
ResourceProvider::ResourceId id_;
gfx::Size size_;
- GLenum format_;
+ ResourceFormat format_;
DISALLOW_COPY_AND_ASSIGN(Resource);
};
diff --git a/chromium/cc/resources/resource_format.cc b/chromium/cc/resources/resource_format.cc
new file mode 100644
index 00000000000..5ea3df12598
--- /dev/null
+++ b/chromium/cc/resources/resource_format.cc
@@ -0,0 +1,25 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/resources/resource_format.h"
+
+namespace cc {
+
+SkBitmap::Config SkBitmapConfigFromFormat(ResourceFormat format) {
+ switch (format) {
+ case RGBA_4444:
+ return SkBitmap::kARGB_4444_Config;
+ case RGBA_8888:
+ case BGRA_8888:
+ return SkBitmap::kARGB_8888_Config;
+ case LUMINANCE_8:
+ case RGB_565:
+ NOTREACHED();
+ break;
+ }
+ NOTREACHED();
+ return SkBitmap::kARGB_8888_Config;
+}
+
+} // namespace cc
diff --git a/chromium/cc/resources/resource_format.h b/chromium/cc/resources/resource_format.h
new file mode 100644
index 00000000000..ef83cc02eb7
--- /dev/null
+++ b/chromium/cc/resources/resource_format.h
@@ -0,0 +1,26 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_RESOURCES_RESOURCE_FORMAT_H_
+#define CC_RESOURCES_RESOURCE_FORMAT_H_
+
+#include "base/logging.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+
+namespace cc {
+
+enum ResourceFormat {
+ RGBA_8888,
+ RGBA_4444,
+ BGRA_8888,
+ LUMINANCE_8,
+ RGB_565,
+ RESOURCE_FORMAT_MAX = RGB_565,
+};
+
+SkBitmap::Config SkBitmapConfigFromFormat(ResourceFormat format);
+
+} // namespace cc
+
+#endif // CC_RESOURCES_RESOURCE_FORMAT_H_
diff --git a/chromium/cc/resources/resource_pool.cc b/chromium/cc/resources/resource_pool.cc
index 06bc1fbe8fd..cef86ded178 100644
--- a/chromium/cc/resources/resource_pool.cc
+++ b/chromium/cc/resources/resource_pool.cc
@@ -10,11 +10,12 @@ namespace cc {
ResourcePool::Resource::Resource(cc::ResourceProvider* resource_provider,
gfx::Size size,
- GLenum format)
+ ResourceFormat format)
: cc::Resource(resource_provider->CreateManagedResource(
size,
- format,
- ResourceProvider::TextureUsageAny),
+ GL_CLAMP_TO_EDGE,
+ ResourceProvider::TextureUsageAny,
+ format),
size,
format),
resource_provider_(resource_provider) {
@@ -42,17 +43,13 @@ ResourcePool::~ResourcePool() {
}
scoped_ptr<ResourcePool::Resource> ResourcePool::AcquireResource(
- gfx::Size size, GLenum format) {
+ gfx::Size size, ResourceFormat format) {
for (ResourceList::iterator it = unused_resources_.begin();
it != unused_resources_.end(); ++it) {
Resource* resource = *it;
- // TODO(epenner): It would be nice to DCHECK that this
- // doesn't happen two frames in a row for any resource
- // in this pool.
if (!resource_provider_->CanLockForWrite(resource->id()))
continue;
-
if (resource->size() != size)
continue;
if (resource->format() != format)
@@ -104,10 +101,15 @@ void ResourcePool::ReduceResourceUsage() {
if (!ResourceUsageTooHigh())
break;
- // MRU eviction pattern as least recently used is less likely to
- // be blocked by read lock fence.
- Resource* resource = unused_resources_.back();
- unused_resources_.pop_back();
+ // LRU eviction pattern. Most recently used might be blocked by
+ // a read lock fence but it's still better to evict the least
+ // recently used as it prevents a resource that is hard to reuse
+ // because of unique size from being kept around. Resources that
+ // can't be locked for write might also not be truly free-able.
+ // We can free the resource here but it doesn't mean that the
+ // memory is necessarily returned to the OS.
+ Resource* resource = unused_resources_.front();
+ unused_resources_.pop_front();
memory_usage_bytes_ -= resource->bytes();
unused_memory_usage_bytes_ -= resource->bytes();
--resource_count_;
diff --git a/chromium/cc/resources/resource_pool.h b/chromium/cc/resources/resource_pool.h
index f6bb8652476..21bbb0a70d0 100644
--- a/chromium/cc/resources/resource_pool.h
+++ b/chromium/cc/resources/resource_pool.h
@@ -11,9 +11,9 @@
#include "cc/base/cc_export.h"
#include "cc/output/renderer.h"
#include "cc/resources/resource.h"
+#include "cc/resources/resource_format.h"
namespace cc {
-class ResourceProvider;
class CC_EXPORT ResourcePool {
public:
@@ -21,7 +21,7 @@ class CC_EXPORT ResourcePool {
public:
Resource(ResourceProvider* resource_provider,
gfx::Size size,
- GLenum format);
+ ResourceFormat format);
~Resource();
private:
@@ -36,8 +36,8 @@ class CC_EXPORT ResourcePool {
virtual ~ResourcePool();
- scoped_ptr<ResourcePool::Resource> AcquireResource(gfx::Size size,
- GLenum format);
+ scoped_ptr<ResourcePool::Resource> AcquireResource(
+ gfx::Size size, ResourceFormat format);
void ReleaseResource(scoped_ptr<ResourcePool::Resource>);
void SetResourceUsageLimits(size_t max_memory_usage_bytes,
@@ -46,6 +46,9 @@ class CC_EXPORT ResourcePool {
void ReduceResourceUsage();
+ size_t total_memory_usage_bytes() const {
+ return memory_usage_bytes_;
+ }
size_t acquired_memory_usage_bytes() const {
return memory_usage_bytes_ - unused_memory_usage_bytes_;
}
diff --git a/chromium/cc/resources/resource_provider.cc b/chromium/cc/resources/resource_provider.cc
index 11eaab07a28..ee761d17564 100644
--- a/chromium/cc/resources/resource_provider.cc
+++ b/chromium/cc/resources/resource_provider.cc
@@ -8,12 +8,13 @@
#include <limits>
#include "base/containers/hash_tables.h"
-#include "base/debug/alias.h"
#include "base/stl_util.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
+#include "cc/base/util.h"
#include "cc/output/gl_renderer.h" // For the GLC() macro.
#include "cc/resources/platform_color.h"
+#include "cc/resources/returned_resource.h"
#include "cc/resources/transferable_resource.h"
#include "cc/scheduler/texture_uploader.h"
#include "gpu/GLES2/gl2extchromium.h"
@@ -29,15 +30,21 @@ namespace cc {
namespace {
-GLenum TextureToStorageFormat(GLenum texture_format) {
+// Measured in seconds.
+const double kSoftwareUploadTickRate = 0.000250;
+const double kTextureUploadTickRate = 0.004;
+
+GLenum TextureToStorageFormat(ResourceFormat format) {
GLenum storage_format = GL_RGBA8_OES;
- switch (texture_format) {
- case GL_RGBA:
+ switch (format) {
+ case RGBA_8888:
break;
- case GL_BGRA_EXT:
+ case BGRA_8888:
storage_format = GL_BGRA8_EXT;
break;
- default:
+ case RGBA_4444:
+ case LUMINANCE_8:
+ case RGB_565:
NOTREACHED();
break;
}
@@ -45,37 +52,54 @@ GLenum TextureToStorageFormat(GLenum texture_format) {
return storage_format;
}
-bool IsTextureFormatSupportedForStorage(GLenum format) {
- return (format == GL_RGBA || format == GL_BGRA_EXT);
+bool IsFormatSupportedForStorage(ResourceFormat format) {
+ switch (format) {
+ case RGBA_8888:
+ case BGRA_8888:
+ return true;
+ case RGBA_4444:
+ case LUMINANCE_8:
+ case RGB_565:
+ return false;
+ }
+ return false;
}
-unsigned CreateTextureId(WebGraphicsContext3D* context3d) {
- unsigned texture_id = 0;
- GLC(context3d, texture_id = context3d->createTexture());
- GLC(context3d, context3d->bindTexture(GL_TEXTURE_2D, texture_id));
- GLC(context3d, context3d->texParameteri(
- GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
- GLC(context3d, context3d->texParameteri(
- GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
- GLC(context3d, context3d->texParameteri(
- GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
- GLC(context3d, context3d->texParameteri(
- GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
- return texture_id;
-}
+class ScopedSetActiveTexture {
+ public:
+ ScopedSetActiveTexture(WebGraphicsContext3D* context3d, GLenum unit)
+ : context3d_(context3d), unit_(unit) {
+ DCHECK_EQ(GL_TEXTURE0, ResourceProvider::GetActiveTextureUnit(context3d_));
+
+ if (unit_ != GL_TEXTURE0)
+ GLC(context3d_, context3d_->activeTexture(unit_));
+ }
+
+ ~ScopedSetActiveTexture() {
+ // Active unit being GL_TEXTURE0 is effectively the ground state.
+ if (unit_ != GL_TEXTURE0)
+ GLC(context3d_, context3d_->activeTexture(GL_TEXTURE0));
+ }
+
+ private:
+ WebGraphicsContext3D* context3d_;
+ GLenum unit_;
+};
} // namespace
ResourceProvider::Resource::Resource()
- : gl_id(0),
+ : child_id(0),
+ gl_id(0),
gl_pixel_buffer_id(0),
gl_upload_query_id(0),
pixels(NULL),
pixel_buffer(NULL),
lock_for_read_count(0),
+ imported_count(0),
+ exported_count(0),
locked_for_write(false),
external(false),
- exported(false),
marked_for_deletion(false),
pending_set_pixels(false),
set_pixels_completion_forced(false),
@@ -83,31 +107,37 @@ ResourceProvider::Resource::Resource()
enable_read_lock_fences(false),
read_lock_fence(NULL),
size(),
- format(0),
+ original_filter(0),
filter(0),
+ target(0),
image_id(0),
texture_pool(0),
+ wrap_mode(0),
+ lost(false),
hint(TextureUsageAny),
- type(static_cast<ResourceType>(0)) {}
+ type(static_cast<ResourceType>(0)),
+ format(RGBA_8888) {}
ResourceProvider::Resource::~Resource() {}
-ResourceProvider::Resource::Resource(
- unsigned texture_id,
- gfx::Size size,
- GLenum format,
- GLenum filter,
- GLenum texture_pool,
- TextureUsageHint hint)
- : gl_id(texture_id),
+ResourceProvider::Resource::Resource(unsigned texture_id,
+ gfx::Size size,
+ GLenum filter,
+ GLenum texture_pool,
+ GLint wrap_mode,
+ TextureUsageHint hint,
+ ResourceFormat format)
+ : child_id(0),
+ gl_id(texture_id),
gl_pixel_buffer_id(0),
gl_upload_query_id(0),
pixels(NULL),
pixel_buffer(NULL),
lock_for_read_count(0),
+ imported_count(0),
+ exported_count(0),
locked_for_write(false),
external(false),
- exported(false),
marked_for_deletion(false),
pending_set_pixels(false),
set_pixels_completion_forced(false),
@@ -115,24 +145,34 @@ ResourceProvider::Resource::Resource(
enable_read_lock_fences(false),
read_lock_fence(NULL),
size(size),
- format(format),
+ original_filter(filter),
filter(filter),
+ target(0),
image_id(0),
texture_pool(texture_pool),
+ wrap_mode(wrap_mode),
+ lost(false),
hint(hint),
- type(GLTexture) {}
+ type(GLTexture),
+ format(format) {
+ DCHECK(wrap_mode == GL_CLAMP_TO_EDGE || wrap_mode == GL_REPEAT);
+}
-ResourceProvider::Resource::Resource(
- uint8_t* pixels, gfx::Size size, GLenum format, GLenum filter)
- : gl_id(0),
+ResourceProvider::Resource::Resource(uint8_t* pixels,
+ gfx::Size size,
+ GLenum filter,
+ GLint wrap_mode)
+ : child_id(0),
+ gl_id(0),
gl_pixel_buffer_id(0),
gl_upload_query_id(0),
pixels(pixels),
pixel_buffer(NULL),
lock_for_read_count(0),
+ imported_count(0),
+ exported_count(0),
locked_for_write(false),
external(false),
- exported(false),
marked_for_deletion(false),
pending_set_pixels(false),
set_pixels_completion_forced(false),
@@ -140,12 +180,18 @@ ResourceProvider::Resource::Resource(
enable_read_lock_fences(false),
read_lock_fence(NULL),
size(size),
- format(format),
+ original_filter(filter),
filter(filter),
+ target(0),
image_id(0),
texture_pool(0),
+ wrap_mode(wrap_mode),
+ lost(false),
hint(TextureUsageAny),
- type(Bitmap) {}
+ type(Bitmap),
+ format(RGBA_8888) {
+ DCHECK(wrap_mode == GL_CLAMP_TO_EDGE || wrap_mode == GL_REPEAT);
+}
ResourceProvider::Child::Child() {}
@@ -153,12 +199,15 @@ ResourceProvider::Child::~Child() {}
scoped_ptr<ResourceProvider> ResourceProvider::Create(
OutputSurface* output_surface,
- int highp_threshold_min) {
+ int highp_threshold_min,
+ bool use_rgba_4444_texture_format) {
scoped_ptr<ResourceProvider> resource_provider(
- new ResourceProvider(output_surface, highp_threshold_min));
+ new ResourceProvider(output_surface,
+ highp_threshold_min,
+ use_rgba_4444_texture_format));
bool success = false;
- if (output_surface->context3d()) {
+ if (resource_provider->Context3d()) {
success = resource_provider->InitializeGL();
} else {
resource_provider->InitializeSoftware();
@@ -173,34 +222,40 @@ scoped_ptr<ResourceProvider> ResourceProvider::Create(
}
ResourceProvider::~ResourceProvider() {
+ while (!children_.empty())
+ DestroyChild(children_.begin()->first);
while (!resources_.empty())
DeleteResourceInternal(resources_.begin(), ForShutdown);
CleanUpGLIfNeeded();
}
-WebGraphicsContext3D* ResourceProvider::GraphicsContext3D() {
- DCHECK(thread_checker_.CalledOnValidThread());
- return output_surface_->context3d();
+bool ResourceProvider::InUseByConsumer(ResourceId id) {
+ Resource* resource = GetResource(id);
+ return resource->lock_for_read_count > 0 || resource->exported_count > 0 ||
+ resource->lost;
}
-bool ResourceProvider::InUseByConsumer(ResourceId id) {
- DCHECK(thread_checker_.CalledOnValidThread());
- ResourceMap::iterator it = resources_.find(id);
- CHECK(it != resources_.end());
- Resource* resource = &it->second;
- return !!resource->lock_for_read_count || resource->exported;
+bool ResourceProvider::IsLost(ResourceId id) {
+ Resource* resource = GetResource(id);
+ return resource->lost;
}
ResourceProvider::ResourceId ResourceProvider::CreateResource(
- gfx::Size size, GLenum format, TextureUsageHint hint) {
+ gfx::Size size,
+ GLint wrap_mode,
+ TextureUsageHint hint,
+ ResourceFormat format) {
DCHECK(!size.IsEmpty());
switch (default_resource_type_) {
case GLTexture:
- return CreateGLTexture(
- size, format, GL_TEXTURE_POOL_UNMANAGED_CHROMIUM, hint);
+ return CreateGLTexture(size,
+ GL_TEXTURE_POOL_UNMANAGED_CHROMIUM,
+ wrap_mode,
+ hint,
+ format);
case Bitmap:
- DCHECK(format == GL_RGBA);
+ DCHECK_EQ(RGBA_8888, format);
return CreateBitmap(size);
case InvalidType:
break;
@@ -211,14 +266,20 @@ ResourceProvider::ResourceId ResourceProvider::CreateResource(
}
ResourceProvider::ResourceId ResourceProvider::CreateManagedResource(
- gfx::Size size, GLenum format, TextureUsageHint hint) {
+ gfx::Size size,
+ GLint wrap_mode,
+ TextureUsageHint hint,
+ ResourceFormat format) {
DCHECK(!size.IsEmpty());
switch (default_resource_type_) {
case GLTexture:
- return CreateGLTexture(
- size, format, GL_TEXTURE_POOL_MANAGED_CHROMIUM, hint);
+ return CreateGLTexture(size,
+ GL_TEXTURE_POOL_MANAGED_CHROMIUM,
+ wrap_mode,
+ hint,
+ format);
case Bitmap:
- DCHECK(format == GL_RGBA);
+ DCHECK_EQ(RGBA_8888, format);
return CreateBitmap(size);
case InvalidType:
break;
@@ -229,13 +290,17 @@ ResourceProvider::ResourceId ResourceProvider::CreateManagedResource(
}
ResourceProvider::ResourceId ResourceProvider::CreateGLTexture(
- gfx::Size size, GLenum format, GLenum texture_pool, TextureUsageHint hint) {
+ gfx::Size size,
+ GLenum texture_pool,
+ GLint wrap_mode,
+ TextureUsageHint hint,
+ ResourceFormat format) {
DCHECK_LE(size.width(), max_texture_size_);
DCHECK_LE(size.height(), max_texture_size_);
DCHECK(thread_checker_.CalledOnValidThread());
ResourceId id = next_id_++;
- Resource resource(0, size, format, GL_LINEAR, texture_pool, hint);
+ Resource resource(0, size, GL_LINEAR, texture_pool, wrap_mode, hint, format);
resource.allocated = false;
resources_[id] = resource;
return id;
@@ -247,7 +312,7 @@ ResourceProvider::ResourceId ResourceProvider::CreateBitmap(gfx::Size size) {
uint8_t* pixels = new uint8_t[4 * size.GetArea()];
ResourceId id = next_id_++;
- Resource resource(pixels, size, GL_RGBA, GL_LINEAR);
+ Resource resource(pixels, size, GL_LINEAR, GL_CLAMP_TO_EDGE);
resource.allocated = true;
resources_[id] = resource;
return id;
@@ -259,7 +324,7 @@ ResourceProvider::CreateResourceFromExternalTexture(
unsigned texture_id) {
DCHECK(thread_checker_.CalledOnValidThread());
- WebGraphicsContext3D* context3d = output_surface_->context3d();
+ WebGraphicsContext3D* context3d = Context3d();
DCHECK(context3d);
GLC(context3d, context3d->bindTexture(texture_target, texture_id));
GLC(context3d, context3d->texParameteri(
@@ -272,7 +337,13 @@ ResourceProvider::CreateResourceFromExternalTexture(
texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
ResourceId id = next_id_++;
- Resource resource(texture_id, gfx::Size(), 0, GL_LINEAR, 0, TextureUsageAny);
+ Resource resource(texture_id,
+ gfx::Size(),
+ GL_LINEAR,
+ 0,
+ GL_CLAMP_TO_EDGE,
+ TextureUsageAny,
+ RGBA_8888);
resource.external = true;
resource.allocated = true;
resources_[id] = resource;
@@ -280,25 +351,35 @@ ResourceProvider::CreateResourceFromExternalTexture(
}
ResourceProvider::ResourceId ResourceProvider::CreateResourceFromTextureMailbox(
- const TextureMailbox& mailbox) {
+ const TextureMailbox& mailbox,
+ scoped_ptr<SingleReleaseCallback> release_callback) {
DCHECK(thread_checker_.CalledOnValidThread());
// Just store the information. Mailbox will be consumed in LockForRead().
ResourceId id = next_id_++;
DCHECK(mailbox.IsValid());
Resource& resource = resources_[id];
if (mailbox.IsTexture()) {
- resource = Resource(0, gfx::Size(), 0, GL_LINEAR, 0, TextureUsageAny);
+ resource = Resource(0,
+ gfx::Size(),
+ GL_LINEAR,
+ 0,
+ GL_CLAMP_TO_EDGE,
+ TextureUsageAny,
+ RGBA_8888);
} else {
DCHECK(mailbox.IsSharedMemory());
base::SharedMemory* shared_memory = mailbox.shared_memory();
DCHECK(shared_memory->memory());
uint8_t* pixels = reinterpret_cast<uint8_t*>(shared_memory->memory());
- resource = Resource(pixels, mailbox.shared_memory_size(),
- GL_RGBA, GL_LINEAR);
+ resource = Resource(
+ pixels, mailbox.shared_memory_size(), GL_LINEAR, GL_CLAMP_TO_EDGE);
}
resource.external = true;
resource.allocated = true;
resource.mailbox = mailbox;
+ resource.release_callback =
+ base::Bind(&SingleReleaseCallback::Run,
+ base::Owned(release_callback.release()));
return id;
}
@@ -309,9 +390,10 @@ void ResourceProvider::DeleteResource(ResourceId id) {
Resource* resource = &it->second;
DCHECK(!resource->lock_for_read_count);
DCHECK(!resource->marked_for_deletion);
+ DCHECK_EQ(resource->imported_count, 0);
DCHECK(resource->pending_set_pixels || !resource->locked_for_write);
- if (resource->exported) {
+ if (resource->exported_count > 0) {
resource->marked_for_deletion = true;
return;
} else {
@@ -322,37 +404,37 @@ void ResourceProvider::DeleteResource(ResourceId id) {
void ResourceProvider::DeleteResourceInternal(ResourceMap::iterator it,
DeleteStyle style) {
Resource* resource = &it->second;
- bool lost_resource = lost_output_surface_;
+ bool lost_resource = lost_output_surface_ || resource->lost;
- DCHECK(!resource->exported || style != Normal);
- if (style == ForShutdown && resource->exported)
+ DCHECK(resource->exported_count == 0 || style != Normal);
+ if (style == ForShutdown && resource->exported_count > 0)
lost_resource = true;
if (resource->image_id) {
- WebGraphicsContext3D* context3d = output_surface_->context3d();
+ WebGraphicsContext3D* context3d = Context3d();
DCHECK(context3d);
GLC(context3d, context3d->destroyImageCHROMIUM(resource->image_id));
}
if (resource->gl_id && !resource->external) {
- WebGraphicsContext3D* context3d = output_surface_->context3d();
+ WebGraphicsContext3D* context3d = Context3d();
DCHECK(context3d);
GLC(context3d, context3d->deleteTexture(resource->gl_id));
}
if (resource->gl_upload_query_id) {
- WebGraphicsContext3D* context3d = output_surface_->context3d();
+ WebGraphicsContext3D* context3d = Context3d();
DCHECK(context3d);
GLC(context3d, context3d->deleteQueryEXT(resource->gl_upload_query_id));
}
if (resource->gl_pixel_buffer_id) {
- WebGraphicsContext3D* context3d = output_surface_->context3d();
+ WebGraphicsContext3D* context3d = Context3d();
DCHECK(context3d);
GLC(context3d, context3d->deleteBuffer(resource->gl_pixel_buffer_id));
}
if (resource->mailbox.IsValid() && resource->external) {
unsigned sync_point = resource->mailbox.sync_point();
if (resource->mailbox.IsTexture()) {
- WebGraphicsContext3D* context3d = output_surface_->context3d();
+ WebGraphicsContext3D* context3d = Context3d();
DCHECK(context3d);
if (resource->gl_id)
GLC(context3d, context3d->deleteTexture(resource->gl_id));
@@ -366,7 +448,7 @@ void ResourceProvider::DeleteResourceInternal(ResourceMap::iterator it,
resource->pixels = NULL;
}
}
- resource->mailbox.RunReleaseCallback(sync_point, lost_resource);
+ resource->release_callback.Run(sync_point, lost_resource);
}
if (resource->pixels)
delete[] resource->pixels;
@@ -378,10 +460,7 @@ void ResourceProvider::DeleteResourceInternal(ResourceMap::iterator it,
ResourceProvider::ResourceType ResourceProvider::GetResourceType(
ResourceId id) {
- ResourceMap::iterator it = resources_.find(id);
- CHECK(it != resources_.end());
- Resource* resource = &it->second;
- return resource->type;
+ return GetResource(id)->type;
}
void ResourceProvider::SetPixels(ResourceId id,
@@ -389,20 +468,17 @@ void ResourceProvider::SetPixels(ResourceId id,
gfx::Rect image_rect,
gfx::Rect source_rect,
gfx::Vector2d dest_offset) {
- DCHECK(thread_checker_.CalledOnValidThread());
- ResourceMap::iterator it = resources_.find(id);
- CHECK(it != resources_.end());
- Resource* resource = &it->second;
+ Resource* resource = GetResource(id);
DCHECK(!resource->locked_for_write);
DCHECK(!resource->lock_for_read_count);
DCHECK(!resource->external);
- DCHECK(!resource->exported);
+ DCHECK_EQ(resource->exported_count, 0);
DCHECK(ReadLockFenceHasPassed(resource));
LazyAllocate(resource);
if (resource->gl_id) {
DCHECK(!resource->pending_set_pixels);
- WebGraphicsContext3D* context3d = output_surface_->context3d();
+ WebGraphicsContext3D* context3d = Context3d();
DCHECK(context3d);
DCHECK(texture_uploader_.get());
context3d->bindTexture(GL_TEXTURE_2D, resource->gl_id);
@@ -416,7 +492,7 @@ void ResourceProvider::SetPixels(ResourceId id,
if (resource->pixels) {
DCHECK(resource->allocated);
- DCHECK(resource->format == GL_RGBA);
+ DCHECK_EQ(RGBA_8888, resource->format);
SkBitmap src_full;
src_full.setConfig(
SkBitmap::kARGB_8888_Config, image_rect.width(), image_rect.height());
@@ -470,23 +546,32 @@ void ResourceProvider::ReleaseCachedData() {
texture_uploader_->ReleaseCachedQueries();
}
+base::TimeDelta ResourceProvider::TextureUpdateTickRate() {
+ // Software resource uploads happen on impl thread, so don't bother batching
+ // them up and trying to wait for them to complete.
+ double rate =
+ texture_uploader_ ? kTextureUploadTickRate : kSoftwareUploadTickRate;
+ return base::TimeDelta::FromMicroseconds(base::Time::kMicrosecondsPerSecond *
+ rate);
+}
+
void ResourceProvider::Flush() {
DCHECK(thread_checker_.CalledOnValidThread());
- WebGraphicsContext3D* context3d = output_surface_->context3d();
+ WebGraphicsContext3D* context3d = Context3d();
if (context3d)
context3d->flush();
}
void ResourceProvider::Finish() {
DCHECK(thread_checker_.CalledOnValidThread());
- WebGraphicsContext3D* context3d = output_surface_->context3d();
+ WebGraphicsContext3D* context3d = Context3d();
if (context3d)
context3d->finish();
}
bool ResourceProvider::ShallowFlushIfSupported() {
DCHECK(thread_checker_.CalledOnValidThread());
- WebGraphicsContext3D* context3d = output_surface_->context3d();
+ WebGraphicsContext3D* context3d = Context3d();
if (!context3d || !use_shallow_flush_)
return false;
@@ -494,16 +579,20 @@ bool ResourceProvider::ShallowFlushIfSupported() {
return true;
}
-const ResourceProvider::Resource* ResourceProvider::LockForRead(ResourceId id) {
+ResourceProvider::Resource* ResourceProvider::GetResource(ResourceId id) {
DCHECK(thread_checker_.CalledOnValidThread());
ResourceMap::iterator it = resources_.find(id);
CHECK(it != resources_.end());
- Resource* resource = &it->second;
+ return &it->second;
+}
+
+const ResourceProvider::Resource* ResourceProvider::LockForRead(ResourceId id) {
+ Resource* resource = GetResource(id);
DCHECK(!resource->locked_for_write ||
resource->set_pixels_completion_forced) <<
"locked for write: " << resource->locked_for_write <<
" pixels completion forced: " << resource->set_pixels_completion_forced;
- DCHECK(!resource->exported);
+ DCHECK_EQ(resource->exported_count, 0);
// Uninitialized! Call SetPixels or LockForWrite first.
DCHECK(resource->allocated);
@@ -511,7 +600,7 @@ const ResourceProvider::Resource* ResourceProvider::LockForRead(ResourceId id) {
if (resource->external) {
if (!resource->gl_id && resource->mailbox.IsTexture()) {
- WebGraphicsContext3D* context3d = output_surface_->context3d();
+ WebGraphicsContext3D* context3d = Context3d();
DCHECK(context3d);
if (resource->mailbox.sync_point()) {
GLC(context3d,
@@ -534,25 +623,20 @@ const ResourceProvider::Resource* ResourceProvider::LockForRead(ResourceId id) {
}
void ResourceProvider::UnlockForRead(ResourceId id) {
- DCHECK(thread_checker_.CalledOnValidThread());
- ResourceMap::iterator it = resources_.find(id);
- CHECK(it != resources_.end());
- Resource* resource = &it->second;
+ Resource* resource = GetResource(id);
DCHECK_GT(resource->lock_for_read_count, 0);
- DCHECK(!resource->exported);
+ DCHECK_EQ(resource->exported_count, 0);
resource->lock_for_read_count--;
}
const ResourceProvider::Resource* ResourceProvider::LockForWrite(
ResourceId id) {
- DCHECK(thread_checker_.CalledOnValidThread());
- ResourceMap::iterator it = resources_.find(id);
- CHECK(it != resources_.end());
- Resource* resource = &it->second;
+ Resource* resource = GetResource(id);
DCHECK(!resource->locked_for_write);
DCHECK(!resource->lock_for_read_count);
- DCHECK(!resource->exported);
+ DCHECK_EQ(resource->exported_count, 0);
DCHECK(!resource->external);
+ DCHECK(!resource->lost);
DCHECK(ReadLockFenceHasPassed(resource));
LazyAllocate(resource);
@@ -561,24 +645,16 @@ const ResourceProvider::Resource* ResourceProvider::LockForWrite(
}
bool ResourceProvider::CanLockForWrite(ResourceId id) {
- DCHECK(thread_checker_.CalledOnValidThread());
- ResourceMap::iterator it = resources_.find(id);
- CHECK(it != resources_.end());
- Resource* resource = &it->second;
- return !resource->locked_for_write &&
- !resource->lock_for_read_count &&
- !resource->exported &&
- !resource->external &&
- ReadLockFenceHasPassed(resource);
+ Resource* resource = GetResource(id);
+ return !resource->locked_for_write && !resource->lock_for_read_count &&
+ !resource->exported_count && !resource->external && !resource->lost &&
+ ReadLockFenceHasPassed(resource);
}
void ResourceProvider::UnlockForWrite(ResourceId id) {
- DCHECK(thread_checker_.CalledOnValidThread());
- ResourceMap::iterator it = resources_.find(id);
- CHECK(it != resources_.end());
- Resource* resource = &it->second;
+ Resource* resource = GetResource(id);
DCHECK(resource->locked_for_write);
- DCHECK(!resource->exported);
+ DCHECK_EQ(resource->exported_count, 0);
DCHECK(!resource->external);
resource->locked_for_write = false;
}
@@ -639,7 +715,7 @@ ResourceProvider::ScopedWriteLockGL::~ScopedWriteLockGL() {
void ResourceProvider::PopulateSkBitmapWithResource(
SkBitmap* sk_bitmap, const Resource* resource) {
DCHECK(resource->pixels);
- DCHECK(resource->format == GL_RGBA);
+ DCHECK_EQ(RGBA_8888, resource->format);
sk_bitmap->setConfig(SkBitmap::kARGB_8888_Config,
resource->size.width(),
resource->size.height());
@@ -674,7 +750,8 @@ ResourceProvider::ScopedWriteLockSoftware::~ScopedWriteLockSoftware() {
}
ResourceProvider::ResourceProvider(OutputSurface* output_surface,
- int highp_threshold_min)
+ int highp_threshold_min,
+ bool use_rgba_4444_texture_format)
: output_surface_(output_surface),
lost_output_surface_(false),
highp_threshold_min_(highp_threshold_min),
@@ -685,7 +762,10 @@ ResourceProvider::ResourceProvider(OutputSurface* output_surface,
use_texture_usage_hint_(false),
use_shallow_flush_(false),
max_texture_size_(0),
- best_texture_format_(0) {}
+ best_texture_format_(RGBA_8888),
+ use_rgba_4444_texture_format_(use_rgba_4444_texture_format) {
+ DCHECK(output_surface_->HasClient());
+}
void ResourceProvider::InitializeSoftware() {
DCHECK(thread_checker_.CalledOnValidThread());
@@ -695,7 +775,7 @@ void ResourceProvider::InitializeSoftware() {
default_resource_type_ = Bitmap;
max_texture_size_ = INT_MAX / 2;
- best_texture_format_ = GL_RGBA;
+ best_texture_format_ = RGBA_8888;
}
bool ResourceProvider::InitializeGL() {
@@ -703,7 +783,7 @@ bool ResourceProvider::InitializeGL() {
DCHECK(!texture_uploader_);
DCHECK_NE(GLTexture, default_resource_type_);
- WebGraphicsContext3D* context3d = output_surface_->context3d();
+ WebGraphicsContext3D* context3d = Context3d();
DCHECK(context3d);
if (!context3d->makeContextCurrent())
@@ -711,24 +791,14 @@ bool ResourceProvider::InitializeGL() {
default_resource_type_ = GLTexture;
- std::string extensions_string =
- UTF16ToASCII(context3d->getString(GL_EXTENSIONS));
- std::vector<std::string> extensions;
- base::SplitString(extensions_string, ' ', &extensions);
- bool use_map_sub = false;
- bool use_bgra = false;
- for (size_t i = 0; i < extensions.size(); ++i) {
- if (extensions[i] == "GL_EXT_texture_storage")
- use_texture_storage_ext_ = true;
- else if (extensions[i] == "GL_ANGLE_texture_usage")
- use_texture_usage_hint_ = true;
- else if (extensions[i] == "GL_CHROMIUM_map_sub")
- use_map_sub = true;
- else if (extensions[i] == "GL_CHROMIUM_shallow_flush")
- use_shallow_flush_ = true;
- else if (extensions[i] == "GL_EXT_texture_format_BGRA8888")
- use_bgra = true;
- }
+ const ContextProvider::Capabilities& caps =
+ output_surface_->context_provider()->ContextCapabilities();
+
+ bool use_map_sub = caps.map_sub;
+ bool use_bgra = caps.texture_format_bgra8888;
+ use_texture_storage_ext_ = caps.texture_storage;
+ use_shallow_flush_ = caps.shallow_flush;
+ use_texture_usage_hint_ = caps.texture_usage;
texture_uploader_ =
TextureUploader::Create(context3d, use_map_sub, use_shallow_flush_);
@@ -740,7 +810,7 @@ bool ResourceProvider::InitializeGL() {
}
void ResourceProvider::CleanUpGLIfNeeded() {
- WebGraphicsContext3D* context3d = output_surface_->context3d();
+ WebGraphicsContext3D* context3d = Context3d();
if (default_resource_type_ != GLTexture) {
// We are not in GL mode, but double check before returning.
DCHECK(!context3d);
@@ -754,9 +824,12 @@ void ResourceProvider::CleanUpGLIfNeeded() {
Finish();
}
-int ResourceProvider::CreateChild() {
+int ResourceProvider::CreateChild(const ReturnCallback& return_callback) {
DCHECK(thread_checker_.CalledOnValidThread());
+
Child child_info;
+ child_info.return_callback = return_callback;
+
int child = next_child_++;
children_[child] = child_info;
return child;
@@ -764,13 +837,26 @@ int ResourceProvider::CreateChild() {
void ResourceProvider::DestroyChild(int child_id) {
DCHECK(thread_checker_.CalledOnValidThread());
+
ChildMap::iterator it = children_.find(child_id);
DCHECK(it != children_.end());
Child& child = it->second;
+
+ ResourceIdArray resources_for_child;
+
for (ResourceIdMap::iterator child_it = child.child_to_parent_map.begin();
child_it != child.child_to_parent_map.end();
- ++child_it)
- DeleteResource(child_it->second);
+ ++child_it) {
+ ResourceId id = child_it->second;
+ resources_for_child.push_back(id);
+ }
+
+ // If the child is going away, don't consider any resources in use.
+ child.in_use_resources.clear();
+
+ DeleteAndReturnUnusedResourcesToChild(
+ &child, ForShutdown, resources_for_child);
+
children_.erase(it);
}
@@ -785,60 +871,21 @@ const ResourceProvider::ResourceIdMap& ResourceProvider::GetChildToParentMap(
void ResourceProvider::PrepareSendToParent(const ResourceIdArray& resources,
TransferableResourceArray* list) {
DCHECK(thread_checker_.CalledOnValidThread());
- WebGraphicsContext3D* context3d = output_surface_->context3d();
- if (!context3d || !context3d->makeContextCurrent()) {
- // TODO(skaslev): Implement this path for software compositing.
- return;
- }
- bool need_sync_point = false;
- for (ResourceIdArray::const_iterator it = resources.begin();
- it != resources.end();
- ++it) {
- TransferableResource resource;
- if (TransferResource(context3d, *it, &resource)) {
- if (!resource.sync_point)
- need_sync_point = true;
- resources_.find(*it)->second.exported = true;
- list->push_back(resource);
- }
- }
- if (need_sync_point) {
- unsigned int sync_point = context3d->insertSyncPoint();
- for (TransferableResourceArray::iterator it = list->begin();
- it != list->end();
- ++it) {
- if (!it->sync_point)
- it->sync_point = sync_point;
- }
- }
-}
-
-void ResourceProvider::PrepareSendToChild(int child,
- const ResourceIdArray& resources,
- TransferableResourceArray* list) {
- DCHECK(thread_checker_.CalledOnValidThread());
- WebGraphicsContext3D* context3d = output_surface_->context3d();
+ WebGraphicsContext3D* context3d = Context3d();
if (!context3d || !context3d->makeContextCurrent()) {
// TODO(skaslev): Implement this path for software compositing.
return;
}
- Child& child_info = children_.find(child)->second;
bool need_sync_point = false;
for (ResourceIdArray::const_iterator it = resources.begin();
it != resources.end();
++it) {
TransferableResource resource;
- if (!TransferResource(context3d, *it, &resource))
- NOTREACHED();
+ TransferResource(context3d, *it, &resource);
if (!resource.sync_point)
need_sync_point = true;
- DCHECK(child_info.parent_to_child_map.find(*it) !=
- child_info.parent_to_child_map.end());
- resource.id = child_info.parent_to_child_map[*it];
- child_info.parent_to_child_map.erase(*it);
- child_info.child_to_parent_map.erase(resource.id);
+ ++resources_.find(*it)->second.exported_count;
list->push_back(resource);
- DeleteResource(*it);
}
if (need_sync_point) {
unsigned int sync_point = context3d->insertSyncPoint();
@@ -854,7 +901,7 @@ void ResourceProvider::PrepareSendToChild(int child,
void ResourceProvider::ReceiveFromChild(
int child, const TransferableResourceArray& resources) {
DCHECK(thread_checker_.CalledOnValidThread());
- WebGraphicsContext3D* context3d = output_surface_->context3d();
+ WebGraphicsContext3D* context3d = Context3d();
if (!context3d || !context3d->makeContextCurrent()) {
// TODO(skaslev): Implement this path for software compositing.
return;
@@ -863,6 +910,12 @@ void ResourceProvider::ReceiveFromChild(
for (TransferableResourceArray::const_iterator it = resources.begin();
it != resources.end();
++it) {
+ ResourceIdMap::iterator resource_in_map_it =
+ child_info.child_to_parent_map.find(it->id);
+ if (resource_in_map_it != child_info.child_to_parent_map.end()) {
+ resources_[resource_in_map_it->second].imported_count++;
+ continue;
+ }
unsigned texture_id;
// NOTE: If the parent is a browser and the child a renderer, the parent
// is not supposed to have its context wait, because that could induce
@@ -876,62 +929,162 @@ void ResourceProvider::ReceiveFromChild(
GLC(context3d, context3d->bindTexture(GL_TEXTURE_2D, texture_id));
GLC(context3d, context3d->consumeTextureCHROMIUM(GL_TEXTURE_2D,
it->mailbox.name));
- ResourceId id = next_id_++;
- Resource resource(
- texture_id, it->size, it->format, it->filter, 0, TextureUsageAny);
+ ResourceId local_id = next_id_++;
+ Resource resource(texture_id,
+ it->size,
+ it->filter,
+ 0,
+ GL_CLAMP_TO_EDGE,
+ TextureUsageAny,
+ it->format);
resource.mailbox.SetName(it->mailbox);
+ resource.child_id = child;
// Don't allocate a texture for a child.
resource.allocated = true;
- resources_[id] = resource;
- child_info.parent_to_child_map[id] = it->id;
- child_info.child_to_parent_map[it->id] = id;
+ resource.imported_count = 1;
+ resources_[local_id] = resource;
+ child_info.parent_to_child_map[local_id] = it->id;
+ child_info.child_to_parent_map[it->id] = local_id;
}
}
-void ResourceProvider::ReceiveFromParent(
- const TransferableResourceArray& resources) {
+void ResourceProvider::DeclareUsedResourcesFromChild(
+ int child,
+ const ResourceIdArray& resources_from_child) {
DCHECK(thread_checker_.CalledOnValidThread());
- WebGraphicsContext3D* context3d = output_surface_->context3d();
+
+ Child& child_info = children_.find(child)->second;
+ child_info.in_use_resources.clear();
+
+ for (size_t i = 0; i < resources_from_child.size(); ++i) {
+ ResourceIdMap::iterator it =
+ child_info.child_to_parent_map.find(resources_from_child[i]);
+ DCHECK(it != child_info.child_to_parent_map.end());
+
+ ResourceId local_id = it->second;
+ child_info.in_use_resources.insert(local_id);
+ }
+
+ ResourceIdArray unused;
+ for (ResourceIdMap::iterator it = child_info.child_to_parent_map.begin();
+ it != child_info.child_to_parent_map.end();
+ ++it) {
+ ResourceId local_id = it->second;
+ bool resource_is_in_use = child_info.in_use_resources.count(local_id) > 0;
+ if (!resource_is_in_use)
+ unused.push_back(local_id);
+ }
+ DeleteAndReturnUnusedResourcesToChild(&child_info, Normal, unused);
+}
+
+// static
+bool ResourceProvider::CompareResourceMapIteratorsByChildId(
+ const std::pair<ReturnedResource, ResourceMap::iterator>& a,
+ const std::pair<ReturnedResource, ResourceMap::iterator>& b) {
+ const ResourceMap::iterator& a_it = a.second;
+ const ResourceMap::iterator& b_it = b.second;
+ const Resource& a_resource = a_it->second;
+ const Resource& b_resource = b_it->second;
+ return a_resource.child_id < b_resource.child_id;
+}
+
+void ResourceProvider::ReceiveReturnsFromParent(
+ const ReturnedResourceArray& resources) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ WebGraphicsContext3D* context3d = Context3d();
if (!context3d || !context3d->makeContextCurrent()) {
// TODO(skaslev): Implement this path for software compositing.
return;
}
- for (TransferableResourceArray::const_iterator it = resources.begin();
+
+ int child_id = 0;
+ Child* child_info = NULL;
+ ResourceIdArray resources_for_child;
+
+ std::vector<std::pair<ReturnedResource, ResourceMap::iterator> >
+ sorted_resources;
+
+ for (ReturnedResourceArray::const_iterator it = resources.begin();
it != resources.end();
++it) {
- ResourceMap::iterator map_iterator = resources_.find(it->id);
- DCHECK(map_iterator != resources_.end());
+ ResourceId local_id = it->id;
+ ResourceMap::iterator map_iterator = resources_.find(local_id);
+
+ // Resource was already lost (e.g. it belonged to a child that was
+ // destroyed).
+ if (map_iterator == resources_.end())
+ continue;
+
+ sorted_resources.push_back(
+ std::pair<ReturnedResource, ResourceMap::iterator>(*it, map_iterator));
+ }
+
+ std::sort(sorted_resources.begin(),
+ sorted_resources.end(),
+ CompareResourceMapIteratorsByChildId);
+
+ for (size_t i = 0; i < sorted_resources.size(); ++i) {
+ ReturnedResource& returned = sorted_resources[i].first;
+ ResourceMap::iterator& map_iterator = sorted_resources[i].second;
+ ResourceId local_id = map_iterator->first;
Resource* resource = &map_iterator->second;
- DCHECK(resource->exported);
- resource->exported = false;
- resource->filter = it->filter;
- DCHECK(resource->mailbox.ContainsMailbox(it->mailbox));
+
+ CHECK_GE(resource->exported_count, returned.count);
+ resource->exported_count -= returned.count;
+ resource->lost |= returned.lost;
+ if (resource->exported_count)
+ continue;
+
if (resource->gl_id) {
- if (it->sync_point)
- GLC(context3d, context3d->waitSyncPoint(it->sync_point));
+ if (returned.sync_point)
+ GLC(context3d, context3d->waitSyncPoint(returned.sync_point));
} else {
- resource->mailbox = TextureMailbox(resource->mailbox.name(),
- resource->mailbox.callback(),
- it->sync_point);
+ resource->mailbox =
+ TextureMailbox(resource->mailbox.name(), returned.sync_point);
}
- if (resource->marked_for_deletion)
+
+ if (!resource->marked_for_deletion)
+ continue;
+
+ if (!resource->child_id) {
+ // The resource belongs to this ResourceProvider, so it can be destroyed.
DeleteResourceInternal(map_iterator, Normal);
+ continue;
+ }
+
+ // Delete the resource and return it to the child it came from one.
+ if (resource->child_id != child_id) {
+ ChildMap::iterator child_it = children_.find(resource->child_id);
+ DCHECK(child_it != children_.end());
+
+ if (child_id) {
+ DCHECK_NE(resources_for_child.size(), 0u);
+ DeleteAndReturnUnusedResourcesToChild(
+ child_info, Normal, resources_for_child);
+ resources_for_child.clear();
+ }
+
+ child_info = &child_it->second;
+ child_id = resource->child_id;
+ }
+ resources_for_child.push_back(local_id);
+ }
+
+ if (child_id) {
+ DCHECK_NE(resources_for_child.size(), 0u);
+ DeleteAndReturnUnusedResourcesToChild(
+ child_info, Normal, resources_for_child);
}
}
-bool ResourceProvider::TransferResource(WebGraphicsContext3D* context,
+void ResourceProvider::TransferResource(WebGraphicsContext3D* context,
ResourceId id,
TransferableResource* resource) {
- DCHECK(thread_checker_.CalledOnValidThread());
- ResourceMap::iterator it = resources_.find(id);
- CHECK(it != resources_.end());
- Resource* source = &it->second;
+ Resource* source = GetResource(id);
DCHECK(!source->locked_for_write);
DCHECK(!source->lock_for_read_count);
DCHECK(!source->external || (source->external && source->mailbox.IsValid()));
DCHECK(source->allocated);
- if (source->exported)
- return false;
resource->id = id;
resource->format = source->format;
resource->filter = source->filter;
@@ -956,30 +1109,114 @@ bool ResourceProvider::TransferResource(WebGraphicsContext3D* context,
resource->sync_point = source->mailbox.sync_point();
source->mailbox.ResetSyncPoint();
}
+}
- return true;
+void ResourceProvider::DeleteAndReturnUnusedResourcesToChild(
+ Child* child_info,
+ DeleteStyle style,
+ const ResourceIdArray& unused) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(child_info);
+
+ if (unused.empty())
+ return;
+
+ WebGraphicsContext3D* context3d = Context3d();
+ if (!context3d || !context3d->makeContextCurrent()) {
+ // TODO(skaslev): Implement this path for software compositing.
+ return;
+ }
+
+ ReturnedResourceArray to_return;
+
+ bool need_sync_point = false;
+ for (size_t i = 0; i < unused.size(); ++i) {
+ ResourceId local_id = unused[i];
+
+ ResourceMap::iterator it = resources_.find(local_id);
+ CHECK(it != resources_.end());
+ Resource& resource = it->second;
+
+ DCHECK(!resource.locked_for_write);
+ DCHECK(!resource.lock_for_read_count);
+ DCHECK_EQ(0u, child_info->in_use_resources.count(local_id));
+ DCHECK(child_info->parent_to_child_map.count(local_id));
+
+ ResourceId child_id = child_info->parent_to_child_map[local_id];
+ DCHECK(child_info->child_to_parent_map.count(child_id));
+
+ bool is_lost = resource.lost || lost_output_surface_;
+ if (resource.exported_count > 0) {
+ if (style != ForShutdown) {
+ // Defer this until we receive the resource back from the parent.
+ resource.marked_for_deletion = true;
+ continue;
+ }
+
+ // We still have an exported_count, so we'll have to lose it.
+ is_lost = true;
+ }
+
+ if (resource.filter != resource.original_filter) {
+ DCHECK(resource.target);
+ DCHECK(resource.gl_id);
+
+ GLC(context3d, context3d->bindTexture(resource.target, resource.gl_id));
+ GLC(context3d,
+ context3d->texParameteri(resource.target,
+ GL_TEXTURE_MIN_FILTER,
+ resource.original_filter));
+ GLC(context3d,
+ context3d->texParameteri(resource.target,
+ GL_TEXTURE_MAG_FILTER,
+ resource.original_filter));
+ }
+
+ ReturnedResource returned;
+ returned.id = child_id;
+ returned.sync_point = resource.mailbox.sync_point();
+ if (!returned.sync_point)
+ need_sync_point = true;
+ returned.count = resource.imported_count;
+ returned.lost = is_lost;
+ to_return.push_back(returned);
+
+ child_info->parent_to_child_map.erase(local_id);
+ child_info->child_to_parent_map.erase(child_id);
+ resource.imported_count = 0;
+ DeleteResourceInternal(it, style);
+ }
+ if (need_sync_point) {
+ unsigned int sync_point = context3d->insertSyncPoint();
+ for (size_t i = 0; i < to_return.size(); ++i) {
+ if (!to_return[i].sync_point)
+ to_return[i].sync_point = sync_point;
+ }
+ }
+
+ if (!to_return.empty())
+ child_info->return_callback.Run(to_return);
}
void ResourceProvider::AcquirePixelBuffer(ResourceId id) {
- DCHECK(thread_checker_.CalledOnValidThread());
- ResourceMap::iterator it = resources_.find(id);
- CHECK(it != resources_.end());
- Resource* resource = &it->second;
+ Resource* resource = GetResource(id);
DCHECK(!resource->external);
- DCHECK(!resource->exported);
+ DCHECK_EQ(resource->exported_count, 0);
DCHECK(!resource->image_id);
if (resource->type == GLTexture) {
- WebGraphicsContext3D* context3d = output_surface_->context3d();
+ WebGraphicsContext3D* context3d = Context3d();
DCHECK(context3d);
if (!resource->gl_pixel_buffer_id)
resource->gl_pixel_buffer_id = context3d->createBuffer();
context3d->bindBuffer(
GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM,
resource->gl_pixel_buffer_id);
+ unsigned bytes_per_pixel = BytesPerPixel(resource->format);
context3d->bufferData(
GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM,
- 4 * resource->size.GetArea(),
+ resource->size.height() * RoundUp(bytes_per_pixel
+ * resource->size.width(), 4u),
NULL,
GL_DYNAMIC_DRAW);
context3d->bindBuffer(GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM, 0);
@@ -994,12 +1231,9 @@ void ResourceProvider::AcquirePixelBuffer(ResourceId id) {
}
void ResourceProvider::ReleasePixelBuffer(ResourceId id) {
- DCHECK(thread_checker_.CalledOnValidThread());
- ResourceMap::iterator it = resources_.find(id);
- CHECK(it != resources_.end());
- Resource* resource = &it->second;
+ Resource* resource = GetResource(id);
DCHECK(!resource->external);
- DCHECK(!resource->exported);
+ DCHECK_EQ(resource->exported_count, 0);
DCHECK(!resource->image_id);
// The pixel buffer can be released while there is a pending "set pixels"
@@ -1017,7 +1251,7 @@ void ResourceProvider::ReleasePixelBuffer(ResourceId id) {
if (resource->type == GLTexture) {
if (!resource->gl_pixel_buffer_id)
return;
- WebGraphicsContext3D* context3d = output_surface_->context3d();
+ WebGraphicsContext3D* context3d = Context3d();
DCHECK(context3d);
context3d->bindBuffer(
GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM,
@@ -1039,16 +1273,13 @@ void ResourceProvider::ReleasePixelBuffer(ResourceId id) {
}
uint8_t* ResourceProvider::MapPixelBuffer(ResourceId id) {
- DCHECK(thread_checker_.CalledOnValidThread());
- ResourceMap::iterator it = resources_.find(id);
- CHECK(it != resources_.end());
- Resource* resource = &it->second;
+ Resource* resource = GetResource(id);
DCHECK(!resource->external);
- DCHECK(!resource->exported);
+ DCHECK_EQ(resource->exported_count, 0);
DCHECK(!resource->image_id);
if (resource->type == GLTexture) {
- WebGraphicsContext3D* context3d = output_surface_->context3d();
+ WebGraphicsContext3D* context3d = Context3d();
DCHECK(context3d);
DCHECK(resource->gl_pixel_buffer_id);
context3d->bindBuffer(
@@ -1070,16 +1301,13 @@ uint8_t* ResourceProvider::MapPixelBuffer(ResourceId id) {
}
void ResourceProvider::UnmapPixelBuffer(ResourceId id) {
- DCHECK(thread_checker_.CalledOnValidThread());
- ResourceMap::iterator it = resources_.find(id);
- CHECK(it != resources_.end());
- Resource* resource = &it->second;
+ Resource* resource = GetResource(id);
DCHECK(!resource->external);
- DCHECK(!resource->exported);
+ DCHECK_EQ(resource->exported_count, 0);
DCHECK(!resource->image_id);
if (resource->type == GLTexture) {
- WebGraphicsContext3D* context3d = output_surface_->context3d();
+ WebGraphicsContext3D* context3d = Context3d();
DCHECK(context3d);
DCHECK(resource->gl_pixel_buffer_id);
context3d->bindBuffer(
@@ -1095,16 +1323,14 @@ void ResourceProvider::BindForSampling(ResourceProvider::ResourceId resource_id,
GLenum unit,
GLenum filter) {
DCHECK(thread_checker_.CalledOnValidThread());
- WebGraphicsContext3D* context3d = output_surface_->context3d();
+ WebGraphicsContext3D* context3d = Context3d();
ResourceMap::iterator it = resources_.find(resource_id);
DCHECK(it != resources_.end());
Resource* resource = &it->second;
DCHECK(resource->lock_for_read_count);
DCHECK(!resource->locked_for_write || resource->set_pixels_completion_forced);
- DCHECK_EQ(GL_TEXTURE0, GetActiveTextureUnit(context3d));
- if (unit != GL_TEXTURE0)
- GLC(context3d, context3d->activeTexture(unit));
+ ScopedSetActiveTexture scoped_active_tex(context3d, unit);
GLC(context3d, context3d->bindTexture(target, resource->gl_id));
if (filter != resource->filter) {
GLC(context3d, context3d->texParameteri(target,
@@ -1114,14 +1340,14 @@ void ResourceProvider::BindForSampling(ResourceProvider::ResourceId resource_id,
GL_TEXTURE_MAG_FILTER,
filter));
resource->filter = filter;
+ if (resource->target == 0)
+ resource->target = target;
+ else
+ DCHECK_EQ(resource->target, target);
}
if (resource->image_id)
context3d->bindTexImage2DCHROMIUM(target, resource->image_id);
-
- // Active unit being GL_TEXTURE0 is effectively the ground state.
- if (unit != GL_TEXTURE0)
- GLC(context3d, context3d->activeTexture(GL_TEXTURE0));
}
void ResourceProvider::UnbindForSampling(
@@ -1134,21 +1360,13 @@ void ResourceProvider::UnbindForSampling(
if (!resource->image_id)
return;
- WebGraphicsContext3D* context3d = output_surface_->context3d();
- DCHECK_EQ(GL_TEXTURE0, GetActiveTextureUnit(context3d));
- if (unit != GL_TEXTURE0)
- GLC(context3d, context3d->activeTexture(unit));
+ WebGraphicsContext3D* context3d = Context3d();
+ ScopedSetActiveTexture scoped_active_tex(context3d, unit);
context3d->releaseTexImage2DCHROMIUM(target, resource->image_id);
- // Active unit being GL_TEXTURE0 is effectively the ground state.
- if (unit != GL_TEXTURE0)
- GLC(context3d, context3d->activeTexture(GL_TEXTURE0));
}
void ResourceProvider::BeginSetPixels(ResourceId id) {
- DCHECK(thread_checker_.CalledOnValidThread());
- ResourceMap::iterator it = resources_.find(id);
- CHECK(it != resources_.end());
- Resource* resource = &it->second;
+ Resource* resource = GetResource(id);
DCHECK(!resource->pending_set_pixels);
LazyCreate(resource);
@@ -1161,7 +1379,7 @@ void ResourceProvider::BeginSetPixels(ResourceId id) {
LockForWrite(id);
if (resource->gl_id) {
- WebGraphicsContext3D* context3d = output_surface_->context3d();
+ WebGraphicsContext3D* context3d = Context3d();
DCHECK(context3d);
DCHECK(resource->gl_pixel_buffer_id);
context3d->bindTexture(GL_TEXTURE_2D, resource->gl_id);
@@ -1171,37 +1389,39 @@ void ResourceProvider::BeginSetPixels(ResourceId id) {
if (!resource->gl_upload_query_id)
resource->gl_upload_query_id = context3d->createQueryEXT();
context3d->beginQueryEXT(
- GL_ASYNC_PIXEL_TRANSFERS_COMPLETED_CHROMIUM,
+ GL_ASYNC_PIXEL_UNPACK_COMPLETED_CHROMIUM,
resource->gl_upload_query_id);
if (allocate) {
- context3d->asyncTexImage2DCHROMIUM(GL_TEXTURE_2D,
- 0, /* level */
- resource->format,
- resource->size.width(),
- resource->size.height(),
- 0, /* border */
- resource->format,
- GL_UNSIGNED_BYTE,
- NULL);
+ context3d->asyncTexImage2DCHROMIUM(
+ GL_TEXTURE_2D,
+ 0, /* level */
+ GetGLInternalFormat(resource->format),
+ resource->size.width(),
+ resource->size.height(),
+ 0, /* border */
+ GetGLDataFormat(resource->format),
+ GetGLDataType(resource->format),
+ NULL);
} else {
- context3d->asyncTexSubImage2DCHROMIUM(GL_TEXTURE_2D,
- 0, /* level */
- 0, /* x */
- 0, /* y */
- resource->size.width(),
- resource->size.height(),
- resource->format,
- GL_UNSIGNED_BYTE,
- NULL);
+ context3d->asyncTexSubImage2DCHROMIUM(
+ GL_TEXTURE_2D,
+ 0, /* level */
+ 0, /* x */
+ 0, /* y */
+ resource->size.width(),
+ resource->size.height(),
+ GetGLDataFormat(resource->format),
+ GetGLDataType(resource->format),
+ NULL);
}
- context3d->endQueryEXT(GL_ASYNC_PIXEL_TRANSFERS_COMPLETED_CHROMIUM);
+ context3d->endQueryEXT(GL_ASYNC_PIXEL_UNPACK_COMPLETED_CHROMIUM);
context3d->bindBuffer(GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM, 0);
}
if (resource->pixels) {
DCHECK(!resource->mailbox.IsValid());
DCHECK(resource->pixel_buffer);
- DCHECK(resource->format == GL_RGBA);
+ DCHECK_EQ(RGBA_8888, resource->format);
std::swap(resource->pixels, resource->pixel_buffer);
delete[] resource->pixel_buffer;
@@ -1213,16 +1433,13 @@ void ResourceProvider::BeginSetPixels(ResourceId id) {
}
void ResourceProvider::ForceSetPixelsToComplete(ResourceId id) {
- DCHECK(thread_checker_.CalledOnValidThread());
- ResourceMap::iterator it = resources_.find(id);
- CHECK(it != resources_.end());
- Resource* resource = &it->second;
+ Resource* resource = GetResource(id);
DCHECK(resource->locked_for_write);
DCHECK(resource->pending_set_pixels);
DCHECK(!resource->set_pixels_completion_forced);
if (resource->gl_id) {
- WebGraphicsContext3D* context3d = output_surface_->context3d();
+ WebGraphicsContext3D* context3d = Context3d();
GLC(context3d, context3d->bindTexture(GL_TEXTURE_2D, resource->gl_id));
GLC(context3d, context3d->waitAsyncTexImage2DCHROMIUM(GL_TEXTURE_2D));
GLC(context3d, context3d->bindTexture(GL_TEXTURE_2D, 0));
@@ -1232,15 +1449,12 @@ void ResourceProvider::ForceSetPixelsToComplete(ResourceId id) {
}
bool ResourceProvider::DidSetPixelsComplete(ResourceId id) {
- DCHECK(thread_checker_.CalledOnValidThread());
- ResourceMap::iterator it = resources_.find(id);
- CHECK(it != resources_.end());
- Resource* resource = &it->second;
+ Resource* resource = GetResource(id);
DCHECK(resource->locked_for_write);
DCHECK(resource->pending_set_pixels);
if (resource->gl_id) {
- WebGraphicsContext3D* context3d = output_surface_->context3d();
+ WebGraphicsContext3D* context3d = Context3d();
DCHECK(context3d);
DCHECK(resource->gl_upload_query_id);
unsigned complete = 1;
@@ -1259,10 +1473,12 @@ bool ResourceProvider::DidSetPixelsComplete(ResourceId id) {
}
void ResourceProvider::CreateForTesting(ResourceId id) {
- ResourceMap::iterator it = resources_.find(id);
- CHECK(it != resources_.end());
- Resource* resource = &it->second;
- LazyCreate(resource);
+ LazyCreate(GetResource(id));
+}
+
+GLint ResourceProvider::WrapModeForTesting(ResourceId id) {
+ Resource* resource = GetResource(id);
+ return resource->wrap_mode;
}
void ResourceProvider::LazyCreate(Resource* resource) {
@@ -1273,10 +1489,20 @@ void ResourceProvider::LazyCreate(Resource* resource) {
if (resource->texture_pool == 0)
return;
- WebGraphicsContext3D* context3d = output_surface_->context3d();
+ WebGraphicsContext3D* context3d = Context3d();
DCHECK(context3d);
+
// Create and set texture properties. Allocation is delayed until needed.
- resource->gl_id = CreateTextureId(context3d);
+ GLC(context3d, resource->gl_id = context3d->createTexture());
+ GLC(context3d, context3d->bindTexture(GL_TEXTURE_2D, resource->gl_id));
+ GLC(context3d, context3d->texParameteri(
+ GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
+ GLC(context3d, context3d->texParameteri(
+ GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
+ GLC(context3d, context3d->texParameteri(
+ GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, resource->wrap_mode));
+ GLC(context3d, context3d->texParameteri(
+ GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, resource->wrap_mode));
GLC(context3d, context3d->texParameteri(GL_TEXTURE_2D,
GL_TEXTURE_POOL_CHROMIUM,
resource->texture_pool));
@@ -1288,10 +1514,7 @@ void ResourceProvider::LazyCreate(Resource* resource) {
}
void ResourceProvider::AllocateForTesting(ResourceId id) {
- ResourceMap::iterator it = resources_.find(id);
- CHECK(it != resources_.end());
- Resource* resource = &it->second;
- LazyAllocate(resource);
+ LazyAllocate(GetResource(id));
}
void ResourceProvider::LazyAllocate(Resource* resource) {
@@ -1302,11 +1525,11 @@ void ResourceProvider::LazyAllocate(Resource* resource) {
if (resource->allocated || !resource->gl_id)
return;
resource->allocated = true;
- WebGraphicsContext3D* context3d = output_surface_->context3d();
+ WebGraphicsContext3D* context3d = Context3d();
gfx::Size& size = resource->size;
- GLenum format = resource->format;
+ ResourceFormat format = resource->format;
GLC(context3d, context3d->bindTexture(GL_TEXTURE_2D, resource->gl_id));
- if (use_texture_storage_ext_ && IsTextureFormatSupportedForStorage(format)) {
+ if (use_texture_storage_ext_ && IsFormatSupportedForStorage(format)) {
GLenum storage_format = TextureToStorageFormat(format);
GLC(context3d, context3d->texStorage2DEXT(GL_TEXTURE_2D,
1,
@@ -1316,33 +1539,26 @@ void ResourceProvider::LazyAllocate(Resource* resource) {
} else {
GLC(context3d, context3d->texImage2D(GL_TEXTURE_2D,
0,
- format,
+ GetGLInternalFormat(format),
size.width(),
size.height(),
0,
- format,
- GL_UNSIGNED_BYTE,
+ GetGLDataFormat(format),
+ GetGLDataType(format),
NULL));
}
}
void ResourceProvider::EnableReadLockFences(ResourceProvider::ResourceId id,
bool enable) {
- DCHECK(thread_checker_.CalledOnValidThread());
- ResourceMap::iterator it = resources_.find(id);
- CHECK(it != resources_.end());
- Resource* resource = &it->second;
+ Resource* resource = GetResource(id);
resource->enable_read_lock_fences = enable;
}
void ResourceProvider::AcquireImage(ResourceId id) {
- DCHECK(thread_checker_.CalledOnValidThread());
- ResourceMap::iterator it = resources_.find(id);
- CHECK(it != resources_.end());
- Resource* resource = &it->second;
-
+ Resource* resource = GetResource(id);
DCHECK(!resource->external);
- DCHECK(!resource->exported);
+ DCHECK_EQ(resource->exported_count, 0);
if (resource->type != GLTexture)
return;
@@ -1351,26 +1567,23 @@ void ResourceProvider::AcquireImage(ResourceId id) {
return;
resource->allocated = true;
- WebGraphicsContext3D* context3d = output_surface_->context3d();
+ WebGraphicsContext3D* context3d = Context3d();
DCHECK(context3d);
+ DCHECK_EQ(RGBA_8888, resource->format);
resource->image_id = context3d->createImageCHROMIUM(
resource->size.width(), resource->size.height(), GL_RGBA8_OES);
DCHECK(resource->image_id);
}
void ResourceProvider::ReleaseImage(ResourceId id) {
- DCHECK(thread_checker_.CalledOnValidThread());
- ResourceMap::iterator it = resources_.find(id);
- CHECK(it != resources_.end());
- Resource* resource = &it->second;
-
+ Resource* resource = GetResource(id);
DCHECK(!resource->external);
- DCHECK(!resource->exported);
+ DCHECK_EQ(resource->exported_count, 0);
if (!resource->image_id)
return;
- WebGraphicsContext3D* context3d = output_surface_->context3d();
+ WebGraphicsContext3D* context3d = Context3d();
DCHECK(context3d);
context3d->destroyImageCHROMIUM(resource->image_id);
resource->image_id = 0;
@@ -1378,17 +1591,13 @@ void ResourceProvider::ReleaseImage(ResourceId id) {
}
uint8_t* ResourceProvider::MapImage(ResourceId id) {
- DCHECK(thread_checker_.CalledOnValidThread());
- ResourceMap::iterator it = resources_.find(id);
- CHECK(it != resources_.end());
- Resource* resource = &it->second;
-
+ Resource* resource = GetResource(id);
DCHECK(ReadLockFenceHasPassed(resource));
DCHECK(!resource->external);
- DCHECK(!resource->exported);
+ DCHECK_EQ(resource->exported_count, 0);
if (resource->image_id) {
- WebGraphicsContext3D* context3d = output_surface_->context3d();
+ WebGraphicsContext3D* context3d = Context3d();
DCHECK(context3d);
return static_cast<uint8_t*>(
context3d->mapImageCHROMIUM(resource->image_id, GL_READ_WRITE));
@@ -1401,34 +1610,26 @@ uint8_t* ResourceProvider::MapImage(ResourceId id) {
}
void ResourceProvider::UnmapImage(ResourceId id) {
- DCHECK(thread_checker_.CalledOnValidThread());
- ResourceMap::iterator it = resources_.find(id);
- CHECK(it != resources_.end());
- Resource* resource = &it->second;
-
+ Resource* resource = GetResource(id);
DCHECK(!resource->external);
- DCHECK(!resource->exported);
+ DCHECK_EQ(resource->exported_count, 0);
if (resource->image_id) {
- WebGraphicsContext3D* context3d = output_surface_->context3d();
+ WebGraphicsContext3D* context3d = Context3d();
DCHECK(context3d);
context3d->unmapImageCHROMIUM(resource->image_id);
}
}
int ResourceProvider::GetImageStride(ResourceId id) {
- DCHECK(thread_checker_.CalledOnValidThread());
- ResourceMap::iterator it = resources_.find(id);
- CHECK(it != resources_.end());
- Resource* resource = &it->second;
-
+ Resource* resource = GetResource(id);
DCHECK(!resource->external);
- DCHECK(!resource->exported);
+ DCHECK_EQ(resource->exported_count, 0);
int stride = 0;
if (resource->image_id) {
- WebGraphicsContext3D* context3d = output_surface_->context3d();
+ WebGraphicsContext3D* context3d = Context3d();
DCHECK(context3d);
context3d->getImageParameterivCHROMIUM(
resource->image_id, GL_IMAGE_ROWBYTES_CHROMIUM, &stride);
@@ -1443,4 +1644,59 @@ GLint ResourceProvider::GetActiveTextureUnit(WebGraphicsContext3D* context) {
return active_unit;
}
+WebKit::WebGraphicsContext3D* ResourceProvider::Context3d() const {
+ ContextProvider* context_provider = output_surface_->context_provider();
+ return context_provider ? context_provider->Context3d() : NULL;
+}
+
+size_t ResourceProvider::BytesPerPixel(ResourceFormat format) {
+ switch (format) {
+ case RGBA_8888:
+ case BGRA_8888:
+ return 4;
+ case RGBA_4444:
+ case RGB_565:
+ return 2;
+ case LUMINANCE_8:
+ return 1;
+ }
+ NOTREACHED();
+ return 4;
+}
+
+GLenum ResourceProvider::GetGLDataType(ResourceFormat format) {
+ switch (format) {
+ case RGBA_4444:
+ return GL_UNSIGNED_SHORT_4_4_4_4;
+ case RGBA_8888:
+ case BGRA_8888:
+ case LUMINANCE_8:
+ return GL_UNSIGNED_BYTE;
+ case RGB_565:
+ return GL_UNSIGNED_SHORT_5_6_5;
+ }
+ NOTREACHED();
+ return GL_UNSIGNED_BYTE;
+}
+
+GLenum ResourceProvider::GetGLDataFormat(ResourceFormat format) {
+ switch (format) {
+ case RGBA_8888:
+ case RGBA_4444:
+ return GL_RGBA;
+ case BGRA_8888:
+ return GL_BGRA_EXT;
+ case LUMINANCE_8:
+ return GL_LUMINANCE;
+ case RGB_565:
+ return GL_RGB;
+ }
+ NOTREACHED();
+ return GL_RGBA;
+}
+
+GLenum ResourceProvider::GetGLInternalFormat(ResourceFormat format) {
+ return GetGLDataFormat(format);
+}
+
} // namespace cc
diff --git a/chromium/cc/resources/resource_provider.h b/chromium/cc/resources/resource_provider.h
index 1d51a2748e7..928d4f9aa2a 100644
--- a/chromium/cc/resources/resource_provider.h
+++ b/chromium/cc/resources/resource_provider.h
@@ -8,6 +8,7 @@
#include <deque>
#include <set>
#include <string>
+#include <utility>
#include <vector>
#include "base/basictypes.h"
@@ -18,6 +19,10 @@
#include "cc/base/cc_export.h"
#include "cc/output/context_provider.h"
#include "cc/output/output_surface.h"
+#include "cc/resources/release_callback.h"
+#include "cc/resources/resource_format.h"
+#include "cc/resources/return_callback.h"
+#include "cc/resources/single_release_callback.h"
#include "cc/resources/texture_mailbox.h"
#include "cc/resources/transferable_resource.h"
#include "third_party/skia/include/core/SkBitmap.h"
@@ -57,8 +62,8 @@ class CC_EXPORT ResourceProvider {
};
static scoped_ptr<ResourceProvider> Create(OutputSurface* output_surface,
- int highp_threshold_min);
-
+ int highp_threshold_min,
+ bool use_rgba_4444_texture_format);
virtual ~ResourceProvider();
void InitializeSoftware();
@@ -66,14 +71,17 @@ class CC_EXPORT ResourceProvider {
void DidLoseOutputSurface() { lost_output_surface_ = true; }
- WebKit::WebGraphicsContext3D* GraphicsContext3D();
int max_texture_size() const { return max_texture_size_; }
- GLenum best_texture_format() const { return best_texture_format_; }
+ ResourceFormat memory_efficient_texture_format() const {
+ return use_rgba_4444_texture_format_ ? RGBA_4444 : best_texture_format_;
+ }
+ ResourceFormat best_texture_format() const { return best_texture_format_; }
size_t num_resources() const { return resources_.size(); }
// Checks whether a resource is in use by a consumer.
bool InUseByConsumer(ResourceId id);
+ bool IsLost(ResourceId id);
// Producer interface.
@@ -82,20 +90,23 @@ class CC_EXPORT ResourceProvider {
// Creates a resource of the default resource type.
ResourceId CreateResource(gfx::Size size,
- GLenum format,
- TextureUsageHint hint);
+ GLint wrap_mode,
+ TextureUsageHint hint,
+ ResourceFormat format);
// Creates a resource which is tagged as being managed for GPU memory
// accounting purposes.
ResourceId CreateManagedResource(gfx::Size size,
- GLenum format,
- TextureUsageHint hint);
+ GLint wrap_mode,
+ TextureUsageHint hint,
+ ResourceFormat format);
// You can also explicitly create a specific resource type.
ResourceId CreateGLTexture(gfx::Size size,
- GLenum format,
GLenum texture_pool,
- TextureUsageHint hint);
+ GLint wrap_mode,
+ TextureUsageHint hint,
+ ResourceFormat format);
ResourceId CreateBitmap(gfx::Size size);
// Wraps an external texture into a GL resource.
@@ -104,7 +115,9 @@ class CC_EXPORT ResourceProvider {
unsigned texture_id);
// Wraps an external texture mailbox into a GL resource.
- ResourceId CreateResourceFromTextureMailbox(const TextureMailbox& mailbox);
+ ResourceId CreateResourceFromTextureMailbox(
+ const TextureMailbox& mailbox,
+ scoped_ptr<SingleReleaseCallback> release_callback);
void DeleteResource(ResourceId id);
@@ -122,6 +135,7 @@ class CC_EXPORT ResourceProvider {
double EstimatedUploadsPerSecond();
void FlushUploads();
void ReleaseCachedData();
+ base::TimeDelta TextureUpdateTickRate();
// Flush all context operations, kicking uploads and ensuring ordering with
// respect to other contexts.
@@ -136,7 +150,7 @@ class CC_EXPORT ResourceProvider {
bool ShallowFlushIfSupported();
// Creates accounting for a child. Returns a child ID.
- int CreateChild();
+ int CreateChild(const ReturnCallback& return_callback);
// Destroys accounting for the child, deleting all accounted resources.
void DestroyChild(int child);
@@ -151,29 +165,31 @@ class CC_EXPORT ResourceProvider {
void PrepareSendToParent(const ResourceIdArray& resources,
TransferableResourceArray* transferable_resources);
- // Prepares resources to be transfered back to the child, moving them to
- // mailboxes and serializing meta-data into TransferableResources.
- // Resources are removed from the ResourceProvider. Note: the resource IDs
- // passed are in the parent namespace and will be translated to the child
- // namespace when returned.
- void PrepareSendToChild(int child,
- const ResourceIdArray& resources,
- TransferableResourceArray* transferable_resources);
-
// Receives resources from a child, moving them from mailboxes. Resource IDs
// passed are in the child namespace, and will be translated to the parent
// namespace, added to the child->parent map.
+ // This adds the resources to the working set in the ResourceProvider without
+ // declaring which resources are in use. Use DeclareUsedResourcesFromChild
+ // after calling this method to do that. All calls to ReceiveFromChild should
+ // be followed by a DeclareUsedResourcesFromChild.
// NOTE: if the sync_point is set on any TransferableResource, this will
// wait on it.
void ReceiveFromChild(
int child, const TransferableResourceArray& transferable_resources);
+ // Once a set of resources have been received, they may or may not be used.
+ // This declares what set of resources are currently in use from the child,
+ // releasing any other resources back to the child.
+ void DeclareUsedResourcesFromChild(
+ int child,
+ const ResourceIdArray& resources_from_child);
+
// Receives resources from the parent, moving them from mailboxes. Resource
// IDs passed are in the child namespace.
// NOTE: if the sync_point is set on any TransferableResource, this will
// wait on it.
- void ReceiveFromParent(
- const TransferableResourceArray& transferable_resources);
+ void ReceiveReturnsFromParent(
+ const ReturnedResourceArray& transferable_resources);
// The following lock classes are part of the ResourceProvider API and are
// needed to read and write the resource contents. The user must ensure
@@ -313,6 +329,8 @@ class CC_EXPORT ResourceProvider {
// For tests only!
void CreateForTesting(ResourceId id);
+ GLint WrapModeForTesting(ResourceId id);
+
// Sets the current read fence. If a resource is locked for read
// and has read fences enabled, the resource will not allow writes
// until this fence has passed.
@@ -327,14 +345,11 @@ class CC_EXPORT ResourceProvider {
// Indicates if we can currently lock this resource for write.
bool CanLockForWrite(ResourceId id);
- cc::ContextProvider* offscreen_context_provider() {
- return offscreen_context_provider_.get();
- }
- void set_offscreen_context_provider(
- scoped_refptr<cc::ContextProvider> offscreen_context_provider) {
- offscreen_context_provider_ = offscreen_context_provider;
- }
static GLint GetActiveTextureUnit(WebKit::WebGraphicsContext3D* context);
+ static size_t BytesPerPixel(ResourceFormat format);
+ static GLenum GetGLDataType(ResourceFormat format);
+ static GLenum GetGLDataFormat(ResourceFormat format);
+ static GLenum GetGLInternalFormat(ResourceFormat format);
private:
struct Resource {
@@ -342,24 +357,31 @@ class CC_EXPORT ResourceProvider {
~Resource();
Resource(unsigned texture_id,
gfx::Size size,
- GLenum format,
GLenum filter,
GLenum texture_pool,
- TextureUsageHint hint);
- Resource(uint8_t* pixels, gfx::Size size, GLenum format, GLenum filter);
+ GLint wrap_mode,
+ TextureUsageHint hint,
+ ResourceFormat format);
+ Resource(uint8_t* pixels,
+ gfx::Size size,
+ GLenum filter,
+ GLint wrap_mode);
+ int child_id;
unsigned gl_id;
// Pixel buffer used for set pixels without unnecessary copying.
unsigned gl_pixel_buffer_id;
// Query used to determine when asynchronous set pixels complete.
unsigned gl_upload_query_id;
TextureMailbox mailbox;
+ ReleaseCallback release_callback;
uint8_t* pixels;
uint8_t* pixel_buffer;
int lock_for_read_count;
+ int imported_count;
+ int exported_count;
bool locked_for_write;
bool external;
- bool exported;
bool marked_for_deletion;
bool pending_set_pixels;
bool set_pixels_completion_forced;
@@ -367,21 +389,32 @@ class CC_EXPORT ResourceProvider {
bool enable_read_lock_fences;
scoped_refptr<Fence> read_lock_fence;
gfx::Size size;
- GLenum format;
// TODO(skyostil): Use a separate sampler object for filter state.
+ GLenum original_filter;
GLenum filter;
+ GLenum target;
unsigned image_id;
GLenum texture_pool;
+ GLint wrap_mode;
+ bool lost;
TextureUsageHint hint;
ResourceType type;
+ ResourceFormat format;
};
typedef base::hash_map<ResourceId, Resource> ResourceMap;
+
+ static bool CompareResourceMapIteratorsByChildId(
+ const std::pair<ReturnedResource, ResourceMap::iterator>& a,
+ const std::pair<ReturnedResource, ResourceMap::iterator>& b);
+
struct Child {
Child();
~Child();
ResourceIdMap child_to_parent_map;
ResourceIdMap parent_to_child_map;
+ ReturnCallback return_callback;
+ ResourceIdSet in_use_resources;
};
typedef base::hash_map<int, Child> ChildMap;
@@ -390,11 +423,13 @@ class CC_EXPORT ResourceProvider {
resource->read_lock_fence->HasPassed();
}
- explicit ResourceProvider(OutputSurface* output_surface,
- int highp_threshold_min);
+ ResourceProvider(OutputSurface* output_surface,
+ int highp_threshold_min,
+ bool use_rgba_4444_texture_format);
void CleanUpGLIfNeeded();
+ Resource* GetResource(ResourceId id);
const Resource* LockForRead(ResourceId id);
void UnlockForRead(ResourceId id);
const Resource* LockForWrite(ResourceId id);
@@ -402,7 +437,7 @@ class CC_EXPORT ResourceProvider {
static void PopulateSkBitmapWithResource(SkBitmap* sk_bitmap,
const Resource* resource);
- bool TransferResource(WebKit::WebGraphicsContext3D* context,
+ void TransferResource(WebKit::WebGraphicsContext3D* context,
ResourceId id,
TransferableResource* resource);
enum DeleteStyle {
@@ -410,6 +445,9 @@ class CC_EXPORT ResourceProvider {
ForShutdown,
};
void DeleteResourceInternal(ResourceMap::iterator it, DeleteStyle style);
+ void DeleteAndReturnUnusedResourcesToChild(Child* child_info,
+ DeleteStyle style,
+ const ResourceIdArray& unused);
void LazyCreate(Resource* resource);
void LazyAllocate(Resource* resource);
@@ -424,6 +462,9 @@ class CC_EXPORT ResourceProvider {
GLenum target,
GLenum unit);
+ // Returns NULL if the output_surface_ does not have a ContextProvider.
+ WebKit::WebGraphicsContext3D* Context3d() const;
+
OutputSurface* output_surface_;
bool lost_output_surface_;
int highp_threshold_min_;
@@ -438,13 +479,12 @@ class CC_EXPORT ResourceProvider {
bool use_shallow_flush_;
scoped_ptr<TextureUploader> texture_uploader_;
int max_texture_size_;
- GLenum best_texture_format_;
-
- scoped_refptr<cc::ContextProvider> offscreen_context_provider_;
+ ResourceFormat best_texture_format_;
base::ThreadChecker thread_checker_;
scoped_refptr<Fence> current_read_lock_fence_;
+ bool use_rgba_4444_texture_format_;
DISALLOW_COPY_AND_ASSIGN(ResourceProvider);
};
diff --git a/chromium/cc/resources/resource_provider_unittest.cc b/chromium/cc/resources/resource_provider_unittest.cc
index a23af0b577c..fed3016e2e5 100644
--- a/chromium/cc/resources/resource_provider_unittest.cc
+++ b/chromium/cc/resources/resource_provider_unittest.cc
@@ -11,10 +11,12 @@
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "cc/base/scoped_ptr_deque.h"
+#include "cc/debug/test_web_graphics_context_3d.h"
#include "cc/output/output_surface.h"
+#include "cc/resources/returned_resource.h"
+#include "cc/resources/single_release_callback.h"
#include "cc/test/fake_output_surface.h"
#include "cc/test/fake_output_surface_client.h"
-#include "cc/test/test_web_graphics_context_3d.h"
#include "gpu/GLES2/gl2extchromium.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -39,24 +41,41 @@ using WebKit::WebGLId;
namespace cc {
namespace {
-size_t TextureSize(gfx::Size size, WGC3Denum format) {
+size_t TextureSize(gfx::Size size, ResourceFormat format) {
unsigned int components_per_pixel = 4;
unsigned int bytes_per_component = 1;
return size.width() * size.height() * components_per_pixel *
bytes_per_component;
}
+class TextureStateTrackingContext : public TestWebGraphicsContext3D {
+ public:
+ MOCK_METHOD2(bindTexture, void(WGC3Denum target, WebGLId texture));
+ MOCK_METHOD3(texParameteri,
+ void(WGC3Denum target, WGC3Denum pname, WGC3Dint param));
+ MOCK_METHOD1(waitSyncPoint, void(unsigned sync_point));
+ MOCK_METHOD0(insertSyncPoint, unsigned(void));
+ MOCK_METHOD2(produceTextureCHROMIUM, void(WGC3Denum target,
+ const WGC3Dbyte* mailbox));
+ MOCK_METHOD2(consumeTextureCHROMIUM, void(WGC3Denum target,
+ const WGC3Dbyte* mailbox));
+
+ // Force all textures to be "1" so we can test for them.
+ virtual WebKit::WebGLId NextTextureId() OVERRIDE { return 1; }
+};
+
struct Texture : public base::RefCounted<Texture> {
- Texture() : format(0), filter(GL_NEAREST_MIPMAP_LINEAR) {}
+ Texture() : format(RGBA_8888),
+ filter(GL_NEAREST_MIPMAP_LINEAR) {}
- void Reallocate(gfx::Size size, WGC3Denum format) {
+ void Reallocate(gfx::Size size, ResourceFormat format) {
this->size = size;
this->format = format;
this->data.reset(new uint8_t[TextureSize(size, format)]);
}
gfx::Size size;
- WGC3Denum format;
+ ResourceFormat format;
WGC3Denum filter;
scoped_ptr<uint8_t[]> data;
@@ -219,7 +238,9 @@ class ResourceProviderContext : public TestWebGraphicsContext3D {
ASSERT_EQ(static_cast<unsigned>(GL_TEXTURE_2D), target);
ASSERT_FALSE(level);
ASSERT_TRUE(textures_[current_texture_].get());
- ASSERT_EQ(textures_[current_texture_]->format, format);
+ ASSERT_EQ(
+ ResourceProvider::GetGLDataFormat(textures_[current_texture_]->format),
+ format);
ASSERT_EQ(static_cast<unsigned>(GL_UNSIGNED_BYTE), type);
ASSERT_TRUE(pixels);
SetPixels(xoffset, yoffset, width, height, pixels);
@@ -262,7 +283,7 @@ class ResourceProviderContext : public TestWebGraphicsContext3D {
mailbox, last_waited_sync_point_);
}
- void GetPixels(gfx::Size size, WGC3Denum format, uint8_t* pixels) {
+ void GetPixels(gfx::Size size, ResourceFormat format, uint8_t* pixels) {
ASSERT_TRUE(current_texture_);
scoped_refptr<Texture> texture = textures_[current_texture_];
ASSERT_TRUE(texture.get());
@@ -293,7 +314,16 @@ class ResourceProviderContext : public TestWebGraphicsContext3D {
ASSERT_TRUE(current_texture_);
scoped_refptr<Texture> texture = textures_[current_texture_];
ASSERT_TRUE(texture.get());
- texture->Reallocate(size, format);
+ ResourceFormat texture_format = RGBA_8888;
+ switch (format) {
+ case GL_RGBA:
+ texture_format = RGBA_8888;
+ break;
+ case GL_BGRA_EXT:
+ texture_format = BGRA_8888;
+ break;
+ }
+ texture->Reallocate(size, texture_format);
}
void SetPixels(int xoffset,
@@ -338,7 +368,7 @@ void GetResourcePixels(ResourceProvider* resource_provider,
ResourceProviderContext* context,
ResourceProvider::ResourceId id,
gfx::Size size,
- WGC3Denum format,
+ ResourceFormat format,
uint8_t* pixels) {
switch (resource_provider->default_resource_type()) {
case ResourceProvider::GLTexture: {
@@ -366,13 +396,21 @@ class ResourceProviderTest
: public testing::TestWithParam<ResourceProvider::ResourceType> {
public:
ResourceProviderTest()
- : shared_data_(ContextSharedData::Create()) {
+ : shared_data_(ContextSharedData::Create()),
+ context3d_(NULL) {
switch (GetParam()) {
- case ResourceProvider::GLTexture:
- output_surface_ =
- FakeOutputSurface::Create3d(ResourceProviderContext::Create(
- shared_data_.get()).PassAs<WebKit::WebGraphicsContext3D>());
+ case ResourceProvider::GLTexture: {
+ scoped_ptr<ResourceProviderContext> context3d(
+ ResourceProviderContext::Create(shared_data_.get()));
+ context3d_ = context3d.get();
+
+ scoped_refptr<TestContextProvider> context_provider =
+ TestContextProvider::Create(
+ context3d.PassAs<TestWebGraphicsContext3D>());
+
+ output_surface_ = FakeOutputSurface::Create3d(context_provider);
break;
+ }
case ResourceProvider::Bitmap:
output_surface_ = FakeOutputSurface::CreateSoftware(
make_scoped_ptr(new SoftwareOutputDevice));
@@ -381,33 +419,33 @@ class ResourceProviderTest
NOTREACHED();
break;
}
- resource_provider_ = ResourceProvider::Create(output_surface_.get(), 0);
+ CHECK(output_surface_->BindToClient(&output_surface_client_));
+ resource_provider_ = ResourceProvider::Create(
+ output_surface_.get(), 0, false);
}
- ResourceProviderContext* context() {
- return static_cast<ResourceProviderContext*>(output_surface_->context3d());
+ static void CollectResources(ReturnedResourceArray* array,
+ const ReturnedResourceArray& returned) {
+ array->insert(array->end(), returned.begin(), returned.end());
}
- void SetResourceFilter(ResourceProvider* resource_provider,
- ResourceProvider::ResourceId id,
- WGC3Denum filter) {
+ static ReturnCallback GetReturnCallback(ReturnedResourceArray* array) {
+ return base::Bind(&ResourceProviderTest::CollectResources, array);
+ }
+
+ static void SetResourceFilter(ResourceProvider* resource_provider,
+ ResourceProvider::ResourceId id,
+ WGC3Denum filter) {
ResourceProvider::ScopedSamplerGL sampler(
resource_provider, id, GL_TEXTURE_2D, filter);
}
- WGC3Denum GetResourceFilter(ResourceProvider* resource_provider,
- ResourceProvider::ResourceId id) {
- DCHECK_EQ(GetParam(), ResourceProvider::GLTexture);
- ResourceProvider::ScopedReadLockGL lock_gl(resource_provider, id);
- EXPECT_NE(0u, lock_gl.texture_id());
- ResourceProviderContext* context = static_cast<ResourceProviderContext*>(
- resource_provider->GraphicsContext3D());
- context->bindTexture(GL_TEXTURE_2D, lock_gl.texture_id());
- return context->GetTextureFilter();
- }
+ ResourceProviderContext* context() { return context3d_; }
protected:
scoped_ptr<ContextSharedData> shared_data_;
+ ResourceProviderContext* context3d_;
+ FakeOutputSurfaceClient output_surface_client_;
scoped_ptr<OutputSurface> output_surface_;
scoped_ptr<ResourceProvider> resource_provider_;
};
@@ -418,12 +456,12 @@ void CheckCreateResource(ResourceProvider::ResourceType expected_default_type,
DCHECK_EQ(expected_default_type, resource_provider->default_resource_type());
gfx::Size size(1, 1);
- WGC3Denum format = GL_RGBA;
+ ResourceFormat format = RGBA_8888;
size_t pixel_size = TextureSize(size, format);
ASSERT_EQ(4U, pixel_size);
ResourceProvider::ResourceId id = resource_provider->CreateResource(
- size, format, ResourceProvider::TextureUsageAny);
+ size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format);
EXPECT_EQ(1, static_cast<int>(resource_provider->num_resources()));
if (expected_default_type == ResourceProvider::GLTexture)
EXPECT_EQ(0, context->texture_count());
@@ -450,12 +488,12 @@ TEST_P(ResourceProviderTest, Basic) {
TEST_P(ResourceProviderTest, Upload) {
gfx::Size size(2, 2);
- WGC3Denum format = GL_RGBA;
+ ResourceFormat format = RGBA_8888;
size_t pixel_size = TextureSize(size, format);
ASSERT_EQ(16U, pixel_size);
ResourceProvider::ResourceId id = resource_provider_->CreateResource(
- size, format, ResourceProvider::TextureUsageAny);
+ size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format);
uint8_t image[16] = { 0 };
gfx::Rect image_rect(size);
@@ -520,29 +558,38 @@ TEST_P(ResourceProviderTest, TransferResources) {
if (GetParam() != ResourceProvider::GLTexture)
return;
- scoped_ptr<OutputSurface> child_output_surface(FakeOutputSurface::Create3d(
- ResourceProviderContext::Create(shared_data_.get())
- .PassAs<WebKit::WebGraphicsContext3D>()));
+ scoped_ptr<ResourceProviderContext> child_context_owned(
+ ResourceProviderContext::Create(shared_data_.get()));
+ ResourceProviderContext* child_context = child_context_owned.get();
+
+ FakeOutputSurfaceClient child_output_surface_client;
+ scoped_ptr<OutputSurface> child_output_surface(
+ FakeOutputSurface::Create3d(
+ child_context_owned.PassAs<TestWebGraphicsContext3D>()));
+ CHECK(child_output_surface->BindToClient(&child_output_surface_client));
+
scoped_ptr<ResourceProvider> child_resource_provider(
- ResourceProvider::Create(child_output_surface.get(), 0));
+ ResourceProvider::Create(child_output_surface.get(), 0, false));
gfx::Size size(1, 1);
- WGC3Denum format = GL_RGBA;
+ ResourceFormat format = RGBA_8888;
size_t pixel_size = TextureSize(size, format);
ASSERT_EQ(4U, pixel_size);
ResourceProvider::ResourceId id1 = child_resource_provider->CreateResource(
- size, format, ResourceProvider::TextureUsageAny);
+ size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format);
uint8_t data1[4] = { 1, 2, 3, 4 };
gfx::Rect rect(size);
child_resource_provider->SetPixels(id1, data1, rect, rect, gfx::Vector2d());
ResourceProvider::ResourceId id2 = child_resource_provider->CreateResource(
- size, format, ResourceProvider::TextureUsageAny);
+ size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format);
uint8_t data2[4] = { 5, 5, 5, 5 };
child_resource_provider->SetPixels(id2, data2, rect, rect, gfx::Vector2d());
- int child_id = resource_provider_->CreateChild();
+ ReturnedResourceArray returned_to_child;
+ int child_id =
+ resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
{
// Transfer some resources to the parent.
ResourceProvider::ResourceIdArray resource_ids_to_transfer;
@@ -557,6 +604,8 @@ TEST_P(ResourceProviderTest, TransferResources) {
EXPECT_TRUE(child_resource_provider->InUseByConsumer(id1));
EXPECT_TRUE(child_resource_provider->InUseByConsumer(id2));
resource_provider_->ReceiveFromChild(child_id, list);
+ resource_provider_->DeclareUsedResourcesFromChild(child_id,
+ resource_ids_to_transfer);
}
EXPECT_EQ(2u, resource_provider_->num_resources());
@@ -579,32 +628,40 @@ TEST_P(ResourceProviderTest, TransferResources) {
EXPECT_EQ(0, memcmp(data2, result, pixel_size));
{
// Check that transfering again the same resource from the child to the
- // parent is a noop.
+ // parent works.
ResourceProvider::ResourceIdArray resource_ids_to_transfer;
resource_ids_to_transfer.push_back(id1);
TransferableResourceArray list;
child_resource_provider->PrepareSendToParent(resource_ids_to_transfer,
&list);
- EXPECT_EQ(0u, list.size());
+ EXPECT_EQ(1u, list.size());
+ EXPECT_EQ(id1, list[0].id);
+ ReturnedResourceArray returned;
+ TransferableResource::ReturnResources(list, &returned);
+ child_resource_provider->ReceiveReturnsFromParent(returned);
+ // id1 was exported twice, we returned it only once, it should still be
+ // in-use.
+ EXPECT_TRUE(child_resource_provider->InUseByConsumer(id1));
}
{
- // Transfer resources back from the parent to the child.
- ResourceProvider::ResourceIdArray resource_ids_to_transfer;
- resource_ids_to_transfer.push_back(mapped_id1);
- resource_ids_to_transfer.push_back(mapped_id2);
- TransferableResourceArray list;
- resource_provider_->PrepareSendToChild(
- child_id, resource_ids_to_transfer, &list);
- ASSERT_EQ(2u, list.size());
- EXPECT_NE(0u, list[0].sync_point);
- EXPECT_NE(0u, list[1].sync_point);
- child_resource_provider->ReceiveFromParent(list);
+ EXPECT_EQ(0u, returned_to_child.size());
+
+ // Transfer resources back from the parent to the child. Set no resources as
+ // being in use.
+ ResourceProvider::ResourceIdArray no_resources;
+ resource_provider_->DeclareUsedResourcesFromChild(child_id, no_resources);
+
+ ASSERT_EQ(2u, returned_to_child.size());
+ EXPECT_NE(0u, returned_to_child[0].sync_point);
+ EXPECT_NE(0u, returned_to_child[1].sync_point);
+ EXPECT_FALSE(returned_to_child[0].lost);
+ EXPECT_FALSE(returned_to_child[1].lost);
+ child_resource_provider->ReceiveReturnsFromParent(returned_to_child);
+ returned_to_child.clear();
}
EXPECT_FALSE(child_resource_provider->InUseByConsumer(id1));
EXPECT_FALSE(child_resource_provider->InUseByConsumer(id2));
- ResourceProviderContext* child_context =
- static_cast<ResourceProviderContext*>(child_output_surface->context3d());
{
ResourceProvider::ScopedReadLockGL lock(child_resource_provider.get(), id1);
ASSERT_NE(0U, lock.texture_id());
@@ -633,98 +690,267 @@ TEST_P(ResourceProviderTest, TransferResources) {
EXPECT_TRUE(child_resource_provider->InUseByConsumer(id1));
EXPECT_TRUE(child_resource_provider->InUseByConsumer(id2));
resource_provider_->ReceiveFromChild(child_id, list);
+ resource_provider_->DeclareUsedResourcesFromChild(child_id,
+ resource_ids_to_transfer);
}
+ EXPECT_EQ(0u, returned_to_child.size());
+
EXPECT_EQ(2u, resource_provider_->num_resources());
resource_provider_->DestroyChild(child_id);
EXPECT_EQ(0u, resource_provider_->num_resources());
+
+ ASSERT_EQ(2u, returned_to_child.size());
+ EXPECT_NE(0u, returned_to_child[0].sync_point);
+ EXPECT_NE(0u, returned_to_child[1].sync_point);
+ EXPECT_FALSE(returned_to_child[0].lost);
+ EXPECT_FALSE(returned_to_child[1].lost);
}
-TEST_P(ResourceProviderTest, DeleteTransferredResources) {
+TEST_P(ResourceProviderTest, DeleteExportedResources) {
// Resource transfer is only supported with GL textures for now.
if (GetParam() != ResourceProvider::GLTexture)
return;
+ scoped_ptr<ResourceProviderContext> child_context_owned(
+ ResourceProviderContext::Create(shared_data_.get()));
+
+ FakeOutputSurfaceClient child_output_surface_client;
scoped_ptr<OutputSurface> child_output_surface(FakeOutputSurface::Create3d(
- ResourceProviderContext::Create(shared_data_.get())
- .PassAs<WebKit::WebGraphicsContext3D>()));
+ child_context_owned.PassAs<TestWebGraphicsContext3D>()));
+ CHECK(child_output_surface->BindToClient(&child_output_surface_client));
+
scoped_ptr<ResourceProvider> child_resource_provider(
- ResourceProvider::Create(child_output_surface.get(), 0));
+ ResourceProvider::Create(child_output_surface.get(), 0, false));
gfx::Size size(1, 1);
- WGC3Denum format = GL_RGBA;
+ ResourceFormat format = RGBA_8888;
size_t pixel_size = TextureSize(size, format);
ASSERT_EQ(4U, pixel_size);
- ResourceProvider::ResourceId id = child_resource_provider->CreateResource(
- size, format, ResourceProvider::TextureUsageAny);
- uint8_t data[4] = { 1, 2, 3, 4 };
+ ResourceProvider::ResourceId id1 = child_resource_provider->CreateResource(
+ size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format);
+ uint8_t data1[4] = {1, 2, 3, 4};
gfx::Rect rect(size);
- child_resource_provider->SetPixels(id, data, rect, rect, gfx::Vector2d());
+ child_resource_provider->SetPixels(id1, data1, rect, rect, gfx::Vector2d());
+
+ ResourceProvider::ResourceId id2 = child_resource_provider->CreateResource(
+ size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format);
+ uint8_t data2[4] = {5, 5, 5, 5};
+ child_resource_provider->SetPixels(id2, data2, rect, rect, gfx::Vector2d());
- int child_id = resource_provider_->CreateChild();
+ ReturnedResourceArray returned_to_child;
+ int child_id =
+ resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
{
- // Transfer some resource to the parent.
+ // Transfer some resources to the parent.
ResourceProvider::ResourceIdArray resource_ids_to_transfer;
- resource_ids_to_transfer.push_back(id);
+ resource_ids_to_transfer.push_back(id1);
+ resource_ids_to_transfer.push_back(id2);
TransferableResourceArray list;
child_resource_provider->PrepareSendToParent(resource_ids_to_transfer,
&list);
- ASSERT_EQ(1u, list.size());
+ ASSERT_EQ(2u, list.size());
EXPECT_NE(0u, list[0].sync_point);
- EXPECT_TRUE(child_resource_provider->InUseByConsumer(id));
+ EXPECT_NE(0u, list[1].sync_point);
+ EXPECT_TRUE(child_resource_provider->InUseByConsumer(id1));
+ EXPECT_TRUE(child_resource_provider->InUseByConsumer(id2));
resource_provider_->ReceiveFromChild(child_id, list);
+ resource_provider_->DeclareUsedResourcesFromChild(child_id,
+ resource_ids_to_transfer);
}
- // Delete textures in the child, while they are transfered.
- child_resource_provider->DeleteResource(id);
- EXPECT_EQ(1u, child_resource_provider->num_resources());
+ EXPECT_EQ(2u, resource_provider_->num_resources());
+ ResourceProvider::ResourceIdMap resource_map =
+ resource_provider_->GetChildToParentMap(child_id);
+ ResourceProvider::ResourceId mapped_id1 = resource_map[id1];
+ ResourceProvider::ResourceId mapped_id2 = resource_map[id2];
+ EXPECT_NE(0u, mapped_id1);
+ EXPECT_NE(0u, mapped_id2);
+ EXPECT_FALSE(resource_provider_->InUseByConsumer(id1));
+ EXPECT_FALSE(resource_provider_->InUseByConsumer(id2));
+
{
- // Transfer resources back from the parent to the child.
- ResourceProvider::ResourceIdMap resource_map =
- resource_provider_->GetChildToParentMap(child_id);
- ResourceProvider::ResourceId mapped_id = resource_map[id];
- EXPECT_NE(0u, mapped_id);
+ // The parent transfers the resources to the grandparent.
ResourceProvider::ResourceIdArray resource_ids_to_transfer;
- resource_ids_to_transfer.push_back(mapped_id);
+ resource_ids_to_transfer.push_back(mapped_id1);
+ resource_ids_to_transfer.push_back(mapped_id2);
TransferableResourceArray list;
- resource_provider_->PrepareSendToChild(
- child_id, resource_ids_to_transfer, &list);
- ASSERT_EQ(1u, list.size());
+ resource_provider_->PrepareSendToParent(resource_ids_to_transfer, &list);
+
+ ASSERT_EQ(2u, list.size());
EXPECT_NE(0u, list[0].sync_point);
- child_resource_provider->ReceiveFromParent(list);
+ EXPECT_NE(0u, list[1].sync_point);
+ EXPECT_TRUE(resource_provider_->InUseByConsumer(id1));
+ EXPECT_TRUE(resource_provider_->InUseByConsumer(id2));
+
+ // Release the resource in the parent. Set no resources as being in use. The
+ // resources are exported so that can't be transferred back yet.
+ ResourceProvider::ResourceIdArray no_resources;
+ resource_provider_->DeclareUsedResourcesFromChild(child_id, no_resources);
+
+ EXPECT_EQ(0u, returned_to_child.size());
+ EXPECT_EQ(2u, resource_provider_->num_resources());
+
+ // Return the resources from the grandparent to the parent. They should be
+ // returned to the child then.
+ EXPECT_EQ(2u, list.size());
+ EXPECT_EQ(mapped_id1, list[0].id);
+ EXPECT_EQ(mapped_id2, list[1].id);
+ ReturnedResourceArray returned;
+ TransferableResource::ReturnResources(list, &returned);
+ resource_provider_->ReceiveReturnsFromParent(returned);
+
+ EXPECT_EQ(0u, resource_provider_->num_resources());
+ ASSERT_EQ(2u, returned_to_child.size());
+ EXPECT_NE(0u, returned_to_child[0].sync_point);
+ EXPECT_NE(0u, returned_to_child[1].sync_point);
+ EXPECT_FALSE(returned_to_child[0].lost);
+ EXPECT_FALSE(returned_to_child[1].lost);
}
- EXPECT_EQ(0u, child_resource_provider->num_resources());
}
-TEST_P(ResourceProviderTest, TextureFilters) {
+TEST_P(ResourceProviderTest, DestroyChildWithExportedResources) {
// Resource transfer is only supported with GL textures for now.
if (GetParam() != ResourceProvider::GLTexture)
return;
+ scoped_ptr<ResourceProviderContext> child_context_owned(
+ ResourceProviderContext::Create(shared_data_.get()));
+
+ FakeOutputSurfaceClient child_output_surface_client;
scoped_ptr<OutputSurface> child_output_surface(FakeOutputSurface::Create3d(
- ResourceProviderContext::Create(shared_data_.get())
- .PassAs<WebKit::WebGraphicsContext3D>()));
+ child_context_owned.PassAs<TestWebGraphicsContext3D>()));
+ CHECK(child_output_surface->BindToClient(&child_output_surface_client));
+
scoped_ptr<ResourceProvider> child_resource_provider(
- ResourceProvider::Create(child_output_surface.get(), 0));
+ ResourceProvider::Create(child_output_surface.get(), 0, false));
gfx::Size size(1, 1);
- WGC3Denum format = GL_RGBA;
+ ResourceFormat format = RGBA_8888;
+ size_t pixel_size = TextureSize(size, format);
+ ASSERT_EQ(4U, pixel_size);
+
+ ResourceProvider::ResourceId id1 = child_resource_provider->CreateResource(
+ size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format);
+ uint8_t data1[4] = {1, 2, 3, 4};
+ gfx::Rect rect(size);
+ child_resource_provider->SetPixels(id1, data1, rect, rect, gfx::Vector2d());
+
+ ResourceProvider::ResourceId id2 = child_resource_provider->CreateResource(
+ size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format);
+ uint8_t data2[4] = {5, 5, 5, 5};
+ child_resource_provider->SetPixels(id2, data2, rect, rect, gfx::Vector2d());
+
+ ReturnedResourceArray returned_to_child;
+ int child_id =
+ resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
+ {
+ // Transfer some resources to the parent.
+ ResourceProvider::ResourceIdArray resource_ids_to_transfer;
+ resource_ids_to_transfer.push_back(id1);
+ resource_ids_to_transfer.push_back(id2);
+ TransferableResourceArray list;
+ child_resource_provider->PrepareSendToParent(resource_ids_to_transfer,
+ &list);
+ ASSERT_EQ(2u, list.size());
+ EXPECT_NE(0u, list[0].sync_point);
+ EXPECT_NE(0u, list[1].sync_point);
+ EXPECT_TRUE(child_resource_provider->InUseByConsumer(id1));
+ EXPECT_TRUE(child_resource_provider->InUseByConsumer(id2));
+ resource_provider_->ReceiveFromChild(child_id, list);
+ resource_provider_->DeclareUsedResourcesFromChild(child_id,
+ resource_ids_to_transfer);
+ }
+
+ EXPECT_EQ(2u, resource_provider_->num_resources());
+ ResourceProvider::ResourceIdMap resource_map =
+ resource_provider_->GetChildToParentMap(child_id);
+ ResourceProvider::ResourceId mapped_id1 = resource_map[id1];
+ ResourceProvider::ResourceId mapped_id2 = resource_map[id2];
+ EXPECT_NE(0u, mapped_id1);
+ EXPECT_NE(0u, mapped_id2);
+ EXPECT_FALSE(resource_provider_->InUseByConsumer(id1));
+ EXPECT_FALSE(resource_provider_->InUseByConsumer(id2));
+
+ {
+ // The parent transfers the resources to the grandparent.
+ ResourceProvider::ResourceIdArray resource_ids_to_transfer;
+ resource_ids_to_transfer.push_back(mapped_id1);
+ resource_ids_to_transfer.push_back(mapped_id2);
+ TransferableResourceArray list;
+ resource_provider_->PrepareSendToParent(resource_ids_to_transfer, &list);
+
+ ASSERT_EQ(2u, list.size());
+ EXPECT_NE(0u, list[0].sync_point);
+ EXPECT_NE(0u, list[1].sync_point);
+ EXPECT_TRUE(resource_provider_->InUseByConsumer(id1));
+ EXPECT_TRUE(resource_provider_->InUseByConsumer(id2));
+
+ // Release the resource in the parent. Set no resources as being in use. The
+ // resources are exported so that can't be transferred back yet.
+ ResourceProvider::ResourceIdArray no_resources;
+ resource_provider_->DeclareUsedResourcesFromChild(child_id, no_resources);
+
+ // Destroy the child, the resources should be returned immediately from the
+ // parent and marked as lost.
+ EXPECT_EQ(0u, returned_to_child.size());
+ EXPECT_EQ(2u, resource_provider_->num_resources());
+
+ resource_provider_->DestroyChild(child_id);
+
+ EXPECT_EQ(0u, resource_provider_->num_resources());
+ ASSERT_EQ(2u, returned_to_child.size());
+ EXPECT_NE(0u, returned_to_child[0].sync_point);
+ EXPECT_NE(0u, returned_to_child[1].sync_point);
+ EXPECT_TRUE(returned_to_child[0].lost);
+ EXPECT_TRUE(returned_to_child[1].lost);
+ returned_to_child.clear();
+
+ // Return the resources from the grandparent to the parent. They should be
+ // dropped on the floor since they were already returned to the child.
+ EXPECT_EQ(2u, list.size());
+ EXPECT_EQ(mapped_id1, list[0].id);
+ EXPECT_EQ(mapped_id2, list[1].id);
+ ReturnedResourceArray returned;
+ TransferableResource::ReturnResources(list, &returned);
+ resource_provider_->ReceiveReturnsFromParent(returned);
+
+ EXPECT_EQ(0u, returned_to_child.size());
+ }
+}
+
+TEST_P(ResourceProviderTest, DeleteTransferredResources) {
+ // Resource transfer is only supported with GL textures for now.
+ if (GetParam() != ResourceProvider::GLTexture)
+ return;
+
+ scoped_ptr<ResourceProviderContext> child_context_owned(
+ ResourceProviderContext::Create(shared_data_.get()));
+
+ FakeOutputSurfaceClient child_output_surface_client;
+ scoped_ptr<OutputSurface> child_output_surface(
+ FakeOutputSurface::Create3d(
+ child_context_owned.PassAs<TestWebGraphicsContext3D>()));
+ CHECK(child_output_surface->BindToClient(&child_output_surface_client));
+
+ scoped_ptr<ResourceProvider> child_resource_provider(
+ ResourceProvider::Create(child_output_surface.get(), 0, false));
+
+ gfx::Size size(1, 1);
+ ResourceFormat format = RGBA_8888;
size_t pixel_size = TextureSize(size, format);
ASSERT_EQ(4U, pixel_size);
ResourceProvider::ResourceId id = child_resource_provider->CreateResource(
- size, format, ResourceProvider::TextureUsageAny);
+ size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format);
uint8_t data[4] = { 1, 2, 3, 4 };
gfx::Rect rect(size);
child_resource_provider->SetPixels(id, data, rect, rect, gfx::Vector2d());
- EXPECT_EQ(static_cast<unsigned>(GL_LINEAR),
- GetResourceFilter(child_resource_provider.get(), id));
- SetResourceFilter(child_resource_provider.get(), id, GL_NEAREST);
- EXPECT_EQ(static_cast<unsigned>(GL_NEAREST),
- GetResourceFilter(child_resource_provider.get(), id));
- int child_id = resource_provider_->CreateChild();
+ ReturnedResourceArray returned_to_child;
+ int child_id =
+ resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
{
// Transfer some resource to the parent.
ResourceProvider::ResourceIdArray resource_ids_to_transfer;
@@ -733,34 +959,196 @@ TEST_P(ResourceProviderTest, TextureFilters) {
child_resource_provider->PrepareSendToParent(resource_ids_to_transfer,
&list);
ASSERT_EQ(1u, list.size());
- EXPECT_EQ(static_cast<unsigned>(GL_NEAREST), list[0].filter);
+ EXPECT_NE(0u, list[0].sync_point);
+ EXPECT_TRUE(child_resource_provider->InUseByConsumer(id));
resource_provider_->ReceiveFromChild(child_id, list);
+ resource_provider_->DeclareUsedResourcesFromChild(child_id,
+ resource_ids_to_transfer);
}
- ResourceProvider::ResourceIdMap resource_map =
- resource_provider_->GetChildToParentMap(child_id);
- ResourceProvider::ResourceId mapped_id = resource_map[id];
- EXPECT_NE(0u, mapped_id);
- EXPECT_EQ(static_cast<unsigned>(GL_NEAREST),
- GetResourceFilter(resource_provider_.get(), mapped_id));
- SetResourceFilter(resource_provider_.get(), mapped_id, GL_LINEAR);
- EXPECT_EQ(static_cast<unsigned>(GL_LINEAR),
- GetResourceFilter(resource_provider_.get(), mapped_id));
+
+ // Delete textures in the child, while they are transfered.
+ child_resource_provider->DeleteResource(id);
+ EXPECT_EQ(1u, child_resource_provider->num_resources());
{
- // Transfer resources back from the parent to the child.
- ResourceProvider::ResourceIdArray resource_ids_to_transfer;
- resource_ids_to_transfer.push_back(mapped_id);
- TransferableResourceArray list;
- resource_provider_->PrepareSendToChild(
- child_id, resource_ids_to_transfer, &list);
- ASSERT_EQ(1u, list.size());
- EXPECT_EQ(static_cast<unsigned>(GL_LINEAR), list[0].filter);
- child_resource_provider->ReceiveFromParent(list);
- }
- EXPECT_EQ(static_cast<unsigned>(GL_LINEAR),
- GetResourceFilter(child_resource_provider.get(), id));
- SetResourceFilter(child_resource_provider.get(), id, GL_NEAREST);
- EXPECT_EQ(static_cast<unsigned>(GL_NEAREST),
- GetResourceFilter(child_resource_provider.get(), id));
+ EXPECT_EQ(0u, returned_to_child.size());
+
+ // Transfer resources back from the parent to the child. Set no resources as
+ // being in use.
+ ResourceProvider::ResourceIdArray no_resources;
+ resource_provider_->DeclareUsedResourcesFromChild(child_id, no_resources);
+
+ ASSERT_EQ(1u, returned_to_child.size());
+ EXPECT_NE(0u, returned_to_child[0].sync_point);
+ child_resource_provider->ReceiveReturnsFromParent(returned_to_child);
+ }
+ EXPECT_EQ(0u, child_resource_provider->num_resources());
+}
+
+class ResourceProviderTestTextureFilters : public ResourceProviderTest {
+ public:
+ static void RunTest(GLenum child_filter, GLenum parent_filter) {
+ scoped_ptr<TextureStateTrackingContext> child_context_owned(
+ new TextureStateTrackingContext);
+ TextureStateTrackingContext* child_context = child_context_owned.get();
+
+ FakeOutputSurfaceClient child_output_surface_client;
+ scoped_ptr<OutputSurface> child_output_surface(FakeOutputSurface::Create3d(
+ child_context_owned.PassAs<TestWebGraphicsContext3D>()));
+ CHECK(child_output_surface->BindToClient(&child_output_surface_client));
+
+ scoped_ptr<ResourceProvider> child_resource_provider(
+ ResourceProvider::Create(child_output_surface.get(), 0, false));
+
+ scoped_ptr<TextureStateTrackingContext> parent_context_owned(
+ new TextureStateTrackingContext);
+ TextureStateTrackingContext* parent_context = parent_context_owned.get();
+
+ FakeOutputSurfaceClient parent_output_surface_client;
+ scoped_ptr<OutputSurface> parent_output_surface(FakeOutputSurface::Create3d(
+ parent_context_owned.PassAs<TestWebGraphicsContext3D>()));
+ CHECK(parent_output_surface->BindToClient(&parent_output_surface_client));
+
+ scoped_ptr<ResourceProvider> parent_resource_provider(
+ ResourceProvider::Create(parent_output_surface.get(), 0, false));
+
+ gfx::Size size(1, 1);
+ ResourceFormat format = RGBA_8888;
+ int texture_id = 1;
+
+ size_t pixel_size = TextureSize(size, format);
+ ASSERT_EQ(4U, pixel_size);
+
+ ResourceProvider::ResourceId id = child_resource_provider->CreateResource(
+ size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format);
+
+ // The new texture is created with GL_LINEAR.
+ EXPECT_CALL(*child_context, bindTexture(GL_TEXTURE_2D, texture_id))
+ .Times(2); // Once to create and once to allocate.
+ EXPECT_CALL(*child_context,
+ texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
+ EXPECT_CALL(*child_context,
+ texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
+ EXPECT_CALL(
+ *child_context,
+ texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
+ EXPECT_CALL(
+ *child_context,
+ texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
+ EXPECT_CALL(*child_context,
+ texParameteri(GL_TEXTURE_2D,
+ GL_TEXTURE_POOL_CHROMIUM,
+ GL_TEXTURE_POOL_UNMANAGED_CHROMIUM));
+ child_resource_provider->AllocateForTesting(id);
+ Mock::VerifyAndClearExpectations(child_context);
+
+ uint8_t data[4] = { 1, 2, 3, 4 };
+ gfx::Rect rect(size);
+
+ EXPECT_CALL(*child_context, bindTexture(GL_TEXTURE_2D, texture_id));
+ child_resource_provider->SetPixels(id, data, rect, rect, gfx::Vector2d());
+ Mock::VerifyAndClearExpectations(child_context);
+
+ // The texture is set to |child_filter| in the child.
+ EXPECT_CALL(*child_context, bindTexture(GL_TEXTURE_2D, texture_id));
+ if (child_filter != GL_LINEAR) {
+ EXPECT_CALL(
+ *child_context,
+ texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, child_filter));
+ EXPECT_CALL(
+ *child_context,
+ texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, child_filter));
+ }
+ SetResourceFilter(child_resource_provider.get(), id, child_filter);
+ Mock::VerifyAndClearExpectations(child_context);
+
+ ReturnedResourceArray returned_to_child;
+ int child_id = parent_resource_provider->CreateChild(
+ GetReturnCallback(&returned_to_child));
+ {
+ // Transfer some resource to the parent.
+ ResourceProvider::ResourceIdArray resource_ids_to_transfer;
+ resource_ids_to_transfer.push_back(id);
+ TransferableResourceArray list;
+
+ EXPECT_CALL(*child_context, bindTexture(GL_TEXTURE_2D, texture_id));
+ EXPECT_CALL(*child_context,
+ produceTextureCHROMIUM(GL_TEXTURE_2D, _));
+ EXPECT_CALL(*child_context, insertSyncPoint());
+ child_resource_provider->PrepareSendToParent(resource_ids_to_transfer,
+ &list);
+ Mock::VerifyAndClearExpectations(child_context);
+
+ ASSERT_EQ(1u, list.size());
+ EXPECT_EQ(static_cast<unsigned>(child_filter), list[0].filter);
+
+ EXPECT_CALL(*parent_context, bindTexture(GL_TEXTURE_2D, texture_id));
+ EXPECT_CALL(*parent_context,
+ consumeTextureCHROMIUM(GL_TEXTURE_2D, _));
+ parent_resource_provider->ReceiveFromChild(child_id, list);
+ Mock::VerifyAndClearExpectations(parent_context);
+
+ parent_resource_provider->DeclareUsedResourcesFromChild(
+ child_id, resource_ids_to_transfer);
+ Mock::VerifyAndClearExpectations(parent_context);
+ }
+ ResourceProvider::ResourceIdMap resource_map =
+ parent_resource_provider->GetChildToParentMap(child_id);
+ ResourceProvider::ResourceId mapped_id = resource_map[id];
+ EXPECT_NE(0u, mapped_id);
+
+ // The texture is set to |parent_filter| in the parent.
+ EXPECT_CALL(*parent_context, bindTexture(GL_TEXTURE_2D, texture_id));
+ EXPECT_CALL(
+ *parent_context,
+ texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, parent_filter));
+ EXPECT_CALL(
+ *parent_context,
+ texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, parent_filter));
+ SetResourceFilter(parent_resource_provider.get(), mapped_id, parent_filter);
+ Mock::VerifyAndClearExpectations(parent_context);
+
+ // The texture should be reset to |child_filter| in the parent when it is
+ // returned, since that is how it was received.
+ EXPECT_CALL(*parent_context, bindTexture(GL_TEXTURE_2D, texture_id));
+ EXPECT_CALL(
+ *parent_context,
+ texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, child_filter));
+ EXPECT_CALL(
+ *parent_context,
+ texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, child_filter));
+
+ {
+ EXPECT_EQ(0u, returned_to_child.size());
+
+ // Transfer resources back from the parent to the child. Set no resources
+ // as being in use.
+ ResourceProvider::ResourceIdArray no_resources;
+ EXPECT_CALL(*parent_context, insertSyncPoint());
+ parent_resource_provider->DeclareUsedResourcesFromChild(child_id,
+ no_resources);
+ Mock::VerifyAndClearExpectations(parent_context);
+
+ ASSERT_EQ(1u, returned_to_child.size());
+ child_resource_provider->ReceiveReturnsFromParent(returned_to_child);
+ }
+
+ // The child remembers the texture filter is set to |child_filter|.
+ EXPECT_CALL(*child_context, bindTexture(GL_TEXTURE_2D, texture_id));
+ SetResourceFilter(child_resource_provider.get(), id, child_filter);
+ Mock::VerifyAndClearExpectations(child_context);
+ }
+};
+
+TEST_P(ResourceProviderTest, TextureFilters_ChildNearestParentLinear) {
+ if (GetParam() != ResourceProvider::GLTexture)
+ return;
+ ResourceProviderTestTextureFilters::RunTest(GL_NEAREST, GL_LINEAR);
+}
+
+TEST_P(ResourceProviderTest, TextureFilters_ChildLinearParentNearest) {
+ if (GetParam() != ResourceProvider::GLTexture)
+ return;
+ ResourceProviderTestTextureFilters::RunTest(GL_LINEAR, GL_NEAREST);
}
void ReleaseTextureMailbox(unsigned* release_sync_point,
@@ -790,11 +1178,12 @@ TEST_P(ResourceProviderTest, TransferMailboxResources) {
unsigned release_sync_point = 0;
bool lost_resource = false;
- TextureMailbox::ReleaseCallback callback =
+ ReleaseCallback callback =
base::Bind(ReleaseTextureMailbox, &release_sync_point, &lost_resource);
ResourceProvider::ResourceId resource =
resource_provider_->CreateResourceFromTextureMailbox(
- TextureMailbox(mailbox, callback, sync_point));
+ TextureMailbox(mailbox, sync_point),
+ SingleReleaseCallback::Create(callback));
EXPECT_EQ(1, context()->texture_count());
EXPECT_EQ(0u, release_sync_point);
{
@@ -814,7 +1203,8 @@ TEST_P(ResourceProviderTest, TransferMailboxResources) {
context()->bindTexture(GL_TEXTURE_2D, other_texture);
context()->consumeTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
uint8_t test_data[4] = { 0 };
- context()->GetPixels(gfx::Size(1, 1), GL_RGBA, test_data);
+ context()->GetPixels(
+ gfx::Size(1, 1), RGBA_8888, test_data);
EXPECT_EQ(0, memcmp(data, test_data, sizeof(data)));
context()->produceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
context()->deleteTexture(other_texture);
@@ -823,7 +1213,9 @@ TEST_P(ResourceProviderTest, TransferMailboxResources) {
// Receive the resource, then delete it, expect the sync points to be
// consistent.
- resource_provider_->ReceiveFromParent(list);
+ ReturnedResourceArray returned;
+ TransferableResource::ReturnResources(list, &returned);
+ resource_provider_->ReceiveReturnsFromParent(returned);
EXPECT_EQ(1, context()->texture_count());
EXPECT_EQ(0u, release_sync_point);
@@ -838,7 +1230,8 @@ TEST_P(ResourceProviderTest, TransferMailboxResources) {
EXPECT_LT(0u, sync_point);
release_sync_point = 0;
resource = resource_provider_->CreateResourceFromTextureMailbox(
- TextureMailbox(mailbox, callback, sync_point));
+ TextureMailbox(mailbox, sync_point),
+ SingleReleaseCallback::Create(callback));
EXPECT_EQ(1, context()->texture_count());
EXPECT_EQ(0u, release_sync_point);
{
@@ -858,7 +1251,8 @@ TEST_P(ResourceProviderTest, TransferMailboxResources) {
context()->bindTexture(GL_TEXTURE_2D, other_texture);
context()->consumeTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
uint8_t test_data[4] = { 0 };
- context()->GetPixels(gfx::Size(1, 1), GL_RGBA, test_data);
+ context()->GetPixels(
+ gfx::Size(1, 1), RGBA_8888, test_data);
EXPECT_EQ(0, memcmp(data, test_data, sizeof(data)));
context()->produceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
context()->deleteTexture(other_texture);
@@ -872,7 +1266,9 @@ TEST_P(ResourceProviderTest, TransferMailboxResources) {
// Then receive the resource which should release the mailbox, expect the
// sync points to be consistent.
- resource_provider_->ReceiveFromParent(list);
+ ReturnedResourceArray returned;
+ TransferableResource::ReturnResources(list, &returned);
+ resource_provider_->ReceiveReturnsFromParent(returned);
EXPECT_LE(list[0].sync_point, release_sync_point);
EXPECT_FALSE(lost_resource);
}
@@ -883,6 +1279,330 @@ TEST_P(ResourceProviderTest, TransferMailboxResources) {
context()->deleteTexture(texture);
}
+TEST_P(ResourceProviderTest, LostResourceInParent) {
+ // Resource transfer is only supported with GL textures for now.
+ if (GetParam() != ResourceProvider::GLTexture)
+ return;
+
+ scoped_ptr<ResourceProviderContext> child_context_owned(
+ ResourceProviderContext::Create(shared_data_.get()));
+
+ FakeOutputSurfaceClient child_output_surface_client;
+ scoped_ptr<OutputSurface> child_output_surface(FakeOutputSurface::Create3d(
+ child_context_owned.PassAs<TestWebGraphicsContext3D>()));
+ CHECK(child_output_surface->BindToClient(&child_output_surface_client));
+
+ scoped_ptr<ResourceProvider> child_resource_provider(
+ ResourceProvider::Create(child_output_surface.get(), 0, false));
+
+ gfx::Size size(1, 1);
+ ResourceFormat format = RGBA_8888;
+ ResourceProvider::ResourceId resource =
+ child_resource_provider->CreateResource(
+ size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format);
+ child_resource_provider->AllocateForTesting(resource);
+
+ ReturnedResourceArray returned_to_child;
+ int child_id =
+ resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
+ {
+ // Transfer the resource to the parent.
+ ResourceProvider::ResourceIdArray resource_ids_to_transfer;
+ resource_ids_to_transfer.push_back(resource);
+ TransferableResourceArray list;
+ child_resource_provider->PrepareSendToParent(resource_ids_to_transfer,
+ &list);
+ EXPECT_EQ(1u, list.size());
+
+ resource_provider_->ReceiveFromChild(child_id, list);
+ resource_provider_->DeclareUsedResourcesFromChild(child_id,
+ resource_ids_to_transfer);
+ }
+
+ // Lose the output surface in the parent.
+ resource_provider_->DidLoseOutputSurface();
+
+ {
+ EXPECT_EQ(0u, returned_to_child.size());
+
+ // Transfer resources back from the parent to the child. Set no resources as
+ // being in use.
+ ResourceProvider::ResourceIdArray no_resources;
+ resource_provider_->DeclareUsedResourcesFromChild(child_id, no_resources);
+
+ // Expect the resource to be lost.
+ ASSERT_EQ(1u, returned_to_child.size());
+ EXPECT_TRUE(returned_to_child[0].lost);
+ child_resource_provider->ReceiveReturnsFromParent(returned_to_child);
+ returned_to_child.clear();
+ }
+
+ // The resource should be lost.
+ EXPECT_TRUE(child_resource_provider->IsLost(resource));
+
+ // Lost resources stay in use in the parent forever.
+ EXPECT_TRUE(child_resource_provider->InUseByConsumer(resource));
+}
+
+TEST_P(ResourceProviderTest, LostResourceInGrandParent) {
+ // Resource transfer is only supported with GL textures for now.
+ if (GetParam() != ResourceProvider::GLTexture)
+ return;
+
+ scoped_ptr<ResourceProviderContext> child_context_owned(
+ ResourceProviderContext::Create(shared_data_.get()));
+
+ FakeOutputSurfaceClient child_output_surface_client;
+ scoped_ptr<OutputSurface> child_output_surface(FakeOutputSurface::Create3d(
+ child_context_owned.PassAs<TestWebGraphicsContext3D>()));
+ CHECK(child_output_surface->BindToClient(&child_output_surface_client));
+
+ scoped_ptr<ResourceProvider> child_resource_provider(
+ ResourceProvider::Create(child_output_surface.get(), 0, false));
+
+ gfx::Size size(1, 1);
+ ResourceFormat format = RGBA_8888;
+ ResourceProvider::ResourceId resource =
+ child_resource_provider->CreateResource(
+ size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format);
+ child_resource_provider->AllocateForTesting(resource);
+
+ ReturnedResourceArray returned_to_child;
+ int child_id =
+ resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
+ {
+ // Transfer the resource to the parent.
+ ResourceProvider::ResourceIdArray resource_ids_to_transfer;
+ resource_ids_to_transfer.push_back(resource);
+ TransferableResourceArray list;
+ child_resource_provider->PrepareSendToParent(resource_ids_to_transfer,
+ &list);
+ EXPECT_EQ(1u, list.size());
+
+ resource_provider_->ReceiveFromChild(child_id, list);
+ resource_provider_->DeclareUsedResourcesFromChild(child_id,
+ resource_ids_to_transfer);
+ }
+
+ {
+ ResourceProvider::ResourceIdMap resource_map =
+ resource_provider_->GetChildToParentMap(child_id);
+ ResourceProvider::ResourceId parent_resource = resource_map[resource];
+ EXPECT_NE(0u, parent_resource);
+
+ // Transfer to a grandparent.
+ ResourceProvider::ResourceIdArray resource_ids_to_transfer;
+ resource_ids_to_transfer.push_back(parent_resource);
+ TransferableResourceArray list;
+ resource_provider_->PrepareSendToParent(resource_ids_to_transfer, &list);
+
+ // Receive back a lost resource from the grandparent.
+ EXPECT_EQ(1u, list.size());
+ EXPECT_EQ(parent_resource, list[0].id);
+ ReturnedResourceArray returned;
+ TransferableResource::ReturnResources(list, &returned);
+ EXPECT_EQ(1u, returned.size());
+ EXPECT_EQ(parent_resource, returned[0].id);
+ returned[0].lost = true;
+ resource_provider_->ReceiveReturnsFromParent(returned);
+
+ // The resource should be lost.
+ EXPECT_TRUE(resource_provider_->IsLost(parent_resource));
+
+ // Lost resources stay in use in the parent forever.
+ EXPECT_TRUE(resource_provider_->InUseByConsumer(parent_resource));
+ }
+
+ {
+ EXPECT_EQ(0u, returned_to_child.size());
+
+ // Transfer resources back from the parent to the child. Set no resources as
+ // being in use.
+ ResourceProvider::ResourceIdArray no_resources;
+ resource_provider_->DeclareUsedResourcesFromChild(child_id, no_resources);
+
+ // Expect the resource to be lost.
+ ASSERT_EQ(1u, returned_to_child.size());
+ EXPECT_TRUE(returned_to_child[0].lost);
+ child_resource_provider->ReceiveReturnsFromParent(returned_to_child);
+ returned_to_child.clear();
+ }
+
+ // The resource should be lost.
+ EXPECT_TRUE(child_resource_provider->IsLost(resource));
+
+ // Lost resources stay in use in the parent forever.
+ EXPECT_TRUE(child_resource_provider->InUseByConsumer(resource));
+}
+
+TEST_P(ResourceProviderTest, LostMailboxInParent) {
+ // Resource transfer is only supported with GL textures for now.
+ if (GetParam() != ResourceProvider::GLTexture)
+ return;
+
+ scoped_ptr<ResourceProviderContext> child_context_owned(
+ ResourceProviderContext::Create(shared_data_.get()));
+ ResourceProviderContext* child_context = child_context_owned.get();
+
+ FakeOutputSurfaceClient child_output_surface_client;
+ scoped_ptr<OutputSurface> child_output_surface(FakeOutputSurface::Create3d(
+ child_context_owned.PassAs<TestWebGraphicsContext3D>()));
+ CHECK(child_output_surface->BindToClient(&child_output_surface_client));
+
+ scoped_ptr<ResourceProvider> child_resource_provider(
+ ResourceProvider::Create(child_output_surface.get(), 0, false));
+
+ unsigned texture = child_context->createTexture();
+ gpu::Mailbox gpu_mailbox;
+ child_context->bindTexture(GL_TEXTURE_2D, texture);
+ child_context->genMailboxCHROMIUM(gpu_mailbox.name);
+ child_context->produceTextureCHROMIUM(GL_TEXTURE_2D, gpu_mailbox.name);
+
+ unsigned release_sync_point = 0;
+ bool lost_resource = false;
+ ReleaseCallback callback =
+ base::Bind(ReleaseTextureMailbox, &release_sync_point, &lost_resource);
+ ResourceProvider::ResourceId resource =
+ child_resource_provider->CreateResourceFromTextureMailbox(
+ TextureMailbox(gpu_mailbox), SingleReleaseCallback::Create(callback));
+
+ ReturnedResourceArray returned_to_child;
+ int child_id =
+ resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
+ {
+ // Transfer the resource to the parent.
+ ResourceProvider::ResourceIdArray resource_ids_to_transfer;
+ resource_ids_to_transfer.push_back(resource);
+ TransferableResourceArray list;
+ child_resource_provider->PrepareSendToParent(resource_ids_to_transfer,
+ &list);
+ EXPECT_EQ(1u, list.size());
+
+ resource_provider_->ReceiveFromChild(child_id, list);
+ resource_provider_->DeclareUsedResourcesFromChild(child_id,
+ resource_ids_to_transfer);
+ }
+
+ // Lose the output surface in the parent.
+ resource_provider_->DidLoseOutputSurface();
+
+ {
+ EXPECT_EQ(0u, returned_to_child.size());
+
+ // Transfer resources back from the parent to the child. Set no resources as
+ // being in use.
+ ResourceProvider::ResourceIdArray no_resources;
+ resource_provider_->DeclareUsedResourcesFromChild(child_id, no_resources);
+
+ // Expect the resource to be lost.
+ ASSERT_EQ(1u, returned_to_child.size());
+ EXPECT_TRUE(returned_to_child[0].lost);
+ child_resource_provider->ReceiveReturnsFromParent(returned_to_child);
+ returned_to_child.clear();
+ }
+
+ // Delete the resource in the child. Expect the resource to be lost.
+ child_resource_provider->DeleteResource(resource);
+ EXPECT_TRUE(lost_resource);
+
+ child_context->waitSyncPoint(release_sync_point);
+ child_context->deleteTexture(texture);
+}
+
+TEST_P(ResourceProviderTest, LostMailboxInGrandParent) {
+ // Resource transfer is only supported with GL textures for now.
+ if (GetParam() != ResourceProvider::GLTexture)
+ return;
+
+ scoped_ptr<ResourceProviderContext> child_context_owned(
+ ResourceProviderContext::Create(shared_data_.get()));
+ ResourceProviderContext* child_context = child_context_owned.get();
+
+ FakeOutputSurfaceClient child_output_surface_client;
+ scoped_ptr<OutputSurface> child_output_surface(FakeOutputSurface::Create3d(
+ child_context_owned.PassAs<TestWebGraphicsContext3D>()));
+ CHECK(child_output_surface->BindToClient(&child_output_surface_client));
+
+ scoped_ptr<ResourceProvider> child_resource_provider(
+ ResourceProvider::Create(child_output_surface.get(), 0, false));
+
+ unsigned texture = child_context->createTexture();
+ gpu::Mailbox gpu_mailbox;
+ child_context->bindTexture(GL_TEXTURE_2D, texture);
+ child_context->genMailboxCHROMIUM(gpu_mailbox.name);
+ child_context->produceTextureCHROMIUM(GL_TEXTURE_2D, gpu_mailbox.name);
+
+ unsigned release_sync_point = 0;
+ bool lost_resource = false;
+ ReleaseCallback callback =
+ base::Bind(ReleaseTextureMailbox, &release_sync_point, &lost_resource);
+ ResourceProvider::ResourceId resource =
+ child_resource_provider->CreateResourceFromTextureMailbox(
+ TextureMailbox(gpu_mailbox), SingleReleaseCallback::Create(callback));
+
+ ReturnedResourceArray returned_to_child;
+ int child_id =
+ resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
+ {
+ // Transfer the resource to the parent.
+ ResourceProvider::ResourceIdArray resource_ids_to_transfer;
+ resource_ids_to_transfer.push_back(resource);
+ TransferableResourceArray list;
+ child_resource_provider->PrepareSendToParent(resource_ids_to_transfer,
+ &list);
+ EXPECT_EQ(1u, list.size());
+
+ resource_provider_->ReceiveFromChild(child_id, list);
+ resource_provider_->DeclareUsedResourcesFromChild(child_id,
+ resource_ids_to_transfer);
+ }
+
+ {
+ ResourceProvider::ResourceIdMap resource_map =
+ resource_provider_->GetChildToParentMap(child_id);
+ ResourceProvider::ResourceId parent_resource = resource_map[resource];
+ EXPECT_NE(0u, parent_resource);
+
+ // Transfer to a grandparent.
+ ResourceProvider::ResourceIdArray resource_ids_to_transfer;
+ resource_ids_to_transfer.push_back(parent_resource);
+ TransferableResourceArray list;
+ resource_provider_->PrepareSendToParent(resource_ids_to_transfer, &list);
+
+ // Receive back a lost resource from the grandparent.
+ EXPECT_EQ(1u, list.size());
+ EXPECT_EQ(parent_resource, list[0].id);
+ ReturnedResourceArray returned;
+ TransferableResource::ReturnResources(list, &returned);
+ EXPECT_EQ(1u, returned.size());
+ EXPECT_EQ(parent_resource, returned[0].id);
+ returned[0].lost = true;
+ resource_provider_->ReceiveReturnsFromParent(returned);
+ }
+
+ {
+ EXPECT_EQ(0u, returned_to_child.size());
+
+ // Transfer resources back from the parent to the child. Set no resources as
+ // being in use.
+ ResourceProvider::ResourceIdArray no_resources;
+ resource_provider_->DeclareUsedResourcesFromChild(child_id, no_resources);
+
+ // Expect the resource to be lost.
+ ASSERT_EQ(1u, returned_to_child.size());
+ EXPECT_TRUE(returned_to_child[0].lost);
+ child_resource_provider->ReceiveReturnsFromParent(returned_to_child);
+ returned_to_child.clear();
+ }
+
+ // Delete the resource in the child. Expect the resource to be lost.
+ child_resource_provider->DeleteResource(resource);
+ EXPECT_TRUE(lost_resource);
+
+ child_context->waitSyncPoint(release_sync_point);
+ child_context->deleteTexture(texture);
+}
+
TEST_P(ResourceProviderTest, Shutdown) {
// TextureMailbox callbacks only exist for GL textures for now.
if (GetParam() != ResourceProvider::GLTexture)
@@ -898,10 +1618,11 @@ TEST_P(ResourceProviderTest, Shutdown) {
unsigned release_sync_point = 0;
bool lost_resource = false;
- TextureMailbox::ReleaseCallback callback =
- base::Bind(ReleaseTextureMailbox, &release_sync_point, &lost_resource);
+ scoped_ptr<SingleReleaseCallback> callback = SingleReleaseCallback::Create(
+ base::Bind(ReleaseTextureMailbox, &release_sync_point, &lost_resource));
resource_provider_->CreateResourceFromTextureMailbox(
- TextureMailbox(mailbox, callback, sync_point));
+ TextureMailbox(mailbox, sync_point),
+ callback.Pass());
EXPECT_EQ(0u, release_sync_point);
EXPECT_FALSE(lost_resource);
@@ -937,10 +1658,11 @@ TEST_P(ResourceProviderTest, ShutdownSharedMemory) {
CreateAndFillSharedMemory(size, 0));
bool release_called = false;
- TextureMailbox::ReleaseCallback callback =
- base::Bind(ReleaseSharedMemoryCallback, &release_called);
+ scoped_ptr<SingleReleaseCallback> callback = SingleReleaseCallback::Create(
+ base::Bind(ReleaseSharedMemoryCallback, &release_called));
resource_provider_->CreateResourceFromTextureMailbox(
- TextureMailbox(shared_memory.get(), size, callback));
+ TextureMailbox(shared_memory.get(), size),
+ callback.Pass());
resource_provider_.reset();
@@ -962,11 +1684,12 @@ TEST_P(ResourceProviderTest, ShutdownWithExportedResource) {
unsigned release_sync_point = 0;
bool lost_resource = false;
- TextureMailbox::ReleaseCallback callback =
- base::Bind(ReleaseTextureMailbox, &release_sync_point, &lost_resource);
+ scoped_ptr<SingleReleaseCallback> callback = SingleReleaseCallback::Create(
+ base::Bind(ReleaseTextureMailbox, &release_sync_point, &lost_resource));
ResourceProvider::ResourceId resource =
resource_provider_->CreateResourceFromTextureMailbox(
- TextureMailbox(mailbox, callback, sync_point));
+ TextureMailbox(mailbox, sync_point),
+ callback.Pass());
// Transfer the resource, so we can't release it properly on shutdown.
ResourceProvider::ResourceIdArray resource_ids_to_transfer;
@@ -999,10 +1722,11 @@ TEST_P(ResourceProviderTest, LostContext) {
unsigned release_sync_point = 0;
bool lost_resource = false;
- TextureMailbox::ReleaseCallback callback =
- base::Bind(ReleaseTextureMailbox, &release_sync_point, &lost_resource);
+ scoped_ptr<SingleReleaseCallback> callback = SingleReleaseCallback::Create(
+ base::Bind(ReleaseTextureMailbox, &release_sync_point, &lost_resource));
resource_provider_->CreateResourceFromTextureMailbox(
- TextureMailbox(mailbox, callback, sync_point));
+ TextureMailbox(mailbox, sync_point),
+ callback.Pass());
EXPECT_EQ(0u, release_sync_point);
EXPECT_FALSE(lost_resource);
@@ -1014,39 +1738,30 @@ TEST_P(ResourceProviderTest, LostContext) {
EXPECT_TRUE(lost_resource);
}
-class TextureStateTrackingContext : public TestWebGraphicsContext3D {
- public:
- MOCK_METHOD2(bindTexture, void(WGC3Denum target, WebGLId texture));
- MOCK_METHOD3(texParameteri,
- void(WGC3Denum target, WGC3Denum pname, WGC3Dint param));
- MOCK_METHOD1(waitSyncPoint, void(unsigned sync_point));
- MOCK_METHOD0(insertSyncPoint, unsigned(void));
- MOCK_METHOD2(produceTextureCHROMIUM, void(WGC3Denum target,
- const WGC3Dbyte* mailbox));
- MOCK_METHOD2(consumeTextureCHROMIUM, void(WGC3Denum target,
- const WGC3Dbyte* mailbox));
-
- // Force all textures to be "1" so we can test for them.
- virtual WebKit::WebGLId NextTextureId() OVERRIDE { return 1; }
-};
-
TEST_P(ResourceProviderTest, ScopedSampler) {
// Sampling is only supported for GL textures.
if (GetParam() != ResourceProvider::GLTexture)
return;
- scoped_ptr<OutputSurface> output_surface(
- FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>(
- new TextureStateTrackingContext)));
- TextureStateTrackingContext* context =
- static_cast<TextureStateTrackingContext*>(output_surface->context3d());
+ scoped_ptr<TextureStateTrackingContext> context_owned(
+ new TextureStateTrackingContext);
+ TextureStateTrackingContext* context = context_owned.get();
+
+ FakeOutputSurfaceClient output_surface_client;
+ scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d(
+ context_owned.PassAs<TestWebGraphicsContext3D>()));
+ CHECK(output_surface->BindToClient(&output_surface_client));
+
scoped_ptr<ResourceProvider> resource_provider(
- ResourceProvider::Create(output_surface.get(), 0));
+ ResourceProvider::Create(output_surface.get(), 0, false));
gfx::Size size(1, 1);
- WGC3Denum format = GL_RGBA;
+ ResourceFormat format = RGBA_8888;
int texture_id = 1;
+ ResourceProvider::ResourceId id = resource_provider->CreateResource(
+ size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format);
+
// Check that the texture gets created with the right sampler settings.
EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id))
.Times(2); // Once to create and once to allocate.
@@ -1064,9 +1779,9 @@ TEST_P(ResourceProviderTest, ScopedSampler) {
texParameteri(GL_TEXTURE_2D,
GL_TEXTURE_POOL_CHROMIUM,
GL_TEXTURE_POOL_UNMANAGED_CHROMIUM));
- ResourceProvider::ResourceId id = resource_provider->CreateResource(
- size, format, ResourceProvider::TextureUsageAny);
+
resource_provider->AllocateForTesting(id);
+ Mock::VerifyAndClearExpectations(context);
// Creating a sampler with the default filter should not change any texture
// parameters.
@@ -1074,6 +1789,7 @@ TEST_P(ResourceProviderTest, ScopedSampler) {
EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id));
ResourceProvider::ScopedSamplerGL sampler(
resource_provider.get(), id, GL_TEXTURE_2D, GL_LINEAR);
+ Mock::VerifyAndClearExpectations(context);
}
// Using a different filter should be reflected in the texture parameters.
@@ -1087,6 +1803,7 @@ TEST_P(ResourceProviderTest, ScopedSampler) {
texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
ResourceProvider::ScopedSamplerGL sampler(
resource_provider.get(), id, GL_TEXTURE_2D, GL_NEAREST);
+ Mock::VerifyAndClearExpectations(context);
}
// Test resetting to the default filter.
@@ -1098,9 +1815,8 @@ TEST_P(ResourceProviderTest, ScopedSampler) {
texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
ResourceProvider::ScopedSamplerGL sampler(
resource_provider.get(), id, GL_TEXTURE_2D, GL_LINEAR);
+ Mock::VerifyAndClearExpectations(context);
}
-
- Mock::VerifyAndClearExpectations(context);
}
TEST_P(ResourceProviderTest, ManagedResource) {
@@ -1108,21 +1824,25 @@ TEST_P(ResourceProviderTest, ManagedResource) {
if (GetParam() != ResourceProvider::GLTexture)
return;
- scoped_ptr<OutputSurface> output_surface(
- FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>(
- new TextureStateTrackingContext)));
- TextureStateTrackingContext* context =
- static_cast<TextureStateTrackingContext*>(output_surface->context3d());
+ scoped_ptr<TextureStateTrackingContext> context_owned(
+ new TextureStateTrackingContext);
+ TextureStateTrackingContext* context = context_owned.get();
+
+ FakeOutputSurfaceClient output_surface_client;
+ scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d(
+ context_owned.PassAs<TestWebGraphicsContext3D>()));
+ CHECK(output_surface->BindToClient(&output_surface_client));
+
scoped_ptr<ResourceProvider> resource_provider(
- ResourceProvider::Create(output_surface.get(), 0));
+ ResourceProvider::Create(output_surface.get(), 0, false));
gfx::Size size(1, 1);
- WGC3Denum format = GL_RGBA;
+ ResourceFormat format = RGBA_8888;
int texture_id = 1;
// Check that the texture gets created with the right sampler settings.
ResourceProvider::ResourceId id = resource_provider->CreateManagedResource(
- size, format, ResourceProvider::TextureUsageAny);
+ size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format);
EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id));
EXPECT_CALL(*context,
texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
@@ -1144,6 +1864,59 @@ TEST_P(ResourceProviderTest, ManagedResource) {
Mock::VerifyAndClearExpectations(context);
}
+TEST_P(ResourceProviderTest, TextureWrapMode) {
+ // Sampling is only supported for GL textures.
+ if (GetParam() != ResourceProvider::GLTexture)
+ return;
+
+ scoped_ptr<TextureStateTrackingContext> context_owned(
+ new TextureStateTrackingContext);
+ TextureStateTrackingContext* context = context_owned.get();
+
+ FakeOutputSurfaceClient output_surface_client;
+ scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d(
+ context_owned.PassAs<TestWebGraphicsContext3D>()));
+ CHECK(output_surface->BindToClient(&output_surface_client));
+
+ scoped_ptr<ResourceProvider> resource_provider(
+ ResourceProvider::Create(output_surface.get(), 0, false));
+
+ gfx::Size size(1, 1);
+ ResourceFormat format = RGBA_8888;
+ int texture_id = 1;
+ GLenum texture_pool = GL_TEXTURE_POOL_UNMANAGED_CHROMIUM;
+
+ for (int i = 0; i < 2; ++i) {
+ GLint wrap_mode = i ? GL_CLAMP_TO_EDGE : GL_REPEAT;
+ // Check that the texture gets created with the right sampler settings.
+ ResourceProvider::ResourceId id =
+ resource_provider->CreateGLTexture(size,
+ texture_pool,
+ wrap_mode,
+ ResourceProvider::TextureUsageAny,
+ format);
+ EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id));
+ EXPECT_CALL(*context,
+ texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
+ EXPECT_CALL(*context,
+ texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
+ EXPECT_CALL(
+ *context,
+ texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_mode));
+ EXPECT_CALL(
+ *context,
+ texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_mode));
+ EXPECT_CALL(*context,
+ texParameteri(GL_TEXTURE_2D,
+ GL_TEXTURE_POOL_CHROMIUM,
+ GL_TEXTURE_POOL_UNMANAGED_CHROMIUM));
+ resource_provider->CreateForTesting(id);
+ EXPECT_NE(0u, id);
+
+ Mock::VerifyAndClearExpectations(context);
+ }
+}
+
static void EmptyReleaseCallback(unsigned sync_point, bool lost_resource) {}
TEST_P(ResourceProviderTest, TextureMailbox_SharedMemory) {
@@ -1155,17 +1928,22 @@ TEST_P(ResourceProviderTest, TextureMailbox_SharedMemory) {
scoped_ptr<base::SharedMemory> shared_memory(
CreateAndFillSharedMemory(size, kBadBeef));
+ FakeOutputSurfaceClient output_surface_client;
scoped_ptr<OutputSurface> output_surface(
FakeOutputSurface::CreateSoftware(make_scoped_ptr(
new SoftwareOutputDevice)));
+ CHECK(output_surface->BindToClient(&output_surface_client));
+
scoped_ptr<ResourceProvider> resource_provider(
- ResourceProvider::Create(output_surface.get(), 0));
+ ResourceProvider::Create(output_surface.get(), 0, false));
- TextureMailbox::ReleaseCallback callback = base::Bind(&EmptyReleaseCallback);
- TextureMailbox mailbox(shared_memory.get(), size, callback);
+ scoped_ptr<SingleReleaseCallback> callback = SingleReleaseCallback::Create(
+ base::Bind(&EmptyReleaseCallback));
+ TextureMailbox mailbox(shared_memory.get(), size);
ResourceProvider::ResourceId id =
- resource_provider->CreateResourceFromTextureMailbox(mailbox);
+ resource_provider->CreateResourceFromTextureMailbox(
+ mailbox, callback.Pass());
EXPECT_NE(0u, id);
{
@@ -1182,13 +1960,17 @@ TEST_P(ResourceProviderTest, TextureMailbox_GLTexture2D) {
if (GetParam() != ResourceProvider::GLTexture)
return;
- scoped_ptr<OutputSurface> output_surface(
- FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>(
- new TextureStateTrackingContext)));
- TextureStateTrackingContext* context =
- static_cast<TextureStateTrackingContext*>(output_surface->context3d());
+ scoped_ptr<TextureStateTrackingContext> context_owned(
+ new TextureStateTrackingContext);
+ TextureStateTrackingContext* context = context_owned.get();
+
+ FakeOutputSurfaceClient output_surface_client;
+ scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d(
+ context_owned.PassAs<TestWebGraphicsContext3D>()));
+ CHECK(output_surface->BindToClient(&output_surface_client));
+
scoped_ptr<ResourceProvider> resource_provider(
- ResourceProvider::Create(output_surface.get(), 0));
+ ResourceProvider::Create(output_surface.get(), 0, false));
unsigned texture_id = 1;
unsigned sync_point = 30;
@@ -1202,14 +1984,14 @@ TEST_P(ResourceProviderTest, TextureMailbox_GLTexture2D) {
gpu::Mailbox gpu_mailbox;
memcpy(gpu_mailbox.name, "Hello world", strlen("Hello world") + 1);
- TextureMailbox::ReleaseCallback callback = base::Bind(&EmptyReleaseCallback);
+ scoped_ptr<SingleReleaseCallback> callback = SingleReleaseCallback::Create(
+ base::Bind(&EmptyReleaseCallback));
- TextureMailbox mailbox(gpu_mailbox,
- callback,
- sync_point);
+ TextureMailbox mailbox(gpu_mailbox, sync_point);
ResourceProvider::ResourceId id =
- resource_provider->CreateResourceFromTextureMailbox(mailbox);
+ resource_provider->CreateResourceFromTextureMailbox(
+ mailbox, callback.Pass());
EXPECT_NE(0u, id);
Mock::VerifyAndClearExpectations(context);
@@ -1242,13 +2024,17 @@ TEST_P(ResourceProviderTest, TextureMailbox_GLTextureExternalOES) {
if (GetParam() != ResourceProvider::GLTexture)
return;
- scoped_ptr<OutputSurface> output_surface(
- FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>(
- new TextureStateTrackingContext)));
- TextureStateTrackingContext* context =
- static_cast<TextureStateTrackingContext*>(output_surface->context3d());
+ scoped_ptr<TextureStateTrackingContext> context_owned(
+ new TextureStateTrackingContext);
+ TextureStateTrackingContext* context = context_owned.get();
+
+ FakeOutputSurfaceClient output_surface_client;
+ scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d(
+ context_owned.PassAs<TestWebGraphicsContext3D>()));
+ CHECK(output_surface->BindToClient(&output_surface_client));
+
scoped_ptr<ResourceProvider> resource_provider(
- ResourceProvider::Create(output_surface.get(), 0));
+ ResourceProvider::Create(output_surface.get(), 0, false));
unsigned texture_id = 1;
unsigned sync_point = 30;
@@ -1262,15 +2048,14 @@ TEST_P(ResourceProviderTest, TextureMailbox_GLTextureExternalOES) {
gpu::Mailbox gpu_mailbox;
memcpy(gpu_mailbox.name, "Hello world", strlen("Hello world") + 1);
- TextureMailbox::ReleaseCallback callback = base::Bind(&EmptyReleaseCallback);
+ scoped_ptr<SingleReleaseCallback> callback = SingleReleaseCallback::Create(
+ base::Bind(&EmptyReleaseCallback));
- TextureMailbox mailbox(gpu_mailbox,
- callback,
- target,
- sync_point);
+ TextureMailbox mailbox(gpu_mailbox, target, sync_point);
ResourceProvider::ResourceId id =
- resource_provider->CreateResourceFromTextureMailbox(mailbox);
+ resource_provider->CreateResourceFromTextureMailbox(
+ mailbox, callback.Pass());
EXPECT_NE(0u, id);
Mock::VerifyAndClearExpectations(context);
@@ -1359,28 +2144,29 @@ TEST_P(ResourceProviderTest, TextureAllocation) {
// Only for GL textures.
if (GetParam() != ResourceProvider::GLTexture)
return;
- scoped_ptr<WebKit::WebGraphicsContext3D> mock_context(
- static_cast<WebKit::WebGraphicsContext3D*>(
- new StrictMock<AllocationTrackingContext3D>));
- scoped_ptr<OutputSurface> output_surface(
- FakeOutputSurface::Create3d(mock_context.Pass()));
+ scoped_ptr<AllocationTrackingContext3D> context_owned(
+ new StrictMock<AllocationTrackingContext3D>);
+ AllocationTrackingContext3D* context = context_owned.get();
+
+ FakeOutputSurfaceClient output_surface_client;
+ scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d(
+ context_owned.PassAs<TestWebGraphicsContext3D>()));
+ CHECK(output_surface->BindToClient(&output_surface_client));
+
+ scoped_ptr<ResourceProvider> resource_provider(
+ ResourceProvider::Create(output_surface.get(), 0, false));
gfx::Size size(2, 2);
gfx::Vector2d offset(0, 0);
gfx::Rect rect(0, 0, 2, 2);
- WGC3Denum format = GL_RGBA;
+ ResourceFormat format = RGBA_8888;
ResourceProvider::ResourceId id = 0;
uint8_t pixels[16] = { 0 };
int texture_id = 123;
- AllocationTrackingContext3D* context =
- static_cast<AllocationTrackingContext3D*>(output_surface->context3d());
- scoped_ptr<ResourceProvider> resource_provider(
- ResourceProvider::Create(output_surface.get(), 0));
-
// Lazy allocation. Don't allocate when creating the resource.
id = resource_provider->CreateResource(
- size, format, ResourceProvider::TextureUsageAny);
+ size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format);
EXPECT_CALL(*context, createTexture()).WillOnce(Return(texture_id));
EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id)).Times(1);
@@ -1393,7 +2179,7 @@ TEST_P(ResourceProviderTest, TextureAllocation) {
// Do allocate when we set the pixels.
id = resource_provider->CreateResource(
- size, format, ResourceProvider::TextureUsageAny);
+ size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format);
EXPECT_CALL(*context, createTexture()).WillOnce(Return(texture_id));
EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id)).Times(3);
@@ -1408,7 +2194,7 @@ TEST_P(ResourceProviderTest, TextureAllocation) {
// Same for async version.
id = resource_provider->CreateResource(
- size, format, ResourceProvider::TextureUsageAny);
+ size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format);
resource_provider->AcquirePixelBuffer(id);
EXPECT_CALL(*context, createTexture()).WillOnce(Return(texture_id));
@@ -1429,24 +2215,25 @@ TEST_P(ResourceProviderTest, TextureAllocation) {
TEST_P(ResourceProviderTest, PixelBuffer_GLTexture) {
if (GetParam() != ResourceProvider::GLTexture)
return;
- scoped_ptr<WebKit::WebGraphicsContext3D> mock_context(
- static_cast<WebKit::WebGraphicsContext3D*>(
- new StrictMock<AllocationTrackingContext3D>));
- scoped_ptr<OutputSurface> output_surface(
- FakeOutputSurface::Create3d(mock_context.Pass()));
+ scoped_ptr<AllocationTrackingContext3D> context_owned(
+ new StrictMock<AllocationTrackingContext3D>);
+ AllocationTrackingContext3D* context = context_owned.get();
+
+ FakeOutputSurfaceClient output_surface_client;
+ scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d(
+ context_owned.PassAs<TestWebGraphicsContext3D>()));
+ CHECK(output_surface->BindToClient(&output_surface_client));
gfx::Size size(2, 2);
- WGC3Denum format = GL_RGBA;
+ ResourceFormat format = RGBA_8888;
ResourceProvider::ResourceId id = 0;
int texture_id = 123;
- AllocationTrackingContext3D* context =
- static_cast<AllocationTrackingContext3D*>(output_surface->context3d());
scoped_ptr<ResourceProvider> resource_provider(
- ResourceProvider::Create(output_surface.get(), 0));
+ ResourceProvider::Create(output_surface.get(), 0, false));
id = resource_provider->CreateResource(
- size, format, ResourceProvider::TextureUsageAny);
+ size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format);
resource_provider->AcquirePixelBuffer(id);
EXPECT_CALL(*context, createTexture()).WillOnce(Return(texture_id));
@@ -1468,20 +2255,22 @@ TEST_P(ResourceProviderTest, PixelBuffer_GLTexture) {
TEST_P(ResourceProviderTest, PixelBuffer_Bitmap) {
if (GetParam() != ResourceProvider::Bitmap)
return;
+ FakeOutputSurfaceClient output_surface_client;
scoped_ptr<OutputSurface> output_surface(
FakeOutputSurface::CreateSoftware(make_scoped_ptr(
new SoftwareOutputDevice)));
+ CHECK(output_surface->BindToClient(&output_surface_client));
gfx::Size size(1, 1);
- WGC3Denum format = GL_RGBA;
+ ResourceFormat format = RGBA_8888;
ResourceProvider::ResourceId id = 0;
const uint32_t kBadBeef = 0xbadbeef;
scoped_ptr<ResourceProvider> resource_provider(
- ResourceProvider::Create(output_surface.get(), 0));
+ ResourceProvider::Create(output_surface.get(), 0, false));
id = resource_provider->CreateResource(
- size, format, ResourceProvider::TextureUsageAny);
+ size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format);
resource_provider->AcquirePixelBuffer(id);
void* data = resource_provider->MapPixelBuffer(id);
@@ -1509,24 +2298,25 @@ TEST_P(ResourceProviderTest, ForcingAsyncUploadToComplete) {
// Only for GL textures.
if (GetParam() != ResourceProvider::GLTexture)
return;
- scoped_ptr<WebKit::WebGraphicsContext3D> mock_context(
- static_cast<WebKit::WebGraphicsContext3D*>(
- new StrictMock<AllocationTrackingContext3D>));
- scoped_ptr<OutputSurface> output_surface(
- FakeOutputSurface::Create3d(mock_context.Pass()));
+ scoped_ptr<AllocationTrackingContext3D> context_owned(
+ new StrictMock<AllocationTrackingContext3D>);
+ AllocationTrackingContext3D* context = context_owned.get();
+
+ FakeOutputSurfaceClient output_surface_client;
+ scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d(
+ context_owned.PassAs<TestWebGraphicsContext3D>()));
+ CHECK(output_surface->BindToClient(&output_surface_client));
gfx::Size size(2, 2);
- WGC3Denum format = GL_RGBA;
+ ResourceFormat format = RGBA_8888;
ResourceProvider::ResourceId id = 0;
int texture_id = 123;
- AllocationTrackingContext3D* context =
- static_cast<AllocationTrackingContext3D*>(output_surface->context3d());
scoped_ptr<ResourceProvider> resource_provider(
- ResourceProvider::Create(output_surface.get(), 0));
+ ResourceProvider::Create(output_surface.get(), 0, false));
id = resource_provider->CreateResource(
- size, format, ResourceProvider::TextureUsageAny);
+ size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format);
resource_provider->AcquirePixelBuffer(id);
EXPECT_CALL(*context, createTexture()).WillOnce(Return(texture_id));
@@ -1549,26 +2339,27 @@ TEST_P(ResourceProviderTest, ForcingAsyncUploadToComplete) {
}
TEST_P(ResourceProviderTest, PixelBufferLostContext) {
- scoped_ptr<WebKit::WebGraphicsContext3D> mock_context(
- static_cast<WebKit::WebGraphicsContext3D*>(
- new NiceMock<AllocationTrackingContext3D>));
- scoped_ptr<OutputSurface> output_surface(
- FakeOutputSurface::Create3d(mock_context.Pass()));
+ scoped_ptr<AllocationTrackingContext3D> context_owned(
+ new NiceMock<AllocationTrackingContext3D>);
+ AllocationTrackingContext3D* context = context_owned.get();
+
+ FakeOutputSurfaceClient output_surface_client;
+ scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d(
+ context_owned.PassAs<TestWebGraphicsContext3D>()));
+ CHECK(output_surface->BindToClient(&output_surface_client));
gfx::Size size(2, 2);
- WGC3Denum format = GL_RGBA;
+ ResourceFormat format = RGBA_8888;
ResourceProvider::ResourceId id = 0;
int texture_id = 123;
- AllocationTrackingContext3D* context =
- static_cast<AllocationTrackingContext3D*>(output_surface->context3d());
scoped_ptr<ResourceProvider> resource_provider(
- ResourceProvider::Create(output_surface.get(), 0));
+ ResourceProvider::Create(output_surface.get(), 0, false));
EXPECT_CALL(*context, createTexture()).WillRepeatedly(Return(texture_id));
id = resource_provider->CreateResource(
- size, format, ResourceProvider::TextureUsageAny);
+ size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format);
context->loseContextCHROMIUM(GL_GUILTY_CONTEXT_RESET_ARB,
GL_INNOCENT_CONTEXT_RESET_ARB);
resource_provider->AcquirePixelBuffer(id);
@@ -1583,27 +2374,28 @@ TEST_P(ResourceProviderTest, Image_GLTexture) {
// Only for GL textures.
if (GetParam() != ResourceProvider::GLTexture)
return;
- scoped_ptr<WebKit::WebGraphicsContext3D> mock_context(
- static_cast<WebKit::WebGraphicsContext3D*>(
- new StrictMock<AllocationTrackingContext3D>));
- scoped_ptr<OutputSurface> output_surface(
- FakeOutputSurface::Create3d(mock_context.Pass()));
+ scoped_ptr<AllocationTrackingContext3D> context_owned(
+ new StrictMock<AllocationTrackingContext3D>);
+ AllocationTrackingContext3D* context = context_owned.get();
+
+ FakeOutputSurfaceClient output_surface_client;
+ scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d(
+ context_owned.PassAs<TestWebGraphicsContext3D>()));
+ CHECK(output_surface->BindToClient(&output_surface_client));
const int kWidth = 2;
const int kHeight = 2;
gfx::Size size(kWidth, kHeight);
- WGC3Denum format = GL_RGBA;
+ ResourceFormat format = RGBA_8888;
ResourceProvider::ResourceId id = 0;
const unsigned kTextureId = 123u;
const unsigned kImageId = 234u;
- AllocationTrackingContext3D* context =
- static_cast<AllocationTrackingContext3D*>(output_surface->context3d());
scoped_ptr<ResourceProvider> resource_provider(
- ResourceProvider::Create(output_surface.get(), 0));
+ ResourceProvider::Create(output_surface.get(), 0, false));
id = resource_provider->CreateResource(
- size, format, ResourceProvider::TextureUsageAny);
+ size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format);
EXPECT_CALL(*context, createImageCHROMIUM(kWidth, kHeight, GL_RGBA8_OES))
.WillOnce(Return(kImageId))
.RetiresOnSaturation();
@@ -1659,20 +2451,22 @@ TEST_P(ResourceProviderTest, Image_GLTexture) {
TEST_P(ResourceProviderTest, Image_Bitmap) {
if (GetParam() != ResourceProvider::Bitmap)
return;
+ FakeOutputSurfaceClient output_surface_client;
scoped_ptr<OutputSurface> output_surface(
FakeOutputSurface::CreateSoftware(make_scoped_ptr(
new SoftwareOutputDevice)));
+ CHECK(output_surface->BindToClient(&output_surface_client));
gfx::Size size(1, 1);
- WGC3Denum format = GL_RGBA;
+ ResourceFormat format = RGBA_8888;
ResourceProvider::ResourceId id = 0;
const uint32_t kBadBeef = 0xbadbeef;
scoped_ptr<ResourceProvider> resource_provider(
- ResourceProvider::Create(output_surface.get(), 0));
+ ResourceProvider::Create(output_surface.get(), 0, false));
id = resource_provider->CreateResource(
- size, format, ResourceProvider::TextureUsageAny);
+ size, GL_CLAMP_TO_EDGE, ResourceProvider::TextureUsageAny, format);
resource_provider->AcquireImage(id);
const int kStride = 0;
@@ -1699,15 +2493,17 @@ TEST_P(ResourceProviderTest, Image_Bitmap) {
void InitializeGLAndCheck(ContextSharedData* shared_data,
ResourceProvider* resource_provider,
FakeOutputSurface* output_surface) {
- scoped_ptr<ResourceProviderContext> context =
+ scoped_ptr<ResourceProviderContext> context_owned =
ResourceProviderContext::Create(shared_data);
- output_surface->SetAndInitializeContext3D(
- context.PassAs<WebKit::WebGraphicsContext3D>());
+ ResourceProviderContext* context = context_owned.get();
+
+ scoped_refptr<TestContextProvider> context_provider =
+ TestContextProvider::Create(
+ context_owned.PassAs<TestWebGraphicsContext3D>());
+ output_surface->InitializeAndSetContext3d(context_provider, NULL);
EXPECT_TRUE(resource_provider->InitializeGL());
- CheckCreateResource(
- ResourceProvider::GLTexture,
- resource_provider,
- static_cast<ResourceProviderContext*>(output_surface->context3d()));
+
+ CheckCreateResource(ResourceProvider::GLTexture, resource_provider, context);
}
TEST(ResourceProviderTest, BasicInitializeGLSoftware) {
@@ -1718,7 +2514,7 @@ TEST(ResourceProviderTest, BasicInitializeGLSoftware) {
scoped_ptr<SoftwareOutputDevice>(new SoftwareOutputDevice)));
EXPECT_TRUE(output_surface->BindToClient(&client));
scoped_ptr<ResourceProvider> resource_provider(
- ResourceProvider::Create(output_surface.get(), 0));
+ ResourceProvider::Create(output_surface.get(), 0, false));
CheckCreateResource(ResourceProvider::Bitmap, resource_provider.get(), NULL);
@@ -1727,6 +2523,7 @@ TEST(ResourceProviderTest, BasicInitializeGLSoftware) {
output_surface.get());
resource_provider->InitializeSoftware();
+ output_surface->ReleaseGL();
CheckCreateResource(ResourceProvider::Bitmap, resource_provider.get(), NULL);
InitializeGLAndCheck(shared_data.get(),
diff --git a/chromium/cc/resources/resource_update_controller.cc b/chromium/cc/resources/resource_update_controller.cc
index a46a7d1a36e..3c8b1e709ff 100644
--- a/chromium/cc/resources/resource_update_controller.cc
+++ b/chromium/cc/resources/resource_update_controller.cc
@@ -16,9 +16,6 @@ namespace {
const size_t kPartialTextureUpdatesMax = 12;
// Measured in seconds.
-const double kTextureUpdateTickRate = 0.004;
-
-// Measured in seconds.
const double kUploaderBusyTickRate = 0.001;
// Number of blocking update intervals to allow.
@@ -36,7 +33,8 @@ size_t ResourceUpdateController::MaxFullUpdatesPerTick(
ResourceProvider* resource_provider) {
double textures_per_second = resource_provider->EstimatedUploadsPerSecond();
size_t textures_per_tick =
- floor(kTextureUpdateTickRate * textures_per_second);
+ floor(resource_provider->TextureUpdateTickRate().InSecondsF() *
+ textures_per_second);
return textures_per_tick ? textures_per_tick : 1;
}
@@ -119,7 +117,7 @@ base::TimeTicks ResourceUpdateController::Now() const {
}
base::TimeDelta ResourceUpdateController::UpdateMoreTexturesTime() const {
- return base::TimeDelta::FromMilliseconds(kTextureUpdateTickRate * 1000);
+ return resource_provider_->TextureUpdateTickRate();
}
size_t ResourceUpdateController::UpdateMoreTexturesSize() const {
diff --git a/chromium/cc/resources/resource_update_controller_unittest.cc b/chromium/cc/resources/resource_update_controller_unittest.cc
index 1060801934b..31b5d71662a 100644
--- a/chromium/cc/resources/resource_update_controller_unittest.cc
+++ b/chromium/cc/resources/resource_update_controller_unittest.cc
@@ -5,11 +5,12 @@
#include "cc/resources/resource_update_controller.h"
#include "base/test/test_simple_task_runner.h"
+#include "cc/debug/test_web_graphics_context_3d.h"
#include "cc/resources/prioritized_resource_manager.h"
#include "cc/test/fake_output_surface.h"
+#include "cc/test/fake_output_surface_client.h"
#include "cc/test/fake_proxy.h"
#include "cc/test/scheduler_test_common.h"
-#include "cc/test/test_web_graphics_context_3d.h"
#include "cc/test/tiled_layer_test_common.h"
#include "cc/trees/single_thread_proxy.h" // For DebugScopedSetImplThread
#include "testing/gtest/include/gtest/gtest.h"
@@ -34,8 +35,9 @@ class ResourceUpdateControllerTest;
class WebGraphicsContext3DForUploadTest : public TestWebGraphicsContext3D {
public:
explicit WebGraphicsContext3DForUploadTest(ResourceUpdateControllerTest* test)
- : test_(test),
- support_shallow_flush_(true) {}
+ : test_(test) {
+ test_capabilities_.shallow_flush = true;
+ }
virtual void flush(void) OVERRIDE;
virtual void shallowFlushCHROMIUM(void) OVERRIDE;
@@ -51,12 +53,6 @@ class WebGraphicsContext3DForUploadTest : public TestWebGraphicsContext3D {
const void* pixels) OVERRIDE;
virtual GrGLInterface* onCreateGrGLInterface() OVERRIDE { return NULL; }
- virtual WebString getString(WGC3Denum name) OVERRIDE {
- if (support_shallow_flush_)
- return WebString("GL_CHROMIUM_shallow_flush");
- return WebString("");
- }
-
virtual void getQueryObjectuivEXT(
WebGLId id,
WGC3Denum pname,
@@ -64,7 +60,6 @@ class WebGraphicsContext3DForUploadTest : public TestWebGraphicsContext3D {
private:
ResourceUpdateControllerTest* test_;
- bool support_shallow_flush_;
};
class ResourceUpdateControllerTest : public Test {
@@ -124,21 +119,25 @@ class ResourceUpdateControllerTest : public Test {
protected:
virtual void SetUp() {
- output_surface_ =
- FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>(
- new WebGraphicsContext3DForUploadTest(this)));
bitmap_.setConfig(SkBitmap::kARGB_8888_Config, 300, 150);
bitmap_.allocPixels();
for (int i = 0; i < 4; i++) {
textures_[i] = PrioritizedResource::Create(resource_manager_.get(),
- gfx::Size(300, 150), GL_RGBA);
+ gfx::Size(300, 150),
+ RGBA_8888);
textures_[i]->
set_request_priority(PriorityCalculator::VisiblePriority(true));
}
resource_manager_->PrioritizeTextures();
- resource_provider_ = ResourceProvider::Create(output_surface_.get(), 0);
+ output_surface_ = FakeOutputSurface::Create3d(
+ scoped_ptr<TestWebGraphicsContext3D>(
+ new WebGraphicsContext3DForUploadTest(this)));
+ CHECK(output_surface_->BindToClient(&output_surface_client_));
+
+ resource_provider_ =
+ ResourceProvider::Create(output_surface_.get(), 0, false);
}
void AppendFullUploadsOfIndexedTextureToUpdateQueue(int count,
@@ -193,6 +192,7 @@ class ResourceUpdateControllerTest : public Test {
protected:
// Classes required to interact and test the ResourceUpdateController
FakeProxy proxy_;
+ FakeOutputSurfaceClient output_surface_client_;
scoped_ptr<OutputSurface> output_surface_;
scoped_ptr<ResourceProvider> resource_provider_;
scoped_ptr<ResourceUpdateQueue> queue_;
diff --git a/chromium/cc/resources/return_callback.h b/chromium/cc/resources/return_callback.h
new file mode 100644
index 00000000000..d12254bd662
--- /dev/null
+++ b/chromium/cc/resources/return_callback.h
@@ -0,0 +1,17 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_RESOURCES_RETURN_CALLBACK_H_
+#define CC_RESOURCES_RETURN_CALLBACK_H_
+
+#include "base/callback.h"
+#include "cc/resources/returned_resource.h"
+
+namespace cc {
+
+typedef base::Callback<void(const ReturnedResourceArray&)> ReturnCallback;
+
+} // namespace cc
+
+#endif // CC_RESOURCES_RETURN_CALLBACK_H_
diff --git a/chromium/cc/resources/returned_resource.h b/chromium/cc/resources/returned_resource.h
new file mode 100644
index 00000000000..e2008b415a3
--- /dev/null
+++ b/chromium/cc/resources/returned_resource.h
@@ -0,0 +1,27 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_RESOURCES_RETURNED_RESOURCE_H_
+#define CC_RESOURCES_RETURNED_RESOURCE_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "cc/base/cc_export.h"
+
+namespace cc {
+
+struct CC_EXPORT ReturnedResource {
+ ReturnedResource() : id(0), sync_point(0), count(0), lost(false) {}
+ unsigned id;
+ unsigned sync_point;
+ int count;
+ bool lost;
+};
+
+typedef std::vector<ReturnedResource> ReturnedResourceArray;
+
+} // namespace cc
+
+#endif // CC_RESOURCES_RETURNED_RESOURCE_H_
diff --git a/chromium/cc/resources/scoped_resource.cc b/chromium/cc/resources/scoped_resource.cc
index e444eee571f..5fcaaca7acc 100644
--- a/chromium/cc/resources/scoped_resource.cc
+++ b/chromium/cc/resources/scoped_resource.cc
@@ -16,13 +16,14 @@ ScopedResource::~ScopedResource() {
}
bool ScopedResource::Allocate(gfx::Size size,
- GLenum format,
- ResourceProvider::TextureUsageHint hint) {
+ ResourceProvider::TextureUsageHint hint,
+ ResourceFormat format) {
DCHECK(!id());
DCHECK(!size.IsEmpty());
set_dimensions(size, format);
- set_id(resource_provider_->CreateResource(size, format, hint));
+ set_id(resource_provider_->CreateResource(
+ size, GL_CLAMP_TO_EDGE, hint, format));
#ifndef NDEBUG
allocate_thread_id_ = base::PlatformThread::CurrentId();
diff --git a/chromium/cc/resources/scoped_resource.h b/chromium/cc/resources/scoped_resource.h
index 6a2c8f1e727..8e316eb76db 100644
--- a/chromium/cc/resources/scoped_resource.h
+++ b/chromium/cc/resources/scoped_resource.h
@@ -26,8 +26,8 @@ class CC_EXPORT ScopedResource : public Resource {
virtual ~ScopedResource();
bool Allocate(gfx::Size size,
- GLenum format,
- ResourceProvider::TextureUsageHint hint);
+ ResourceProvider::TextureUsageHint hint,
+ ResourceFormat texture_format);
void Free();
void Leak();
diff --git a/chromium/cc/resources/scoped_resource_unittest.cc b/chromium/cc/resources/scoped_resource_unittest.cc
index b08c1e21daf..8b59995f289 100644
--- a/chromium/cc/resources/scoped_resource_unittest.cc
+++ b/chromium/cc/resources/scoped_resource_unittest.cc
@@ -6,17 +6,20 @@
#include "cc/output/renderer.h"
#include "cc/test/fake_output_surface.h"
+#include "cc/test/fake_output_surface_client.h"
#include "cc/test/tiled_layer_test_common.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/khronos/GLES2/gl2.h"
namespace cc {
namespace {
TEST(ScopedResourceTest, NewScopedResource) {
- scoped_ptr<OutputSurface> context(CreateFakeOutputSurface());
+ FakeOutputSurfaceClient output_surface_client;
+ scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d());
+ CHECK(output_surface->BindToClient(&output_surface_client));
+
scoped_ptr<ResourceProvider> resource_provider(
- ResourceProvider::Create(context.get(), 0));
+ ResourceProvider::Create(output_surface.get(), 0, false));
scoped_ptr<ScopedResource> texture =
ScopedResource::create(resource_provider.get());
@@ -29,34 +32,42 @@ TEST(ScopedResourceTest, NewScopedResource) {
}
TEST(ScopedResourceTest, CreateScopedResource) {
- scoped_ptr<OutputSurface> context(CreateFakeOutputSurface());
+ FakeOutputSurfaceClient output_surface_client;
+ scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d());
+ CHECK(output_surface->BindToClient(&output_surface_client));
+
scoped_ptr<ResourceProvider> resource_provider(
- ResourceProvider::Create(context.get(), 0));
+ ResourceProvider::Create(output_surface.get(), 0, false));
scoped_ptr<ScopedResource> texture =
ScopedResource::create(resource_provider.get());
- texture->Allocate(
- gfx::Size(30, 30), GL_RGBA, ResourceProvider::TextureUsageAny);
+ texture->Allocate(gfx::Size(30, 30),
+ ResourceProvider::TextureUsageAny,
+ RGBA_8888);
// The texture has an allocated byte-size now.
size_t expected_bytes = 30 * 30 * 4;
EXPECT_EQ(expected_bytes, texture->bytes());
EXPECT_LT(0u, texture->id());
- EXPECT_EQ(static_cast<unsigned>(GL_RGBA), texture->format());
+ EXPECT_EQ(static_cast<unsigned>(RGBA_8888), texture->format());
EXPECT_EQ(gfx::Size(30, 30), texture->size());
}
TEST(ScopedResourceTest, ScopedResourceIsDeleted) {
- scoped_ptr<OutputSurface> context(CreateFakeOutputSurface());
+ FakeOutputSurfaceClient output_surface_client;
+ scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d());
+ CHECK(output_surface->BindToClient(&output_surface_client));
+
scoped_ptr<ResourceProvider> resource_provider(
- ResourceProvider::Create(context.get(), 0));
+ ResourceProvider::Create(output_surface.get(), 0, false));
{
scoped_ptr<ScopedResource> texture =
ScopedResource::create(resource_provider.get());
EXPECT_EQ(0u, resource_provider->num_resources());
- texture->Allocate(
- gfx::Size(30, 30), GL_RGBA, ResourceProvider::TextureUsageAny);
+ texture->Allocate(gfx::Size(30, 30),
+ ResourceProvider::TextureUsageAny,
+ RGBA_8888);
EXPECT_LT(0u, texture->id());
EXPECT_EQ(1u, resource_provider->num_resources());
}
@@ -66,8 +77,9 @@ TEST(ScopedResourceTest, ScopedResourceIsDeleted) {
scoped_ptr<ScopedResource> texture =
ScopedResource::create(resource_provider.get());
EXPECT_EQ(0u, resource_provider->num_resources());
- texture->Allocate(
- gfx::Size(30, 30), GL_RGBA, ResourceProvider::TextureUsageAny);
+ texture->Allocate(gfx::Size(30, 30),
+ ResourceProvider::TextureUsageAny,
+ RGBA_8888);
EXPECT_LT(0u, texture->id());
EXPECT_EQ(1u, resource_provider->num_resources());
texture->Free();
@@ -76,16 +88,20 @@ TEST(ScopedResourceTest, ScopedResourceIsDeleted) {
}
TEST(ScopedResourceTest, LeakScopedResource) {
- scoped_ptr<OutputSurface> context(CreateFakeOutputSurface());
+ FakeOutputSurfaceClient output_surface_client;
+ scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d());
+ CHECK(output_surface->BindToClient(&output_surface_client));
+
scoped_ptr<ResourceProvider> resource_provider(
- ResourceProvider::Create(context.get(), 0));
+ ResourceProvider::Create(output_surface.get(), 0, false));
{
scoped_ptr<ScopedResource> texture =
ScopedResource::create(resource_provider.get());
EXPECT_EQ(0u, resource_provider->num_resources());
- texture->Allocate(
- gfx::Size(30, 30), GL_RGBA, ResourceProvider::TextureUsageAny);
+ texture->Allocate(gfx::Size(30, 30),
+ ResourceProvider::TextureUsageAny,
+ RGBA_8888);
EXPECT_LT(0u, texture->id());
EXPECT_EQ(1u, resource_provider->num_resources());
diff --git a/chromium/cc/resources/scoped_ui_resource.cc b/chromium/cc/resources/scoped_ui_resource.cc
index 16225528cf7..e69b4bc9368 100644
--- a/chromium/cc/resources/scoped_ui_resource.cc
+++ b/chromium/cc/resources/scoped_ui_resource.cc
@@ -12,12 +12,12 @@ namespace cc {
scoped_ptr<ScopedUIResource> ScopedUIResource::Create(
LayerTreeHost* host,
- scoped_refptr<UIResourceBitmap> bitmap) {
+ const UIResourceBitmap& bitmap) {
return make_scoped_ptr(new ScopedUIResource(host, bitmap));
}
ScopedUIResource::ScopedUIResource(LayerTreeHost* host,
- scoped_refptr<UIResourceBitmap> bitmap)
+ const UIResourceBitmap& bitmap)
: bitmap_(bitmap), host_(host) {
DCHECK(host_);
id_ = host_->CreateUIResource(this);
@@ -32,12 +32,9 @@ ScopedUIResource::~ScopedUIResource() {
}
}
-scoped_refptr<UIResourceBitmap> ScopedUIResource::GetBitmap(
- UIResourceId uid,
- bool resource_lost) {
+UIResourceBitmap ScopedUIResource::GetBitmap(UIResourceId uid,
+ bool resource_lost) {
return bitmap_;
}
-ScopedUIResource::ScopedUIResource() {}
-
} // namespace cc
diff --git a/chromium/cc/resources/scoped_ui_resource.h b/chromium/cc/resources/scoped_ui_resource.h
index 628d372c552..c257e1e25dc 100644
--- a/chromium/cc/resources/scoped_ui_resource.h
+++ b/chromium/cc/resources/scoped_ui_resource.h
@@ -15,25 +15,27 @@ namespace cc {
class LayerTreeHost;
+// ScopedUIResource creates an UIResource from a bitmap and a LayerTreeHost.
+// This class holds a pointer to the host so that when the instance goes out of
+// scope, the created resource is deleted. On a GetBitmap call from the
+// UIResource manager, ScopeUIResource always returns the reference to the
+// initially given bitmap regardless of whether the request was due to lost
+// resource or not.
class CC_EXPORT ScopedUIResource : public UIResourceClient {
public:
- static scoped_ptr<ScopedUIResource> Create(
- LayerTreeHost* host,
- scoped_refptr<UIResourceBitmap> bitmap);
+ static scoped_ptr<ScopedUIResource> Create(LayerTreeHost* host,
+ const UIResourceBitmap& bitmap);
virtual ~ScopedUIResource();
- virtual scoped_refptr<UIResourceBitmap> GetBitmap(
- UIResourceId uid,
- bool resource_lost) OVERRIDE;
+ // UIResourceClient implementation.
+ virtual UIResourceBitmap GetBitmap(UIResourceId uid,
+ bool resource_lost) OVERRIDE;
UIResourceId id() { return id_; }
protected:
- ScopedUIResource(LayerTreeHost* host, scoped_refptr<UIResourceBitmap> bitmap);
+ ScopedUIResource(LayerTreeHost* host, const UIResourceBitmap& bitmap);
- // An empty default contructor for testing.
- ScopedUIResource();
-
- scoped_refptr<UIResourceBitmap> bitmap_;
+ UIResourceBitmap bitmap_;
LayerTreeHost* host_;
UIResourceId id_;
diff --git a/chromium/cc/resources/single_release_callback.cc b/chromium/cc/resources/single_release_callback.cc
new file mode 100644
index 00000000000..9c6b9de5f07
--- /dev/null
+++ b/chromium/cc/resources/single_release_callback.cc
@@ -0,0 +1,29 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/resources/single_release_callback.h"
+
+#include "base/callback_helpers.h"
+#include "base/logging.h"
+
+namespace cc {
+
+SingleReleaseCallback::SingleReleaseCallback(const ReleaseCallback& callback)
+ : has_been_run_(false), callback_(callback) {
+ DCHECK(!callback_.is_null())
+ << "Use a NULL SingleReleaseCallback for an empty callback.";
+}
+
+SingleReleaseCallback::~SingleReleaseCallback() {
+ DCHECK(callback_.is_null() || has_been_run_)
+ << "SingleReleaseCallback was never run.";
+}
+
+void SingleReleaseCallback::Run(unsigned sync_point, bool is_lost) {
+ DCHECK(!has_been_run_) << "SingleReleaseCallback was run more than once.";
+ has_been_run_ = true;
+ callback_.Run(sync_point, is_lost);
+}
+
+} // namespace cc
diff --git a/chromium/cc/resources/single_release_callback.h b/chromium/cc/resources/single_release_callback.h
new file mode 100644
index 00000000000..fe1a3ba92b4
--- /dev/null
+++ b/chromium/cc/resources/single_release_callback.h
@@ -0,0 +1,33 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_RESOURCES_SINGLE_RELEASE_CALLBACK_H_
+#define CC_RESOURCES_SINGLE_RELEASE_CALLBACK_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "cc/base/cc_export.h"
+#include "cc/resources/release_callback.h"
+
+namespace cc {
+
+class CC_EXPORT SingleReleaseCallback {
+ public:
+ static scoped_ptr<SingleReleaseCallback> Create(const ReleaseCallback& cb) {
+ return make_scoped_ptr(new SingleReleaseCallback(cb));
+ }
+
+ ~SingleReleaseCallback();
+
+ void Run(unsigned sync_point, bool is_lost);
+
+ private:
+ explicit SingleReleaseCallback(const ReleaseCallback& callback);
+
+ bool has_been_run_;
+ ReleaseCallback callback_;
+};
+
+} // namespace cc
+
+#endif // CC_RESOURCES_SINGLE_RELEASE_CALLBACK_H_
diff --git a/chromium/cc/resources/skpicture_content_layer_updater.cc b/chromium/cc/resources/skpicture_content_layer_updater.cc
index e44dbc25205..79769bc847c 100644
--- a/chromium/cc/resources/skpicture_content_layer_updater.cc
+++ b/chromium/cc/resources/skpicture_content_layer_updater.cc
@@ -17,8 +17,7 @@ SkPictureContentLayerUpdater::SkPictureContentLayerUpdater(
scoped_ptr<LayerPainter> painter,
RenderingStatsInstrumentation* stats_instrumentation,
int layer_id)
- : ContentLayerUpdater(painter.Pass(), stats_instrumentation, layer_id),
- layer_is_opaque_(false) {}
+ : ContentLayerUpdater(painter.Pass(), stats_instrumentation, layer_id) {}
SkPictureContentLayerUpdater::~SkPictureContentLayerUpdater() {}
@@ -33,7 +32,7 @@ void SkPictureContentLayerUpdater::PrepareToUpdate(
base::TimeTicks start_time =
rendering_stats_instrumentation_->StartRecording();
PaintContents(canvas,
- content_rect,
+ content_rect.origin(),
contents_width_scale,
contents_height_scale,
resulting_opaque_rect);
@@ -49,8 +48,4 @@ void SkPictureContentLayerUpdater::DrawPicture(SkCanvas* canvas) {
canvas->drawPicture(picture_);
}
-void SkPictureContentLayerUpdater::SetOpaque(bool opaque) {
- layer_is_opaque_ = opaque;
-}
-
} // namespace cc
diff --git a/chromium/cc/resources/skpicture_content_layer_updater.h b/chromium/cc/resources/skpicture_content_layer_updater.h
index 41a9f01e14e..511d8ee9bfd 100644
--- a/chromium/cc/resources/skpicture_content_layer_updater.h
+++ b/chromium/cc/resources/skpicture_content_layer_updater.h
@@ -20,9 +20,6 @@ class LayerPainter;
// FrameBufferSkPictureContentLayerUpdater are two examples of such
// implementations.
class SkPictureContentLayerUpdater : public ContentLayerUpdater {
- public:
- virtual void SetOpaque(bool opaque) OVERRIDE;
-
protected:
SkPictureContentLayerUpdater(
scoped_ptr<LayerPainter> painter,
@@ -37,13 +34,9 @@ class SkPictureContentLayerUpdater : public ContentLayerUpdater {
gfx::Rect* resulting_opaque_rect) OVERRIDE;
void DrawPicture(SkCanvas* canvas);
- bool layer_is_opaque() const { return layer_is_opaque_; }
-
private:
// Recording canvas.
SkPicture picture_;
- // True when it is known that all output pixels will be opaque.
- bool layer_is_opaque_;
DISALLOW_COPY_AND_ASSIGN(SkPictureContentLayerUpdater);
};
diff --git a/chromium/cc/resources/texture_mailbox.cc b/chromium/cc/resources/texture_mailbox.cc
index 0d4787438f4..149d56c8d1a 100644
--- a/chromium/cc/resources/texture_mailbox.cc
+++ b/chromium/cc/resources/texture_mailbox.cc
@@ -15,69 +15,48 @@ TextureMailbox::TextureMailbox()
shared_memory_(NULL) {
}
-TextureMailbox::TextureMailbox(
- const std::string& mailbox_name,
- const ReleaseCallback& callback)
- : callback_(callback),
- target_(GL_TEXTURE_2D),
+TextureMailbox::TextureMailbox(const std::string& mailbox_name)
+ : target_(GL_TEXTURE_2D),
sync_point_(0),
shared_memory_(NULL) {
- DCHECK(mailbox_name.empty() == callback.is_null());
if (!mailbox_name.empty()) {
CHECK(mailbox_name.size() == sizeof(name_.name));
name_.SetName(reinterpret_cast<const int8*>(mailbox_name.data()));
}
}
-TextureMailbox::TextureMailbox(
- const gpu::Mailbox& mailbox_name,
- const ReleaseCallback& callback)
- : callback_(callback),
- target_(GL_TEXTURE_2D),
+TextureMailbox::TextureMailbox(const gpu::Mailbox& mailbox_name)
+ : target_(GL_TEXTURE_2D),
sync_point_(0),
shared_memory_(NULL) {
- DCHECK(mailbox_name.IsZero() == callback.is_null());
name_.SetName(mailbox_name.name);
}
-TextureMailbox::TextureMailbox(
- const gpu::Mailbox& mailbox_name,
- const ReleaseCallback& callback,
- unsigned sync_point)
- : callback_(callback),
- target_(GL_TEXTURE_2D),
+TextureMailbox::TextureMailbox(const gpu::Mailbox& mailbox_name,
+ unsigned sync_point)
+ : target_(GL_TEXTURE_2D),
sync_point_(sync_point),
shared_memory_(NULL) {
- DCHECK(mailbox_name.IsZero() == callback.is_null());
name_.SetName(mailbox_name.name);
}
-TextureMailbox::TextureMailbox(
- const gpu::Mailbox& mailbox_name,
- const ReleaseCallback& callback,
- unsigned texture_target,
- unsigned sync_point)
- : callback_(callback),
- target_(texture_target),
+TextureMailbox::TextureMailbox(const gpu::Mailbox& mailbox_name,
+ unsigned texture_target,
+ unsigned sync_point)
+ : target_(texture_target),
sync_point_(sync_point),
shared_memory_(NULL) {
- DCHECK(mailbox_name.IsZero() == callback.is_null());
name_.SetName(mailbox_name.name);
}
-TextureMailbox::TextureMailbox(
- base::SharedMemory* shared_memory,
- gfx::Size size,
- const ReleaseCallback& callback)
- : callback_(callback),
- target_(GL_TEXTURE_2D),
+TextureMailbox::TextureMailbox(base::SharedMemory* shared_memory,
+ gfx::Size size)
+ : target_(GL_TEXTURE_2D),
sync_point_(0),
shared_memory_(shared_memory),
- shared_memory_size_(size) {
-}
+ shared_memory_size_(size) {}
-TextureMailbox::~TextureMailbox() {
-}
+TextureMailbox::~TextureMailbox() {}
bool TextureMailbox::Equals(const TextureMailbox& other) const {
if (other.IsTexture())
@@ -97,22 +76,9 @@ bool TextureMailbox::ContainsHandle(base::SharedMemoryHandle handle) const {
return shared_memory_ && shared_memory_->handle() == handle;
}
-void TextureMailbox::RunReleaseCallback(unsigned sync_point,
- bool lost_resource) const {
- if (!callback_.is_null())
- callback_.Run(sync_point, lost_resource);
-}
-
-void TextureMailbox::SetName(const gpu::Mailbox& other) {
+void TextureMailbox::SetName(const gpu::Mailbox& name) {
DCHECK(shared_memory_ == NULL);
- name_.SetName(other.name);
-}
-
-TextureMailbox TextureMailbox::CopyWithNewCallback(
- const ReleaseCallback& callback) const {
- TextureMailbox result(*this);
- result.callback_ = callback;
- return result;
+ name_ = name;
}
size_t TextureMailbox::shared_memory_size_in_bytes() const {
diff --git a/chromium/cc/resources/texture_mailbox.h b/chromium/cc/resources/texture_mailbox.h
index 855287df2d1..a9b021b2390 100644
--- a/chromium/cc/resources/texture_mailbox.h
+++ b/chromium/cc/resources/texture_mailbox.h
@@ -19,23 +19,16 @@ namespace cc {
// can hold a shared memory resource as well as a texture mailbox.
class CC_EXPORT TextureMailbox {
public:
- typedef base::Callback<void(unsigned sync_point,
- bool lost_resource)> ReleaseCallback;
TextureMailbox();
- TextureMailbox(const std::string& mailbox_name,
- const ReleaseCallback& callback);
+ explicit TextureMailbox(const std::string& mailbox_name);
+ explicit TextureMailbox(const gpu::Mailbox& mailbox_name);
TextureMailbox(const gpu::Mailbox& mailbox_name,
- const ReleaseCallback& callback);
- TextureMailbox(const gpu::Mailbox& mailbox_name,
- const ReleaseCallback& callback,
unsigned sync_point);
TextureMailbox(const gpu::Mailbox& mailbox_name,
- const ReleaseCallback& callback,
unsigned texture_target,
unsigned sync_point);
TextureMailbox(base::SharedMemory* shared_memory,
- gfx::Size size,
- const ReleaseCallback& callback);
+ gfx::Size size);
~TextureMailbox();
@@ -47,12 +40,9 @@ class CC_EXPORT TextureMailbox {
bool ContainsMailbox(const gpu::Mailbox&) const;
bool ContainsHandle(base::SharedMemoryHandle handle) const;
- const ReleaseCallback& callback() const { return callback_; }
const int8* data() const { return name_.name; }
const gpu::Mailbox& name() const { return name_; }
void ResetSyncPoint() { sync_point_ = 0; }
- void RunReleaseCallback(unsigned sync_point, bool lost_resource) const;
- void SetName(const gpu::Mailbox&);
unsigned target() const { return target_; }
unsigned sync_point() const { return sync_point_; }
@@ -60,11 +50,12 @@ class CC_EXPORT TextureMailbox {
gfx::Size shared_memory_size() const { return shared_memory_size_; }
size_t shared_memory_size_in_bytes() const;
- TextureMailbox CopyWithNewCallback(const ReleaseCallback& callback) const;
+ // TODO(danakj): ReleaseCallback should be separate from this class, and stop
+ // storing a TextureMailbox in ResourceProvider. Then we can remove this.
+ void SetName(const gpu::Mailbox& name);
private:
gpu::Mailbox name_;
- ReleaseCallback callback_;
unsigned target_;
unsigned sync_point_;
base::SharedMemory* shared_memory_;
diff --git a/chromium/cc/resources/texture_mailbox_deleter.cc b/chromium/cc/resources/texture_mailbox_deleter.cc
new file mode 100644
index 00000000000..ae6df17691d
--- /dev/null
+++ b/chromium/cc/resources/texture_mailbox_deleter.cc
@@ -0,0 +1,92 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/resources/texture_mailbox_deleter.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "cc/output/context_provider.h"
+#include "cc/resources/single_release_callback.h"
+#include "third_party/WebKit/public/platform/WebGraphicsContext3D.h"
+
+namespace cc {
+
+static void DeleteTextureOnImplThread(
+ const scoped_refptr<ContextProvider>& context_provider,
+ unsigned texture_id,
+ unsigned sync_point,
+ bool is_lost) {
+ if (sync_point)
+ context_provider->Context3d()->waitSyncPoint(sync_point);
+ context_provider->Context3d()->deleteTexture(texture_id);
+}
+
+static void PostTaskFromMainToImplThread(
+ scoped_refptr<base::SingleThreadTaskRunner> impl_task_runner,
+ ReleaseCallback run_impl_callback,
+ unsigned sync_point,
+ bool is_lost) {
+ // This posts the task to RunDeleteTextureOnImplThread().
+ impl_task_runner->PostTask(
+ FROM_HERE, base::Bind(run_impl_callback, sync_point, is_lost));
+}
+
+TextureMailboxDeleter::TextureMailboxDeleter() : weak_ptr_factory_(this) {}
+
+TextureMailboxDeleter::~TextureMailboxDeleter() {
+ for (size_t i = 0; i < impl_callbacks_.size(); ++i)
+ impl_callbacks_.at(i)->Run(0, true);
+}
+
+scoped_ptr<SingleReleaseCallback> TextureMailboxDeleter::GetReleaseCallback(
+ const scoped_refptr<ContextProvider>& context_provider,
+ unsigned texture_id) {
+ // This callback owns a reference on the |context_provider|. It must be
+ // destroyed on the impl thread. Upon destruction of this class, the
+ // callback must immediately be destroyed.
+ scoped_ptr<SingleReleaseCallback> impl_callback =
+ SingleReleaseCallback::Create(base::Bind(&DeleteTextureOnImplThread,
+ context_provider,
+ texture_id));
+
+ impl_callbacks_.push_back(impl_callback.Pass());
+
+ // The raw pointer to the impl-side callback is valid as long as this
+ // class is alive. So we guard it with a WeakPtr.
+ ReleaseCallback run_impl_callback(
+ base::Bind(&TextureMailboxDeleter::RunDeleteTextureOnImplThread,
+ weak_ptr_factory_.GetWeakPtr(),
+ impl_callbacks_.back()));
+
+ // Provide a callback for the main thread that posts back to the impl
+ // thread.
+ scoped_ptr<SingleReleaseCallback> main_callback =
+ SingleReleaseCallback::Create(base::Bind(
+ &PostTaskFromMainToImplThread,
+ base::MessageLoopProxy::current(),
+ run_impl_callback));
+
+ return main_callback.Pass();
+}
+
+void TextureMailboxDeleter::RunDeleteTextureOnImplThread(
+ SingleReleaseCallback* impl_callback,
+ unsigned sync_point,
+ bool is_lost) {
+ for (size_t i = 0; i < impl_callbacks_.size(); ++i) {
+ if (impl_callbacks_.at(i) == impl_callback) {
+ // Run the callback, then destroy it here on the impl thread.
+ impl_callbacks_.at(i)->Run(sync_point, is_lost);
+ impl_callbacks_.erase(impl_callbacks_.begin() + i);
+ return;
+ }
+ }
+
+ NOTREACHED() << "The Callback returned by GetDeleteCallback() was called "
+ << "more than once.";
+}
+
+} // namespace cc
diff --git a/chromium/cc/resources/texture_mailbox_deleter.h b/chromium/cc/resources/texture_mailbox_deleter.h
new file mode 100644
index 00000000000..072dcab375b
--- /dev/null
+++ b/chromium/cc/resources/texture_mailbox_deleter.h
@@ -0,0 +1,46 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_RESOURCES_TEXTURE_MAILBOX_DELETER_H_
+#define CC_RESOURCES_TEXTURE_MAILBOX_DELETER_H_
+
+#include "base/memory/weak_ptr.h"
+#include "cc/base/cc_export.h"
+#include "cc/base/scoped_ptr_vector.h"
+
+namespace cc {
+class ContextProvider;
+class SingleReleaseCallback;
+
+class CC_EXPORT TextureMailboxDeleter {
+ public:
+ TextureMailboxDeleter();
+ ~TextureMailboxDeleter();
+
+ // Returns a Callback that can be used as the ReleaseCallback for a
+ // TextureMailbox attached to the |texture_id|. The ReleaseCallback can
+ // be passed to other threads and will destroy the texture, once it is
+ // run, on the impl thread. If the TextureMailboxDeleter is destroyed
+ // due to the compositor shutting down, then the ReleaseCallback will
+ // become a no-op and the texture will be deleted immediately on the
+ // impl thread, along with dropping the reference to the ContextProvider.
+ scoped_ptr<SingleReleaseCallback> GetReleaseCallback(
+ const scoped_refptr<ContextProvider>& context_provider,
+ unsigned texture_id);
+
+ private:
+ // Runs the |impl_callback| to delete the texture and removes the callback
+ // from the |impl_callbacks_| list.
+ void RunDeleteTextureOnImplThread(
+ SingleReleaseCallback* impl_callback,
+ unsigned sync_point,
+ bool is_lost);
+
+ base::WeakPtrFactory<TextureMailboxDeleter> weak_ptr_factory_;
+ ScopedPtrVector<SingleReleaseCallback> impl_callbacks_;
+};
+
+} // namespace cc
+
+#endif // CC_RESOURCES_TEXTURE_MAILBOX_DELETER_H_
diff --git a/chromium/cc/resources/texture_mailbox_deleter_unittest.cc b/chromium/cc/resources/texture_mailbox_deleter_unittest.cc
new file mode 100644
index 00000000000..a6ec48ed2ed
--- /dev/null
+++ b/chromium/cc/resources/texture_mailbox_deleter_unittest.cc
@@ -0,0 +1,44 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/resources/texture_mailbox_deleter.h"
+
+#include "cc/debug/test_context_provider.h"
+#include "cc/debug/test_web_graphics_context_3d.h"
+#include "cc/resources/single_release_callback.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cc {
+namespace {
+
+TEST(TextureMailboxDeleterTest, Destroy) {
+ scoped_ptr<TextureMailboxDeleter> deleter(new TextureMailboxDeleter);
+
+ scoped_refptr<TestContextProvider> context_provider =
+ TestContextProvider::Create();
+ context_provider->BindToCurrentThread();
+
+ unsigned texture_id = context_provider->Context3d()->createTexture();
+
+ EXPECT_TRUE(context_provider->HasOneRef());
+ EXPECT_EQ(1u, context_provider->TestContext3d()->NumTextures());
+
+ scoped_ptr<SingleReleaseCallback> cb =
+ deleter->GetReleaseCallback(context_provider, texture_id).Pass();
+ EXPECT_FALSE(context_provider->HasOneRef());
+ EXPECT_EQ(1u, context_provider->TestContext3d()->NumTextures());
+
+ // When the deleter is destroyed, it immediately drops its ref on the
+ // ContextProvider, and deletes the texture.
+ deleter.reset();
+ EXPECT_TRUE(context_provider->HasOneRef());
+ EXPECT_EQ(0u, context_provider->TestContext3d()->NumTextures());
+
+ // Run the scoped release callback before destroying it, but it won't do
+ // anything.
+ cb->Run(0, false);
+}
+
+} // namespace
+} // namespace cc
diff --git a/chromium/cc/resources/tile.cc b/chromium/cc/resources/tile.cc
index ab4fe7af549..d7e1d2c2da2 100644
--- a/chromium/cc/resources/tile.cc
+++ b/chromium/cc/resources/tile.cc
@@ -37,10 +37,28 @@ Tile::Tile(TileManager* tile_manager,
Tile::~Tile() {
TRACE_EVENT_OBJECT_DELETED_WITH_ID(
- TRACE_DISABLED_BY_DEFAULT("cc.debug"), "cc::Tile", this);
+ TRACE_DISABLED_BY_DEFAULT("cc.debug") ","
+ TRACE_DISABLED_BY_DEFAULT("cc.debug.quads"),
+ "cc::Tile", this);
tile_manager_->UnregisterTile(this);
}
+void Tile::SetPriority(WhichTree tree, const TilePriority& priority) {
+ if (priority == priority_[tree])
+ return;
+
+ priority_[tree] = priority;
+ tile_manager_->DidChangeTilePriority(this);
+}
+
+void Tile::MarkRequiredForActivation() {
+ if (priority_[PENDING_TREE].required_for_activation)
+ return;
+
+ priority_[PENDING_TREE].required_for_activation = true;
+ tile_manager_->DidChangeTilePriority(this);
+}
+
scoped_ptr<base::Value> Tile::AsValue() const {
scoped_ptr<base::DictionaryValue> res(new base::DictionaryValue());
TracedValue::MakeDictIntoImplicitSnapshot(res.get(), "cc::Tile", this);
diff --git a/chromium/cc/resources/tile.h b/chromium/cc/resources/tile.h
index be9b19a4f1b..3caa407ae9c 100644
--- a/chromium/cc/resources/tile.h
+++ b/chromium/cc/resources/tile.h
@@ -53,13 +53,9 @@ class CC_EXPORT Tile : public base::RefCounted<Tile> {
priority_[PENDING_TREE]);
}
- void SetPriority(WhichTree tree, const TilePriority& priority) {
- priority_[tree] = priority;
- }
+ void SetPriority(WhichTree tree, const TilePriority& priority);
- void mark_required_for_activation() {
- priority_[PENDING_TREE].required_for_activation = true;
- }
+ void MarkRequiredForActivation();
bool required_for_activation() const {
return priority_[PENDING_TREE].required_for_activation;
@@ -75,7 +71,7 @@ class CC_EXPORT Tile : public base::RefCounted<Tile> {
scoped_ptr<base::Value> AsValue() const;
- bool IsReadyToDraw() const {
+ inline bool IsReadyToDraw() const {
for (int mode = 0; mode < NUM_RASTER_MODES; ++mode) {
if (managed_state_.tile_versions[mode].IsReadyToDraw())
return true;
@@ -117,6 +113,8 @@ class CC_EXPORT Tile : public base::RefCounted<Tile> {
return managed_state_.tile_versions[mode];
}
+ gfx::Size size() const { return tile_size_.size(); }
+
private:
// Methods called by by tile manager.
friend class TileManager;
@@ -126,10 +124,6 @@ class CC_EXPORT Tile : public base::RefCounted<Tile> {
ManagedTileState& managed_state() { return managed_state_; }
const ManagedTileState& managed_state() const { return managed_state_; }
- inline size_t bytes_consumed_if_allocated() const {
- return 4 * tile_size_.width() * tile_size_.height();
- }
-
// Normal private methods.
friend class base::RefCounted<Tile>;
~Tile();
@@ -141,7 +135,7 @@ class CC_EXPORT Tile : public base::RefCounted<Tile> {
float contents_scale_;
gfx::Rect opaque_rect_;
- TilePriority priority_[NUM_BIN_PRIORITIES];
+ TilePriority priority_[NUM_TREES];
ManagedTileState managed_state_;
int layer_id_;
int source_frame_number_;
diff --git a/chromium/cc/resources/tile_manager.cc b/chromium/cc/resources/tile_manager.cc
index e6850bcfd84..b8cbdca1fa7 100644
--- a/chromium/cc/resources/tile_manager.cc
+++ b/chromium/cc/resources/tile_manager.cc
@@ -32,7 +32,8 @@ const ManagedTileBin kBinPolicyMap[NUM_TILE_MEMORY_LIMIT_POLICIES][NUM_BINS] = {
NEVER_BIN, // [SOON_BIN]
NEVER_BIN, // [EVENTUALLY_AND_ACTIVE_BIN]
NEVER_BIN, // [EVENTUALLY_BIN]
- NEVER_BIN, // [NEVER_AND_ACTIVE_BIN]
+ NEVER_BIN, // [AT_LAST_AND_ACTIVE_BIN]
+ NEVER_BIN, // [AT_LAST_BIN]
NEVER_BIN // [NEVER_BIN]
}, { // [ALLOW_ABSOLUTE_MINIMUM]
NOW_AND_READY_TO_DRAW_BIN, // [NOW_AND_READY_TO_DRAW_BIN]
@@ -40,7 +41,8 @@ const ManagedTileBin kBinPolicyMap[NUM_TILE_MEMORY_LIMIT_POLICIES][NUM_BINS] = {
NEVER_BIN, // [SOON_BIN]
NEVER_BIN, // [EVENTUALLY_AND_ACTIVE_BIN]
NEVER_BIN, // [EVENTUALLY_BIN]
- NEVER_BIN, // [NEVER_AND_ACTIVE_BIN]
+ NEVER_BIN, // [AT_LAST_AND_ACTIVE_BIN]
+ NEVER_BIN, // [AT_LAST_BIN]
NEVER_BIN // [NEVER_BIN]
}, { // [ALLOW_PREPAINT_ONLY]
NOW_AND_READY_TO_DRAW_BIN, // [NOW_AND_READY_TO_DRAW_BIN]
@@ -48,7 +50,8 @@ const ManagedTileBin kBinPolicyMap[NUM_TILE_MEMORY_LIMIT_POLICIES][NUM_BINS] = {
SOON_BIN, // [SOON_BIN]
NEVER_BIN, // [EVENTUALLY_AND_ACTIVE_BIN]
NEVER_BIN, // [EVENTUALLY_BIN]
- NEVER_BIN, // [NEVER_AND_ACTIVE_BIN]
+ NEVER_BIN, // [AT_LAST_AND_ACTIVE_BIN]
+ NEVER_BIN, // [AT_LAST_BIN]
NEVER_BIN // [NEVER_BIN]
}, { // [ALLOW_ANYTHING]
NOW_AND_READY_TO_DRAW_BIN, // [NOW_AND_READY_TO_DRAW_BIN]
@@ -56,7 +59,8 @@ const ManagedTileBin kBinPolicyMap[NUM_TILE_MEMORY_LIMIT_POLICIES][NUM_BINS] = {
SOON_BIN, // [SOON_BIN]
EVENTUALLY_AND_ACTIVE_BIN, // [EVENTUALLY_AND_ACTIVE_BIN]
EVENTUALLY_BIN, // [EVENTUALLY_BIN]
- NEVER_AND_ACTIVE_BIN, // [NEVER_AND_ACTIVE_BIN]
+ AT_LAST_AND_ACTIVE_BIN, // [AT_LAST_AND_ACTIVE_BIN]
+ AT_LAST_BIN, // [AT_LAST_BIN]
NEVER_BIN // [NEVER_BIN]
}
};
@@ -78,7 +82,7 @@ inline ManagedTileBin BinFromTilePriority(const TilePriority& prio,
if (prio.distance_to_visible_in_pixels ==
std::numeric_limits<float>::infinity())
- return is_active ? NEVER_AND_ACTIVE_BIN : NEVER_BIN;
+ return NEVER_BIN;
if (can_be_in_now_bin && prio.time_to_visible_in_seconds == 0)
return is_ready_to_draw ? NOW_AND_READY_TO_DRAW_BIN : NOW_BIN;
@@ -120,7 +124,8 @@ scoped_ptr<TileManager> TileManager::Create(
ResourceProvider* resource_provider,
size_t num_raster_threads,
RenderingStatsInstrumentation* rendering_stats_instrumentation,
- bool use_map_image) {
+ bool use_map_image,
+ size_t max_transfer_buffer_usage_bytes) {
return make_scoped_ptr(
new TileManager(client,
resource_provider,
@@ -128,10 +133,11 @@ scoped_ptr<TileManager> TileManager::Create(
ImageRasterWorkerPool::Create(
resource_provider, num_raster_threads) :
PixelBufferRasterWorkerPool::Create(
- resource_provider, num_raster_threads),
+ resource_provider,
+ num_raster_threads,
+ max_transfer_buffer_usage_bytes),
num_raster_threads,
- rendering_stats_instrumentation,
- resource_provider->best_texture_format()));
+ rendering_stats_instrumentation));
}
TileManager::TileManager(
@@ -139,18 +145,21 @@ TileManager::TileManager(
ResourceProvider* resource_provider,
scoped_ptr<RasterWorkerPool> raster_worker_pool,
size_t num_raster_threads,
- RenderingStatsInstrumentation* rendering_stats_instrumentation,
- GLenum texture_format)
+ RenderingStatsInstrumentation* rendering_stats_instrumentation)
: client_(client),
resource_pool_(ResourcePool::Create(resource_provider)),
raster_worker_pool_(raster_worker_pool.Pass()),
+ prioritized_tiles_dirty_(false),
all_tiles_that_need_to_be_rasterized_have_memory_(true),
all_tiles_required_for_activation_have_memory_(true),
- all_tiles_required_for_activation_have_been_initialized_(true),
+ memory_required_bytes_(0),
+ memory_nice_to_have_bytes_(0),
+ bytes_releasable_(0),
+ resources_releasable_(0),
ever_exceeded_memory_budget_(false),
rendering_stats_instrumentation_(rendering_stats_instrumentation),
did_initialize_visible_tile_(false),
- texture_format_(texture_format) {
+ did_check_for_completed_tasks_since_last_schedule_tasks_(true) {
raster_worker_pool_->SetClient(this);
}
@@ -159,17 +168,18 @@ TileManager::~TileManager() {
// our memory usage to drop to zero.
global_state_ = GlobalStateThatImpactsTilePriority();
- // Clear |prioritized_tiles_| so that tiles kept alive by it can be freed.
- prioritized_tiles_.Clear();
DCHECK_EQ(0u, tiles_.size());
- TileVector empty;
- ScheduleTasks(empty);
+ RasterWorkerPool::RasterTask::Queue empty;
+ raster_worker_pool_->ScheduleTasks(&empty);
// This should finish all pending tasks and release any uninitialized
// resources.
raster_worker_pool_->Shutdown();
raster_worker_pool_->CheckForCompletedTasks();
+
+ DCHECK_EQ(0u, bytes_releasable_);
+ DCHECK_EQ(0u, resources_releasable_);
}
void TileManager::SetGlobalState(
@@ -186,6 +196,8 @@ void TileManager::RegisterTile(Tile* tile) {
DCHECK(tiles_.find(tile->id()) == tiles_.end());
tiles_[tile->id()] = tile;
+ used_layer_counts_[tile->layer_id()]++;
+ prioritized_tiles_dirty_ = true;
}
void TileManager::UnregisterTile(Tile* tile) {
@@ -193,12 +205,35 @@ void TileManager::UnregisterTile(Tile* tile) {
DCHECK(tiles_.find(tile->id()) != tiles_.end());
tiles_.erase(tile->id());
+
+ LayerCountMap::iterator layer_it = used_layer_counts_.find(tile->layer_id());
+ DCHECK_GT(layer_it->second, 0);
+ if (--layer_it->second == 0) {
+ used_layer_counts_.erase(layer_it);
+ image_decode_tasks_.erase(tile->layer_id());
+ }
+
+ prioritized_tiles_dirty_ = true;
+}
+
+void TileManager::DidChangeTilePriority(Tile* tile) {
+ prioritized_tiles_dirty_ = true;
}
bool TileManager::ShouldForceTasksRequiredForActivationToComplete() const {
return GlobalState().tree_priority != SMOOTHNESS_TAKES_PRIORITY;
}
+PrioritizedTileSet* TileManager::GetPrioritizedTileSet() {
+ if (!prioritized_tiles_dirty_)
+ return &prioritized_tiles_;
+
+ prioritized_tiles_.Clear();
+ GetTilesWithAssignedBins(&prioritized_tiles_);
+ prioritized_tiles_dirty_ = false;
+ return &prioritized_tiles_;
+}
+
void TileManager::DidFinishRunningTasks() {
TRACE_EVENT0("cc", "TileManager::DidFinishRunningTasks");
@@ -208,9 +243,10 @@ void TileManager::DidFinishRunningTasks() {
return;
raster_worker_pool_->CheckForCompletedTasks();
+ did_check_for_completed_tasks_since_last_schedule_tasks_ = true;
TileVector tiles_that_need_to_be_rasterized;
- AssignGpuMemoryToTiles(&prioritized_tiles_,
+ AssignGpuMemoryToTiles(GetPrioritizedTileSet(),
&tiles_that_need_to_be_rasterized);
// |tiles_that_need_to_be_rasterized| will be empty when we reach a
@@ -220,6 +256,12 @@ void TileManager::DidFinishRunningTasks() {
return;
}
+ // We don't reserve memory for required-for-activation tiles during
+ // accelerated gestures, so we just postpone activation when we don't
+ // have these tiles, and activate after the accelerated gesture.
+ bool allow_rasterize_on_demand =
+ global_state_.tree_priority != SMOOTHNESS_TAKES_PRIORITY;
+
// Use on-demand raster for any required-for-activation tiles that have not
// been been assigned memory after reaching a steady memory state. This
// ensures that we activate even when OOM.
@@ -229,8 +271,12 @@ void TileManager::DidFinishRunningTasks() {
ManagedTileState::TileVersion& tile_version =
mts.tile_versions[mts.raster_mode];
- if (tile->required_for_activation() && !tile_version.IsReadyToDraw())
+ if (tile->required_for_activation() && !tile_version.IsReadyToDraw()) {
+ // If we can't raster on demand, give up early (and don't activate).
+ if (!allow_rasterize_on_demand)
+ return;
tile_version.set_rasterize_on_demand();
+ }
}
client_->NotifyReadyToActivate();
@@ -251,6 +297,10 @@ void TileManager::DidFinishRunningTasksRequiredForActivation() {
void TileManager::GetTilesWithAssignedBins(PrioritizedTileSet* tiles) {
TRACE_EVENT0("cc", "TileManager::GetTilesWithAssignedBins");
+ // Compute new stats to be return by GetMemoryStats().
+ memory_required_bytes_ = 0;
+ memory_nice_to_have_bytes_ = 0;
+
const TileMemoryLimitPolicy memory_policy = global_state_.memory_limit_policy;
const TreePriority tree_priority = global_state_.tree_priority;
@@ -259,72 +309,85 @@ void TileManager::GetTilesWithAssignedBins(PrioritizedTileSet* tiles) {
Tile* tile = it->second;
ManagedTileState& mts = tile->managed_state();
- TilePriority prio[NUM_BIN_PRIORITIES];
+ const ManagedTileState::TileVersion& tile_version =
+ tile->GetTileVersionForDrawing();
+ bool tile_is_ready_to_draw = tile_version.IsReadyToDraw();
+ bool tile_is_active =
+ tile_is_ready_to_draw ||
+ !mts.tile_versions[mts.raster_mode].raster_task_.is_null();
+
+ // Get the active priority and bin.
+ TilePriority active_priority = tile->priority(ACTIVE_TREE);
+ ManagedTileBin active_bin = BinFromTilePriority(
+ active_priority, tree_priority, tile_is_ready_to_draw, tile_is_active);
+ mts.tree_bin[ACTIVE_TREE] = kBinPolicyMap[memory_policy][active_bin];
+
+ // Get the pending priority and bin.
+ TilePriority pending_priority = tile->priority(PENDING_TREE);
+ ManagedTileBin pending_bin = BinFromTilePriority(
+ pending_priority, tree_priority, tile_is_ready_to_draw, tile_is_active);
+ mts.tree_bin[PENDING_TREE] = kBinPolicyMap[memory_policy][pending_bin];
+
+ // Get the combined priority and bin.
+ TilePriority combined_priority = tile->combined_priority();
+ ManagedTileBin combined_bin = BinFromTilePriority(combined_priority,
+ tree_priority,
+ tile_is_ready_to_draw,
+ tile_is_active);
+
+ // The bin that the tile would have if the GPU memory manager had
+ // a maximally permissive policy, send to the GPU memory manager
+ // to determine policy.
+ ManagedTileBin gpu_memmgr_stats_bin = NEVER_BIN;
+
+ TilePriority* high_priority = NULL;
switch (tree_priority) {
case SAME_PRIORITY_FOR_BOTH_TREES:
- prio[HIGH_PRIORITY_BIN] = prio[LOW_PRIORITY_BIN] =
- tile->combined_priority();
+ mts.bin = kBinPolicyMap[memory_policy][combined_bin];
+ gpu_memmgr_stats_bin = combined_bin;
+ high_priority = &combined_priority;
break;
case SMOOTHNESS_TAKES_PRIORITY:
- prio[HIGH_PRIORITY_BIN] = tile->priority(ACTIVE_TREE);
- prio[LOW_PRIORITY_BIN] = tile->priority(PENDING_TREE);
+ mts.bin = mts.tree_bin[ACTIVE_TREE];
+ gpu_memmgr_stats_bin = active_bin;
+ high_priority = &active_priority;
break;
case NEW_CONTENT_TAKES_PRIORITY:
- prio[HIGH_PRIORITY_BIN] = tile->priority(PENDING_TREE);
- prio[LOW_PRIORITY_BIN] = tile->priority(ACTIVE_TREE);
+ mts.bin = mts.tree_bin[PENDING_TREE];
+ gpu_memmgr_stats_bin = pending_bin;
+ high_priority = &pending_priority;
break;
}
- bool tile_is_ready_to_draw = tile->IsReadyToDraw();
- bool tile_is_active =
- tile_is_ready_to_draw ||
- !mts.tile_versions[mts.raster_mode].raster_task_.is_null();
+ if (!tile_is_ready_to_draw || tile_version.requires_resource()) {
+ if ((gpu_memmgr_stats_bin == NOW_BIN) ||
+ (gpu_memmgr_stats_bin == NOW_AND_READY_TO_DRAW_BIN))
+ memory_required_bytes_ += BytesConsumedIfAllocated(tile);
+ if (gpu_memmgr_stats_bin != NEVER_BIN)
+ memory_nice_to_have_bytes_ += BytesConsumedIfAllocated(tile);
+ }
- mts.resolution = prio[HIGH_PRIORITY_BIN].resolution;
- mts.time_to_needed_in_seconds =
- prio[HIGH_PRIORITY_BIN].time_to_visible_in_seconds;
- mts.distance_to_visible_in_pixels =
- prio[HIGH_PRIORITY_BIN].distance_to_visible_in_pixels;
- mts.required_for_activation =
- prio[HIGH_PRIORITY_BIN].required_for_activation;
-
- mts.bin[HIGH_PRIORITY_BIN] =
- BinFromTilePriority(prio[HIGH_PRIORITY_BIN],
- tree_priority,
- tile_is_ready_to_draw,
- tile_is_active);
- mts.bin[LOW_PRIORITY_BIN] =
- BinFromTilePriority(prio[LOW_PRIORITY_BIN],
- tree_priority,
- tile_is_ready_to_draw,
- tile_is_active);
- mts.gpu_memmgr_stats_bin =
- BinFromTilePriority(tile->combined_priority(),
- tree_priority,
- tile_is_ready_to_draw,
- tile_is_active);
-
- ManagedTileBin active_bin =
- BinFromTilePriority(tile->priority(ACTIVE_TREE),
- tree_priority,
- tile_is_ready_to_draw,
- tile_is_active);
- mts.tree_bin[ACTIVE_TREE] = kBinPolicyMap[memory_policy][active_bin];
+ // Bump up the priority if we determined it's NEVER_BIN on one tree,
+ // but is still required on the other tree.
+ bool is_in_never_bin_on_both_trees =
+ mts.tree_bin[ACTIVE_TREE] == NEVER_BIN &&
+ mts.tree_bin[PENDING_TREE] == NEVER_BIN;
- ManagedTileBin pending_bin =
- BinFromTilePriority(tile->priority(PENDING_TREE),
- tree_priority,
- tile_is_ready_to_draw,
- tile_is_active);
- mts.tree_bin[PENDING_TREE] = kBinPolicyMap[memory_policy][pending_bin];
+ if (mts.bin == NEVER_BIN && !is_in_never_bin_on_both_trees)
+ mts.bin = tile_is_active ? AT_LAST_AND_ACTIVE_BIN : AT_LAST_BIN;
- for (int i = 0; i < NUM_BIN_PRIORITIES; ++i)
- mts.bin[i] = kBinPolicyMap[memory_policy][mts.bin[i]];
+ DCHECK(high_priority != NULL);
+
+ mts.resolution = high_priority->resolution;
+ mts.time_to_needed_in_seconds = high_priority->time_to_visible_in_seconds;
+ mts.distance_to_visible_in_pixels =
+ high_priority->distance_to_visible_in_pixels;
+ mts.required_for_activation = high_priority->required_for_activation;
mts.visible_and_ready_to_draw =
mts.tree_bin[ACTIVE_TREE] == NOW_AND_READY_TO_DRAW_BIN;
- if (mts.is_in_never_bin_on_both_trees()) {
+ if (mts.bin == NEVER_BIN) {
FreeResourcesForTile(tile);
continue;
}
@@ -336,45 +399,44 @@ void TileManager::GetTilesWithAssignedBins(PrioritizedTileSet* tiles) {
// priority.
ManagedTileBin priority_bin = mts.visible_and_ready_to_draw
? NOW_AND_READY_TO_DRAW_BIN
- : mts.bin[HIGH_PRIORITY_BIN];
+ : mts.bin;
// Insert the tile into a priority set.
tiles->InsertTile(tile, priority_bin);
}
}
-void TileManager::GetPrioritizedTileSet(PrioritizedTileSet* tiles) {
- TRACE_EVENT0("cc", "TileManager::GetPrioritizedTileSet");
-
- GetTilesWithAssignedBins(tiles);
- tiles->Sort();
-}
-
void TileManager::ManageTiles() {
TRACE_EVENT0("cc", "TileManager::ManageTiles");
- // Clear |prioritized_tiles_| so that tiles kept alive by it can be freed.
- prioritized_tiles_.Clear();
-
- GetPrioritizedTileSet(&prioritized_tiles_);
+ // We need to call CheckForCompletedTasks() once in-between each call
+ // to ScheduleTasks() to prevent canceled tasks from being scheduled.
+ if (!did_check_for_completed_tasks_since_last_schedule_tasks_) {
+ raster_worker_pool_->CheckForCompletedTasks();
+ did_check_for_completed_tasks_since_last_schedule_tasks_ = true;
+ }
TileVector tiles_that_need_to_be_rasterized;
- AssignGpuMemoryToTiles(&prioritized_tiles_,
+ AssignGpuMemoryToTiles(GetPrioritizedTileSet(),
&tiles_that_need_to_be_rasterized);
- CleanUpUnusedImageDecodeTasks();
+
+ // Finally, schedule rasterizer tasks.
+ ScheduleTasks(tiles_that_need_to_be_rasterized);
TRACE_EVENT_INSTANT1(
"cc", "DidManage", TRACE_EVENT_SCOPE_THREAD,
"state", TracedValue::FromValue(BasicStateAsValue().release()));
- // Finally, schedule rasterizer tasks.
- ScheduleTasks(tiles_that_need_to_be_rasterized);
+ TRACE_COUNTER_ID1("cc", "unused_memory_bytes", this,
+ resource_pool_->total_memory_usage_bytes() -
+ resource_pool_->acquired_memory_usage_bytes());
}
bool TileManager::UpdateVisibleTiles() {
TRACE_EVENT0("cc", "TileManager::UpdateVisibleTiles");
raster_worker_pool_->CheckForCompletedTasks();
+ did_check_for_completed_tasks_since_last_schedule_tasks_ = true;
TRACE_EVENT_INSTANT1(
"cc", "DidUpdateVisibleTiles", TRACE_EVENT_SCOPE_THREAD,
@@ -391,29 +453,12 @@ bool TileManager::UpdateVisibleTiles() {
void TileManager::GetMemoryStats(
size_t* memory_required_bytes,
size_t* memory_nice_to_have_bytes,
+ size_t* memory_allocated_bytes,
size_t* memory_used_bytes) const {
- *memory_required_bytes = 0;
- *memory_nice_to_have_bytes = 0;
+ *memory_required_bytes = memory_required_bytes_;
+ *memory_nice_to_have_bytes = memory_nice_to_have_bytes_;
+ *memory_allocated_bytes = resource_pool_->total_memory_usage_bytes();
*memory_used_bytes = resource_pool_->acquired_memory_usage_bytes();
- for (TileMap::const_iterator it = tiles_.begin();
- it != tiles_.end();
- ++it) {
- const Tile* tile = it->second;
- const ManagedTileState& mts = tile->managed_state();
-
- const ManagedTileState::TileVersion& tile_version =
- tile->GetTileVersionForDrawing();
- if (tile_version.IsReadyToDraw() &&
- !tile_version.requires_resource())
- continue;
-
- size_t tile_bytes = tile->bytes_consumed_if_allocated();
- if ((mts.gpu_memmgr_stats_bin == NOW_BIN) ||
- (mts.gpu_memmgr_stats_bin == NOW_AND_READY_TO_DRAW_BIN))
- *memory_required_bytes += tile_bytes;
- if (mts.gpu_memmgr_stats_bin != NEVER_BIN)
- *memory_nice_to_have_bytes += tile_bytes;
- }
}
scoped_ptr<base::Value> TileManager::BasicStateAsValue() const {
@@ -440,13 +485,16 @@ scoped_ptr<base::Value> TileManager::GetMemoryRequirementsAsValue() const {
size_t memory_required_bytes;
size_t memory_nice_to_have_bytes;
+ size_t memory_allocated_bytes;
size_t memory_used_bytes;
GetMemoryStats(&memory_required_bytes,
&memory_nice_to_have_bytes,
+ &memory_allocated_bytes,
&memory_used_bytes);
requirements->SetInteger("memory_required_bytes", memory_required_bytes);
requirements->SetInteger("memory_nice_to_have_bytes",
memory_nice_to_have_bytes);
+ requirements->SetInteger("memory_allocated_bytes", memory_allocated_bytes);
requirements->SetInteger("memory_used_bytes", memory_used_bytes);
return requirements.PassAs<base::Value>();
}
@@ -477,32 +525,16 @@ void TileManager::AssignGpuMemoryToTiles(
// Now give memory out to the tiles until we're out, and build
// the needs-to-be-rasterized queue.
- size_t bytes_releasable = 0;
- size_t resources_releasable = 0;
- for (PrioritizedTileSet::PriorityIterator it(tiles);
- it;
- ++it) {
- const Tile* tile = *it;
- const ManagedTileState& mts = tile->managed_state();
- for (int mode = 0; mode < NUM_RASTER_MODES; ++mode) {
- if (mts.tile_versions[mode].resource_) {
- bytes_releasable += tile->bytes_consumed_if_allocated();
- resources_releasable++;
- }
- }
- }
-
all_tiles_that_need_to_be_rasterized_have_memory_ = true;
all_tiles_required_for_activation_have_memory_ = true;
- all_tiles_required_for_activation_have_been_initialized_ = true;
// Cast to prevent overflow.
int64 bytes_available =
- static_cast<int64>(bytes_releasable) +
+ static_cast<int64>(bytes_releasable_) +
static_cast<int64>(global_state_.memory_limit_in_bytes) -
static_cast<int64>(resource_pool_->acquired_memory_usage_bytes());
int resources_available =
- resources_releasable +
+ resources_releasable_ +
global_state_.num_resources_limit -
resource_pool_->acquired_resource_count();
@@ -516,7 +548,7 @@ void TileManager::AssignGpuMemoryToTiles(
bool oomed = false;
unsigned schedule_priority = 1u;
- for (PrioritizedTileSet::PriorityIterator it(tiles);
+ for (PrioritizedTileSet::Iterator it(tiles, true);
it;
++it) {
Tile* tile = *it;
@@ -534,7 +566,7 @@ void TileManager::AssignGpuMemoryToTiles(
continue;
// If the tile is not needed, free it up.
- if (mts.is_in_never_bin_on_both_trees()) {
+ if (mts.bin == NEVER_BIN) {
FreeResourcesForTile(tile);
continue;
}
@@ -545,7 +577,7 @@ void TileManager::AssignGpuMemoryToTiles(
// It costs to maintain a resource.
for (int mode = 0; mode < NUM_RASTER_MODES; ++mode) {
if (mts.tile_versions[mode].resource_) {
- tile_bytes += tile->bytes_consumed_if_allocated();
+ tile_bytes += BytesConsumedIfAllocated(tile);
tile_resources++;
}
}
@@ -557,7 +589,7 @@ void TileManager::AssignGpuMemoryToTiles(
// If we don't have the required version, and it's not in flight
// then we'll have to pay to create a new task.
if (!tile_version.resource_ && tile_version.raster_task_.is_null()) {
- tile_bytes += tile->bytes_consumed_if_allocated();
+ tile_bytes += BytesConsumedIfAllocated(tile);
tile_resources++;
}
}
@@ -584,9 +616,6 @@ void TileManager::AssignGpuMemoryToTiles(
DCHECK(!tile_version.resource_);
- if (tile->required_for_activation())
- all_tiles_required_for_activation_have_been_initialized_ = false;
-
// Tile shouldn't be rasterized if |tiles_that_need_to_be_rasterized|
// has reached it's limit or we've failed to assign gpu memory to this
// or any higher priority tile. Preventing tiles that fit into memory
@@ -599,6 +628,7 @@ void TileManager::AssignGpuMemoryToTiles(
all_tiles_that_need_to_be_rasterized_have_memory_ = false;
if (tile->required_for_activation())
all_tiles_required_for_activation_have_memory_ = false;
+ it.DisablePriorityOrdering();
continue;
}
@@ -616,40 +646,22 @@ void TileManager::AssignGpuMemoryToTiles(
memory_stats_from_last_assign_.bytes_allocated =
bytes_allocatable - bytes_left;
memory_stats_from_last_assign_.bytes_unreleasable =
- bytes_allocatable - bytes_releasable;
+ bytes_allocatable - bytes_releasable_;
memory_stats_from_last_assign_.bytes_over =
bytes_that_exceeded_memory_budget;
}
-void TileManager::CleanUpUnusedImageDecodeTasks() {
- // Calculate a set of layers that are used by at least one tile.
- base::hash_set<int> used_layers;
- for (TileMap::iterator it = tiles_.begin(); it != tiles_.end(); ++it)
- used_layers.insert(it->second->layer_id());
-
- // Now calculate the set of layers in |image_decode_tasks_| that are not used
- // by any tile.
- std::vector<int> unused_layers;
- for (LayerPixelRefTaskMap::iterator it = image_decode_tasks_.begin();
- it != image_decode_tasks_.end();
- ++it) {
- if (used_layers.find(it->first) == used_layers.end())
- unused_layers.push_back(it->first);
- }
-
- // Erase unused layers from |image_decode_tasks_|.
- for (std::vector<int>::iterator it = unused_layers.begin();
- it != unused_layers.end();
- ++it) {
- image_decode_tasks_.erase(*it);
- }
-}
-
void TileManager::FreeResourceForTile(Tile* tile, RasterMode mode) {
ManagedTileState& mts = tile->managed_state();
if (mts.tile_versions[mode].resource_) {
resource_pool_->ReleaseResource(
mts.tile_versions[mode].resource_.Pass());
+
+ DCHECK_GE(bytes_releasable_, BytesConsumedIfAllocated(tile));
+ DCHECK_GE(resources_releasable_, 1u);
+
+ bytes_releasable_ -= BytesConsumedIfAllocated(tile);
+ --resources_releasable_;
}
}
@@ -682,6 +694,8 @@ void TileManager::ScheduleTasks(
"count", tiles_that_need_to_be_rasterized.size());
RasterWorkerPool::RasterTask::Queue tasks;
+ DCHECK(did_check_for_completed_tasks_since_last_schedule_tasks_);
+
// Build a new task queue containing all task currently needed. Tasks
// are added in order of priority, highest priority task first.
for (TileVector::const_iterator it = tiles_that_need_to_be_rasterized.begin();
@@ -709,6 +723,8 @@ void TileManager::ScheduleTasks(
// scheduled tasks and effectively cancels all tasks not present
// in |tasks|.
raster_worker_pool_->ScheduleTasks(&tasks);
+
+ did_check_for_completed_tasks_since_last_schedule_tasks_ = false;
}
RasterWorkerPool::Task TileManager::CreateImageDecodeTask(
@@ -727,8 +743,9 @@ RasterWorkerPool::RasterTask TileManager::CreateRasterTask(Tile* tile) {
ManagedTileState& mts = tile->managed_state();
scoped_ptr<ResourcePool::Resource> resource =
- resource_pool_->AcquireResource(tile->tile_size_.size(),
- texture_format_);
+ resource_pool_->AcquireResource(
+ tile->tile_size_.size(),
+ raster_worker_pool_->GetResourceFormat());
const Resource* const_resource = resource.get();
// Create and queue all image decode tasks that this tile depends on.
@@ -833,6 +850,9 @@ void TileManager::OnRasterTaskCompleted(
} else {
tile_version.set_use_resource();
tile_version.resource_ = resource.Pass();
+
+ bytes_releasable_ += BytesConsumedIfAllocated(tile);
+ ++resources_releasable_;
}
FreeUnusedResourcesForTile(tile);
diff --git a/chromium/cc/resources/tile_manager.h b/chromium/cc/resources/tile_manager.h
index 2e226c9999d..3f262bcf28d 100644
--- a/chromium/cc/resources/tile_manager.h
+++ b/chromium/cc/resources/tile_manager.h
@@ -52,7 +52,8 @@ class CC_EXPORT TileManager : public RasterWorkerPoolClient {
ResourceProvider* resource_provider,
size_t num_raster_threads,
RenderingStatsInstrumentation* rendering_stats_instrumentation,
- bool use_map_image);
+ bool use_map_image,
+ size_t max_transfer_buffer_usage_bytes);
virtual ~TileManager();
const GlobalStateThatImpactsTilePriority& GlobalState() const {
@@ -69,14 +70,32 @@ class CC_EXPORT TileManager : public RasterWorkerPoolClient {
scoped_ptr<base::Value> AllTilesAsValue() const;
void GetMemoryStats(size_t* memory_required_bytes,
size_t* memory_nice_to_have_bytes,
+ size_t* memory_allocated_bytes,
size_t* memory_used_bytes) const;
const MemoryHistory::Entry& memory_stats_from_last_assign() const {
return memory_stats_from_last_assign_;
}
- bool AreTilesRequiredForActivationReady() const {
- return all_tiles_required_for_activation_have_been_initialized_;
+ void InitializeTilesWithResourcesForTesting(
+ const std::vector<Tile*>& tiles,
+ ResourceProvider* resource_provider) {
+ for (size_t i = 0; i < tiles.size(); ++i) {
+ ManagedTileState& mts = tiles[i]->managed_state();
+ ManagedTileState::TileVersion& tile_version =
+ mts.tile_versions[HIGH_QUALITY_NO_LCD_RASTER_MODE];
+
+ tile_version.resource_ = make_scoped_ptr(
+ new ResourcePool::Resource(resource_provider,
+ gfx::Size(1, 1),
+ resource_provider->best_texture_format()));
+
+ bytes_releasable_ += BytesConsumedIfAllocated(tiles[i]);
+ ++resources_releasable_;
+ }
+ }
+ RasterWorkerPool* RasterWorkerPoolForTesting() {
+ return raster_worker_pool_.get();
}
protected:
@@ -84,13 +103,13 @@ class CC_EXPORT TileManager : public RasterWorkerPoolClient {
ResourceProvider* resource_provider,
scoped_ptr<RasterWorkerPool> raster_worker_pool,
size_t num_raster_threads,
- RenderingStatsInstrumentation* rendering_stats_instrumentation,
- GLenum texture_format);
+ RenderingStatsInstrumentation* rendering_stats_instrumentation);
// Methods called by Tile
friend class Tile;
void RegisterTile(Tile* tile);
void UnregisterTile(Tile* tile);
+ void DidChangeTilePriority(Tile* tile);
// Overriden from RasterWorkerPoolClient:
virtual bool ShouldForceTasksRequiredForActivationToComplete() const
@@ -109,7 +128,6 @@ class CC_EXPORT TileManager : public RasterWorkerPoolClient {
PrioritizedTileSet* tiles,
TileVector* tiles_that_need_to_be_rasterized);
void GetTilesWithAssignedBins(PrioritizedTileSet* tiles);
- void GetPrioritizedTileSet(PrioritizedTileSet* tiles);
private:
void OnImageDecodeTaskCompleted(
@@ -123,8 +141,12 @@ class CC_EXPORT TileManager : public RasterWorkerPoolClient {
const PicturePileImpl::Analysis& analysis,
bool was_canceled);
+ inline size_t BytesConsumedIfAllocated(const Tile* tile) const {
+ return Resource::MemorySizeBytes(tile->size(),
+ raster_worker_pool_->GetResourceFormat());
+ }
+
RasterMode DetermineRasterMode(const Tile* tile) const;
- void CleanUpUnusedImageDecodeTasks();
void FreeResourceForTile(Tile* tile, RasterMode mode);
void FreeResourcesForTile(Tile* tile);
void FreeUnusedResourcesForTile(Tile* tile);
@@ -132,6 +154,7 @@ class CC_EXPORT TileManager : public RasterWorkerPoolClient {
Tile* tile, skia::LazyPixelRef* pixel_ref);
RasterWorkerPool::RasterTask CreateRasterTask(Tile* tile);
scoped_ptr<base::Value> GetMemoryRequirementsAsValue() const;
+ PrioritizedTileSet* GetPrioritizedTileSet();
TileManagerClient* client_;
scoped_ptr<ResourcePool> resource_pool_;
@@ -142,10 +165,16 @@ class CC_EXPORT TileManager : public RasterWorkerPoolClient {
TileMap tiles_;
PrioritizedTileSet prioritized_tiles_;
+ bool prioritized_tiles_dirty_;
bool all_tiles_that_need_to_be_rasterized_have_memory_;
bool all_tiles_required_for_activation_have_memory_;
- bool all_tiles_required_for_activation_have_been_initialized_;
+
+ size_t memory_required_bytes_;
+ size_t memory_nice_to_have_bytes_;
+
+ size_t bytes_releasable_;
+ size_t resources_releasable_;
bool ever_exceeded_memory_budget_;
MemoryHistory::Entry memory_stats_from_last_assign_;
@@ -153,13 +182,15 @@ class CC_EXPORT TileManager : public RasterWorkerPoolClient {
RenderingStatsInstrumentation* rendering_stats_instrumentation_;
bool did_initialize_visible_tile_;
-
- GLenum texture_format_;
+ bool did_check_for_completed_tasks_since_last_schedule_tasks_;
typedef base::hash_map<uint32_t, RasterWorkerPool::Task> PixelRefTaskMap;
typedef base::hash_map<int, PixelRefTaskMap> LayerPixelRefTaskMap;
LayerPixelRefTaskMap image_decode_tasks_;
+ typedef base::hash_map<int, int> LayerCountMap;
+ LayerCountMap used_layer_counts_;
+
RasterTaskCompletionStats update_visible_tiles_stats_;
DISALLOW_COPY_AND_ASSIGN(TileManager);
diff --git a/chromium/cc/resources/tile_manager_perftest.cc b/chromium/cc/resources/tile_manager_perftest.cc
index ca6f9971c60..99d16a187e7 100644
--- a/chromium/cc/resources/tile_manager_perftest.cc
+++ b/chromium/cc/resources/tile_manager_perftest.cc
@@ -6,12 +6,15 @@
#include "cc/resources/tile.h"
#include "cc/resources/tile_priority.h"
#include "cc/test/fake_output_surface.h"
+#include "cc/test/fake_output_surface_client.h"
#include "cc/test/fake_picture_pile_impl.h"
#include "cc/test/fake_tile_manager.h"
#include "cc/test/fake_tile_manager_client.h"
+#include "cc/test/lap_timer.h"
#include "cc/test/test_tile_priorities.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/perf/perf_test.h"
namespace cc {
@@ -23,21 +26,30 @@ static const int kTimeCheckInterval = 10;
class TileManagerPerfTest : public testing::Test {
public:
- typedef std::vector<scoped_refptr<Tile> > TileVector;
+ typedef std::vector<std::pair<scoped_refptr<Tile>, ManagedTileBin> >
+ TileBinVector;
- TileManagerPerfTest() : num_runs_(0) {}
+ TileManagerPerfTest()
+ : timer_(kWarmupRuns,
+ base::TimeDelta::FromMilliseconds(kTimeLimitMillis),
+ kTimeCheckInterval) {}
// Overridden from testing::Test:
virtual void SetUp() OVERRIDE {
output_surface_ = FakeOutputSurface::Create3d();
- resource_provider_ = ResourceProvider::Create(output_surface_.get(), 0);
+ CHECK(output_surface_->BindToClient(&output_surface_client_));
+
+ resource_provider_ =
+ ResourceProvider::Create(output_surface_.get(), 0, false);
tile_manager_ = make_scoped_ptr(
new FakeTileManager(&tile_manager_client_, resource_provider_.get()));
GlobalStateThatImpactsTilePriority state;
gfx::Size tile_size = settings_.default_tile_size;
state.memory_limit_in_bytes =
- 10000 * 4 * tile_size.width() * tile_size.height();
+ 10000u * 4u *
+ static_cast<size_t>(tile_size.width() * tile_size.height());
+ state.num_resources_limit = 10000;
state.memory_limit_policy = ALLOW_ANYTHING;
state.tree_priority = SMOOTHNESS_TAKES_PRIORITY;
@@ -50,34 +62,47 @@ class TileManagerPerfTest : public testing::Test {
picture_pile_ = NULL;
}
- void EndTest() {
- elapsed_ = base::TimeTicks::HighResNow() - start_time_;
- }
-
- void AfterTest(const std::string test_name) {
- // Format matches chrome/test/perf/perf_test.h:PrintResult
- printf("*RESULT %s: %.2f runs/s\n",
- test_name.c_str(),
- num_runs_ / elapsed_.InSecondsF());
+ TilePriority GetTilePriorityFromBin(ManagedTileBin bin) {
+ switch (bin) {
+ case NOW_AND_READY_TO_DRAW_BIN:
+ case NOW_BIN:
+ return TilePriorityForNowBin();
+ case SOON_BIN:
+ return TilePriorityForSoonBin();
+ case EVENTUALLY_AND_ACTIVE_BIN:
+ case EVENTUALLY_BIN:
+ return TilePriorityForEventualBin();
+ case AT_LAST_BIN:
+ case AT_LAST_AND_ACTIVE_BIN:
+ case NEVER_BIN:
+ return TilePriority();
+ default:
+ NOTREACHED();
+ return TilePriority();
+ }
}
- bool DidRun() {
- ++num_runs_;
- if (num_runs_ == kWarmupRuns)
- start_time_ = base::TimeTicks::HighResNow();
-
- if (!start_time_.is_null() && (num_runs_ % kTimeCheckInterval) == 0) {
- base::TimeDelta elapsed = base::TimeTicks::HighResNow() - start_time_;
- if (elapsed >= base::TimeDelta::FromMilliseconds(kTimeLimitMillis)) {
- elapsed_ = elapsed;
- return false;
- }
+ ManagedTileBin GetNextBin(ManagedTileBin bin) {
+ switch (bin) {
+ case NOW_AND_READY_TO_DRAW_BIN:
+ case NOW_BIN:
+ return SOON_BIN;
+ case SOON_BIN:
+ return EVENTUALLY_BIN;
+ case EVENTUALLY_AND_ACTIVE_BIN:
+ case EVENTUALLY_BIN:
+ return NEVER_BIN;
+ case AT_LAST_BIN:
+ case AT_LAST_AND_ACTIVE_BIN:
+ case NEVER_BIN:
+ return NOW_BIN;
+ default:
+ NOTREACHED();
+ return NEVER_BIN;
}
-
- return true;
}
- void CreateBinTiles(int count, TilePriority priority, TileVector* tiles) {
+ void CreateBinTiles(int count, ManagedTileBin bin, TileBinVector* tiles) {
for (int i = 0; i < count; ++i) {
scoped_refptr<Tile> tile =
make_scoped_refptr(new Tile(tile_manager_.get(),
@@ -89,32 +114,50 @@ class TileManagerPerfTest : public testing::Test {
0,
0,
true));
- tile->SetPriority(ACTIVE_TREE, priority);
- tile->SetPriority(PENDING_TREE, priority);
- tiles->push_back(tile);
+ tile->SetPriority(ACTIVE_TREE, GetTilePriorityFromBin(bin));
+ tile->SetPriority(PENDING_TREE, GetTilePriorityFromBin(bin));
+ tiles->push_back(std::make_pair(tile, bin));
}
}
- void CreateTiles(int count, TileVector* tiles) {
+ void CreateTiles(int count, TileBinVector* tiles) {
// Roughly an equal amount of all bins.
int count_per_bin = count / NUM_BINS;
- CreateBinTiles(count_per_bin, TilePriorityForNowBin(), tiles);
- CreateBinTiles(count_per_bin, TilePriorityForSoonBin(), tiles);
- CreateBinTiles(count_per_bin, TilePriorityForEventualBin(), tiles);
- CreateBinTiles(count - 3 * count_per_bin, TilePriority(), tiles);
+ CreateBinTiles(count_per_bin, NOW_BIN, tiles);
+ CreateBinTiles(count_per_bin, SOON_BIN, tiles);
+ CreateBinTiles(count_per_bin, EVENTUALLY_BIN, tiles);
+ CreateBinTiles(count - 3 * count_per_bin, NEVER_BIN, tiles);
}
- void RunManageTilesTest(const std::string test_name,
- unsigned tile_count) {
- start_time_ = base::TimeTicks();
- num_runs_ = 0;
- TileVector tiles;
+ void RunManageTilesTest(const std::string& test_name,
+ unsigned tile_count,
+ int priority_change_percent) {
+ DCHECK_GE(tile_count, 100u);
+ DCHECK_GE(priority_change_percent, 0);
+ DCHECK_LE(priority_change_percent, 100);
+ TileBinVector tiles;
CreateTiles(tile_count, &tiles);
+ timer_.Reset();
do {
+ if (priority_change_percent > 0) {
+ for (unsigned i = 0;
+ i < tile_count;
+ i += 100 / priority_change_percent) {
+ Tile* tile = tiles[i].first.get();
+ ManagedTileBin bin = GetNextBin(tiles[i].second);
+ tile->SetPriority(ACTIVE_TREE, GetTilePriorityFromBin(bin));
+ tile->SetPriority(PENDING_TREE, GetTilePriorityFromBin(bin));
+ tiles[i].second = bin;
+ }
+ }
+
tile_manager_->ManageTiles();
- } while (DidRun());
+ tile_manager_->CheckForCompletedTasks();
+ timer_.NextLap();
+ } while (!timer_.HasTimeLimitExpired());
- AfterTest(test_name);
+ perf_test::PrintResult("manage_tiles", "", test_name,
+ timer_.LapsPerSecond(), "runs/s", true);
}
private:
@@ -122,18 +165,22 @@ class TileManagerPerfTest : public testing::Test {
LayerTreeSettings settings_;
scoped_ptr<FakeTileManager> tile_manager_;
scoped_refptr<FakePicturePileImpl> picture_pile_;
+ FakeOutputSurfaceClient output_surface_client_;
scoped_ptr<FakeOutputSurface> output_surface_;
scoped_ptr<ResourceProvider> resource_provider_;
-
- base::TimeTicks start_time_;
- base::TimeDelta elapsed_;
- int num_runs_;
+ LapTimer timer_;
};
TEST_F(TileManagerPerfTest, ManageTiles) {
- RunManageTilesTest("manage_tiles_100", 100);
- RunManageTilesTest("manage_tiles_1000", 1000);
- RunManageTilesTest("manage_tiles_10000", 10000);
+ RunManageTilesTest("100_0", 100, 0);
+ RunManageTilesTest("1000_0", 1000, 0);
+ RunManageTilesTest("10000_0", 10000, 0);
+ RunManageTilesTest("100_10", 100, 10);
+ RunManageTilesTest("1000_10", 1000, 10);
+ RunManageTilesTest("10000_10", 10000, 10);
+ RunManageTilesTest("100_100", 100, 100);
+ RunManageTilesTest("1000_100", 1000, 100);
+ RunManageTilesTest("10000_100", 10000, 100);
}
} // namespace
diff --git a/chromium/cc/resources/tile_manager_unittest.cc b/chromium/cc/resources/tile_manager_unittest.cc
index 6fb06908568..0274914a324 100644
--- a/chromium/cc/resources/tile_manager_unittest.cc
+++ b/chromium/cc/resources/tile_manager_unittest.cc
@@ -5,6 +5,7 @@
#include "cc/resources/tile.h"
#include "cc/resources/tile_priority.h"
#include "cc/test/fake_output_surface.h"
+#include "cc/test/fake_output_surface_client.h"
#include "cc/test/fake_picture_pile_impl.h"
#include "cc/test/fake_tile_manager.h"
#include "cc/test/fake_tile_manager_client.h"
@@ -22,7 +23,10 @@ class TileManagerTest : public testing::TestWithParam<bool> {
TileMemoryLimitPolicy memory_limit_policy,
TreePriority tree_priority) {
output_surface_ = FakeOutputSurface::Create3d();
- resource_provider_ = ResourceProvider::Create(output_surface_.get(), 0);
+ CHECK(output_surface_->BindToClient(&output_surface_client_));
+
+ resource_provider_ =
+ ResourceProvider::Create(output_surface_.get(), 0, false);
tile_manager_ = make_scoped_ptr(
new FakeTileManager(&tile_manager_client_, resource_provider_.get()));
@@ -41,6 +45,7 @@ class TileManagerTest : public testing::TestWithParam<bool> {
state.memory_limit_in_bytes = 100 * 1000 * 1000;
state.num_resources_limit = max_tiles;
}
+ state.unused_memory_limit_in_bytes = state.memory_limit_in_bytes;
state.memory_limit_policy = memory_limit_policy;
state.tree_priority = tree_priority;
@@ -53,6 +58,7 @@ class TileManagerTest : public testing::TestWithParam<bool> {
gfx::Size tile_size = settings_.default_tile_size;
state.memory_limit_in_bytes =
max_memory_tiles_ * 4 * tile_size.width() * tile_size.height();
+ state.unused_memory_limit_in_bytes = state.memory_limit_in_bytes;
state.memory_limit_policy = memory_limit_policy_;
state.num_resources_limit = 100;
state.tree_priority = tree_priority;
@@ -66,15 +72,16 @@ class TileManagerTest : public testing::TestWithParam<bool> {
testing::Test::TearDown();
}
- TileVector CreateTiles(int count,
- TilePriority active_priority,
- TilePriority pending_priority) {
+ TileVector CreateTilesWithSize(int count,
+ TilePriority active_priority,
+ TilePriority pending_priority,
+ gfx::Size tile_size) {
TileVector tiles;
for (int i = 0; i < count; ++i) {
scoped_refptr<Tile> tile =
make_scoped_refptr(new Tile(tile_manager_.get(),
picture_pile_.get(),
- settings_.default_tile_size,
+ tile_size,
gfx::Rect(),
gfx::Rect(),
1.0,
@@ -88,6 +95,15 @@ class TileManagerTest : public testing::TestWithParam<bool> {
return tiles;
}
+ TileVector CreateTiles(int count,
+ TilePriority active_priority,
+ TilePriority pending_priority) {
+ return CreateTilesWithSize(count,
+ active_priority,
+ pending_priority,
+ settings_.default_tile_size);
+ }
+
FakeTileManager* tile_manager() {
return tile_manager_.get();
}
@@ -119,6 +135,7 @@ class TileManagerTest : public testing::TestWithParam<bool> {
LayerTreeSettings settings_;
scoped_ptr<FakeTileManager> tile_manager_;
scoped_refptr<FakePicturePileImpl> picture_pile_;
+ FakeOutputSurfaceClient output_surface_client_;
scoped_ptr<FakeOutputSurface> output_surface_;
scoped_ptr<ResourceProvider> resource_provider_;
TileMemoryLimitPolicy memory_limit_policy_;
@@ -461,6 +478,48 @@ TEST_P(TileManagerTest, TextReRasterAsNoLCD) {
EXPECT_EQ(0, TilesWithLCDCount(pending_tree_tiles));
}
+TEST_P(TileManagerTest, RespectMemoryLimit) {
+ Initialize(5, ALLOW_ANYTHING, SMOOTHNESS_TAKES_PRIORITY);
+ TileVector large_tiles = CreateTiles(
+ 5, TilePriorityForNowBin(), TilePriority());
+
+ size_t memory_required_bytes;
+ size_t memory_nice_to_have_bytes;
+ size_t memory_allocated_bytes;
+ size_t memory_used_bytes;
+
+ tile_manager()->ManageTiles();
+ tile_manager()->GetMemoryStats(&memory_required_bytes,
+ &memory_nice_to_have_bytes,
+ &memory_allocated_bytes,
+ &memory_used_bytes);
+ // Allocated bytes should never be more than the memory limit.
+ EXPECT_LE(memory_allocated_bytes,
+ tile_manager()->GlobalState().memory_limit_in_bytes);
+
+ // Finish raster of large tiles.
+ tile_manager()->UpdateVisibleTiles();
+
+ // Remove all large tiles. This will leave the memory currently
+ // used by these tiles as unused when ManageTiles() is called.
+ large_tiles.clear();
+
+ // Create a new set of tiles using a different size. These tiles
+ // can use the memory currently assigned to the lerge tiles but
+ // they can't use the same resources as the size doesn't match.
+ TileVector small_tiles = CreateTilesWithSize(
+ 5, TilePriorityForNowBin(), TilePriority(), gfx::Size(128, 128));
+
+ tile_manager()->ManageTiles();
+ tile_manager()->GetMemoryStats(&memory_required_bytes,
+ &memory_nice_to_have_bytes,
+ &memory_allocated_bytes,
+ &memory_used_bytes);
+ // Allocated bytes should never be more than the memory limit.
+ EXPECT_LE(memory_allocated_bytes,
+ tile_manager()->GlobalState().memory_limit_in_bytes);
+}
+
// If true, the max tile limit should be applied as bytes; if false,
// as num_resources_limit.
INSTANTIATE_TEST_CASE_P(TileManagerTests,
diff --git a/chromium/cc/resources/tile_priority.cc b/chromium/cc/resources/tile_priority.cc
index 970ea1d81a5..eeb87cdcbbc 100644
--- a/chromium/cc/resources/tile_priority.cc
+++ b/chromium/cc/resources/tile_priority.cc
@@ -87,11 +87,10 @@ scoped_ptr<base::Value> TileResolutionAsValue(
case NON_IDEAL_RESOLUTION:
return scoped_ptr<base::Value>(base::Value::CreateStringValue(
"NON_IDEAL_RESOLUTION"));
- default:
- DCHECK(false) << "Unrecognized TileResolution value " << resolution;
- return scoped_ptr<base::Value>(base::Value::CreateStringValue(
- "<unknown TileResolution value>"));
}
+ DCHECK(false) << "Unrecognized TileResolution value " << resolution;
+ return scoped_ptr<base::Value>(base::Value::CreateStringValue(
+ "<unknown TileResolution value>"));
}
scoped_ptr<base::Value> TilePriority::AsValue() const {
@@ -176,11 +175,10 @@ scoped_ptr<base::Value> TreePriorityAsValue(TreePriority prio) {
case NEW_CONTENT_TAKES_PRIORITY:
return scoped_ptr<base::Value>(base::Value::CreateStringValue(
"NEW_CONTENT_TAKES_PRIORITY"));
- default:
- DCHECK(false) << "Unrecognized priority value " << prio;
- return scoped_ptr<base::Value>(base::Value::CreateStringValue(
- "<unknown>"));
}
+ DCHECK(false) << "Unrecognized priority value " << prio;
+ return scoped_ptr<base::Value>(base::Value::CreateStringValue(
+ "<unknown>"));
}
scoped_ptr<base::Value> GlobalStateThatImpactsTilePriority::AsValue() const {
diff --git a/chromium/cc/resources/tile_priority.h b/chromium/cc/resources/tile_priority.h
index 5c92d2f178e..e613dd77b04 100644
--- a/chromium/cc/resources/tile_priority.h
+++ b/chromium/cc/resources/tile_priority.h
@@ -106,7 +106,8 @@ struct CC_EXPORT TilePriority {
bool operator ==(const TilePriority& other) const {
return resolution == other.resolution &&
time_to_visible_in_seconds == other.time_to_visible_in_seconds &&
- distance_to_visible_in_pixels == other.distance_to_visible_in_pixels;
+ distance_to_visible_in_pixels == other.distance_to_visible_in_pixels &&
+ required_for_activation == other.required_for_activation;
// No need to compare current_screen_quad which is for debug only and
// never changes by itself.
}
diff --git a/chromium/cc/resources/transferable_resource.cc b/chromium/cc/resources/transferable_resource.cc
index be2be1a448c..1b8930f34ad 100644
--- a/chromium/cc/resources/transferable_resource.cc
+++ b/chromium/cc/resources/transferable_resource.cc
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "base/logging.h"
+#include "cc/resources/returned_resource.h"
#include "cc/resources/transferable_resource.h"
namespace cc {
@@ -10,11 +11,28 @@ namespace cc {
TransferableResource::TransferableResource()
: id(0),
sync_point(0),
- format(0),
+ format(RGBA_8888),
filter(0) {
}
TransferableResource::~TransferableResource() {
}
+ReturnedResource TransferableResource::ToReturnedResource() const {
+ ReturnedResource returned;
+ returned.id = id;
+ returned.sync_point = sync_point;
+ returned.count = 1;
+ return returned;
+}
+
+// static
+void TransferableResource::ReturnResources(
+ const TransferableResourceArray& input,
+ ReturnedResourceArray* output) {
+ for (TransferableResourceArray::const_iterator it = input.begin();
+ it != input.end(); ++it)
+ output->push_back(it->ToReturnedResource());
+}
+
} // namespace cc
diff --git a/chromium/cc/resources/transferable_resource.h b/chromium/cc/resources/transferable_resource.h
index 5c979433c90..0ea62436991 100644
--- a/chromium/cc/resources/transferable_resource.h
+++ b/chromium/cc/resources/transferable_resource.h
@@ -9,25 +9,33 @@
#include "base/basictypes.h"
#include "cc/base/cc_export.h"
+#include "cc/resources/resource_format.h"
#include "gpu/command_buffer/common/mailbox.h"
#include "ui/gfx/size.h"
namespace cc {
+struct ReturnedResource;
+typedef std::vector<ReturnedResource> ReturnedResourceArray;
+struct TransferableResource;
+typedef std::vector<TransferableResource> TransferableResourceArray;
+
struct CC_EXPORT TransferableResource {
TransferableResource();
~TransferableResource();
+ ReturnedResource ToReturnedResource() const;
+ static void ReturnResources(const TransferableResourceArray& input,
+ ReturnedResourceArray* output);
+
unsigned id;
unsigned sync_point;
- uint32 format;
+ ResourceFormat format;
uint32 filter;
gfx::Size size;
gpu::Mailbox mailbox;
};
-typedef std::vector<TransferableResource> TransferableResourceArray;
-
} // namespace cc
#endif // CC_RESOURCES_TRANSFERABLE_RESOURCE_H_
diff --git a/chromium/cc/resources/ui_resource_bitmap.cc b/chromium/cc/resources/ui_resource_bitmap.cc
index 8bbfb37deee..86acfa5185c 100644
--- a/chromium/cc/resources/ui_resource_bitmap.cc
+++ b/chromium/cc/resources/ui_resource_bitmap.cc
@@ -4,23 +4,52 @@
#include "cc/resources/ui_resource_bitmap.h"
+#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
+#include "third_party/skia/include/core/SkBitmap.h"
namespace cc {
-scoped_refptr<UIResourceBitmap>
-UIResourceBitmap::Create(uint8_t* pixels,
- UIResourceFormat format,
- gfx::Size size) {
- scoped_refptr<UIResourceBitmap> ret = new UIResourceBitmap();
- ret->pixels_ = scoped_ptr<uint8_t[]>(pixels);
- ret->format_ = format;
- ret->size_ = size;
+void UIResourceBitmap::Create(const skia::RefPtr<SkPixelRef>& pixel_ref,
+ UIResourceFormat format,
+ UIResourceWrapMode wrap_mode,
+ gfx::Size size) {
+ DCHECK(size.width());
+ DCHECK(size.height());
+ DCHECK(pixel_ref);
+ DCHECK(pixel_ref->isImmutable());
+ format_ = format;
+ wrap_mode_ = wrap_mode;
+ size_ = size;
+ pixel_ref_ = pixel_ref;
+}
+
+UIResourceBitmap::UIResourceBitmap(const SkBitmap& skbitmap,
+ UIResourceWrapMode wrap_mode) {
+ DCHECK_EQ(skbitmap.config(), SkBitmap::kARGB_8888_Config);
+ DCHECK_EQ(skbitmap.width(), skbitmap.rowBytesAsPixels());
+ DCHECK(skbitmap.isImmutable());
- return ret;
+ skia::RefPtr<SkPixelRef> pixel_ref = skia::SharePtr(skbitmap.pixelRef());
+ Create(pixel_ref,
+ UIResourceBitmap::RGBA8,
+ wrap_mode,
+ gfx::Size(skbitmap.width(), skbitmap.height()));
}
-UIResourceBitmap::UIResourceBitmap() {}
UIResourceBitmap::~UIResourceBitmap() {}
+AutoLockUIResourceBitmap::AutoLockUIResourceBitmap(
+ const UIResourceBitmap& bitmap) : bitmap_(bitmap) {
+ bitmap_.pixel_ref_->lockPixels();
+}
+
+AutoLockUIResourceBitmap::~AutoLockUIResourceBitmap() {
+ bitmap_.pixel_ref_->unlockPixels();
+}
+
+const uint8_t* AutoLockUIResourceBitmap::GetPixels() const {
+ return static_cast<const uint8_t*>(bitmap_.pixel_ref_->pixels());
+}
+
} // namespace cc
diff --git a/chromium/cc/resources/ui_resource_bitmap.h b/chromium/cc/resources/ui_resource_bitmap.h
index dbf70690792..78cb4658125 100644
--- a/chromium/cc/resources/ui_resource_bitmap.h
+++ b/chromium/cc/resources/ui_resource_bitmap.h
@@ -8,41 +8,62 @@
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "cc/base/cc_export.h"
+#include "skia/ext/refptr.h"
+#include "third_party/skia/include/core/SkPixelRef.h"
#include "third_party/skia/include/core/SkTypes.h"
#include "ui/gfx/size.h"
+class SkBitmap;
+
namespace cc {
-// Ref-counted bitmap class (can’t use SkBitmap because of ETC1). Thread-safety
-// ensures that both main and impl threads can hold references to the bitmap and
-// that asynchronous uploads are allowed.
-class CC_EXPORT UIResourceBitmap
- : public base::RefCountedThreadSafe<UIResourceBitmap> {
+// A bitmap class that contains a ref-counted reference to a SkPixelRef* that
+// holds the content of the bitmap (cannot use SkBitmap because of ETC1).
+// Thread-safety (by ways of SkPixelRef) ensures that both main and impl threads
+// can hold references to the bitmap and that asynchronous uploads are allowed.
+class CC_EXPORT UIResourceBitmap {
public:
enum UIResourceFormat {
RGBA8
};
-
- // Takes ownership of “pixels”.
- static scoped_refptr<UIResourceBitmap> Create(uint8_t* pixels,
- UIResourceFormat format,
- gfx::Size size);
+ enum UIResourceWrapMode {
+ CLAMP_TO_EDGE,
+ REPEAT
+ };
gfx::Size GetSize() const { return size_; }
UIResourceFormat GetFormat() const { return format_; }
- uint8_t* GetPixels() { return pixels_.get(); }
+ UIResourceWrapMode GetWrapMode() const { return wrap_mode_; }
- private:
- friend class base::RefCountedThreadSafe<UIResourceBitmap>;
+ // The constructor for the UIResourceBitmap. User must ensure that |skbitmap|
+ // is immutable. The SkBitmap format should be in 32-bit RGBA. Wrap mode is
+ // unnecessary for most UI resources and is defaulted to CLAMP_TO_EDGE.
+ UIResourceBitmap(const SkBitmap& skbitmap,
+ UIResourceWrapMode wrap_mode = CLAMP_TO_EDGE);
- UIResourceBitmap();
~UIResourceBitmap();
- scoped_ptr<uint8_t[]> pixels_;
+ private:
+ friend class AutoLockUIResourceBitmap;
+ void Create(const skia::RefPtr<SkPixelRef>& pixel_ref,
+ UIResourceFormat format,
+ UIResourceWrapMode wrap_mode,
+ gfx::Size size);
+
+ skia::RefPtr<SkPixelRef> pixel_ref_;
UIResourceFormat format_;
+ UIResourceWrapMode wrap_mode_;
gfx::Size size_;
+};
- DISALLOW_COPY_AND_ASSIGN(UIResourceBitmap);
+class CC_EXPORT AutoLockUIResourceBitmap {
+ public:
+ explicit AutoLockUIResourceBitmap(const UIResourceBitmap& bitmap);
+ ~AutoLockUIResourceBitmap();
+ const uint8_t* GetPixels() const;
+
+ private:
+ const UIResourceBitmap& bitmap_;
};
} // namespace cc
diff --git a/chromium/cc/resources/ui_resource_client.h b/chromium/cc/resources/ui_resource_client.h
index d647936baee..24309a52bb7 100644
--- a/chromium/cc/resources/ui_resource_client.h
+++ b/chromium/cc/resources/ui_resource_client.h
@@ -24,8 +24,8 @@ class CC_EXPORT UIResourceClient {
// delete a UIResourceClient object after DeleteUIResource has been called for
// all IDs associated with it. A valid bitmap always must be returned but it
// doesn't need to be the same size or format as the original.
- virtual scoped_refptr<UIResourceBitmap> GetBitmap(UIResourceId uid,
- bool resource_lost) = 0;
+ virtual UIResourceBitmap GetBitmap(UIResourceId uid,
+ bool resource_lost) = 0;
virtual ~UIResourceClient() {}
};
diff --git a/chromium/cc/resources/video_resource_updater.cc b/chromium/cc/resources/video_resource_updater.cc
index 4239e989e56..34c7ab65c35 100644
--- a/chromium/cc/resources/video_resource_updater.cc
+++ b/chromium/cc/resources/video_resource_updater.cc
@@ -10,21 +10,24 @@
#include "gpu/GLES2/gl2extchromium.h"
#include "media/base/video_frame.h"
#include "media/filters/skcanvas_video_renderer.h"
+#include "third_party/WebKit/public/platform/WebGraphicsContext3D.h"
#include "third_party/khronos/GLES2/gl2.h"
#include "third_party/khronos/GLES2/gl2ext.h"
#include "ui/gfx/size_conversions.h"
-const unsigned kYUVResourceFormat = GL_LUMINANCE;
-const unsigned kRGBResourceFormat = GL_RGBA;
-
namespace cc {
+const ResourceFormat kYUVResourceFormat = LUMINANCE_8;
+const ResourceFormat kRGBResourceFormat = RGBA_8888;
+
VideoFrameExternalResources::VideoFrameExternalResources() : type(NONE) {}
VideoFrameExternalResources::~VideoFrameExternalResources() {}
-VideoResourceUpdater::VideoResourceUpdater(ResourceProvider* resource_provider)
- : resource_provider_(resource_provider) {
+VideoResourceUpdater::VideoResourceUpdater(ContextProvider* context_provider,
+ ResourceProvider* resource_provider)
+ : context_provider_(context_provider),
+ resource_provider_(resource_provider) {
}
VideoResourceUpdater::~VideoResourceUpdater() {
@@ -87,7 +90,7 @@ bool VideoResourceUpdater::VerifyFrame(
static gfx::Size SoftwarePlaneDimension(
media::VideoFrame::Format input_frame_format,
gfx::Size coded_size,
- GLenum output_resource_format,
+ ResourceFormat output_resource_format,
int plane_index) {
if (output_resource_format == kYUVResourceFormat) {
if (plane_index == media::VideoFrame::kYPlane ||
@@ -113,7 +116,7 @@ static gfx::Size SoftwarePlaneDimension(
}
}
- DCHECK_EQ(output_resource_format, static_cast<unsigned>(kRGBResourceFormat));
+ DCHECK_EQ(output_resource_format, kRGBResourceFormat);
return coded_size;
}
@@ -138,9 +141,9 @@ VideoFrameExternalResources VideoResourceUpdater::CreateForSoftwarePlanes(
input_frame_format != media::VideoFrame::YV16)
return VideoFrameExternalResources();
- bool software_compositor = !resource_provider_->GraphicsContext3D();
+ bool software_compositor = context_provider_ == NULL;
- GLenum output_resource_format = kYUVResourceFormat;
+ ResourceFormat output_resource_format = kYUVResourceFormat;
size_t output_plane_count =
(input_frame_format == media::VideoFrame::YV12A) ? 4 : 3;
@@ -191,15 +194,17 @@ VideoFrameExternalResources VideoResourceUpdater::CreateForSoftwarePlanes(
// ResourceProvider and stop using ResourceProvider in this class.
resource_id =
resource_provider_->CreateResource(output_plane_resource_size,
- output_resource_format,
- ResourceProvider::TextureUsageAny);
+ GL_CLAMP_TO_EDGE,
+ ResourceProvider::TextureUsageAny,
+ output_resource_format);
DCHECK(mailbox.IsZero());
if (!software_compositor) {
+ DCHECK(context_provider_);
+
WebKit::WebGraphicsContext3D* context =
- resource_provider_->GraphicsContext3D();
- DCHECK(context);
+ context_provider_->Context3d();
GLC(context, context->genMailboxCHROMIUM(mailbox.name));
if (mailbox.IsZero()) {
@@ -266,13 +271,12 @@ VideoFrameExternalResources VideoResourceUpdater::CreateForSoftwarePlanes(
plane_resources[0].resource_format,
gpu::Mailbox()
};
- TextureMailbox::ReleaseCallback callback_to_free_resource =
- base::Bind(&RecycleResource,
- AsWeakPtr(),
- recycle_data);
+
external_resources.software_resources.push_back(
plane_resources[0].resource_id);
- external_resources.software_release_callback = callback_to_free_resource;
+ external_resources.software_release_callback =
+ base::Bind(&RecycleResource, AsWeakPtr(), recycle_data);
+
external_resources.type = VideoFrameExternalResources::SOFTWARE_RESOURCE;
return external_resources;
@@ -280,8 +284,7 @@ VideoFrameExternalResources VideoResourceUpdater::CreateForSoftwarePlanes(
for (size_t i = 0; i < plane_resources.size(); ++i) {
// Update each plane's resource id with its content.
- DCHECK_EQ(plane_resources[i].resource_format,
- static_cast<unsigned>(kYUVResourceFormat));
+ DCHECK_EQ(plane_resources[i].resource_format, kYUVResourceFormat);
const uint8_t* input_plane_pixels = video_frame->data(i);
@@ -302,13 +305,11 @@ VideoFrameExternalResources VideoResourceUpdater::CreateForSoftwarePlanes(
plane_resources[i].resource_format,
plane_resources[i].mailbox
};
- TextureMailbox::ReleaseCallback callback_to_free_resource =
- base::Bind(&RecycleResource,
- AsWeakPtr(),
- recycle_data);
+
external_resources.mailboxes.push_back(
- TextureMailbox(plane_resources[i].mailbox,
- callback_to_free_resource));
+ TextureMailbox(plane_resources[i].mailbox));
+ external_resources.release_callbacks.push_back(
+ base::Bind(&RecycleResource, AsWeakPtr(), recycle_data));
}
external_resources.type = VideoFrameExternalResources::YUV_RESOURCE;
@@ -330,9 +331,7 @@ VideoFrameExternalResources VideoResourceUpdater::CreateForHardwarePlanes(
if (frame_format != media::VideoFrame::NATIVE_TEXTURE)
return VideoFrameExternalResources();
- WebKit::WebGraphicsContext3D* context =
- resource_provider_->GraphicsContext3D();
- if (!context)
+ if (!context_provider_)
return VideoFrameExternalResources();
VideoFrameExternalResources external_resources;
@@ -355,14 +354,12 @@ VideoFrameExternalResources VideoResourceUpdater::CreateForHardwarePlanes(
scoped_refptr<media::VideoFrame::MailboxHolder> mailbox_holder =
video_frame->texture_mailbox();
- TextureMailbox::ReleaseCallback callback_to_return_resource =
- base::Bind(&ReturnTexture, mailbox_holder);
-
external_resources.mailboxes.push_back(
TextureMailbox(mailbox_holder->mailbox(),
- callback_to_return_resource,
video_frame->texture_target(),
mailbox_holder->sync_point()));
+ external_resources.release_callbacks.push_back(
+ base::Bind(&ReturnTexture, mailbox_holder));
return external_resources;
}
@@ -377,10 +374,11 @@ void VideoResourceUpdater::RecycleResource(
return;
}
- WebKit::WebGraphicsContext3D* context =
- updater->resource_provider_->GraphicsContext3D();
- if (context && sync_point)
- GLC(context, context->waitSyncPoint(sync_point));
+ ContextProvider* context_provider = updater->context_provider_;
+ if (context_provider && sync_point) {
+ GLC(context_provider->Context3d(),
+ context_provider->Context3d()->waitSyncPoint(sync_point));
+ }
if (lost_resource) {
updater->DeleteResource(data.resource_id);
diff --git a/chromium/cc/resources/video_resource_updater.h b/chromium/cc/resources/video_resource_updater.h
index 9be79d12b33..e3476d44bb4 100644
--- a/chromium/cc/resources/video_resource_updater.h
+++ b/chromium/cc/resources/video_resource_updater.h
@@ -12,6 +12,8 @@
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "cc/base/cc_export.h"
+#include "cc/resources/release_callback.h"
+#include "cc/resources/resource_format.h"
#include "cc/resources/texture_mailbox.h"
#include "ui/gfx/size.h"
@@ -21,6 +23,7 @@ class VideoFrame;
}
namespace cc {
+class ContextProvider;
class ResourceProvider;
class CC_EXPORT VideoFrameExternalResources {
@@ -47,10 +50,11 @@ class CC_EXPORT VideoFrameExternalResources {
ResourceType type;
std::vector<TextureMailbox> mailboxes;
+ std::vector<ReleaseCallback> release_callbacks;
// TODO(danakj): Remove these too.
std::vector<unsigned> software_resources;
- TextureMailbox::ReleaseCallback software_release_callback;
+ ReleaseCallback software_release_callback;
VideoFrameExternalResources();
~VideoFrameExternalResources();
@@ -61,7 +65,8 @@ class CC_EXPORT VideoFrameExternalResources {
class CC_EXPORT VideoResourceUpdater
: public base::SupportsWeakPtr<VideoResourceUpdater> {
public:
- explicit VideoResourceUpdater(ResourceProvider* resource_provider);
+ explicit VideoResourceUpdater(ContextProvider* context_provider,
+ ResourceProvider* resource_provider);
~VideoResourceUpdater();
VideoFrameExternalResources CreateExternalResourcesFromVideoFrame(
@@ -71,12 +76,12 @@ class CC_EXPORT VideoResourceUpdater
struct PlaneResource {
unsigned resource_id;
gfx::Size resource_size;
- unsigned resource_format;
+ ResourceFormat resource_format;
gpu::Mailbox mailbox;
PlaneResource(unsigned resource_id,
gfx::Size resource_size,
- unsigned resource_format,
+ ResourceFormat resource_format,
gpu::Mailbox mailbox)
: resource_id(resource_id),
resource_size(resource_size),
@@ -94,7 +99,7 @@ class CC_EXPORT VideoResourceUpdater
struct RecycleResourceData {
unsigned resource_id;
gfx::Size resource_size;
- unsigned resource_format;
+ ResourceFormat resource_format;
gpu::Mailbox mailbox;
};
static void RecycleResource(base::WeakPtr<VideoResourceUpdater> updater,
@@ -102,6 +107,7 @@ class CC_EXPORT VideoResourceUpdater
unsigned sync_point,
bool lost_resource);
+ ContextProvider* context_provider_;
ResourceProvider* resource_provider_;
scoped_ptr<media::SkCanvasVideoRenderer> video_renderer_;
diff --git a/chromium/cc/resources/video_resource_updater_unittest.cc b/chromium/cc/resources/video_resource_updater_unittest.cc
index ff574fff7fb..c36689e16c1 100644
--- a/chromium/cc/resources/video_resource_updater_unittest.cc
+++ b/chromium/cc/resources/video_resource_updater_unittest.cc
@@ -5,9 +5,10 @@
#include "cc/resources/video_resource_updater.h"
#include "base/memory/shared_memory.h"
+#include "cc/debug/test_web_graphics_context_3d.h"
#include "cc/resources/resource_provider.h"
#include "cc/test/fake_output_surface.h"
-#include "cc/test/test_web_graphics_context_3d.h"
+#include "cc/test/fake_output_surface_client.h"
#include "media/base/video_frame.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -21,10 +22,11 @@ class VideoResourceUpdaterTest : public testing::Test {
TestWebGraphicsContext3D::Create();
context3d_ = context3d.get();
- output_surface3d_ = FakeOutputSurface::Create3d(
- context3d.PassAs<WebKit::WebGraphicsContext3D>());
+ output_surface3d_ =
+ FakeOutputSurface::Create3d(context3d.Pass());
+ CHECK(output_surface3d_->BindToClient(&client_));
resource_provider3d_ =
- ResourceProvider::Create(output_surface3d_.get(), 0);
+ ResourceProvider::Create(output_surface3d_.get(), 0, false);
}
scoped_refptr<media::VideoFrame> CreateTestYUVVideoFrame() {
@@ -50,12 +52,14 @@ class VideoResourceUpdaterTest : public testing::Test {
}
TestWebGraphicsContext3D* context3d_;
+ FakeOutputSurfaceClient client_;
scoped_ptr<FakeOutputSurface> output_surface3d_;
scoped_ptr<ResourceProvider> resource_provider3d_;
};
TEST_F(VideoResourceUpdaterTest, SoftwareFrame) {
- VideoResourceUpdater updater(resource_provider3d_.get());
+ VideoResourceUpdater updater(output_surface3d_->context_provider().get(),
+ resource_provider3d_.get());
scoped_refptr<media::VideoFrame> video_frame = CreateTestYUVVideoFrame();
VideoFrameExternalResources resources =
@@ -64,7 +68,8 @@ TEST_F(VideoResourceUpdaterTest, SoftwareFrame) {
}
TEST_F(VideoResourceUpdaterTest, LostContextForSoftwareFrame) {
- VideoResourceUpdater updater(resource_provider3d_.get());
+ VideoResourceUpdater updater(output_surface3d_->context_provider().get(),
+ resource_provider3d_.get());
scoped_refptr<media::VideoFrame> video_frame = CreateTestYUVVideoFrame();
// Fail while creating the mailbox for the second YUV plane.
diff --git a/chromium/cc/resources/worker_pool.h b/chromium/cc/resources/worker_pool.h
index c26ed077eb6..cc8c39f3642 100644
--- a/chromium/cc/resources/worker_pool.h
+++ b/chromium/cc/resources/worker_pool.h
@@ -10,12 +10,12 @@
#include <vector>
#include "base/cancelable_callback.h"
+#include "base/containers/scoped_ptr_hash_map.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop.h"
#include "cc/base/cc_export.h"
-#include "cc/base/scoped_ptr_hash_map.h"
namespace cc {
namespace internal {
@@ -113,7 +113,7 @@ class CC_EXPORT WorkerPool {
// dependencies pointing in the direction of the dependents. Each task
// need to be assigned a unique priority and a run count that matches
// the number of dependencies.
- typedef ScopedPtrHashMap<internal::WorkerPoolTask*, internal::GraphNode>
+ typedef base::ScopedPtrHashMap<internal::WorkerPoolTask*, internal::GraphNode>
GraphNodeMap;
typedef GraphNodeMap TaskGraph;
diff --git a/chromium/cc/resources/worker_pool_perftest.cc b/chromium/cc/resources/worker_pool_perftest.cc
index aee1f24a27d..06a2b9806df 100644
--- a/chromium/cc/resources/worker_pool_perftest.cc
+++ b/chromium/cc/resources/worker_pool_perftest.cc
@@ -6,7 +6,9 @@
#include "base/time/time.h"
#include "cc/base/completion_event.h"
+#include "cc/test/lap_timer.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/perf/perf_test.h"
namespace cc {
@@ -144,7 +146,10 @@ class PerfWorkerPool : public WorkerPool {
class WorkerPoolPerfTest : public testing::Test {
public:
- WorkerPoolPerfTest() : num_runs_(0) {}
+ WorkerPoolPerfTest()
+ : timer_(kWarmupRuns,
+ base::TimeDelta::FromMilliseconds(kTimeLimitMillis),
+ kTimeCheckInterval) {}
// Overridden from testing::Test:
virtual void SetUp() OVERRIDE {
@@ -155,38 +160,16 @@ class WorkerPoolPerfTest : public testing::Test {
worker_pool_->CheckForCompletedTasks();
}
- void EndTest() {
- elapsed_ = base::TimeTicks::HighResNow() - start_time_;
- }
-
- void AfterTest(const std::string test_name) {
+ void AfterTest(const std::string& test_name) {
// Format matches chrome/test/perf/perf_test.h:PrintResult
- printf("*RESULT %s: %.2f runs/s\n",
- test_name.c_str(),
- num_runs_ / elapsed_.InSecondsF());
- }
-
- bool DidRun() {
- ++num_runs_;
- if (num_runs_ == kWarmupRuns)
- start_time_ = base::TimeTicks::HighResNow();
-
- if (!start_time_.is_null() && (num_runs_ % kTimeCheckInterval) == 0) {
- base::TimeDelta elapsed = base::TimeTicks::HighResNow() - start_time_;
- if (elapsed >= base::TimeDelta::FromMilliseconds(kTimeLimitMillis)) {
- elapsed_ = elapsed;
- return false;
- }
- }
-
- return true;
+ printf(
+ "*RESULT %s: %.2f runs/s\n", test_name.c_str(), timer_.LapsPerSecond());
}
- void RunScheduleTasksTest(const std::string test_name,
+ void RunScheduleTasksTest(const std::string& test_name,
unsigned max_depth,
unsigned num_children_per_node) {
- start_time_ = base::TimeTicks();
- num_runs_ = 0;
+ timer_.Reset();
do {
scoped_refptr<PerfControlWorkerPoolTaskImpl> leaf_task(
new PerfControlWorkerPoolTaskImpl);
@@ -196,16 +179,17 @@ class WorkerPoolPerfTest : public testing::Test {
worker_pool_->ScheduleTasks(NULL, NULL, 0, 0);
worker_pool_->CheckForCompletedTasks();
leaf_task->AllowTaskToFinish();
- } while (DidRun());
+ timer_.NextLap();
+ } while (!timer_.HasTimeLimitExpired());
- AfterTest(test_name);
+ perf_test::PrintResult("schedule_tasks", "", test_name,
+ timer_.LapsPerSecond(), "runs/s", true);
}
- void RunExecuteTasksTest(const std::string test_name,
+ void RunExecuteTasksTest(const std::string& test_name,
unsigned max_depth,
unsigned num_children_per_node) {
- start_time_ = base::TimeTicks();
- num_runs_ = 0;
+ timer_.Reset();
do {
scoped_refptr<PerfControlWorkerPoolTaskImpl> root_task(
new PerfControlWorkerPoolTaskImpl);
@@ -214,36 +198,36 @@ class WorkerPoolPerfTest : public testing::Test {
root_task->WaitForTaskToStartRunning();
root_task->AllowTaskToFinish();
worker_pool_->CheckForCompletedTasks();
- } while (DidRun());
+ timer_.NextLap();
+ } while (!timer_.HasTimeLimitExpired());
- AfterTest(test_name);
+ perf_test::PrintResult("execute_tasks", "", test_name,
+ timer_.LapsPerSecond(), "runs/s", true);
}
protected:
scoped_ptr<PerfWorkerPool> worker_pool_;
- base::TimeTicks start_time_;
- base::TimeDelta elapsed_;
- int num_runs_;
+ LapTimer timer_;
};
TEST_F(WorkerPoolPerfTest, ScheduleTasks) {
- RunScheduleTasksTest("schedule_tasks_1_10", 1, 10);
- RunScheduleTasksTest("schedule_tasks_1_1000", 1, 1000);
- RunScheduleTasksTest("schedule_tasks_2_10", 2, 10);
- RunScheduleTasksTest("schedule_tasks_5_5", 5, 5);
- RunScheduleTasksTest("schedule_tasks_10_2", 10, 2);
- RunScheduleTasksTest("schedule_tasks_1000_1", 1000, 1);
- RunScheduleTasksTest("schedule_tasks_10_1", 10, 1);
+ RunScheduleTasksTest("1_10", 1, 10);
+ RunScheduleTasksTest("1_1000", 1, 1000);
+ RunScheduleTasksTest("2_10", 2, 10);
+ RunScheduleTasksTest("5_5", 5, 5);
+ RunScheduleTasksTest("10_2", 10, 2);
+ RunScheduleTasksTest("1000_1", 1000, 1);
+ RunScheduleTasksTest("10_1", 10, 1);
}
TEST_F(WorkerPoolPerfTest, ExecuteTasks) {
- RunExecuteTasksTest("execute_tasks_1_10", 1, 10);
- RunExecuteTasksTest("execute_tasks_1_1000", 1, 1000);
- RunExecuteTasksTest("execute_tasks_2_10", 2, 10);
- RunExecuteTasksTest("execute_tasks_5_5", 5, 5);
- RunExecuteTasksTest("execute_tasks_10_2", 10, 2);
- RunExecuteTasksTest("execute_tasks_1000_1", 1000, 1);
- RunExecuteTasksTest("execute_tasks_10_1", 10, 1);
+ RunExecuteTasksTest("1_10", 1, 10);
+ RunExecuteTasksTest("1_1000", 1, 1000);
+ RunExecuteTasksTest("2_10", 2, 10);
+ RunExecuteTasksTest("5_5", 5, 5);
+ RunExecuteTasksTest("10_2", 10, 2);
+ RunExecuteTasksTest("1000_1", 1000, 1);
+ RunExecuteTasksTest("10_1", 10, 1);
}
} // namespace
diff --git a/chromium/cc/scheduler/delay_based_time_source.cc b/chromium/cc/scheduler/delay_based_time_source.cc
index 44003970b35..d150c717a12 100644
--- a/chromium/cc/scheduler/delay_based_time_source.cc
+++ b/chromium/cc/scheduler/delay_based_time_source.cc
@@ -17,10 +17,10 @@ namespace cc {
namespace {
-// kDoubleTickThreshold prevents ticks from running within the specified
+// kDoubleTickDivisor prevents ticks from running within the specified
// fraction of an interval. This helps account for jitter in the timebase as
// well as quick timer reactivation.
-static const double kDoubleTickThreshold = 0.25;
+static const int kDoubleTickDivisor = 2;
// kIntervalChangeThreshold is the fraction of the interval that will trigger an
// immediate interval change. kPhaseChangeThreshold is the fraction of the
@@ -42,43 +42,42 @@ scoped_refptr<DelayBasedTimeSource> DelayBasedTimeSource::Create(
DelayBasedTimeSource::DelayBasedTimeSource(
base::TimeDelta interval, base::SingleThreadTaskRunner* task_runner)
: client_(NULL),
- has_tick_target_(false),
+ last_tick_time_(base::TimeTicks() - interval),
current_parameters_(interval, base::TimeTicks()),
next_parameters_(interval, base::TimeTicks()),
- state_(STATE_INACTIVE),
+ active_(false),
task_runner_(task_runner),
weak_factory_(this) {}
DelayBasedTimeSource::~DelayBasedTimeSource() {}
-void DelayBasedTimeSource::SetActive(bool active) {
+base::TimeTicks DelayBasedTimeSource::SetActive(bool active) {
TRACE_EVENT1("cc", "DelayBasedTimeSource::SetActive", "active", active);
- if (!active) {
- state_ = STATE_INACTIVE;
+ if (active == active_)
+ return base::TimeTicks();
+ active_ = active;
+
+ if (!active_) {
weak_factory_.InvalidateWeakPtrs();
- return;
+ return base::TimeTicks();
}
- if (state_ == STATE_STARTING || state_ == STATE_ACTIVE)
- return;
+ PostNextTickTask(Now());
- if (!has_tick_target_) {
- // Becoming active the first time is deferred: we post a 0-delay task.
- // When it runs, we use that to establish the timebase, become truly
- // active, and fire the first tick.
- state_ = STATE_STARTING;
- task_runner_->PostTask(FROM_HERE,
- base::Bind(&DelayBasedTimeSource::OnTimerFired,
- weak_factory_.GetWeakPtr()));
- return;
+ // Determine if there was a tick that was missed while not active.
+ base::TimeTicks last_tick_time_if_always_active =
+ current_parameters_.tick_target - current_parameters_.interval;
+ base::TimeTicks new_tick_time_threshold =
+ last_tick_time_ + current_parameters_.interval / kDoubleTickDivisor;
+ if (last_tick_time_if_always_active > new_tick_time_threshold) {
+ last_tick_time_ = last_tick_time_if_always_active;
+ return last_tick_time_;
}
- state_ = STATE_ACTIVE;
-
- PostNextTickTask(Now());
+ return base::TimeTicks();
}
-bool DelayBasedTimeSource::Active() const { return state_ != STATE_INACTIVE; }
+bool DelayBasedTimeSource::Active() const { return active_; }
base::TimeTicks DelayBasedTimeSource::LastTickTime() { return last_tick_time_; }
@@ -87,17 +86,11 @@ base::TimeTicks DelayBasedTimeSource::NextTickTime() {
}
void DelayBasedTimeSource::OnTimerFired() {
- DCHECK(state_ != STATE_INACTIVE);
+ DCHECK(active_);
- base::TimeTicks now = this->Now();
- last_tick_time_ = now;
+ last_tick_time_ = current_parameters_.tick_target;
- if (state_ == STATE_STARTING) {
- SetTimebaseAndInterval(now, current_parameters_.interval);
- state_ = STATE_ACTIVE;
- }
-
- PostNextTickTask(now);
+ PostNextTickTask(Now());
// Fire the tick.
if (client_)
@@ -112,9 +105,8 @@ void DelayBasedTimeSource::SetTimebaseAndInterval(base::TimeTicks timebase,
base::TimeDelta interval) {
next_parameters_.interval = interval;
next_parameters_.tick_target = timebase;
- has_tick_target_ = true;
- if (state_ != STATE_ACTIVE) {
+ if (!active_) {
// If we aren't active, there's no need to reset the timer.
return;
}
@@ -125,6 +117,8 @@ void DelayBasedTimeSource::SetTimebaseAndInterval(base::TimeTicks timebase,
std::abs((interval - current_parameters_.interval).InSecondsF());
double interval_change = interval_delta / interval.InSecondsF();
if (interval_change > kIntervalChangeThreshold) {
+ TRACE_EVENT_INSTANT0("cc", "DelayBasedTimeSource::IntervalChanged",
+ TRACE_EVENT_SCOPE_THREAD);
SetActive(false);
SetActive(true);
return;
@@ -142,6 +136,8 @@ void DelayBasedTimeSource::SetTimebaseAndInterval(base::TimeTicks timebase,
fmod(target_delta, interval.InSecondsF()) / interval.InSecondsF();
if (phase_change > kPhaseChangeThreshold &&
phase_change < (1.0 - kPhaseChangeThreshold)) {
+ TRACE_EVENT_INSTANT0("cc", "DelayBasedTimeSource::PhaseChanged",
+ TRACE_EVENT_SCOPE_THREAD);
SetActive(false);
SetActive(true);
return;
@@ -208,25 +204,32 @@ base::TimeTicks DelayBasedTimeSource::Now() const {
// tick(), PostDelayedTask(floor(50.000-37)) --> PostDelayedTask(13)
base::TimeTicks DelayBasedTimeSource::NextTickTarget(base::TimeTicks now) {
base::TimeDelta new_interval = next_parameters_.interval;
- int intervals_elapsed =
- static_cast<int>(floor((now - next_parameters_.tick_target).InSecondsF() /
- new_interval.InSecondsF()));
- base::TimeTicks last_effective_tick =
- next_parameters_.tick_target + new_interval * intervals_elapsed;
- base::TimeTicks new_tick_target = last_effective_tick + new_interval;
- DCHECK(now < new_tick_target)
+
+ // |interval_offset| is the offset from |now| to the next multiple of
+ // |interval| after |tick_target|, possibly negative if in the past.
+ base::TimeDelta interval_offset = base::TimeDelta::FromInternalValue(
+ (next_parameters_.tick_target - now).ToInternalValue() %
+ new_interval.ToInternalValue());
+ // If |now| is exactly on the interval (i.e. offset==0), don't adjust.
+ // Otherwise, if |tick_target| was in the past, adjust forward to the next
+ // tick after |now|.
+ if (interval_offset.ToInternalValue() != 0 &&
+ next_parameters_.tick_target < now) {
+ interval_offset += new_interval;
+ }
+
+ base::TimeTicks new_tick_target = now + interval_offset;
+ DCHECK(now <= new_tick_target)
<< "now = " << now.ToInternalValue()
<< "; new_tick_target = " << new_tick_target.ToInternalValue()
<< "; new_interval = " << new_interval.InMicroseconds()
<< "; tick_target = " << next_parameters_.tick_target.ToInternalValue()
- << "; intervals_elapsed = " << intervals_elapsed
- << "; last_effective_tick = " << last_effective_tick.ToInternalValue();
+ << "; interval_offset = " << interval_offset.ToInternalValue();
// Avoid double ticks when:
// 1) Turning off the timer and turning it right back on.
// 2) Jittery data is passed to SetTimebaseAndInterval().
- if (new_tick_target - last_tick_time_ <=
- new_interval / static_cast<int>(1.0 / kDoubleTickThreshold))
+ if (new_tick_target - last_tick_time_ <= new_interval / kDoubleTickDivisor)
new_tick_target += new_interval;
return new_tick_target;
@@ -236,10 +239,9 @@ void DelayBasedTimeSource::PostNextTickTask(base::TimeTicks now) {
base::TimeTicks new_tick_target = NextTickTarget(now);
// Post another task *before* the tick and update state
- base::TimeDelta delay = new_tick_target - now;
- DCHECK(delay.InMillisecondsF() <=
- next_parameters_.interval.InMillisecondsF() *
- (1.0 + kDoubleTickThreshold));
+ base::TimeDelta delay;
+ if (now <= new_tick_target)
+ delay = new_tick_target - now;
task_runner_->PostDelayedTask(FROM_HERE,
base::Bind(&DelayBasedTimeSource::OnTimerFired,
weak_factory_.GetWeakPtr()),
diff --git a/chromium/cc/scheduler/delay_based_time_source.h b/chromium/cc/scheduler/delay_based_time_source.h
index 1dc4d6fff11..55aac5a97fb 100644
--- a/chromium/cc/scheduler/delay_based_time_source.h
+++ b/chromium/cc/scheduler/delay_based_time_source.h
@@ -27,7 +27,7 @@ class CC_EXPORT DelayBasedTimeSource : public TimeSource {
virtual void SetTimebaseAndInterval(base::TimeTicks timebase,
base::TimeDelta interval) OVERRIDE;
- virtual void SetActive(bool active) OVERRIDE;
+ virtual base::TimeTicks SetActive(bool active) OVERRIDE;
virtual bool Active() const OVERRIDE;
// Get the last and next tick times. nextTimeTime() returns null when
@@ -47,12 +47,6 @@ class CC_EXPORT DelayBasedTimeSource : public TimeSource {
void PostNextTickTask(base::TimeTicks now);
void OnTimerFired();
- enum State {
- STATE_INACTIVE,
- STATE_STARTING,
- STATE_ACTIVE,
- };
-
struct Parameters {
Parameters(base::TimeDelta interval, base::TimeTicks tick_target)
: interval(interval), tick_target(tick_target) {}
@@ -61,7 +55,6 @@ class CC_EXPORT DelayBasedTimeSource : public TimeSource {
};
TimeSourceClient* client_;
- bool has_tick_target_;
base::TimeTicks last_tick_time_;
// current_parameters_ should only be written by PostNextTickTask.
@@ -71,7 +64,7 @@ class CC_EXPORT DelayBasedTimeSource : public TimeSource {
Parameters current_parameters_;
Parameters next_parameters_;
- State state_;
+ bool active_;
base::SingleThreadTaskRunner* task_runner_;
base::WeakPtrFactory<DelayBasedTimeSource> weak_factory_;
diff --git a/chromium/cc/scheduler/delay_based_time_source_unittest.cc b/chromium/cc/scheduler/delay_based_time_source_unittest.cc
index df84c978e57..42652e21868 100644
--- a/chromium/cc/scheduler/delay_based_time_source_unittest.cc
+++ b/chromium/cc/scheduler/delay_based_time_source_unittest.cc
@@ -88,7 +88,7 @@ TEST(DelayBasedTimeSource, NextDelaySaneWhenExactlyOnRequestedTime) {
FakeDelayBasedTimeSource::Create(Interval(), task_runner.get());
timer->SetClient(&client);
timer->SetActive(true);
- // Run the first task, as that activates the timer and picks up a timebase.
+ // Run the first tick.
task_runner->RunPendingTasks();
EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds());
@@ -109,7 +109,7 @@ TEST(DelayBasedTimeSource, NextDelaySaneWhenSlightlyAfterRequestedTime) {
FakeDelayBasedTimeSource::Create(Interval(), task_runner.get());
timer->SetClient(&client);
timer->SetActive(true);
- // Run the first task, as that activates the timer and picks up a timebase.
+ // Run the first tick.
task_runner->RunPendingTasks();
EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds());
@@ -122,7 +122,7 @@ TEST(DelayBasedTimeSource, NextDelaySaneWhenSlightlyAfterRequestedTime) {
}
// At 60Hz, when the tick returns at exactly 2*interval after the requested next
-// time, make sure a 16ms next delay is posted.
+// time, make sure a 0ms next delay is posted.
TEST(DelayBasedTimeSource, NextDelaySaneWhenExactlyTwiceAfterRequestedTime) {
scoped_refptr<base::TestSimpleTaskRunner> task_runner =
new base::TestSimpleTaskRunner;
@@ -131,7 +131,7 @@ TEST(DelayBasedTimeSource, NextDelaySaneWhenExactlyTwiceAfterRequestedTime) {
FakeDelayBasedTimeSource::Create(Interval(), task_runner.get());
timer->SetClient(&client);
timer->SetActive(true);
- // Run the first task, as that activates the timer and picks up a timebase.
+ // Run the first tick.
task_runner->RunPendingTasks();
EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds());
@@ -139,7 +139,7 @@ TEST(DelayBasedTimeSource, NextDelaySaneWhenExactlyTwiceAfterRequestedTime) {
timer->SetNow(timer->Now() + 2 * Interval());
task_runner->RunPendingTasks();
- EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds());
+ EXPECT_EQ(0, task_runner->NextPendingTaskDelay().InMilliseconds());
}
// At 60Hz, when the tick returns at 2*interval and a bit after the requested
@@ -152,7 +152,7 @@ TEST(DelayBasedTimeSource, NextDelaySaneWhenSlightlyAfterTwiceRequestedTime) {
FakeDelayBasedTimeSource::Create(Interval(), task_runner.get());
timer->SetClient(&client);
timer->SetActive(true);
- // Run the first task, as that activates the timer and picks up a timebase.
+ // Run the first tick.
task_runner->RunPendingTasks();
EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds());
@@ -174,7 +174,7 @@ TEST(DelayBasedTimeSource, NextDelaySaneWhenHalfAfterRequestedTime) {
FakeDelayBasedTimeSource::Create(Interval(), task_runner.get());
timer->SetClient(&client);
timer->SetActive(true);
- // Run the first task, as that activates the timer and picks up a timebase.
+ // Run the first tick.
task_runner->RunPendingTasks();
EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds());
@@ -196,7 +196,7 @@ TEST(DelayBasedTimeSource, SaneHandlingOfJitteryTimebase) {
FakeDelayBasedTimeSource::Create(Interval(), task_runner.get());
timer->SetClient(&client);
timer->SetActive(true);
- // Run the first task, as that activates the timer and picks up a timebase.
+ // Run the first tick.
task_runner->RunPendingTasks();
EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds());
@@ -227,7 +227,7 @@ TEST(DelayBasedTimeSource, HandlesSignificantTimebaseChangesImmediately) {
FakeDelayBasedTimeSource::Create(Interval(), task_runner.get());
timer->SetClient(&client);
timer->SetActive(true);
- // Run the first task, as that activates the timer and picks up a timebase.
+ // Run the first tick.
task_runner->RunPendingTasks();
EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds());
@@ -246,10 +246,10 @@ TEST(DelayBasedTimeSource, HandlesSignificantTimebaseChangesImmediately) {
timer->SetTimebaseAndInterval(timer->Now() + jitter, Interval());
EXPECT_FALSE(client.TickCalled()); // Make sure pending tasks were canceled.
- EXPECT_EQ(7, task_runner->NextPendingTaskDelay().InMilliseconds());
+ EXPECT_EQ(16 + 7, task_runner->NextPendingTaskDelay().InMilliseconds());
// Tick, then shift timebase by -7ms.
- timer->SetNow(timer->Now() + jitter);
+ timer->SetNow(timer->Now() + Interval() + jitter);
task_runner->RunPendingTasks();
EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds());
@@ -271,7 +271,7 @@ TEST(DelayBasedTimeSource, HanldlesSignificantIntervalChangesImmediately) {
FakeDelayBasedTimeSource::Create(Interval(), task_runner.get());
timer->SetClient(&client);
timer->SetActive(true);
- // Run the first task, as that activates the timer and picks up a timebase.
+ // Run the first tick.
task_runner->RunPendingTasks();
EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds());
@@ -305,6 +305,115 @@ TEST(DelayBasedTimeSource, HanldlesSignificantIntervalChangesImmediately) {
EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds());
}
+TEST(DelayBasedTimeSource, JitteryRuntimeWithFutureTimebases) {
+ scoped_refptr<base::TestSimpleTaskRunner> task_runner =
+ new base::TestSimpleTaskRunner;
+ FakeTimeSourceClient client;
+ scoped_refptr<FakeDelayBasedTimeSource> timer =
+ FakeDelayBasedTimeSource::Create(Interval(), task_runner.get());
+ timer->SetClient(&client);
+ timer->SetActive(true);
+
+ // Run the first tick.
+ task_runner->RunPendingTasks();
+ EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds());
+
+ base::TimeTicks future_timebase = timer->Now() + Interval() * 10;
+
+ // 1ms jitter
+ base::TimeDelta jitter1 = base::TimeDelta::FromMilliseconds(1);
+
+ // Tick with +1ms of jitter
+ future_timebase += Interval();
+ timer->SetTimebaseAndInterval(future_timebase, Interval());
+ timer->SetNow(timer->Now() + Interval() + jitter1);
+ task_runner->RunPendingTasks();
+ EXPECT_EQ(15, task_runner->NextPendingTaskDelay().InMilliseconds());
+
+ // Tick with 0ms of jitter
+ future_timebase += Interval();
+ timer->SetTimebaseAndInterval(future_timebase, Interval());
+ timer->SetNow(timer->Now() + Interval() - jitter1);
+ task_runner->RunPendingTasks();
+ EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds());
+
+ // Tick with -1ms of jitter
+ future_timebase += Interval();
+ timer->SetTimebaseAndInterval(future_timebase, Interval());
+ timer->SetNow(timer->Now() + Interval() - jitter1);
+ task_runner->RunPendingTasks();
+ EXPECT_EQ(17, task_runner->NextPendingTaskDelay().InMilliseconds());
+
+ // Tick with 0ms of jitter
+ future_timebase += Interval();
+ timer->SetTimebaseAndInterval(future_timebase, Interval());
+ timer->SetNow(timer->Now() + Interval() + jitter1);
+ task_runner->RunPendingTasks();
+ EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds());
+
+ // 8 ms jitter
+ base::TimeDelta jitter8 = base::TimeDelta::FromMilliseconds(8);
+
+ // Tick with +8ms of jitter
+ future_timebase += Interval();
+ timer->SetTimebaseAndInterval(future_timebase, Interval());
+ timer->SetNow(timer->Now() + Interval() + jitter8);
+ task_runner->RunPendingTasks();
+ EXPECT_EQ(8, task_runner->NextPendingTaskDelay().InMilliseconds());
+
+ // Tick with 0ms of jitter
+ future_timebase += Interval();
+ timer->SetTimebaseAndInterval(future_timebase, Interval());
+ timer->SetNow(timer->Now() + Interval() - jitter8);
+ task_runner->RunPendingTasks();
+ EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds());
+
+ // Tick with -8ms of jitter
+ future_timebase += Interval();
+ timer->SetTimebaseAndInterval(future_timebase, Interval());
+ timer->SetNow(timer->Now() + Interval() - jitter8);
+ task_runner->RunPendingTasks();
+ EXPECT_EQ(24, task_runner->NextPendingTaskDelay().InMilliseconds());
+
+ // Tick with 0ms of jitter
+ future_timebase += Interval();
+ timer->SetTimebaseAndInterval(future_timebase, Interval());
+ timer->SetNow(timer->Now() + Interval() + jitter8);
+ task_runner->RunPendingTasks();
+ EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds());
+
+ // 15 ms jitter
+ base::TimeDelta jitter15 = base::TimeDelta::FromMilliseconds(15);
+
+ // Tick with +15ms jitter
+ future_timebase += Interval();
+ timer->SetTimebaseAndInterval(future_timebase, Interval());
+ timer->SetNow(timer->Now() + Interval() + jitter15);
+ task_runner->RunPendingTasks();
+ EXPECT_EQ(1, task_runner->NextPendingTaskDelay().InMilliseconds());
+
+ // Tick with 0ms of jitter
+ future_timebase += Interval();
+ timer->SetTimebaseAndInterval(future_timebase, Interval());
+ timer->SetNow(timer->Now() + Interval() - jitter15);
+ task_runner->RunPendingTasks();
+ EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds());
+
+ // Tick with -15ms of jitter
+ future_timebase += Interval();
+ timer->SetTimebaseAndInterval(future_timebase, Interval());
+ timer->SetNow(timer->Now() + Interval() - jitter15);
+ task_runner->RunPendingTasks();
+ EXPECT_EQ(31, task_runner->NextPendingTaskDelay().InMilliseconds());
+
+ // Tick with 0ms of jitter
+ future_timebase += Interval();
+ timer->SetTimebaseAndInterval(future_timebase, Interval());
+ timer->SetNow(timer->Now() + Interval() + jitter15);
+ task_runner->RunPendingTasks();
+ EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds());
+}
+
TEST(DelayBasedTimeSourceTest, AchievesTargetRateWithNoNoise) {
int num_iterations = 10;
@@ -397,5 +506,22 @@ TEST(DelayBasedTimeSource, TestDeactivateAndReactivateAfterNextTickTime) {
EXPECT_EQ(13, task_runner->NextPendingTaskDelay().InMilliseconds());
}
+TEST(DelayBasedTimeSource, TestOverflow) {
+ // int(big_now / interval) < 0, so this causes a crash if the number of
+ // intervals elapsed is attempted to be stored in an int.
+ base::TimeDelta interval = base::TimeDelta::FromInternalValue(4000);
+ base::TimeTicks big_now = base::TimeTicks::FromInternalValue(8635916564000);
+
+ scoped_refptr<base::TestSimpleTaskRunner> task_runner =
+ new base::TestSimpleTaskRunner;
+ FakeTimeSourceClient client;
+ scoped_refptr<FakeDelayBasedTimeSource> timer =
+ FakeDelayBasedTimeSource::Create(interval, task_runner.get());
+ timer->SetClient(&client);
+ timer->SetNow(big_now);
+ timer->SetActive(true);
+ EXPECT_EQ(0, task_runner->NextPendingTaskDelay().InMilliseconds());
+}
+
} // namespace
} // namespace cc
diff --git a/chromium/cc/scheduler/frame_rate_controller.cc b/chromium/cc/scheduler/frame_rate_controller.cc
index 4844b16faee..cefe764fc78 100644
--- a/chromium/cc/scheduler/frame_rate_controller.cc
+++ b/chromium/cc/scheduler/frame_rate_controller.cc
@@ -70,11 +70,15 @@ BeginFrameArgs FrameRateController::SetActive(bool active) {
if (active_ == active)
return BeginFrameArgs();
TRACE_EVENT1("cc", "FrameRateController::SetActive", "active", active);
- bool just_activated = active && !active_;
active_ = active;
if (is_time_source_throttling_) {
- time_source_->SetActive(active);
+ base::TimeTicks missed_tick_time = time_source_->SetActive(active);
+ if (!missed_tick_time.is_null()) {
+ base::TimeTicks deadline = NextTickTime();
+ return BeginFrameArgs::Create(
+ missed_tick_time, deadline + deadline_adjustment_, interval_);
+ }
} else {
if (active)
PostManualTick();
@@ -82,12 +86,6 @@ BeginFrameArgs FrameRateController::SetActive(bool active) {
weak_factory_.InvalidateWeakPtrs();
}
- if (just_activated) {
- // TODO(brianderson): Use an adaptive parent compositor deadline.
- base::TimeTicks frame_time = NextTickTime() - interval_;
- base::TimeTicks deadline = NextTickTime();
- return BeginFrameArgs::Create(frame_time, deadline, interval_);
- }
return BeginFrameArgs();
}
@@ -119,10 +117,10 @@ void FrameRateController::OnTimerTick() {
if (client_) {
// TODO(brianderson): Use an adaptive parent compositor deadline.
base::TimeTicks frame_time = LastTickTime();
- base::TimeTicks deadline = NextTickTime() + deadline_adjustment_;
- client_->FrameRateControllerTick(
- throttled,
- BeginFrameArgs::Create(frame_time, deadline, interval_));
+ base::TimeTicks deadline = NextTickTime();
+ BeginFrameArgs args = BeginFrameArgs::Create(
+ frame_time, deadline + deadline_adjustment_, interval_);
+ client_->FrameRateControllerTick(throttled, args);
}
if (!is_time_source_throttling_ && !throttled)
diff --git a/chromium/cc/scheduler/frame_rate_controller.h b/chromium/cc/scheduler/frame_rate_controller.h
index ed769238fe3..b68c73db0c8 100644
--- a/chromium/cc/scheduler/frame_rate_controller.h
+++ b/chromium/cc/scheduler/frame_rate_controller.h
@@ -61,12 +61,6 @@ class CC_EXPORT FrameRateController {
int MaxSwapsPending() const { return max_swaps_pending_; }
int NumSwapsPendingForTesting() const { return num_frames_pending_; }
- // This returns null for unthrottled frame-rate.
- base::TimeTicks NextTickTime();
-
- // This returns now for unthrottled frame-rate.
- base::TimeTicks LastTickTime();
-
void SetTimebaseAndInterval(base::TimeTicks timebase,
base::TimeDelta interval);
void SetDeadlineAdjustment(base::TimeDelta delta);
@@ -78,6 +72,11 @@ class CC_EXPORT FrameRateController {
void PostManualTick();
void ManualTick();
+ // This returns null for unthrottled frame-rate.
+ base::TimeTicks NextTickTime();
+ // This returns now for unthrottled frame-rate.
+ base::TimeTicks LastTickTime();
+
FrameRateControllerClient* client_;
int num_frames_pending_;
int max_swaps_pending_;
diff --git a/chromium/cc/scheduler/scheduler.cc b/chromium/cc/scheduler/scheduler.cc
index 010cc990b47..c81513feebd 100644
--- a/chromium/cc/scheduler/scheduler.cc
+++ b/chromium/cc/scheduler/scheduler.cc
@@ -4,9 +4,11 @@
#include "cc/scheduler/scheduler.h"
+#include <algorithm>
#include "base/auto_reset.h"
#include "base/debug/trace_event.h"
#include "base/logging.h"
+#include "cc/debug/traced_value.h"
namespace cc {
@@ -16,17 +18,14 @@ Scheduler::Scheduler(SchedulerClient* client,
client_(client),
weak_factory_(this),
last_set_needs_begin_frame_(false),
- has_pending_begin_frame_(false),
- safe_to_expect_begin_frame_(false),
state_machine_(scheduler_settings),
- inside_process_scheduled_actions_(false) {
+ inside_process_scheduled_actions_(false),
+ inside_action_(SchedulerStateMachine::ACTION_NONE) {
DCHECK(client_);
- DCHECK(!state_machine_.BeginFrameNeededToDrawByImplThread());
+ DCHECK(!state_machine_.BeginFrameNeededByImplThread());
}
-Scheduler::~Scheduler() {
- client_->SetNeedsBeginFrameOnImplThread(false);
-}
+Scheduler::~Scheduler() {}
void Scheduler::SetCanStart() {
state_machine_.SetCanStart();
@@ -43,19 +42,23 @@ void Scheduler::SetCanDraw(bool can_draw) {
ProcessScheduledActions();
}
-void Scheduler::SetHasPendingTree(bool has_pending_tree) {
- state_machine_.SetHasPendingTree(has_pending_tree);
+void Scheduler::NotifyReadyToActivate() {
+ state_machine_.NotifyReadyToActivate();
ProcessScheduledActions();
}
+void Scheduler::ActivatePendingTree() {
+ client_->ScheduledActionActivatePendingTree();
+}
+
void Scheduler::SetNeedsCommit() {
state_machine_.SetNeedsCommit();
ProcessScheduledActions();
}
-void Scheduler::SetNeedsForcedCommit() {
+void Scheduler::SetNeedsForcedCommitForReadback() {
state_machine_.SetNeedsCommit();
- state_machine_.SetNeedsForcedCommit();
+ state_machine_.SetNeedsForcedCommitForReadback();
ProcessScheduledActions();
}
@@ -64,13 +67,19 @@ void Scheduler::SetNeedsRedraw() {
ProcessScheduledActions();
}
-void Scheduler::DidSwapUseIncompleteTile() {
- state_machine_.DidSwapUseIncompleteTile();
+void Scheduler::SetNeedsManageTiles() {
+ DCHECK(!IsInsideAction(SchedulerStateMachine::ACTION_MANAGE_TILES));
+ state_machine_.SetNeedsManageTiles();
+ ProcessScheduledActions();
+}
+
+void Scheduler::SetSwapUsedIncompleteTile(bool used_incomplete_tile) {
+ state_machine_.SetSwapUsedIncompleteTile(used_incomplete_tile);
ProcessScheduledActions();
}
-void Scheduler::SetNeedsForcedRedraw() {
- state_machine_.SetNeedsForcedRedraw();
+void Scheduler::SetSmoothnessTakesPriority(bool smoothness_takes_priority) {
+ state_machine_.SetSmoothnessTakesPriority(smoothness_takes_priority);
ProcessScheduledActions();
}
@@ -93,16 +102,17 @@ void Scheduler::BeginFrameAbortedByMainThread(bool did_handle) {
void Scheduler::DidLoseOutputSurface() {
TRACE_EVENT0("cc", "Scheduler::DidLoseOutputSurface");
+ last_set_needs_begin_frame_ = false;
+ begin_frame_deadline_closure_.Cancel();
state_machine_.DidLoseOutputSurface();
ProcessScheduledActions();
}
void Scheduler::DidCreateAndInitializeOutputSurface() {
TRACE_EVENT0("cc", "Scheduler::DidCreateAndInitializeOutputSurface");
+ DCHECK(!last_set_needs_begin_frame_);
+ DCHECK(begin_frame_deadline_closure_.IsCancelled());
state_machine_.DidCreateAndInitializeOutputSurface();
- has_pending_begin_frame_ = false;
- last_set_needs_begin_frame_ = false;
- safe_to_expect_begin_frame_ = false;
ProcessScheduledActions();
}
@@ -113,12 +123,11 @@ base::TimeTicks Scheduler::AnticipatedDrawTime() {
last_begin_frame_args_.interval <= base::TimeDelta())
return base::TimeTicks();
- // TODO(brianderson): Express this in terms of the deadline.
base::TimeTicks now = base::TimeTicks::Now();
- int64 intervals = 1 + ((now - last_begin_frame_args_.frame_time) /
- last_begin_frame_args_.interval);
- return last_begin_frame_args_.frame_time +
- (last_begin_frame_args_.interval * intervals);
+ base::TimeTicks timebase = std::max(last_begin_frame_args_.frame_time,
+ last_begin_frame_args_.deadline);
+ int64 intervals = 1 + ((now - timebase) / last_begin_frame_args_.interval);
+ return timebase + (last_begin_frame_args_.interval * intervals);
}
base::TimeTicks Scheduler::LastBeginFrameOnImplThreadTime() {
@@ -126,69 +135,138 @@ base::TimeTicks Scheduler::LastBeginFrameOnImplThreadTime() {
}
void Scheduler::SetupNextBeginFrameIfNeeded() {
- bool needs_begin_frame_to_draw =
- state_machine_.BeginFrameNeededToDrawByImplThread();
- // We want to avoid proactive begin frames with the synchronous compositor
- // because every SetNeedsBeginFrame will force a redraw.
- bool proactive_begin_frame_wanted =
- state_machine_.ProactiveBeginFrameWantedByImplThread() &&
- !settings_.using_synchronous_renderer_compositor &&
- settings_.throttle_frame_production;
- bool needs_begin_frame = needs_begin_frame_to_draw ||
- proactive_begin_frame_wanted;
- bool immediate_disables_needed =
- settings_.using_synchronous_renderer_compositor;
-
- if (needs_begin_frame_to_draw)
- safe_to_expect_begin_frame_ = true;
-
- // Determine if we need BeginFrame notifications.
- // If we do, always request the BeginFrame immediately.
- // If not, only disable on the next BeginFrame to avoid unnecessary toggles.
- // The synchronous renderer compositor requires immediate disables though.
- if ((needs_begin_frame ||
- state_machine_.inside_begin_frame() ||
- immediate_disables_needed) &&
- (needs_begin_frame != last_set_needs_begin_frame_)) {
- has_pending_begin_frame_ = false;
+ bool needs_begin_frame =
+ state_machine_.BeginFrameNeededByImplThread();
+
+ bool at_end_of_deadline =
+ state_machine_.begin_frame_state() ==
+ SchedulerStateMachine::BEGIN_FRAME_STATE_INSIDE_DEADLINE;
+
+ bool should_call_set_needs_begin_frame =
+ // Always request the BeginFrame immediately if it wasn't needed before.
+ (needs_begin_frame && !last_set_needs_begin_frame_) ||
+ // We always need to explicitly request our next BeginFrame.
+ at_end_of_deadline;
+
+ if (should_call_set_needs_begin_frame) {
client_->SetNeedsBeginFrameOnImplThread(needs_begin_frame);
- if (safe_to_expect_begin_frame_)
- last_set_needs_begin_frame_ = needs_begin_frame;
+ last_set_needs_begin_frame_ = needs_begin_frame;
}
- // Request another BeginFrame if we haven't drawn for now until we have
- // deadlines implemented.
- if (state_machine_.inside_begin_frame() && has_pending_begin_frame_) {
- has_pending_begin_frame_ = false;
- client_->SetNeedsBeginFrameOnImplThread(true);
- return;
+ // Setup PollForAnticipatedDrawTriggers if we need to monitor state but
+ // aren't expecting any more BeginFrames. This should only be needed by the
+ // synchronous compositor when BeginFrameNeededByImplThread is false.
+ if (state_machine_.ShouldPollForAnticipatedDrawTriggers()) {
+ DCHECK(settings_.using_synchronous_renderer_compositor);
+ DCHECK(!needs_begin_frame);
+ if (poll_for_draw_triggers_closure_.IsCancelled()) {
+ poll_for_draw_triggers_closure_.Reset(
+ base::Bind(&Scheduler::PollForAnticipatedDrawTriggers,
+ weak_factory_.GetWeakPtr()));
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ poll_for_draw_triggers_closure_.callback(),
+ last_begin_frame_args_.interval);
+ }
+ } else {
+ poll_for_draw_triggers_closure_.Cancel();
}
}
void Scheduler::BeginFrame(const BeginFrameArgs& args) {
TRACE_EVENT0("cc", "Scheduler::BeginFrame");
- DCHECK(!has_pending_begin_frame_);
- has_pending_begin_frame_ = true;
- safe_to_expect_begin_frame_ = true;
+ DCHECK(state_machine_.begin_frame_state() ==
+ SchedulerStateMachine::BEGIN_FRAME_STATE_IDLE);
+ DCHECK(state_machine_.HasInitializedOutputSurface());
last_begin_frame_args_ = args;
- state_machine_.DidEnterBeginFrame(args);
+ last_begin_frame_args_.deadline -= client_->DrawDurationEstimate();
+ state_machine_.OnBeginFrame(last_begin_frame_args_);
+ ProcessScheduledActions();
+
+ if (!state_machine_.HasInitializedOutputSurface())
+ return;
+
+ state_machine_.OnBeginFrameDeadlinePending();
+
+ if (settings_.using_synchronous_renderer_compositor) {
+ // The synchronous renderer compositor has to make its GL calls
+ // within this call to BeginFrame.
+ // TODO(brianderson): Have the OutputSurface initiate the deadline tasks
+ // so the sychronous renderer compoistor can take advantage of splitting
+ // up the BeginFrame and deadline as well.
+ OnBeginFrameDeadline();
+ } else if (!settings_.deadline_scheduling_enabled) {
+ // We emulate the old non-deadline scheduler here by posting the
+ // deadline task without any delay.
+ PostBeginFrameDeadline(base::TimeTicks());
+ } else if (state_machine_.ShouldTriggerBeginFrameDeadlineEarly()) {
+ // We are ready to draw a new active tree immediately.
+ PostBeginFrameDeadline(base::TimeTicks());
+ } else if (state_machine_.needs_redraw()) {
+ // We have an animation or fast input path on the impl thread that wants
+ // to draw, so don't wait too long for a new active tree.
+ PostBeginFrameDeadline(last_begin_frame_args_.deadline);
+ } else {
+ // The impl thread doesn't have anything it wants to draw and we are just
+ // waiting for a new active tree, so post the deadline for the next
+ // expected BeginFrame start. This allows us to draw immediately when
+ // there is a new active tree, instead of waiting for the next BeginFrame.
+ // TODO(brianderson): Handle long deadlines (that are past the next frame's
+ // frame time) properly instead of using this hack.
+ PostBeginFrameDeadline(last_begin_frame_args_.frame_time +
+ last_begin_frame_args_.interval);
+ }
+}
+
+void Scheduler::PostBeginFrameDeadline(base::TimeTicks deadline) {
+ begin_frame_deadline_closure_.Cancel();
+ begin_frame_deadline_closure_.Reset(
+ base::Bind(&Scheduler::OnBeginFrameDeadline, weak_factory_.GetWeakPtr()));
+ client_->PostBeginFrameDeadline(begin_frame_deadline_closure_.callback(),
+ deadline);
+}
+
+void Scheduler::OnBeginFrameDeadline() {
+ TRACE_EVENT0("cc", "Scheduler::OnBeginFrameDeadline");
+ DCHECK(state_machine_.HasInitializedOutputSurface());
+ begin_frame_deadline_closure_.Cancel();
+ state_machine_.OnBeginFrameDeadline();
+ ProcessScheduledActions();
+
+ if (state_machine_.HasInitializedOutputSurface()) {
+ // We only transition out of BEGIN_FRAME_STATE_INSIDE_DEADLINE when all
+ // actions that occur back-to-back in response to entering
+ // BEGIN_FRAME_STATE_INSIDE_DEADLINE have completed. This is important
+ // because sending the BeginFrame to the main thread will not occur if
+ // we transition to BEGIN_FRAME_STATE_IDLE too early.
+ state_machine_.OnBeginFrameIdle();
+ }
+
+ client_->DidBeginFrameDeadlineOnImplThread();
+}
+
+void Scheduler::PollForAnticipatedDrawTriggers() {
+ TRACE_EVENT0("cc", "Scheduler::PollForAnticipatedDrawTriggers");
+ state_machine_.DidEnterPollForAnticipatedDrawTriggers();
ProcessScheduledActions();
- state_machine_.DidLeaveBeginFrame();
+ state_machine_.DidLeavePollForAnticipatedDrawTriggers();
+
+ poll_for_draw_triggers_closure_.Cancel();
}
void Scheduler::DrawAndSwapIfPossible() {
- ScheduledActionDrawAndSwapResult result =
+ DrawSwapReadbackResult result =
client_->ScheduledActionDrawAndSwapIfPossible();
state_machine_.DidDrawIfPossibleCompleted(result.did_draw);
- if (result.did_swap)
- has_pending_begin_frame_ = false;
}
void Scheduler::DrawAndSwapForced() {
- ScheduledActionDrawAndSwapResult result =
- client_->ScheduledActionDrawAndSwapForced();
- if (result.did_swap)
- has_pending_begin_frame_ = false;
+ client_->ScheduledActionDrawAndSwapForced();
+}
+
+void Scheduler::DrawAndReadback() {
+ DrawSwapReadbackResult result = client_->ScheduledActionDrawAndReadback();
+ DCHECK(!result.did_swap);
}
void Scheduler::ProcessScheduledActions() {
@@ -199,9 +277,17 @@ void Scheduler::ProcessScheduledActions() {
base::AutoReset<bool> mark_inside(&inside_process_scheduled_actions_, true);
- SchedulerStateMachine::Action action = state_machine_.NextAction();
- while (action != SchedulerStateMachine::ACTION_NONE) {
+ SchedulerStateMachine::Action action;
+ do {
+ state_machine_.CheckInvariants();
+ action = state_machine_.NextAction();
+ TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"),
+ "SchedulerStateMachine",
+ "state",
+ TracedValue::FromValue(state_machine_.AsValue().release()));
state_machine_.UpdateState(action);
+ base::AutoReset<SchedulerStateMachine::Action>
+ mark_inside_action(&inside_action_, action);
switch (action) {
case SchedulerStateMachine::ACTION_NONE:
break;
@@ -214,31 +300,43 @@ void Scheduler::ProcessScheduledActions() {
case SchedulerStateMachine::ACTION_UPDATE_VISIBLE_TILES:
client_->ScheduledActionUpdateVisibleTiles();
break;
- case SchedulerStateMachine::ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED:
- client_->ScheduledActionActivatePendingTreeIfNeeded();
+ case SchedulerStateMachine::ACTION_ACTIVATE_PENDING_TREE:
+ ActivatePendingTree();
break;
- case SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE:
+ case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE:
DrawAndSwapIfPossible();
break;
- case SchedulerStateMachine::ACTION_DRAW_FORCED:
+ case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_FORCED:
DrawAndSwapForced();
break;
+ case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT:
+ // No action is actually performed, but this allows the state machine to
+ // advance out of its waiting to draw state without actually drawing.
+ break;
+ case SchedulerStateMachine::ACTION_DRAW_AND_READBACK:
+ DrawAndReadback();
+ break;
case SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION:
client_->ScheduledActionBeginOutputSurfaceCreation();
break;
case SchedulerStateMachine::ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD:
client_->ScheduledActionAcquireLayerTexturesForMainThread();
break;
+ case SchedulerStateMachine::ACTION_MANAGE_TILES:
+ client_->ScheduledActionManageTiles();
+ break;
}
- action = state_machine_.NextAction();
- }
+ } while (action != SchedulerStateMachine::ACTION_NONE);
SetupNextBeginFrameIfNeeded();
client_->DidAnticipatedDrawTimeChange(AnticipatedDrawTime());
+
+ if (state_machine_.ShouldTriggerBeginFrameDeadlineEarly())
+ PostBeginFrameDeadline(base::TimeTicks());
}
bool Scheduler::WillDrawIfNeeded() const {
- return !state_machine_.DrawSuspendedUntilCommit();
+ return !state_machine_.PendingDrawsShouldBeAborted();
}
} // namespace cc
diff --git a/chromium/cc/scheduler/scheduler.h b/chromium/cc/scheduler/scheduler.h
index 230f20d0ec9..3403b7a2e06 100644
--- a/chromium/cc/scheduler/scheduler.h
+++ b/chromium/cc/scheduler/scheduler.h
@@ -8,6 +8,7 @@
#include <string>
#include "base/basictypes.h"
+#include "base/cancelable_callback.h"
#include "base/memory/scoped_ptr.h"
#include "base/time/time.h"
#include "cc/base/cc_export.h"
@@ -20,34 +21,36 @@ namespace cc {
class Thread;
-struct ScheduledActionDrawAndSwapResult {
- ScheduledActionDrawAndSwapResult()
- : did_draw(false),
- did_swap(false) {}
- ScheduledActionDrawAndSwapResult(bool did_draw, bool did_swap)
- : did_draw(did_draw),
- did_swap(did_swap) {}
+struct DrawSwapReadbackResult {
+ DrawSwapReadbackResult()
+ : did_draw(false), did_swap(false), did_readback(false) {}
+ DrawSwapReadbackResult(bool did_draw, bool did_swap, bool did_readback)
+ : did_draw(did_draw), did_swap(did_swap), did_readback(did_readback) {}
bool did_draw;
bool did_swap;
+ bool did_readback;
};
class SchedulerClient {
public:
virtual void SetNeedsBeginFrameOnImplThread(bool enable) = 0;
virtual void ScheduledActionSendBeginFrameToMainThread() = 0;
- virtual ScheduledActionDrawAndSwapResult
- ScheduledActionDrawAndSwapIfPossible() = 0;
- virtual ScheduledActionDrawAndSwapResult
- ScheduledActionDrawAndSwapForced() = 0;
+ virtual DrawSwapReadbackResult ScheduledActionDrawAndSwapIfPossible() = 0;
+ virtual DrawSwapReadbackResult ScheduledActionDrawAndSwapForced() = 0;
+ virtual DrawSwapReadbackResult ScheduledActionDrawAndReadback() = 0;
virtual void ScheduledActionCommit() = 0;
virtual void ScheduledActionUpdateVisibleTiles() = 0;
- virtual void ScheduledActionActivatePendingTreeIfNeeded() = 0;
+ virtual void ScheduledActionActivatePendingTree() = 0;
virtual void ScheduledActionBeginOutputSurfaceCreation() = 0;
virtual void ScheduledActionAcquireLayerTexturesForMainThread() = 0;
+ virtual void ScheduledActionManageTiles() = 0;
virtual void DidAnticipatedDrawTimeChange(base::TimeTicks time) = 0;
virtual base::TimeDelta DrawDurationEstimate() = 0;
virtual base::TimeDelta BeginFrameToCommitDurationEstimate() = 0;
virtual base::TimeDelta CommitToActivateDurationEstimate() = 0;
+ virtual void PostBeginFrameDeadline(const base::Closure& closure,
+ base::TimeTicks deadline) = 0;
+ virtual void DidBeginFrameDeadlineOnImplThread() = 0;
protected:
virtual ~SchedulerClient() {}
@@ -67,23 +70,23 @@ class CC_EXPORT Scheduler {
void SetVisible(bool visible);
void SetCanDraw(bool can_draw);
- void SetHasPendingTree(bool has_pending_tree);
+ void NotifyReadyToActivate();
void SetNeedsCommit();
// Like SetNeedsCommit(), but ensures a commit will definitely happen even if
- // we are not visible.
- void SetNeedsForcedCommit();
+ // we are not visible. Will eventually result in a forced draw internally.
+ void SetNeedsForcedCommitForReadback();
void SetNeedsRedraw();
+ void SetNeedsManageTiles();
+
void SetMainThreadNeedsLayerTextures();
- // Like SetNeedsRedraw(), but ensures the draw will definitely happen even if
- // we are not visible.
- void SetNeedsForcedRedraw();
+ void SetSwapUsedIncompleteTile(bool used_incomplete_tile);
- void DidSwapUseIncompleteTile();
+ void SetSmoothnessTakesPriority(bool smoothness_takes_priority);
void FinishCommit();
void BeginFrameAbortedByMainThread(bool did_handle);
@@ -96,6 +99,9 @@ class CC_EXPORT Scheduler {
bool CommitPending() const { return state_machine_.CommitPending(); }
bool RedrawPending() const { return state_machine_.RedrawPending(); }
+ bool ManageTilesPending() const {
+ return state_machine_.ManageTilesPending();
+ }
bool WillDrawIfNeeded() const;
@@ -104,16 +110,27 @@ class CC_EXPORT Scheduler {
base::TimeTicks LastBeginFrameOnImplThreadTime();
void BeginFrame(const BeginFrameArgs& args);
+ void OnBeginFrameDeadline();
+ void PollForAnticipatedDrawTriggers();
- std::string StateAsStringForTesting() { return state_machine_.ToString(); }
+ scoped_ptr<base::Value> StateAsValue() {
+ return state_machine_.AsValue().Pass();
+ }
+
+ bool IsInsideAction(SchedulerStateMachine::Action action) {
+ return inside_action_ == action;
+ }
private:
Scheduler(SchedulerClient* client,
const SchedulerSettings& scheduler_settings);
+ void PostBeginFrameDeadline(base::TimeTicks deadline);
void SetupNextBeginFrameIfNeeded();
+ void ActivatePendingTree();
void DrawAndSwapIfPossible();
void DrawAndSwapForced();
+ void DrawAndReadback();
void ProcessScheduledActions();
const SchedulerSettings settings_;
@@ -121,14 +138,13 @@ class CC_EXPORT Scheduler {
base::WeakPtrFactory<Scheduler> weak_factory_;
bool last_set_needs_begin_frame_;
- bool has_pending_begin_frame_;
- // TODO(brianderson): crbug.com/249806 : Remove safe_to_expect_begin_frame_
- // workaround.
- bool safe_to_expect_begin_frame_;
BeginFrameArgs last_begin_frame_args_;
+ base::CancelableClosure begin_frame_deadline_closure_;
+ base::CancelableClosure poll_for_draw_triggers_closure_;
SchedulerStateMachine state_machine_;
bool inside_process_scheduled_actions_;
+ SchedulerStateMachine::Action inside_action_;
DISALLOW_COPY_AND_ASSIGN(Scheduler);
};
diff --git a/chromium/cc/scheduler/scheduler_settings.cc b/chromium/cc/scheduler/scheduler_settings.cc
index 90e9e89bbdc..6c1db6bf10c 100644
--- a/chromium/cc/scheduler/scheduler_settings.cc
+++ b/chromium/cc/scheduler/scheduler_settings.cc
@@ -7,8 +7,10 @@
namespace cc {
SchedulerSettings::SchedulerSettings()
- : impl_side_painting(false),
+ : deadline_scheduling_enabled(false),
+ impl_side_painting(false),
timeout_and_draw_when_animation_checkerboards(true),
+ maximum_number_of_failed_draws_before_draw_is_forced_(3),
using_synchronous_renderer_compositor(false),
throttle_frame_production(true) {}
diff --git a/chromium/cc/scheduler/scheduler_settings.h b/chromium/cc/scheduler/scheduler_settings.h
index ff00fc32b36..7857a70fe9e 100644
--- a/chromium/cc/scheduler/scheduler_settings.h
+++ b/chromium/cc/scheduler/scheduler_settings.h
@@ -14,8 +14,10 @@ class CC_EXPORT SchedulerSettings {
SchedulerSettings();
~SchedulerSettings();
+ bool deadline_scheduling_enabled;
bool impl_side_painting;
bool timeout_and_draw_when_animation_checkerboards;
+ int maximum_number_of_failed_draws_before_draw_is_forced_;
bool using_synchronous_renderer_compositor;
bool throttle_frame_production;
};
diff --git a/chromium/cc/scheduler/scheduler_state_machine.cc b/chromium/cc/scheduler/scheduler_state_machine.cc
index ed9c15f43f9..955a6b32eee 100644
--- a/chromium/cc/scheduler/scheduler_state_machine.cc
+++ b/chromium/cc/scheduler/scheduler_state_machine.cc
@@ -7,166 +7,394 @@
#include "base/format_macros.h"
#include "base/logging.h"
#include "base/strings/stringprintf.h"
+#include "base/values.h"
namespace cc {
SchedulerStateMachine::SchedulerStateMachine(const SchedulerSettings& settings)
: settings_(settings),
+ output_surface_state_(OUTPUT_SURFACE_LOST),
+ begin_frame_state_(BEGIN_FRAME_STATE_IDLE),
commit_state_(COMMIT_STATE_IDLE),
+ texture_state_(LAYER_TEXTURE_STATE_UNLOCKED),
+ forced_redraw_state_(FORCED_REDRAW_STATE_IDLE),
+ readback_state_(READBACK_STATE_IDLE),
commit_count_(0),
current_frame_number_(0),
- last_frame_number_where_begin_frame_sent_to_main_thread_(-1),
- last_frame_number_where_draw_was_called_(-1),
- last_frame_number_where_tree_activation_attempted_(-1),
- last_frame_number_where_update_visible_tiles_was_called_(-1),
+ last_frame_number_swap_performed_(-1),
+ last_frame_number_begin_frame_sent_to_main_thread_(-1),
+ last_frame_number_update_visible_tiles_was_called_(-1),
consecutive_failed_draws_(0),
- maximum_number_of_failed_draws_before_draw_is_forced_(3),
needs_redraw_(false),
+ needs_manage_tiles_(false),
swap_used_incomplete_tile_(false),
- needs_forced_redraw_(false),
- needs_forced_redraw_after_next_commit_(false),
- needs_redraw_after_next_commit_(false),
needs_commit_(false),
- needs_forced_commit_(false),
- expect_immediate_begin_frame_for_main_thread_(false),
main_thread_needs_layer_textures_(false),
- inside_begin_frame_(false),
+ inside_poll_for_anticipated_draw_triggers_(false),
visible_(false),
can_start_(false),
can_draw_(false),
has_pending_tree_(false),
+ pending_tree_is_ready_for_activation_(false),
+ active_tree_needs_first_draw_(false),
draw_if_possible_failed_(false),
- texture_state_(LAYER_TEXTURE_STATE_UNLOCKED),
- output_surface_state_(OUTPUT_SURFACE_LOST),
- did_create_and_initialize_first_output_surface_(false) {}
-
-std::string SchedulerStateMachine::ToString() {
- std::string str;
- base::StringAppendF(&str,
- "settings_.impl_side_painting = %d; ",
- settings_.impl_side_painting);
- base::StringAppendF(&str, "commit_state_ = %d; ", commit_state_);
- base::StringAppendF(&str, "commit_count_ = %d; ", commit_count_);
- base::StringAppendF(
- &str, "current_frame_number_ = %d; ", current_frame_number_);
- base::StringAppendF(&str,
- "last_frame_number_where_draw_was_called_ = %d; ",
- last_frame_number_where_draw_was_called_);
- base::StringAppendF(
- &str,
- "last_frame_number_where_tree_activation_attempted_ = %d; ",
- last_frame_number_where_tree_activation_attempted_);
- base::StringAppendF(
- &str,
- "last_frame_number_where_update_visible_tiles_was_called_ = %d; ",
- last_frame_number_where_update_visible_tiles_was_called_);
- base::StringAppendF(
- &str, "consecutive_failed_draws_ = %d; ", consecutive_failed_draws_);
- base::StringAppendF(
- &str,
- "maximum_number_of_failed_draws_before_draw_is_forced_ = %d; ",
- maximum_number_of_failed_draws_before_draw_is_forced_);
- base::StringAppendF(&str, "needs_redraw_ = %d; ", needs_redraw_);
- base::StringAppendF(
- &str, "swap_used_incomplete_tile_ = %d; ", swap_used_incomplete_tile_);
- base::StringAppendF(
- &str, "needs_forced_redraw_ = %d; ", needs_forced_redraw_);
- base::StringAppendF(&str,
- "needs_forced_redraw_after_next_commit_ = %d; ",
- needs_forced_redraw_after_next_commit_);
- base::StringAppendF(&str, "needs_commit_ = %d; ", needs_commit_);
- base::StringAppendF(
- &str, "needs_forced_commit_ = %d; ", needs_forced_commit_);
- base::StringAppendF(&str,
- "expect_immediate_begin_frame_for_main_thread_ = %d; ",
- expect_immediate_begin_frame_for_main_thread_);
- base::StringAppendF(&str,
- "main_thread_needs_layer_textures_ = %d; ",
- main_thread_needs_layer_textures_);
- base::StringAppendF(&str, "inside_begin_frame_ = %d; ",
- inside_begin_frame_);
- base::StringAppendF(&str, "last_frame_time_ = %" PRId64 "; ",
- (last_begin_frame_args_.frame_time - base::TimeTicks())
- .InMilliseconds());
- base::StringAppendF(&str, "last_deadline_ = %" PRId64 "; ",
- (last_begin_frame_args_.deadline - base::TimeTicks()).InMilliseconds());
- base::StringAppendF(&str, "last_interval_ = %" PRId64 "; ",
- last_begin_frame_args_.interval.InMilliseconds());
- base::StringAppendF(&str, "visible_ = %d; ", visible_);
- base::StringAppendF(&str, "can_start_ = %d; ", can_start_);
- base::StringAppendF(&str, "can_draw_ = %d; ", can_draw_);
- base::StringAppendF(
- &str, "draw_if_possible_failed_ = %d; ", draw_if_possible_failed_);
- base::StringAppendF(&str, "has_pending_tree_ = %d; ", has_pending_tree_);
- base::StringAppendF(&str, "texture_state_ = %d; ", texture_state_);
- base::StringAppendF(
- &str, "output_surface_state_ = %d; ", output_surface_state_);
- return str;
-}
-
-bool SchedulerStateMachine::HasDrawnThisFrame() const {
- return current_frame_number_ == last_frame_number_where_draw_was_called_;
-}
-
-bool SchedulerStateMachine::HasAttemptedTreeActivationThisFrame() const {
+ did_create_and_initialize_first_output_surface_(false),
+ smoothness_takes_priority_(false) {}
+
+const char* SchedulerStateMachine::OutputSurfaceStateToString(
+ OutputSurfaceState state) {
+ switch (state) {
+ case OUTPUT_SURFACE_ACTIVE:
+ return "OUTPUT_SURFACE_ACTIVE";
+ case OUTPUT_SURFACE_LOST:
+ return "OUTPUT_SURFACE_LOST";
+ case OUTPUT_SURFACE_CREATING:
+ return "OUTPUT_SURFACE_CREATING";
+ case OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT:
+ return "OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT";
+ case OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION:
+ return "OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION";
+ }
+ NOTREACHED();
+ return "???";
+}
+
+const char* SchedulerStateMachine::BeginFrameStateToString(
+ BeginFrameState state) {
+ switch (state) {
+ case BEGIN_FRAME_STATE_IDLE:
+ return "BEGIN_FRAME_STATE_IDLE";
+ case BEGIN_FRAME_STATE_BEGIN_FRAME_STARTING:
+ return "BEGIN_FRAME_STATE_BEGIN_FRAME_STARTING";
+ case BEGIN_FRAME_STATE_INSIDE_BEGIN_FRAME:
+ return "BEGIN_FRAME_STATE_INSIDE_BEGIN_FRAME";
+ case BEGIN_FRAME_STATE_INSIDE_DEADLINE:
+ return "BEGIN_FRAME_STATE_INSIDE_DEADLINE";
+ }
+ NOTREACHED();
+ return "???";
+}
+
+const char* SchedulerStateMachine::CommitStateToString(CommitState state) {
+ switch (state) {
+ case COMMIT_STATE_IDLE:
+ return "COMMIT_STATE_IDLE";
+ case COMMIT_STATE_FRAME_IN_PROGRESS:
+ return "COMMIT_STATE_FRAME_IN_PROGRESS";
+ case COMMIT_STATE_READY_TO_COMMIT:
+ return "COMMIT_STATE_READY_TO_COMMIT";
+ case COMMIT_STATE_WAITING_FOR_FIRST_DRAW:
+ return "COMMIT_STATE_WAITING_FOR_FIRST_DRAW";
+ }
+ NOTREACHED();
+ return "???";
+}
+
+const char* SchedulerStateMachine::TextureStateToString(TextureState state) {
+ switch (state) {
+ case LAYER_TEXTURE_STATE_UNLOCKED:
+ return "LAYER_TEXTURE_STATE_UNLOCKED";
+ case LAYER_TEXTURE_STATE_ACQUIRED_BY_MAIN_THREAD:
+ return "LAYER_TEXTURE_STATE_ACQUIRED_BY_MAIN_THREAD";
+ case LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD:
+ return "LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD";
+ }
+ NOTREACHED();
+ return "???";
+}
+
+const char* SchedulerStateMachine::SynchronousReadbackStateToString(
+ SynchronousReadbackState state) {
+ switch (state) {
+ case READBACK_STATE_IDLE:
+ return "READBACK_STATE_IDLE";
+ case READBACK_STATE_NEEDS_BEGIN_FRAME:
+ return "READBACK_STATE_NEEDS_BEGIN_FRAME";
+ case READBACK_STATE_WAITING_FOR_COMMIT:
+ return "READBACK_STATE_WAITING_FOR_COMMIT";
+ case READBACK_STATE_WAITING_FOR_ACTIVATION:
+ return "READBACK_STATE_WAITING_FOR_ACTIVATION";
+ case READBACK_STATE_WAITING_FOR_DRAW_AND_READBACK:
+ return "READBACK_STATE_WAITING_FOR_DRAW_AND_READBACK";
+ case READBACK_STATE_WAITING_FOR_REPLACEMENT_COMMIT:
+ return "READBACK_STATE_WAITING_FOR_REPLACEMENT_COMMIT";
+ case READBACK_STATE_WAITING_FOR_REPLACEMENT_ACTIVATION:
+ return "READBACK_STATE_WAITING_FOR_REPLACEMENT_ACTIVATION";
+ }
+ NOTREACHED();
+ return "???";
+}
+
+const char* SchedulerStateMachine::ForcedRedrawOnTimeoutStateToString(
+ ForcedRedrawOnTimeoutState state) {
+ switch (state) {
+ case FORCED_REDRAW_STATE_IDLE:
+ return "FORCED_REDRAW_STATE_IDLE";
+ case FORCED_REDRAW_STATE_WAITING_FOR_COMMIT:
+ return "FORCED_REDRAW_STATE_WAITING_FOR_COMMIT";
+ case FORCED_REDRAW_STATE_WAITING_FOR_ACTIVATION:
+ return "FORCED_REDRAW_STATE_WAITING_FOR_ACTIVATION";
+ case FORCED_REDRAW_STATE_WAITING_FOR_DRAW:
+ return "FORCED_REDRAW_STATE_WAITING_FOR_DRAW";
+ }
+ NOTREACHED();
+ return "???";
+}
+
+const char* SchedulerStateMachine::ActionToString(Action action) {
+ switch (action) {
+ case ACTION_NONE:
+ return "ACTION_NONE";
+ case ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD:
+ return "ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD";
+ case ACTION_COMMIT:
+ return "ACTION_COMMIT";
+ case ACTION_UPDATE_VISIBLE_TILES:
+ return "ACTION_UPDATE_VISIBLE_TILES";
+ case ACTION_ACTIVATE_PENDING_TREE:
+ return "ACTION_ACTIVATE_PENDING_TREE";
+ case ACTION_DRAW_AND_SWAP_IF_POSSIBLE:
+ return "ACTION_DRAW_AND_SWAP_IF_POSSIBLE";
+ case ACTION_DRAW_AND_SWAP_FORCED:
+ return "ACTION_DRAW_AND_SWAP_FORCED";
+ case ACTION_DRAW_AND_SWAP_ABORT:
+ return "ACTION_DRAW_AND_SWAP_ABORT";
+ case ACTION_DRAW_AND_READBACK:
+ return "ACTION_DRAW_AND_READBACK";
+ case ACTION_BEGIN_OUTPUT_SURFACE_CREATION:
+ return "ACTION_BEGIN_OUTPUT_SURFACE_CREATION";
+ case ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD:
+ return "ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD";
+ case ACTION_MANAGE_TILES:
+ return "ACTION_MANAGE_TILES";
+ }
+ NOTREACHED();
+ return "???";
+}
+
+scoped_ptr<base::Value> SchedulerStateMachine::AsValue() const {
+ scoped_ptr<base::DictionaryValue> state(new base::DictionaryValue);
+
+ scoped_ptr<base::DictionaryValue> major_state(new base::DictionaryValue);
+ major_state->SetString("next_action", ActionToString(NextAction()));
+ major_state->SetString("begin_frame_state",
+ BeginFrameStateToString(begin_frame_state_));
+ major_state->SetString("commit_state", CommitStateToString(commit_state_));
+ major_state->SetString("texture_state_",
+ TextureStateToString(texture_state_));
+ major_state->SetString("output_surface_state_",
+ OutputSurfaceStateToString(output_surface_state_));
+ major_state->SetString(
+ "forced_redraw_state",
+ ForcedRedrawOnTimeoutStateToString(forced_redraw_state_));
+ major_state->SetString("readback_state",
+ SynchronousReadbackStateToString(readback_state_));
+ state->Set("major_state", major_state.release());
+
+ scoped_ptr<base::DictionaryValue> timestamps_state(new base::DictionaryValue);
+ base::TimeTicks now = base::TimeTicks::Now();
+ timestamps_state->SetDouble(
+ "0_interval", last_begin_frame_args_.interval.InMicroseconds() / 1000.0L);
+ timestamps_state->SetDouble(
+ "1_now_to_deadline",
+ (last_begin_frame_args_.deadline - now).InMicroseconds() / 1000.0L);
+ timestamps_state->SetDouble(
+ "2_frame_time_to_now",
+ (now - last_begin_frame_args_.frame_time).InMicroseconds() / 1000.0L);
+ timestamps_state->SetDouble(
+ "3_frame_time_to_deadline",
+ (last_begin_frame_args_.deadline - last_begin_frame_args_.frame_time)
+ .InMicroseconds() /
+ 1000.0L);
+ timestamps_state->SetDouble(
+ "4_now", (now - base::TimeTicks()).InMicroseconds() / 1000.0L);
+ timestamps_state->SetDouble(
+ "5_frame_time",
+ (last_begin_frame_args_.frame_time - base::TimeTicks()).InMicroseconds() /
+ 1000.0L);
+ timestamps_state->SetDouble(
+ "6_deadline",
+ (last_begin_frame_args_.deadline - base::TimeTicks()).InMicroseconds() /
+ 1000.0L);
+ state->Set("major_timestamps_in_ms", timestamps_state.release());
+
+ scoped_ptr<base::DictionaryValue> minor_state(new base::DictionaryValue);
+ minor_state->SetInteger("commit_count", commit_count_);
+ minor_state->SetInteger("current_frame_number", current_frame_number_);
+
+ minor_state->SetInteger("last_frame_number_swap_performed",
+ last_frame_number_swap_performed_);
+ minor_state->SetInteger(
+ "last_frame_number_begin_frame_sent_to_main_thread",
+ last_frame_number_begin_frame_sent_to_main_thread_);
+ minor_state->SetInteger(
+ "last_frame_number_update_visible_tiles_was_called",
+ last_frame_number_update_visible_tiles_was_called_);
+
+ minor_state->SetInteger("consecutive_failed_draws",
+ consecutive_failed_draws_);
+ minor_state->SetBoolean("needs_redraw", needs_redraw_);
+ minor_state->SetBoolean("needs_manage_tiles", needs_manage_tiles_);
+ minor_state->SetBoolean("swap_used_incomplete_tile",
+ swap_used_incomplete_tile_);
+ minor_state->SetBoolean("needs_commit", needs_commit_);
+ minor_state->SetBoolean("main_thread_needs_layer_textures",
+ main_thread_needs_layer_textures_);
+ minor_state->SetBoolean("visible", visible_);
+ minor_state->SetBoolean("can_start", can_start_);
+ minor_state->SetBoolean("can_draw", can_draw_);
+ minor_state->SetBoolean("has_pending_tree", has_pending_tree_);
+ minor_state->SetBoolean("pending_tree_is_ready_for_activation",
+ pending_tree_is_ready_for_activation_);
+ minor_state->SetBoolean("active_tree_needs_first_draw",
+ active_tree_needs_first_draw_);
+ minor_state->SetBoolean("draw_if_possible_failed", draw_if_possible_failed_);
+ minor_state->SetBoolean("did_create_and_initialize_first_output_surface",
+ did_create_and_initialize_first_output_surface_);
+ minor_state->SetBoolean("smoothness_takes_priority",
+ smoothness_takes_priority_);
+ state->Set("minor_state", minor_state.release());
+
+ return state.PassAs<base::Value>();
+}
+
+bool SchedulerStateMachine::HasSentBeginFrameToMainThreadThisFrame() const {
return current_frame_number_ ==
- last_frame_number_where_tree_activation_attempted_;
+ last_frame_number_begin_frame_sent_to_main_thread_;
}
bool SchedulerStateMachine::HasUpdatedVisibleTilesThisFrame() const {
return current_frame_number_ ==
- last_frame_number_where_update_visible_tiles_was_called_;
+ last_frame_number_update_visible_tiles_was_called_;
}
-void SchedulerStateMachine::SetPostCommitFlags() {
- // This post-commit work is common to both completed and aborted commits.
- if (needs_forced_redraw_after_next_commit_) {
- needs_forced_redraw_after_next_commit_ = false;
- needs_forced_redraw_ = true;
- }
- if (needs_redraw_after_next_commit_) {
- needs_redraw_after_next_commit_ = false;
- needs_redraw_ = true;
- }
- texture_state_ = LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD;
+bool SchedulerStateMachine::HasSwappedThisFrame() const {
+ return current_frame_number_ == last_frame_number_swap_performed_;
}
-bool SchedulerStateMachine::DrawSuspendedUntilCommit() const {
+bool SchedulerStateMachine::PendingDrawsShouldBeAborted() const {
+ // These are all the cases where we normally cannot or do not want to draw
+ // but, if needs_redraw_ is true and we do not draw to make forward progress,
+ // we might deadlock with the main thread.
+ // This should be a superset of PendingActivationsShouldBeForced() since
+ // activation of the pending tree is blocked by drawing of the active tree and
+ // the main thread might be blocked on activation of the most recent commit.
+ if (PendingActivationsShouldBeForced())
+ return true;
+
+ // Additional states where we should abort draws.
+ // Note: We don't force activation in these cases because doing so would
+ // result in checkerboarding on resize, becoming visible, etc.
if (!can_draw_)
return true;
if (!visible_)
return true;
+ return false;
+}
+
+bool SchedulerStateMachine::PendingActivationsShouldBeForced() const {
+ // These are all the cases where, if we do not force activations to make
+ // forward progress, we might deadlock with the main thread.
+
+ // The impl thread cannot lock layer textures unless the pending
+ // tree can be activated to unblock the commit.
if (texture_state_ == LAYER_TEXTURE_STATE_ACQUIRED_BY_MAIN_THREAD)
return true;
+
+ // There is no output surface to trigger our activations.
+ if (output_surface_state_ == OUTPUT_SURFACE_LOST)
+ return true;
+
return false;
}
-bool SchedulerStateMachine::ScheduledToDraw() const {
- if (!needs_redraw_)
+bool SchedulerStateMachine::ShouldBeginOutputSurfaceCreation() const {
+ // Don't try to initialize too early.
+ if (!can_start_)
+ return false;
+
+ // We only want to start output surface initialization after the
+ // previous commit is complete.
+ if (commit_state_ != COMMIT_STATE_IDLE)
return false;
- if (DrawSuspendedUntilCommit())
+
+ // We want to clear the pipline of any pending draws and activations
+ // before starting output surface initialization. This allows us to avoid
+ // weird corner cases where we abort draws or force activation while we
+ // are initializing the output surface and can potentially have a pending
+ // readback.
+ if (active_tree_needs_first_draw_ || has_pending_tree_)
return false;
- return true;
+
+ // We need to create the output surface if we don't have one and we haven't
+ // started creating one yet.
+ return output_surface_state_ == OUTPUT_SURFACE_LOST;
}
bool SchedulerStateMachine::ShouldDraw() const {
- if (needs_forced_redraw_)
+ // After a readback, make sure not to draw again until we've replaced the
+ // readback commit with a real one.
+ if (readback_state_ == READBACK_STATE_WAITING_FOR_REPLACEMENT_COMMIT ||
+ readback_state_ == READBACK_STATE_WAITING_FOR_REPLACEMENT_ACTIVATION)
+ return false;
+
+ // Draw immediately for readbacks to unblock the main thread quickly.
+ if (readback_state_ == READBACK_STATE_WAITING_FOR_DRAW_AND_READBACK) {
+ DCHECK_EQ(commit_state_, COMMIT_STATE_WAITING_FOR_FIRST_DRAW);
return true;
+ }
- if (!ScheduledToDraw())
- return false;
- if (!inside_begin_frame_)
+ // If we need to abort draws, we should do so ASAP since the draw could
+ // be blocking other important actions (like output surface initialization),
+ // from occuring. If we are waiting for the first draw, then perfom the
+ // aborted draw to keep things moving. If we are not waiting for the first
+ // draw however, we don't want to abort for no reason.
+ if (PendingDrawsShouldBeAborted())
+ return active_tree_needs_first_draw_;
+
+ // After this line, we only want to swap once per frame.
+ if (HasSwappedThisFrame())
return false;
- if (HasDrawnThisFrame())
+
+ // Except for the cases above, do not draw outside of the BeginFrame deadline.
+ if (begin_frame_state_ != BEGIN_FRAME_STATE_INSIDE_DEADLINE)
return false;
- if (output_surface_state_ != OUTPUT_SURFACE_ACTIVE)
+
+ // Only handle forced redraws due to timeouts on the regular deadline.
+ if (forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_DRAW) {
+ DCHECK_EQ(commit_state_, COMMIT_STATE_WAITING_FOR_FIRST_DRAW);
+ return true;
+ }
+
+ return needs_redraw_;
+}
+
+bool SchedulerStateMachine::ShouldAcquireLayerTexturesForMainThread() const {
+ if (!main_thread_needs_layer_textures_)
return false;
- return true;
+ if (texture_state_ == LAYER_TEXTURE_STATE_UNLOCKED)
+ return true;
+ DCHECK_EQ(texture_state_, LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD);
+ return false;
}
-bool SchedulerStateMachine::ShouldAttemptTreeActivation() const {
- return has_pending_tree_ && inside_begin_frame_ &&
- !HasAttemptedTreeActivationThisFrame();
+bool SchedulerStateMachine::ShouldActivatePendingTree() const {
+ // There is nothing to activate.
+ if (!has_pending_tree_)
+ return false;
+
+ // We should not activate a second tree before drawing the first one.
+ // Even if we need to force activation of the pending tree, we should abort
+ // drawing the active tree first.
+ if (active_tree_needs_first_draw_)
+ return false;
+
+ // If we want to force activation, do so ASAP.
+ if (PendingActivationsShouldBeForced())
+ return true;
+
+ // At this point, only activate if we are ready to activate.
+ return pending_tree_is_ready_for_activation_;
}
bool SchedulerStateMachine::ShouldUpdateVisibleTiles() const {
@@ -175,240 +403,535 @@ bool SchedulerStateMachine::ShouldUpdateVisibleTiles() const {
if (HasUpdatedVisibleTilesThisFrame())
return false;
- return ShouldAttemptTreeActivation() || ShouldDraw() ||
- swap_used_incomplete_tile_;
-}
+ // There's no reason to check for tiles if we don't have an output surface.
+ if (!HasInitializedOutputSurface())
+ return false;
-bool SchedulerStateMachine::ShouldAcquireLayerTexturesForMainThread() const {
- if (!main_thread_needs_layer_textures_)
+ // We should not check for visible tiles until we've entered the deadline so
+ // we check as late as possible and give the tiles more time to initialize.
+ if (begin_frame_state_ != BEGIN_FRAME_STATE_INSIDE_DEADLINE)
return false;
- if (texture_state_ == LAYER_TEXTURE_STATE_UNLOCKED)
+
+ // If the last swap drew with checkerboard or missing tiles, we should
+ // poll for any new visible tiles so we can be notified to draw again
+ // when there are.
+ if (swap_used_incomplete_tile_)
return true;
- DCHECK_EQ(texture_state_, LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD);
- // Transfer the lock from impl thread to main thread immediately if the
- // impl thread is not even scheduled to draw. Guards against deadlocking.
- if (!ScheduledToDraw())
+
+ return false;
+}
+
+bool SchedulerStateMachine::ShouldSendBeginFrameToMainThread() const {
+ if (!needs_commit_)
+ return false;
+
+ // Only send BeginFrame to the main thread when there isn't another commit
+ // pending already.
+ if (commit_state_ != COMMIT_STATE_IDLE)
+ return false;
+
+ // We can't accept a commit if we have a pending tree.
+ if (has_pending_tree_)
+ return false;
+
+ // We want to handle readback commits immediately to unblock the main thread.
+ // Note: This BeginFrame will correspond to the replacement commit that comes
+ // after the readback commit itself, so we only send the BeginFrame if a
+ // commit isn't already pending behind the readback.
+ if (readback_state_ == READBACK_STATE_NEEDS_BEGIN_FRAME)
+ return !CommitPending();
+
+ // We do not need commits if we are not visible, unless there's a
+ // request for a readback.
+ if (!visible_)
+ return false;
+
+ // We want to start the first commit after we get a new output surface ASAP.
+ if (output_surface_state_ == OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT)
return true;
- if (!BeginFrameNeededToDrawByImplThread())
+
+ // With deadline scheduling enabled, we should not send BeginFrame to the
+ // main thread while we are in BEGIN_FRAME_STATE_IDLE, since we might have
+ // new user input coming in soon.
+ // However, if we are not expecting a BeginFrame on the Impl thread to take
+ // us out of idle, we should not early out here to avoid blocking commits
+ // forever.
+ // This only works well when deadline scheduling is enabled because there is
+ // an interval over which to accept the commit and draw. Without deadline
+ // scheduling, delaying the commit could prevent us from having something
+ // to draw on the next BeginFrame.
+ // TODO(brianderson): Allow sending BeginFrame to main thread while idle
+ // when the main thread isn't consuming user input.
+ if (settings_.deadline_scheduling_enabled &&
+ begin_frame_state_ == BEGIN_FRAME_STATE_IDLE &&
+ BeginFrameNeededByImplThread())
+ return false;
+
+ // We need a new commit for the forced redraw. This honors the
+ // single commit per interval because the result will be swapped to screen.
+ if (forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_COMMIT)
return true;
- return false;
+
+ // After this point, we only start a commit once per frame.
+ if (HasSentBeginFrameToMainThreadThisFrame())
+ return false;
+
+ // We shouldn't normally accept commits if there isn't an OutputSurface.
+ if (!HasInitializedOutputSurface())
+ return false;
+
+ return true;
+}
+
+bool SchedulerStateMachine::ShouldCommit() const {
+ return commit_state_ == COMMIT_STATE_READY_TO_COMMIT;
+}
+
+bool SchedulerStateMachine::ShouldManageTiles() const {
+ // Limiting to once per-frame is not enough, since we only want to
+ // manage tiles _after_ draws. Polling for draw triggers and
+ // begin-frame are mutually exclusive, so we limit to these two cases.
+ if (begin_frame_state_ != BEGIN_FRAME_STATE_INSIDE_DEADLINE &&
+ !inside_poll_for_anticipated_draw_triggers_)
+ return false;
+ return needs_manage_tiles_;
}
SchedulerStateMachine::Action SchedulerStateMachine::NextAction() const {
if (ShouldAcquireLayerTexturesForMainThread())
return ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD;
-
- switch (commit_state_) {
- case COMMIT_STATE_IDLE: {
- if (output_surface_state_ != OUTPUT_SURFACE_ACTIVE &&
- needs_forced_redraw_)
- return ACTION_DRAW_FORCED;
- if (output_surface_state_ != OUTPUT_SURFACE_ACTIVE &&
- needs_forced_commit_)
- // TODO(enne): Should probably drop the active tree on force commit.
- return has_pending_tree_ ? ACTION_NONE
- : ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD;
- if (output_surface_state_ == OUTPUT_SURFACE_LOST && can_start_)
- return ACTION_BEGIN_OUTPUT_SURFACE_CREATION;
- if (output_surface_state_ == OUTPUT_SURFACE_CREATING)
- return ACTION_NONE;
- if (ShouldUpdateVisibleTiles())
- return ACTION_UPDATE_VISIBLE_TILES;
- if (ShouldAttemptTreeActivation())
- return ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED;
- if (ShouldDraw()) {
- return needs_forced_redraw_ ? ACTION_DRAW_FORCED
- : ACTION_DRAW_IF_POSSIBLE;
- }
- bool can_commit_this_frame =
- visible_ &&
- current_frame_number_ >
- last_frame_number_where_begin_frame_sent_to_main_thread_;
- if (needs_commit_ && ((can_commit_this_frame &&
- output_surface_state_ == OUTPUT_SURFACE_ACTIVE) ||
- needs_forced_commit_))
- // TODO(enne): Should probably drop the active tree on force commit.
- return has_pending_tree_ ? ACTION_NONE
- : ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD;
- return ACTION_NONE;
- }
- case COMMIT_STATE_FRAME_IN_PROGRESS:
- if (ShouldUpdateVisibleTiles())
- return ACTION_UPDATE_VISIBLE_TILES;
- if (ShouldAttemptTreeActivation())
- return ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED;
- if (ShouldDraw()) {
- return needs_forced_redraw_ ? ACTION_DRAW_FORCED
- : ACTION_DRAW_IF_POSSIBLE;
- }
- return ACTION_NONE;
-
- case COMMIT_STATE_READY_TO_COMMIT:
- return ACTION_COMMIT;
-
- case COMMIT_STATE_WAITING_FOR_FIRST_DRAW: {
- if (ShouldUpdateVisibleTiles())
- return ACTION_UPDATE_VISIBLE_TILES;
- if (ShouldAttemptTreeActivation())
- return ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED;
- if (ShouldDraw() || output_surface_state_ == OUTPUT_SURFACE_LOST) {
- return needs_forced_redraw_ ? ACTION_DRAW_FORCED
- : ACTION_DRAW_IF_POSSIBLE;
- }
- // COMMIT_STATE_WAITING_FOR_FIRST_DRAW wants to enforce a draw. If
- // can_draw_ is false or textures are not available, proceed to the next
- // step (similar as in COMMIT_STATE_IDLE).
- bool can_commit =
- needs_forced_commit_ ||
- (visible_ &&
- current_frame_number_ >
- last_frame_number_where_begin_frame_sent_to_main_thread_);
- if (needs_commit_ && can_commit && DrawSuspendedUntilCommit())
- return has_pending_tree_ ? ACTION_NONE
- : ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD;
- return ACTION_NONE;
- }
-
- case COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW:
- if (ShouldUpdateVisibleTiles())
- return ACTION_UPDATE_VISIBLE_TILES;
- if (ShouldAttemptTreeActivation())
- return ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED;
- if (needs_forced_redraw_)
- return ACTION_DRAW_FORCED;
- return ACTION_NONE;
+ if (ShouldUpdateVisibleTiles())
+ return ACTION_UPDATE_VISIBLE_TILES;
+ if (ShouldActivatePendingTree())
+ return ACTION_ACTIVATE_PENDING_TREE;
+ if (ShouldCommit())
+ return ACTION_COMMIT;
+ if (ShouldDraw()) {
+ if (readback_state_ == READBACK_STATE_WAITING_FOR_DRAW_AND_READBACK)
+ return ACTION_DRAW_AND_READBACK;
+ else if (PendingDrawsShouldBeAborted())
+ return ACTION_DRAW_AND_SWAP_ABORT;
+ else if (forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_DRAW)
+ return ACTION_DRAW_AND_SWAP_FORCED;
+ else
+ return ACTION_DRAW_AND_SWAP_IF_POSSIBLE;
}
- NOTREACHED();
+ if (ShouldManageTiles())
+ return ACTION_MANAGE_TILES;
+ if (ShouldSendBeginFrameToMainThread())
+ return ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD;
+ if (ShouldBeginOutputSurfaceCreation())
+ return ACTION_BEGIN_OUTPUT_SURFACE_CREATION;
return ACTION_NONE;
}
+void SchedulerStateMachine::CheckInvariants() {
+ // We should never try to perform a draw for readback and forced draw due to
+ // timeout simultaneously.
+ DCHECK(!(forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_DRAW &&
+ readback_state_ == READBACK_STATE_WAITING_FOR_DRAW_AND_READBACK));
+}
+
void SchedulerStateMachine::UpdateState(Action action) {
switch (action) {
case ACTION_NONE:
return;
case ACTION_UPDATE_VISIBLE_TILES:
- last_frame_number_where_update_visible_tiles_was_called_ =
+ last_frame_number_update_visible_tiles_was_called_ =
current_frame_number_;
return;
- case ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED:
- last_frame_number_where_tree_activation_attempted_ =
- current_frame_number_;
+ case ACTION_ACTIVATE_PENDING_TREE:
+ UpdateStateOnActivation();
return;
case ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD:
DCHECK(!has_pending_tree_);
- if (!needs_forced_commit_) {
- DCHECK(visible_);
- DCHECK_GT(current_frame_number_,
- last_frame_number_where_begin_frame_sent_to_main_thread_);
- }
+ DCHECK(visible_ || readback_state_ == READBACK_STATE_NEEDS_BEGIN_FRAME);
commit_state_ = COMMIT_STATE_FRAME_IN_PROGRESS;
needs_commit_ = false;
- needs_forced_commit_ = false;
- last_frame_number_where_begin_frame_sent_to_main_thread_ =
+ if (readback_state_ == READBACK_STATE_NEEDS_BEGIN_FRAME)
+ readback_state_ = READBACK_STATE_WAITING_FOR_COMMIT;
+ last_frame_number_begin_frame_sent_to_main_thread_ =
current_frame_number_;
return;
- case ACTION_COMMIT:
- commit_count_++;
- if (expect_immediate_begin_frame_for_main_thread_)
- commit_state_ = COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW;
- else
- commit_state_ = COMMIT_STATE_WAITING_FOR_FIRST_DRAW;
- // When impl-side painting, we draw on activation instead of on commit.
- if (!settings_.impl_side_painting)
- needs_redraw_ = true;
- if (draw_if_possible_failed_)
- last_frame_number_where_draw_was_called_ = -1;
- SetPostCommitFlags();
+ case ACTION_COMMIT: {
+ bool commit_was_aborted = false;
+ UpdateStateOnCommit(commit_was_aborted);
return;
+ }
- case ACTION_DRAW_FORCED:
- case ACTION_DRAW_IF_POSSIBLE:
- needs_redraw_ = false;
- needs_forced_redraw_ = false;
- draw_if_possible_failed_ = false;
- swap_used_incomplete_tile_ = false;
- if (inside_begin_frame_)
- last_frame_number_where_draw_was_called_ = current_frame_number_;
- if (commit_state_ == COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW) {
- DCHECK(expect_immediate_begin_frame_for_main_thread_);
- commit_state_ = COMMIT_STATE_FRAME_IN_PROGRESS;
- expect_immediate_begin_frame_for_main_thread_ = false;
- } else if (commit_state_ == COMMIT_STATE_WAITING_FOR_FIRST_DRAW) {
- commit_state_ = COMMIT_STATE_IDLE;
- }
- if (texture_state_ == LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD)
- texture_state_ = LAYER_TEXTURE_STATE_UNLOCKED;
+ case ACTION_DRAW_AND_SWAP_FORCED:
+ case ACTION_DRAW_AND_SWAP_IF_POSSIBLE: {
+ bool did_swap = true;
+ UpdateStateOnDraw(did_swap);
+ return;
+ }
+
+ case ACTION_DRAW_AND_SWAP_ABORT:
+ case ACTION_DRAW_AND_READBACK: {
+ bool did_swap = false;
+ UpdateStateOnDraw(did_swap);
return;
+ }
case ACTION_BEGIN_OUTPUT_SURFACE_CREATION:
- DCHECK_EQ(commit_state_, COMMIT_STATE_IDLE);
DCHECK_EQ(output_surface_state_, OUTPUT_SURFACE_LOST);
output_surface_state_ = OUTPUT_SURFACE_CREATING;
+
+ // The following DCHECKs make sure we are in the proper quiescent state.
+ // The pipeline should be flushed entirely before we start output
+ // surface creation to avoid complicated corner cases.
+ DCHECK_EQ(commit_state_, COMMIT_STATE_IDLE);
+ DCHECK(!has_pending_tree_);
+ DCHECK(!active_tree_needs_first_draw_);
return;
case ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD:
texture_state_ = LAYER_TEXTURE_STATE_ACQUIRED_BY_MAIN_THREAD;
main_thread_needs_layer_textures_ = false;
return;
+
+ case ACTION_MANAGE_TILES:
+ UpdateStateOnManageTiles();
+ return;
}
}
+void SchedulerStateMachine::UpdateStateOnCommit(bool commit_was_aborted) {
+ commit_count_++;
+
+ // If we are impl-side-painting but the commit was aborted, then we behave
+ // mostly as if we are not impl-side-painting since there is no pending tree.
+ has_pending_tree_ = settings_.impl_side_painting && !commit_was_aborted;
+
+ // Update state related to readbacks.
+ if (readback_state_ == READBACK_STATE_WAITING_FOR_COMMIT) {
+ // Update the state if this is the readback commit.
+ readback_state_ = has_pending_tree_
+ ? READBACK_STATE_WAITING_FOR_ACTIVATION
+ : READBACK_STATE_WAITING_FOR_DRAW_AND_READBACK;
+ } else if (readback_state_ == READBACK_STATE_WAITING_FOR_REPLACEMENT_COMMIT) {
+ // Update the state if this is the commit replacing the readback commit.
+ readback_state_ = has_pending_tree_
+ ? READBACK_STATE_WAITING_FOR_REPLACEMENT_ACTIVATION
+ : READBACK_STATE_IDLE;
+ } else {
+ DCHECK(readback_state_ == READBACK_STATE_IDLE);
+ }
+
+ // Readbacks can interrupt output surface initialization and forced draws,
+ // so we do not want to advance those states if we are in the middle of a
+ // readback. Note: It is possible for the readback's replacement commit to
+ // be the output surface's first commit and/or the forced redraw's commit.
+ if (readback_state_ == READBACK_STATE_IDLE ||
+ readback_state_ == READBACK_STATE_WAITING_FOR_REPLACEMENT_ACTIVATION) {
+ // Update state related to forced draws.
+ if (forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_COMMIT) {
+ forced_redraw_state_ = has_pending_tree_
+ ? FORCED_REDRAW_STATE_WAITING_FOR_ACTIVATION
+ : FORCED_REDRAW_STATE_WAITING_FOR_DRAW;
+ }
+
+ // Update the output surface state.
+ DCHECK_NE(output_surface_state_,
+ OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION);
+ if (output_surface_state_ == OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT) {
+ if (has_pending_tree_) {
+ output_surface_state_ = OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION;
+ } else {
+ output_surface_state_ = OUTPUT_SURFACE_ACTIVE;
+ needs_redraw_ = true;
+ }
+ }
+ }
+
+ // Update the commit state. We expect and wait for a draw if the commit
+ // was not aborted or if we are in a readback or forced draw.
+ if (!commit_was_aborted)
+ commit_state_ = COMMIT_STATE_WAITING_FOR_FIRST_DRAW;
+ else if (readback_state_ != READBACK_STATE_IDLE ||
+ forced_redraw_state_ != FORCED_REDRAW_STATE_IDLE)
+ commit_state_ = COMMIT_STATE_WAITING_FOR_FIRST_DRAW;
+ else
+ commit_state_ = COMMIT_STATE_IDLE;
+
+ // Update state if we have a new active tree to draw, or if the active tree
+ // was unchanged but we need to do a readback or forced draw.
+ if (!has_pending_tree_ &&
+ (!commit_was_aborted ||
+ readback_state_ == READBACK_STATE_WAITING_FOR_DRAW_AND_READBACK ||
+ forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_DRAW)) {
+ needs_redraw_ = true;
+ active_tree_needs_first_draw_ = true;
+ }
+
+ // This post-commit work is common to both completed and aborted commits.
+ pending_tree_is_ready_for_activation_ = false;
+
+ if (draw_if_possible_failed_)
+ last_frame_number_swap_performed_ = -1;
+
+ // If we are planing to draw with the new commit, lock the layer textures for
+ // use on the impl thread. Otherwise, leave them unlocked.
+ if (has_pending_tree_ || needs_redraw_)
+ texture_state_ = LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD;
+ else
+ texture_state_ = LAYER_TEXTURE_STATE_UNLOCKED;
+}
+
+void SchedulerStateMachine::UpdateStateOnActivation() {
+ // Update output surface state.
+ if (output_surface_state_ == OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION)
+ output_surface_state_ = OUTPUT_SURFACE_ACTIVE;
+
+ // Update readback state
+ if (forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_ACTIVATION)
+ forced_redraw_state_ = FORCED_REDRAW_STATE_WAITING_FOR_DRAW;
+
+ // Update forced redraw state
+ if (readback_state_ == READBACK_STATE_WAITING_FOR_ACTIVATION)
+ readback_state_ = READBACK_STATE_WAITING_FOR_DRAW_AND_READBACK;
+ else if (readback_state_ == READBACK_STATE_WAITING_FOR_REPLACEMENT_ACTIVATION)
+ readback_state_ = READBACK_STATE_IDLE;
+
+ has_pending_tree_ = false;
+ pending_tree_is_ready_for_activation_ = false;
+ active_tree_needs_first_draw_ = true;
+ needs_redraw_ = true;
+}
+
+void SchedulerStateMachine::UpdateStateOnDraw(bool did_swap) {
+ DCHECK(readback_state_ != READBACK_STATE_WAITING_FOR_REPLACEMENT_COMMIT &&
+ readback_state_ != READBACK_STATE_WAITING_FOR_REPLACEMENT_ACTIVATION)
+ << *AsValue();
+
+ if (readback_state_ == READBACK_STATE_WAITING_FOR_DRAW_AND_READBACK) {
+ // The draw correspons to a readback commit.
+ DCHECK_EQ(commit_state_, COMMIT_STATE_WAITING_FOR_FIRST_DRAW);
+ // We are blocking commits from the main thread until after this draw, so
+ // we should not have a pending tree.
+ DCHECK(!has_pending_tree_);
+ // We transition to COMMIT_STATE_FRAME_IN_PROGRESS because there is a
+ // pending BeginFrame on the main thread behind the readback request.
+ commit_state_ = COMMIT_STATE_FRAME_IN_PROGRESS;
+ readback_state_ = READBACK_STATE_WAITING_FOR_REPLACEMENT_COMMIT;
+ } else if (forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_DRAW) {
+ DCHECK_EQ(commit_state_, COMMIT_STATE_WAITING_FOR_FIRST_DRAW);
+ commit_state_ = COMMIT_STATE_IDLE;
+ forced_redraw_state_ = FORCED_REDRAW_STATE_IDLE;
+ } else if (commit_state_ == COMMIT_STATE_WAITING_FOR_FIRST_DRAW &&
+ !has_pending_tree_) {
+ commit_state_ = COMMIT_STATE_IDLE;
+ }
+
+ if (texture_state_ == LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD)
+ texture_state_ = LAYER_TEXTURE_STATE_UNLOCKED;
+
+ needs_redraw_ = false;
+ draw_if_possible_failed_ = false;
+ active_tree_needs_first_draw_ = false;
+
+ if (did_swap)
+ last_frame_number_swap_performed_ = current_frame_number_;
+}
+
+void SchedulerStateMachine::UpdateStateOnManageTiles() {
+ needs_manage_tiles_ = false;
+}
+
void SchedulerStateMachine::SetMainThreadNeedsLayerTextures() {
DCHECK(!main_thread_needs_layer_textures_);
DCHECK_NE(texture_state_, LAYER_TEXTURE_STATE_ACQUIRED_BY_MAIN_THREAD);
main_thread_needs_layer_textures_ = true;
}
+bool SchedulerStateMachine::BeginFrameNeededByImplThread() const {
+ // Proactive BeginFrames are bad for the synchronous compositor because we
+ // have to draw when we get the BeginFrame and could end up drawing many
+ // duplicate frames if our new frame isn't ready in time.
+ // To poll for state with the synchronous compositor without having to draw,
+ // we rely on ShouldPollForAnticipatedDrawTriggers instead.
+ if (settings_.using_synchronous_renderer_compositor)
+ return BeginFrameNeededToDrawByImplThread();
+
+ return BeginFrameNeededToDrawByImplThread() ||
+ ProactiveBeginFrameWantedByImplThread();
+}
+
+bool SchedulerStateMachine::ShouldPollForAnticipatedDrawTriggers() const {
+ // ShouldPollForAnticipatedDrawTriggers is what we use in place of
+ // ProactiveBeginFrameWantedByImplThread when we are using the synchronous
+ // compositor.
+ if (settings_.using_synchronous_renderer_compositor) {
+ return !BeginFrameNeededToDrawByImplThread() &&
+ ProactiveBeginFrameWantedByImplThread();
+ }
+
+ // Non synchronous compositors should rely on
+ // ProactiveBeginFrameWantedByImplThread to poll for state instead.
+ return false;
+}
+
+// These are the cases where we definitely (or almost definitely) have a
+// new frame to draw and can draw.
bool SchedulerStateMachine::BeginFrameNeededToDrawByImplThread() const {
+ // The output surface is the provider of BeginFrames for the impl thread,
+ // so we are not going to get them even if we ask for them.
+ if (!HasInitializedOutputSurface())
+ return false;
+
// If we can't draw, don't tick until we are notified that we can draw again.
if (!can_draw_)
return false;
- if (needs_forced_redraw_)
+ // The forced draw respects our normal draw scheduling, so we need to
+ // request a BeginFrame on the impl thread for it.
+ if (forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_DRAW)
return true;
- if (visible_ && swap_used_incomplete_tile_)
+ // There's no need to produce frames if we are not visible.
+ if (!visible_)
+ return false;
+
+ // We need to draw a more complete frame than we did the last BeginFrame,
+ // so request another BeginFrame in anticipation that we will have
+ // additional visible tiles.
+ if (swap_used_incomplete_tile_)
return true;
- return needs_redraw_ && visible_ &&
- output_surface_state_ == OUTPUT_SURFACE_ACTIVE;
+ return needs_redraw_;
}
+// These are cases where we are very likely to draw soon, but might not
+// actually have a new frame to draw when we receive the next BeginFrame.
+// Proactively requesting the BeginFrame helps hide the round trip latency of
+// the SetNeedsBeginFrame request that has to go to the Browser.
bool SchedulerStateMachine::ProactiveBeginFrameWantedByImplThread() const {
+ // The output surface is the provider of BeginFrames for the impl thread,
+ // so we are not going to get them even if we ask for them.
+ if (!HasInitializedOutputSurface())
+ return false;
+
+ // Do not be proactive if vsync is off.
+ if (!settings_.throttle_frame_production)
+ return false;
+
// Do not be proactive when invisible.
- if (!visible_ || output_surface_state_ != OUTPUT_SURFACE_ACTIVE)
+ if (!visible_)
return false;
- // We should proactively request a BeginFrame if a commit or a tree activation
- // is pending.
- return (needs_commit_ || needs_forced_commit_ ||
- commit_state_ != COMMIT_STATE_IDLE || has_pending_tree_);
+ // We should proactively request a BeginFrame if a commit is pending
+ // because we will want to draw if the commit completes quickly.
+ if (needs_commit_ || commit_state_ != COMMIT_STATE_IDLE)
+ return true;
+
+ // If the pending tree activates quickly, we'll want a BeginFrame soon
+ // to draw the new active tree.
+ if (has_pending_tree_)
+ return true;
+
+ // Changing priorities may allow us to activate (given the new priorities),
+ // which may result in a new frame.
+ if (needs_manage_tiles_)
+ return true;
+
+ // If we just swapped, it's likely that we are going to produce another
+ // frame soon. This helps avoid negative glitches in our SetNeedsBeginFrame
+ // requests, which may propagate to the BeginFrame provider and get sampled
+ // at an inopportune time, delaying the next BeginFrame.
+ if (last_frame_number_swap_performed_ == current_frame_number_)
+ return true;
+
+ return false;
}
-void SchedulerStateMachine::DidEnterBeginFrame(const BeginFrameArgs& args) {
+void SchedulerStateMachine::OnBeginFrame(const BeginFrameArgs& args) {
current_frame_number_++;
- inside_begin_frame_ = true;
last_begin_frame_args_ = args;
+ DCHECK_EQ(begin_frame_state_, BEGIN_FRAME_STATE_IDLE) << *AsValue();
+ begin_frame_state_ = BEGIN_FRAME_STATE_BEGIN_FRAME_STARTING;
+}
+
+void SchedulerStateMachine::OnBeginFrameDeadlinePending() {
+ DCHECK_EQ(begin_frame_state_, BEGIN_FRAME_STATE_BEGIN_FRAME_STARTING)
+ << *AsValue();
+ begin_frame_state_ = BEGIN_FRAME_STATE_INSIDE_BEGIN_FRAME;
+}
+
+void SchedulerStateMachine::OnBeginFrameDeadline() {
+ DCHECK_EQ(begin_frame_state_, BEGIN_FRAME_STATE_INSIDE_BEGIN_FRAME)
+ << *AsValue();
+ begin_frame_state_ = BEGIN_FRAME_STATE_INSIDE_DEADLINE;
}
-void SchedulerStateMachine::DidLeaveBeginFrame() {
- inside_begin_frame_ = false;
+void SchedulerStateMachine::OnBeginFrameIdle() {
+ DCHECK_EQ(begin_frame_state_, BEGIN_FRAME_STATE_INSIDE_DEADLINE)
+ << *AsValue();
+ begin_frame_state_ = BEGIN_FRAME_STATE_IDLE;
+}
+
+bool SchedulerStateMachine::ShouldTriggerBeginFrameDeadlineEarly() const {
+ // TODO(brianderson): This should take into account multiple commit sources.
+
+ // If we are in the middle of the readback, we won't swap, so there is
+ // no reason to trigger the deadline early.
+ if (readback_state_ != READBACK_STATE_IDLE)
+ return false;
+
+ if (begin_frame_state_ != BEGIN_FRAME_STATE_INSIDE_BEGIN_FRAME)
+ return false;
+
+ if (active_tree_needs_first_draw_)
+ return true;
+
+ if (!needs_redraw_)
+ return false;
+
+ // This is used to prioritize impl-thread draws when the main thread isn't
+ // producing anything, e.g., after an aborted commit. We also check that we
+ // don't have a pending tree -- otherwise we should give it a chance to
+ // activate.
+ // TODO(skyostil): Revisit this when we have more accurate deadline estimates.
+ if (commit_state_ == COMMIT_STATE_IDLE && !has_pending_tree_)
+ return true;
+
+ // Prioritize impl-thread draws in smoothness mode.
+ if (smoothness_takes_priority_)
+ return true;
+
+ return false;
+}
+
+void SchedulerStateMachine::DidEnterPollForAnticipatedDrawTriggers() {
+ current_frame_number_++;
+ inside_poll_for_anticipated_draw_triggers_ = true;
+}
+
+void SchedulerStateMachine::DidLeavePollForAnticipatedDrawTriggers() {
+ inside_poll_for_anticipated_draw_triggers_ = false;
}
void SchedulerStateMachine::SetVisible(bool visible) { visible_ = visible; }
+void SchedulerStateMachine::SetCanDraw(bool can_draw) { can_draw_ = can_draw; }
+
void SchedulerStateMachine::SetNeedsRedraw() { needs_redraw_ = true; }
-void SchedulerStateMachine::DidSwapUseIncompleteTile() {
- swap_used_incomplete_tile_ = true;
+void SchedulerStateMachine::SetNeedsManageTiles() {
+ needs_manage_tiles_ = true;
+}
+
+void SchedulerStateMachine::SetSwapUsedIncompleteTile(
+ bool used_incomplete_tile) {
+ swap_used_incomplete_tile_ = used_incomplete_tile;
}
-void SchedulerStateMachine::SetNeedsForcedRedraw() {
- needs_forced_redraw_ = true;
+void SchedulerStateMachine::SetSmoothnessTakesPriority(
+ bool smoothness_takes_priority) {
+ smoothness_takes_priority_ = smoothness_takes_priority;
}
void SchedulerStateMachine::DidDrawIfPossibleCompleted(bool success) {
@@ -419,11 +942,11 @@ void SchedulerStateMachine::DidDrawIfPossibleCompleted(bool success) {
consecutive_failed_draws_++;
if (settings_.timeout_and_draw_when_animation_checkerboards &&
consecutive_failed_draws_ >=
- maximum_number_of_failed_draws_before_draw_is_forced_) {
+ settings_.maximum_number_of_failed_draws_before_draw_is_forced_) {
consecutive_failed_draws_ = 0;
// We need to force a draw, but it doesn't make sense to do this until
// we've committed and have new textures.
- needs_forced_redraw_after_next_commit_ = true;
+ forced_redraw_state_ = FORCED_REDRAW_STATE_WAITING_FOR_COMMIT;
}
} else {
consecutive_failed_draws_ = 0;
@@ -432,27 +955,39 @@ void SchedulerStateMachine::DidDrawIfPossibleCompleted(bool success) {
void SchedulerStateMachine::SetNeedsCommit() { needs_commit_ = true; }
-void SchedulerStateMachine::SetNeedsForcedCommit() {
- needs_forced_commit_ = true;
- expect_immediate_begin_frame_for_main_thread_ = true;
+void SchedulerStateMachine::SetNeedsForcedCommitForReadback() {
+ // If this is called in READBACK_STATE_IDLE, this is a "first" readback
+ // request.
+ // If this is called in READBACK_STATE_WAITING_FOR_REPLACEMENT_COMMIT, this
+ // is a back-to-back readback request that started before the replacement
+ // commit had a chance to land.
+ DCHECK(readback_state_ == READBACK_STATE_IDLE ||
+ readback_state_ == READBACK_STATE_WAITING_FOR_REPLACEMENT_COMMIT);
+
+ // If there is already a commit in progress when we get the readback request
+ // (we are in COMMIT_STATE_FRAME_IN_PROGRESS), then we don't need to send a
+ // BeginFrame for the replacement commit, since there's already a BeginFrame
+ // behind the readback request. In that case, we can skip
+ // READBACK_STATE_NEEDS_BEGIN_FRAME and go directly to
+ // READBACK_STATE_WAITING_FOR_COMMIT
+ if (commit_state_ == COMMIT_STATE_FRAME_IN_PROGRESS)
+ readback_state_ = READBACK_STATE_WAITING_FOR_COMMIT;
+ else
+ readback_state_ = READBACK_STATE_NEEDS_BEGIN_FRAME;
}
void SchedulerStateMachine::FinishCommit() {
- DCHECK(commit_state_ == COMMIT_STATE_FRAME_IN_PROGRESS ||
- (expect_immediate_begin_frame_for_main_thread_ &&
- commit_state_ != COMMIT_STATE_IDLE))
- << ToString();
+ DCHECK(commit_state_ == COMMIT_STATE_FRAME_IN_PROGRESS) << *AsValue();
commit_state_ = COMMIT_STATE_READY_TO_COMMIT;
}
void SchedulerStateMachine::BeginFrameAbortedByMainThread(bool did_handle) {
DCHECK_EQ(commit_state_, COMMIT_STATE_FRAME_IN_PROGRESS);
- if (expect_immediate_begin_frame_for_main_thread_) {
- expect_immediate_begin_frame_for_main_thread_ = false;
- } else if (did_handle) {
- commit_state_ = COMMIT_STATE_IDLE;
- SetPostCommitFlags();
+ if (did_handle) {
+ bool commit_was_aborted = true;
+ UpdateStateOnCommit(commit_was_aborted);
} else {
+ DCHECK_NE(readback_state_, READBACK_STATE_WAITING_FOR_COMMIT);
commit_state_ = COMMIT_STATE_IDLE;
SetNeedsCommit();
}
@@ -463,38 +998,40 @@ void SchedulerStateMachine::DidLoseOutputSurface() {
output_surface_state_ == OUTPUT_SURFACE_CREATING)
return;
output_surface_state_ = OUTPUT_SURFACE_LOST;
+ needs_redraw_ = false;
+ begin_frame_state_ = BEGIN_FRAME_STATE_IDLE;
}
-void SchedulerStateMachine::SetHasPendingTree(bool has_pending_tree) {
- has_pending_tree_ = has_pending_tree;
+void SchedulerStateMachine::NotifyReadyToActivate() {
+ if (has_pending_tree_)
+ pending_tree_is_ready_for_activation_ = true;
}
-void SchedulerStateMachine::SetCanDraw(bool can) { can_draw_ = can; }
-
void SchedulerStateMachine::DidCreateAndInitializeOutputSurface() {
DCHECK_EQ(output_surface_state_, OUTPUT_SURFACE_CREATING);
- output_surface_state_ = OUTPUT_SURFACE_ACTIVE;
+ output_surface_state_ = OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT;
if (did_create_and_initialize_first_output_surface_) {
// TODO(boliu): See if we can remove this when impl-side painting is always
// on. Does anything on the main thread need to update after recreate?
needs_commit_ = true;
- // If anything has requested a redraw, we don't want to actually draw
- // when the output surface is restored until things have a chance to
- // sort themselves out with a commit.
- needs_redraw_ = false;
}
- needs_redraw_after_next_commit_ = true;
did_create_and_initialize_first_output_surface_ = true;
}
bool SchedulerStateMachine::HasInitializedOutputSurface() const {
- return output_surface_state_ == OUTPUT_SURFACE_ACTIVE;
-}
+ switch (output_surface_state_) {
+ case OUTPUT_SURFACE_LOST:
+ case OUTPUT_SURFACE_CREATING:
+ return false;
-void SchedulerStateMachine::SetMaximumNumberOfFailedDrawsBeforeDrawIsForced(
- int num_draws) {
- maximum_number_of_failed_draws_before_draw_is_forced_ = num_draws;
+ case OUTPUT_SURFACE_ACTIVE:
+ case OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT:
+ case OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION:
+ return true;
+ }
+ NOTREACHED();
+ return false;
}
} // namespace cc
diff --git a/chromium/cc/scheduler/scheduler_state_machine.h b/chromium/cc/scheduler/scheduler_state_machine.h
index 5c7d5b5b8a7..a3d67ef765e 100644
--- a/chromium/cc/scheduler/scheduler_state_machine.h
+++ b/chromium/cc/scheduler/scheduler_state_machine.h
@@ -8,11 +8,16 @@
#include <string>
#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
#include "base/time/time.h"
#include "cc/base/cc_export.h"
#include "cc/output/begin_frame_args.h"
#include "cc/scheduler/scheduler_settings.h"
+namespace base {
+class Value;
+}
+
namespace cc {
// The SchedulerStateMachine decides how to coordinate main thread activites
@@ -31,25 +36,62 @@ class CC_EXPORT SchedulerStateMachine {
// settings must be valid for the lifetime of this class.
explicit SchedulerStateMachine(const SchedulerSettings& settings);
+ enum OutputSurfaceState {
+ OUTPUT_SURFACE_ACTIVE,
+ OUTPUT_SURFACE_LOST,
+ OUTPUT_SURFACE_CREATING,
+ OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT,
+ OUTPUT_SURFACE_WAITING_FOR_FIRST_ACTIVATION,
+ };
+ static const char* OutputSurfaceStateToString(OutputSurfaceState state);
+
+ // Note: BeginFrameState will always cycle through all the states in order.
+ // Whether or not it actually waits or draws, it will at least try to wait in
+ // BEGIN_FRAME_STATE_INSIDE_BEGIN_FRAME and try to draw in
+ // BEGIN_FRAME_STATE_INSIDE_DEADLINE
+ enum BeginFrameState {
+ BEGIN_FRAME_STATE_IDLE,
+ BEGIN_FRAME_STATE_BEGIN_FRAME_STARTING,
+ BEGIN_FRAME_STATE_INSIDE_BEGIN_FRAME,
+ BEGIN_FRAME_STATE_INSIDE_DEADLINE,
+ };
+ static const char* BeginFrameStateToString(BeginFrameState state);
+
enum CommitState {
COMMIT_STATE_IDLE,
COMMIT_STATE_FRAME_IN_PROGRESS,
COMMIT_STATE_READY_TO_COMMIT,
COMMIT_STATE_WAITING_FOR_FIRST_DRAW,
- COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW,
};
+ static const char* CommitStateToString(CommitState state);
enum TextureState {
LAYER_TEXTURE_STATE_UNLOCKED,
LAYER_TEXTURE_STATE_ACQUIRED_BY_MAIN_THREAD,
LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD,
};
-
- enum OutputSurfaceState {
- OUTPUT_SURFACE_ACTIVE,
- OUTPUT_SURFACE_LOST,
- OUTPUT_SURFACE_CREATING,
+ static const char* TextureStateToString(TextureState state);
+
+ enum SynchronousReadbackState {
+ READBACK_STATE_IDLE,
+ READBACK_STATE_NEEDS_BEGIN_FRAME,
+ READBACK_STATE_WAITING_FOR_COMMIT,
+ READBACK_STATE_WAITING_FOR_ACTIVATION,
+ READBACK_STATE_WAITING_FOR_DRAW_AND_READBACK,
+ READBACK_STATE_WAITING_FOR_REPLACEMENT_COMMIT,
+ READBACK_STATE_WAITING_FOR_REPLACEMENT_ACTIVATION,
+ };
+ static const char* SynchronousReadbackStateToString(
+ SynchronousReadbackState state);
+
+ enum ForcedRedrawOnTimeoutState {
+ FORCED_REDRAW_STATE_IDLE,
+ FORCED_REDRAW_STATE_WAITING_FOR_COMMIT,
+ FORCED_REDRAW_STATE_WAITING_FOR_ACTIVATION,
+ FORCED_REDRAW_STATE_WAITING_FOR_DRAW,
};
+ static const char* ForcedRedrawOnTimeoutStateToString(
+ ForcedRedrawOnTimeoutState state);
bool CommitPending() const {
return commit_state_ == COMMIT_STATE_FRAME_IN_PROGRESS ||
@@ -57,32 +99,61 @@ class CC_EXPORT SchedulerStateMachine {
}
bool RedrawPending() const { return needs_redraw_; }
+ bool ManageTilesPending() const { return needs_manage_tiles_; }
enum Action {
ACTION_NONE,
ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD,
ACTION_COMMIT,
ACTION_UPDATE_VISIBLE_TILES,
- ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED,
- ACTION_DRAW_IF_POSSIBLE,
- ACTION_DRAW_FORCED,
+ ACTION_ACTIVATE_PENDING_TREE,
+ ACTION_DRAW_AND_SWAP_IF_POSSIBLE,
+ ACTION_DRAW_AND_SWAP_FORCED,
+ ACTION_DRAW_AND_SWAP_ABORT,
+ ACTION_DRAW_AND_READBACK,
ACTION_BEGIN_OUTPUT_SURFACE_CREATION,
ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD,
+ ACTION_MANAGE_TILES,
};
+ static const char* ActionToString(Action action);
+
+ scoped_ptr<base::Value> AsValue() const;
+
Action NextAction() const;
void UpdateState(Action action);
+ void CheckInvariants();
+
// Indicates whether the main thread needs a begin frame callback in order to
// make progress.
- bool BeginFrameNeededToDrawByImplThread() const;
- bool ProactiveBeginFrameWantedByImplThread() const;
+ bool BeginFrameNeededByImplThread() const;
+
+ // Idicates that we need to independently poll for new state and actions
+ // because we can't expect a BeginFrame. This is mostly used to avoid
+ // drawing repeat frames with the synchronous compositor without dropping
+ // necessary actions on the floor.
+ bool ShouldPollForAnticipatedDrawTriggers() const;
// Indicates that the system has entered and left a BeginFrame callback.
// The scheduler will not draw more than once in a given BeginFrame
// callback nor send more than one BeginFrame message.
- void DidEnterBeginFrame(const BeginFrameArgs& args);
- void DidLeaveBeginFrame();
- bool inside_begin_frame() const { return inside_begin_frame_; }
+ void OnBeginFrame(const BeginFrameArgs& args);
+ void OnBeginFrameDeadlinePending();
+ void OnBeginFrameDeadline();
+ void OnBeginFrameIdle();
+ bool ShouldTriggerBeginFrameDeadlineEarly() const;
+ BeginFrameState begin_frame_state() const {
+ return begin_frame_state_;
+ }
+
+ // PollForAnticipatedDrawTriggers is used by the synchronous compositor to
+ // avoid requesting BeginImplFrames when we won't actually draw but still
+ // need to advance our state at vsync intervals.
+ void DidEnterPollForAnticipatedDrawTriggers();
+ void DidLeavePollForAnticipatedDrawTriggers();
+ bool inside_poll_for_anticipated_draw_triggers() const {
+ return inside_poll_for_anticipated_draw_triggers_;
+ }
// Indicates whether the LayerTreeHostImpl is visible.
void SetVisible(bool visible);
@@ -90,16 +161,21 @@ class CC_EXPORT SchedulerStateMachine {
// Indicates that a redraw is required, either due to the impl tree changing
// or the screen being damaged and simply needing redisplay.
void SetNeedsRedraw();
+ bool needs_redraw() const { return needs_redraw_; }
- // As SetNeedsRedraw(), but ensures the draw will definitely happen even if
- // we are not visible.
- void SetNeedsForcedRedraw();
+ // Indicates that manage-tiles is required. This guarantees another
+ // ManageTiles will occur shortly (even if no redraw is required).
+ void SetNeedsManageTiles();
- // Indicates that a redraw is required because we are currently rendering
+ // Indicates whether a redraw is required because we are currently rendering
// with a low resolution or checkerboarded tile.
- void DidSwapUseIncompleteTile();
+ void SetSwapUsedIncompleteTile(bool used_incomplete_tile);
+
+ // Indicates whether to prioritize animation smoothness over new content
+ // activation.
+ void SetSmoothnessTakesPriority(bool smoothness_takes_priority);
- // Indicates whether ACTION_DRAW_IF_POSSIBLE drew to the screen or not.
+ // Indicates whether ACTION_DRAW_AND_SWAP_IF_POSSIBLE drew to the screen.
void DidDrawIfPossibleCompleted(bool success);
// Indicates that a new commit flow needs to be performed, either to pull
@@ -111,7 +187,7 @@ class CC_EXPORT SchedulerStateMachine {
// thread even if we are not visible. After this call we expect to go through
// the forced commit flow and then return to waiting for a non-forced
// begin frame to finish.
- void SetNeedsForcedCommit();
+ void SetNeedsForcedCommitForReadback();
// Call this only in response to receiving an
// ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD from NextAction.
@@ -126,7 +202,8 @@ class CC_EXPORT SchedulerStateMachine {
// Request exclusive access to the textures that back single buffered
// layers on behalf of the main thread. Upon acquisition,
- // ACTION_DRAW_IF_POSSIBLE will not draw until the main thread releases the
+ // ACTION_DRAW_AND_SWAP_IF_POSSIBLE will not draw until the main thread
+ // releases the
// textures to the impl thread by committing the layers.
void SetMainThreadNeedsLayerTextures();
@@ -138,70 +215,78 @@ class CC_EXPORT SchedulerStateMachine {
// when such behavior would be undesirable.
void SetCanDraw(bool can);
- // Indicates whether or not there is a pending tree. This influences
- // whether or not we can succesfully commit at this time. If the
- // last commit is still being processed (but not blocking), it may not
- // be possible to take another commit yet. This overrides force commit,
- // as a commit is already still in flight.
- void SetHasPendingTree(bool has_pending_tree);
+ // Indicates that the pending tree is ready for activation.
+ void NotifyReadyToActivate();
+
bool has_pending_tree() const { return has_pending_tree_; }
void DidLoseOutputSurface();
void DidCreateAndInitializeOutputSurface();
bool HasInitializedOutputSurface() const;
- // Exposed for testing purposes.
- void SetMaximumNumberOfFailedDrawsBeforeDrawIsForced(int num_draws);
+ // True if we need to abort draws to make forward progress.
+ bool PendingDrawsShouldBeAborted() const;
- // False if drawing is not being prevented, true if drawing won't happen
- // for some reason, such as not being visible.
- bool DrawSuspendedUntilCommit() const;
+ protected:
+ bool BeginFrameNeededToDrawByImplThread() const;
+ bool ProactiveBeginFrameWantedByImplThread() const;
- std::string ToString();
+ // True if we need to force activations to make forward progress.
+ bool PendingActivationsShouldBeForced() const;
- protected:
+ bool ShouldBeginOutputSurfaceCreation() const;
bool ShouldDrawForced() const;
- bool ScheduledToDraw() const;
bool ShouldDraw() const;
- bool ShouldAttemptTreeActivation() const;
+ bool ShouldActivatePendingTree() const;
bool ShouldAcquireLayerTexturesForMainThread() const;
bool ShouldUpdateVisibleTiles() const;
- bool HasDrawnThisFrame() const;
- bool HasAttemptedTreeActivationThisFrame() const;
+ bool ShouldSendBeginFrameToMainThread() const;
+ bool ShouldCommit() const;
+ bool ShouldManageTiles() const;
+
+ bool HasSentBeginFrameToMainThreadThisFrame() const;
+ bool HasScheduledManageTilesThisFrame() const;
bool HasUpdatedVisibleTilesThisFrame() const;
- void SetPostCommitFlags();
+ bool HasSwappedThisFrame() const;
+
+ void UpdateStateOnCommit(bool commit_was_aborted);
+ void UpdateStateOnActivation();
+ void UpdateStateOnDraw(bool did_swap);
+ void UpdateStateOnManageTiles();
const SchedulerSettings settings_;
+ OutputSurfaceState output_surface_state_;
+ BeginFrameState begin_frame_state_;
CommitState commit_state_;
- int commit_count_;
+ TextureState texture_state_;
+ ForcedRedrawOnTimeoutState forced_redraw_state_;
+ SynchronousReadbackState readback_state_;
+
+ BeginFrameArgs last_begin_frame_args_;
+ int commit_count_;
int current_frame_number_;
- int last_frame_number_where_begin_frame_sent_to_main_thread_;
- int last_frame_number_where_draw_was_called_;
- int last_frame_number_where_tree_activation_attempted_;
- int last_frame_number_where_update_visible_tiles_was_called_;
+ int last_frame_number_swap_performed_;
+ int last_frame_number_begin_frame_sent_to_main_thread_;
+ int last_frame_number_update_visible_tiles_was_called_;
+
int consecutive_failed_draws_;
- int maximum_number_of_failed_draws_before_draw_is_forced_;
bool needs_redraw_;
+ bool needs_manage_tiles_;
bool swap_used_incomplete_tile_;
- bool needs_forced_redraw_;
- bool needs_forced_redraw_after_next_commit_;
- bool needs_redraw_after_next_commit_;
bool needs_commit_;
- bool needs_forced_commit_;
- bool expect_immediate_begin_frame_for_main_thread_;
bool main_thread_needs_layer_textures_;
- bool inside_begin_frame_;
- BeginFrameArgs last_begin_frame_args_;
+ bool inside_poll_for_anticipated_draw_triggers_;
bool visible_;
bool can_start_;
bool can_draw_;
bool has_pending_tree_;
+ bool pending_tree_is_ready_for_activation_;
+ bool active_tree_needs_first_draw_;
bool draw_if_possible_failed_;
- TextureState texture_state_;
- OutputSurfaceState output_surface_state_;
bool did_create_and_initialize_first_output_surface_;
+ bool smoothness_takes_priority_;
private:
DISALLOW_COPY_AND_ASSIGN(SchedulerStateMachine);
diff --git a/chromium/cc/scheduler/scheduler_state_machine_unittest.cc b/chromium/cc/scheduler/scheduler_state_machine_unittest.cc
index c7210eb2a05..af3231b0db3 100644
--- a/chromium/cc/scheduler/scheduler_state_machine_unittest.cc
+++ b/chromium/cc/scheduler/scheduler_state_machine_unittest.cc
@@ -7,35 +7,98 @@
#include "cc/scheduler/scheduler.h"
#include "testing/gtest/include/gtest/gtest.h"
+#define EXPECT_ACTION_UPDATE_STATE(action) \
+ EXPECT_EQ(action, state.NextAction()) << *state.AsValue(); \
+ if (action == SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE || \
+ action == SchedulerStateMachine::ACTION_DRAW_AND_SWAP_FORCED) { \
+ if (SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW == \
+ state.CommitState() && \
+ SchedulerStateMachine::OUTPUT_SURFACE_ACTIVE != \
+ state.output_surface_state()) \
+ return; \
+ EXPECT_EQ(SchedulerStateMachine::BEGIN_FRAME_STATE_INSIDE_DEADLINE, \
+ state.begin_frame_state()) \
+ << *state.AsValue(); \
+ } \
+ state.UpdateState(action); \
+ if (action == SchedulerStateMachine::ACTION_NONE) { \
+ if (state.begin_frame_state() == \
+ SchedulerStateMachine::BEGIN_FRAME_STATE_BEGIN_FRAME_STARTING) \
+ state.OnBeginFrameDeadlinePending(); \
+ if (state.begin_frame_state() == \
+ SchedulerStateMachine::BEGIN_FRAME_STATE_INSIDE_DEADLINE) \
+ state.OnBeginFrameIdle(); \
+ }
+
namespace cc {
namespace {
+const SchedulerStateMachine::BeginFrameState all_begin_frame_states[] = {
+ SchedulerStateMachine::BEGIN_FRAME_STATE_IDLE,
+ SchedulerStateMachine::BEGIN_FRAME_STATE_BEGIN_FRAME_STARTING,
+ SchedulerStateMachine::BEGIN_FRAME_STATE_INSIDE_BEGIN_FRAME,
+ SchedulerStateMachine::BEGIN_FRAME_STATE_INSIDE_DEADLINE, };
+
const SchedulerStateMachine::CommitState all_commit_states[] = {
- SchedulerStateMachine::COMMIT_STATE_IDLE,
- SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS,
- SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT,
- SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW
-};
+ SchedulerStateMachine::COMMIT_STATE_IDLE,
+ SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS,
+ SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT,
+ SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW, };
// Exposes the protected state fields of the SchedulerStateMachine for testing
class StateMachine : public SchedulerStateMachine {
public:
explicit StateMachine(const SchedulerSettings& scheduler_settings)
: SchedulerStateMachine(scheduler_settings) {}
+
+ void CreateAndInitializeOutputSurfaceWithActivatedCommit() {
+ DidCreateAndInitializeOutputSurface();
+ output_surface_state_ = OUTPUT_SURFACE_ACTIVE;
+ }
+
void SetCommitState(CommitState cs) { commit_state_ = cs; }
CommitState CommitState() const { return commit_state_; }
+ void SetBeginFrameState(BeginFrameState bfs) { begin_frame_state_ = bfs; }
+
+ BeginFrameState begin_frame_state() const { return begin_frame_state_; }
+
+ OutputSurfaceState output_surface_state() const {
+ return output_surface_state_;
+ }
+
bool NeedsCommit() const { return needs_commit_; }
void SetNeedsRedraw(bool b) { needs_redraw_ = b; }
- bool NeedsRedraw() const { return needs_redraw_; }
- void SetNeedsForcedRedraw(bool b) { needs_forced_redraw_ = b; }
- bool NeedsForcedRedraw() const { return needs_forced_redraw_; }
+ void SetNeedsForcedRedrawForTimeout(bool b) {
+ forced_redraw_state_ = FORCED_REDRAW_STATE_WAITING_FOR_COMMIT;
+ commit_state_ = COMMIT_STATE_WAITING_FOR_FIRST_DRAW;
+ }
+ bool NeedsForcedRedrawForTimeout() const {
+ return forced_redraw_state_ != FORCED_REDRAW_STATE_IDLE;
+ }
+
+ void SetNeedsForcedRedrawForReadback() {
+ readback_state_ = READBACK_STATE_WAITING_FOR_DRAW_AND_READBACK;
+ commit_state_ = COMMIT_STATE_WAITING_FOR_FIRST_DRAW;
+ }
+
+ bool NeedsForcedRedrawForReadback() const {
+ return readback_state_ != READBACK_STATE_IDLE;
+ }
+
+ void SetActiveTreeNeedsFirstDraw(bool needs_first_draw) {
+ active_tree_needs_first_draw_ = needs_first_draw;
+ }
bool CanDraw() const { return can_draw_; }
bool Visible() const { return visible_; }
+
+ bool PendingActivationsShouldBeForced() const {
+ return SchedulerStateMachine::PendingActivationsShouldBeForced();
+ }
};
TEST(SchedulerStateMachineTest, TestNextActionBeginsMainFrameIfNeeded) {
@@ -45,19 +108,21 @@ TEST(SchedulerStateMachineTest, TestNextActionBeginsMainFrameIfNeeded) {
{
StateMachine state(default_scheduler_settings);
state.SetCanStart();
- state.UpdateState(state.NextAction());
- state.DidCreateAndInitializeOutputSurface();
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION)
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetCommitState(SchedulerStateMachine::COMMIT_STATE_IDLE);
state.SetNeedsRedraw(false);
state.SetVisible(true);
- EXPECT_FALSE(state.BeginFrameNeededToDrawByImplThread());
+ EXPECT_FALSE(state.BeginFrameNeededByImplThread());
- state.DidLeaveBeginFrame();
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
- EXPECT_FALSE(state.BeginFrameNeededToDrawByImplThread());
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ EXPECT_FALSE(state.BeginFrameNeededByImplThread());
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ state.OnBeginFrameDeadline();
}
// If commit requested but can_start is still false, do nothing.
@@ -67,13 +132,13 @@ TEST(SchedulerStateMachineTest, TestNextActionBeginsMainFrameIfNeeded) {
state.SetNeedsRedraw(false);
state.SetVisible(true);
- EXPECT_FALSE(state.BeginFrameNeededToDrawByImplThread());
+ EXPECT_FALSE(state.BeginFrameNeededByImplThread());
- state.DidLeaveBeginFrame();
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
- EXPECT_FALSE(state.BeginFrameNeededToDrawByImplThread());
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ EXPECT_FALSE(state.BeginFrameNeededByImplThread());
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ state.OnBeginFrameDeadline();
}
// If commit requested, begin a main frame.
@@ -83,7 +148,7 @@ TEST(SchedulerStateMachineTest, TestNextActionBeginsMainFrameIfNeeded) {
state.SetCanStart();
state.SetNeedsRedraw(false);
state.SetVisible(true);
- EXPECT_FALSE(state.BeginFrameNeededToDrawByImplThread());
+ EXPECT_FALSE(state.BeginFrameNeededByImplThread());
}
// Begin the frame, make sure needs_commit and commit_state update correctly.
@@ -91,52 +156,44 @@ TEST(SchedulerStateMachineTest, TestNextActionBeginsMainFrameIfNeeded) {
StateMachine state(default_scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
- state.DidCreateAndInitializeOutputSurface();
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetVisible(true);
state.UpdateState(
SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS,
state.CommitState());
EXPECT_FALSE(state.NeedsCommit());
- EXPECT_FALSE(state.BeginFrameNeededToDrawByImplThread());
}
}
-TEST(SchedulerStateMachineTest, TestSetForcedRedrawDoesNotSetsNormalRedraw) {
- SchedulerSettings default_scheduler_settings;
- SchedulerStateMachine state(default_scheduler_settings);
- state.SetCanDraw(true);
- state.SetNeedsForcedRedraw();
- EXPECT_FALSE(state.RedrawPending());
- EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread());
-}
-
TEST(SchedulerStateMachineTest,
TestFailedDrawSetsNeedsCommitAndDoesNotDrawAgain) {
SchedulerSettings default_scheduler_settings;
- SchedulerStateMachine state(default_scheduler_settings);
+ StateMachine state(default_scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
- state.DidCreateAndInitializeOutputSurface();
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetVisible(true);
state.SetCanDraw(true);
- state.SetNeedsRedraw();
+ state.SetNeedsRedraw(true);
EXPECT_TRUE(state.RedrawPending());
- EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread());
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_TRUE(state.BeginFrameNeededByImplThread());
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ state.OnBeginFrameDeadline();
// We're drawing now.
- EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction());
- state.UpdateState(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE);
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+
EXPECT_FALSE(state.RedrawPending());
EXPECT_FALSE(state.CommitPending());
// Failing the draw makes us require a commit.
state.DidDrawIfPossibleCompleted(false);
- EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD,
- state.NextAction());
- state.UpdateState(
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
EXPECT_TRUE(state.RedrawPending());
EXPECT_TRUE(state.CommitPending());
@@ -145,316 +202,279 @@ TEST(SchedulerStateMachineTest,
TEST(SchedulerStateMachineTest,
TestsetNeedsRedrawDuringFailedDrawDoesNotRemoveNeedsRedraw) {
SchedulerSettings default_scheduler_settings;
- SchedulerStateMachine state(default_scheduler_settings);
+ StateMachine state(default_scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
- state.DidCreateAndInitializeOutputSurface();
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetVisible(true);
state.SetCanDraw(true);
- state.SetNeedsRedraw();
+ state.SetNeedsRedraw(true);
EXPECT_TRUE(state.RedrawPending());
- EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread());
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_TRUE(state.BeginFrameNeededByImplThread());
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ state.OnBeginFrameDeadline();
// We're drawing now.
- EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction());
- state.UpdateState(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE);
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
EXPECT_FALSE(state.RedrawPending());
EXPECT_FALSE(state.CommitPending());
// While still in the same begin frame callback on the main thread,
// set needs redraw again. This should not redraw.
- state.SetNeedsRedraw();
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ state.SetNeedsRedraw(true);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Failing the draw makes us require a commit.
state.DidDrawIfPossibleCompleted(false);
- EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD,
- state.NextAction());
- EXPECT_TRUE(state.RedrawPending());
-}
-
-TEST(SchedulerStateMachineTest,
- TestCommitAfterFailedDrawAllowsDrawInSameFrame) {
- SchedulerSettings default_scheduler_settings;
- SchedulerStateMachine state(default_scheduler_settings);
- state.SetCanStart();
- state.UpdateState(state.NextAction());
- state.DidCreateAndInitializeOutputSurface();
- state.SetVisible(true);
- state.SetCanDraw(true);
-
- // Start a commit.
- state.SetNeedsCommit();
- EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD,
- state.NextAction());
- state.UpdateState(
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
- EXPECT_TRUE(state.CommitPending());
-
- // Then initiate a draw.
- state.SetNeedsRedraw();
- EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread());
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction());
- EXPECT_TRUE(state.RedrawPending());
-
- // Fail the draw.
- state.UpdateState(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE);
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
- state.DidDrawIfPossibleCompleted(false);
- EXPECT_TRUE(state.RedrawPending());
- // But the commit is ongoing.
- EXPECT_TRUE(state.CommitPending());
-
- // Finish the commit.
- state.FinishCommit();
- EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction());
- state.UpdateState(SchedulerStateMachine::ACTION_COMMIT);
EXPECT_TRUE(state.RedrawPending());
-
- // And we should be allowed to draw again.
- EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction());
}
-TEST(SchedulerStateMachineTest,
- TestCommitAfterFailedAndSuccessfulDrawDoesNotAllowDrawInSameFrame) {
- SchedulerSettings default_scheduler_settings;
- SchedulerStateMachine state(default_scheduler_settings);
+void TestFailedDrawsWillEventuallyForceADrawAfterTheNextCommit(
+ bool deadline_scheduling_enabled) {
+ SchedulerSettings scheduler_settings;
+ scheduler_settings.maximum_number_of_failed_draws_before_draw_is_forced_ = 1;
+ scheduler_settings.deadline_scheduling_enabled = deadline_scheduling_enabled;
+ StateMachine state(scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
- state.DidCreateAndInitializeOutputSurface();
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetVisible(true);
state.SetCanDraw(true);
// Start a commit.
state.SetNeedsCommit();
- EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD,
- state.NextAction());
- state.UpdateState(
- SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
+ if (!deadline_scheduling_enabled) {
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
+ }
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ if (deadline_scheduling_enabled) {
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
+ }
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
EXPECT_TRUE(state.CommitPending());
// Then initiate a draw.
- state.SetNeedsRedraw();
- EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread());
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction());
- EXPECT_TRUE(state.RedrawPending());
+ state.SetNeedsRedraw(true);
+ state.OnBeginFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
// Fail the draw.
- state.UpdateState(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE);
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
state.DidDrawIfPossibleCompleted(false);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ EXPECT_TRUE(state.BeginFrameNeededByImplThread());
EXPECT_TRUE(state.RedrawPending());
// But the commit is ongoing.
EXPECT_TRUE(state.CommitPending());
- // Force a draw.
- state.SetNeedsForcedRedraw();
- EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_FORCED, state.NextAction());
-
- // Do the forced draw.
- state.UpdateState(SchedulerStateMachine::ACTION_DRAW_FORCED);
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
- EXPECT_FALSE(state.RedrawPending());
- // And the commit is still ongoing.
- EXPECT_TRUE(state.CommitPending());
-
- // Finish the commit.
+ // Finish the commit. Note, we should not yet be forcing a draw, but should
+ // continue the commit as usual.
state.FinishCommit();
- EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction());
- state.UpdateState(SchedulerStateMachine::ACTION_COMMIT);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
EXPECT_TRUE(state.RedrawPending());
- // And we should not be allowed to draw again in the same frame..
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ // The redraw should be forced at the end of the next BeginFrame.
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ state.OnBeginFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_DRAW_AND_SWAP_FORCED);
}
TEST(SchedulerStateMachineTest,
TestFailedDrawsWillEventuallyForceADrawAfterTheNextCommit) {
- SchedulerSettings default_scheduler_settings;
- SchedulerStateMachine state(default_scheduler_settings);
- state.SetCanStart();
- state.UpdateState(state.NextAction());
- state.DidCreateAndInitializeOutputSurface();
- state.SetVisible(true);
- state.SetCanDraw(true);
- state.SetMaximumNumberOfFailedDrawsBeforeDrawIsForced(1);
-
- // Start a commit.
- state.SetNeedsCommit();
- EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD,
- state.NextAction());
- state.UpdateState(
- SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
- EXPECT_TRUE(state.CommitPending());
-
- // Then initiate a draw.
- state.SetNeedsRedraw();
- EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread());
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction());
- EXPECT_TRUE(state.RedrawPending());
-
- // Fail the draw.
- state.UpdateState(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE);
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
- state.DidDrawIfPossibleCompleted(false);
- EXPECT_TRUE(state.RedrawPending());
- // But the commit is ongoing.
- EXPECT_TRUE(state.CommitPending());
-
- // Finish the commit. Note, we should not yet be forcing a draw, but should
- // continue the commit as usual.
- state.FinishCommit();
- EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction());
- state.UpdateState(SchedulerStateMachine::ACTION_COMMIT);
- EXPECT_TRUE(state.RedrawPending());
+ bool deadline_scheduling_enabled = false;
+ TestFailedDrawsWillEventuallyForceADrawAfterTheNextCommit(
+ deadline_scheduling_enabled);
+}
- // The redraw should be forced in this case.
- EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_FORCED, state.NextAction());
+TEST(SchedulerStateMachineTest,
+ TestFailedDrawsWillEventuallyForceADrawAfterTheNextCommit_Deadline) {
+ bool deadline_scheduling_enabled = true;
+ TestFailedDrawsWillEventuallyForceADrawAfterTheNextCommit(
+ deadline_scheduling_enabled);
}
TEST(SchedulerStateMachineTest,
TestFailedDrawIsRetriedInNextBeginFrameForImplThread) {
SchedulerSettings default_scheduler_settings;
- SchedulerStateMachine state(default_scheduler_settings);
+ StateMachine state(default_scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
- state.DidCreateAndInitializeOutputSurface();
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetVisible(true);
state.SetCanDraw(true);
// Start a draw.
- state.SetNeedsRedraw();
- EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread());
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction());
+ state.SetNeedsRedraw(true);
+ EXPECT_TRUE(state.BeginFrameNeededByImplThread());
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ state.OnBeginFrameDeadline();
EXPECT_TRUE(state.RedrawPending());
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
- // Fail the draw.
- state.UpdateState(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE);
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ // Fail the draw
state.DidDrawIfPossibleCompleted(false);
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
EXPECT_TRUE(state.RedrawPending());
// We should not be trying to draw again now, but we have a commit pending.
- EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD,
- state.NextAction());
-
- state.DidLeaveBeginFrame();
- EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread());
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
-
- // We should try to draw again in the next begin frame on the impl thread.
- EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction());
+ EXPECT_TRUE(state.BeginFrameNeededByImplThread());
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+
+ // We should try to draw again at the end of the next BeginFrame on
+ // the impl thread.
+ state.OnBeginFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
}
TEST(SchedulerStateMachineTest, TestDoestDrawTwiceInSameFrame) {
SchedulerSettings default_scheduler_settings;
- SchedulerStateMachine state(default_scheduler_settings);
+ StateMachine state(default_scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
- state.DidCreateAndInitializeOutputSurface();
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetVisible(true);
state.SetCanDraw(true);
- state.SetNeedsRedraw();
- EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread());
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction());
- state.UpdateState(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE);
-
- // While still in the same begin frame for the impl thread, set needs redraw
- // again. This should not redraw.
- state.SetNeedsRedraw();
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ state.SetNeedsRedraw(true);
- // Move to another frame. This should now draw.
+ // Draw the first frame.
+ EXPECT_TRUE(state.BeginFrameNeededByImplThread());
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+
+ state.OnBeginFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
state.DidDrawIfPossibleCompleted(true);
- state.DidLeaveBeginFrame();
- EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread());
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
- EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction());
- state.UpdateState(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE);
+ // Before the next begin frame for the impl thread, set needs redraw
+ // again. This should not redraw until the next begin frame.
+ state.SetNeedsRedraw(true);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+
+ // Move to another frame. This should now draw.
+ EXPECT_TRUE(state.BeginFrameNeededByImplThread());
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+
+ state.OnBeginFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
state.DidDrawIfPossibleCompleted(true);
- EXPECT_FALSE(state.BeginFrameNeededToDrawByImplThread());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+
+ // We just swapped, so we should proactively request another BeginFrame.
+ EXPECT_TRUE(state.BeginFrameNeededByImplThread());
}
TEST(SchedulerStateMachineTest, TestNextActionDrawsOnBeginFrame) {
SchedulerSettings default_scheduler_settings;
- // When not in BeginFrame, or in BeginFrame but not visible,
+ // When not in BeginFrame deadline, or in BeginFrame deadline but not visible,
// don't draw.
size_t num_commit_states =
sizeof(all_commit_states) / sizeof(SchedulerStateMachine::CommitState);
+ size_t num_begin_frame_states =
+ sizeof(all_begin_frame_states) /
+ sizeof(SchedulerStateMachine::BeginFrameState);
for (size_t i = 0; i < num_commit_states; ++i) {
- for (size_t j = 0; j < 2; ++j) {
+ for (size_t j = 0; j < num_begin_frame_states; ++j) {
StateMachine state(default_scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
- state.DidCreateAndInitializeOutputSurface();
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetCommitState(all_commit_states[i]);
- bool visible = j;
- if (!visible) {
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
- state.SetVisible(false);
- } else {
- state.SetVisible(true);
- }
+ state.SetBeginFrameState(all_begin_frame_states[j]);
+ bool visible = (all_begin_frame_states[j] !=
+ SchedulerStateMachine::BEGIN_FRAME_STATE_INSIDE_DEADLINE);
+ state.SetVisible(visible);
// Case 1: needs_commit=false
- EXPECT_NE(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE,
+ EXPECT_NE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE,
state.NextAction());
// Case 2: needs_commit=true
state.SetNeedsCommit();
- EXPECT_NE(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE,
- state.NextAction());
+ EXPECT_NE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE,
+ state.NextAction())
+ << *state.AsValue();
}
}
- // When in BeginFrame, or not in BeginFrame but needs_forced_dedraw
- // set, should always draw except if you're ready to commit, in which case
- // commit.
+ // When in BeginFrame deadline we should always draw for SetNeedsRedraw or
+ // SetNeedsForcedRedrawForReadback have been called... except if we're
+ // ready to commit, in which case we expect a commit first.
for (size_t i = 0; i < num_commit_states; ++i) {
for (size_t j = 0; j < 2; ++j) {
+ bool request_readback = j;
+
+ // Skip invalid states
+ if (request_readback &&
+ (SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW !=
+ all_commit_states[i]))
+ continue;
+
StateMachine state(default_scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
- state.DidCreateAndInitializeOutputSurface();
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetCanDraw(true);
state.SetCommitState(all_commit_states[i]);
- bool forced_draw = j;
- if (!forced_draw) {
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
+ state.SetBeginFrameState(
+ SchedulerStateMachine::BEGIN_FRAME_STATE_INSIDE_DEADLINE);
+ if (request_readback) {
+ state.SetNeedsForcedRedrawForReadback();
+ } else {
state.SetNeedsRedraw(true);
state.SetVisible(true);
- } else {
- state.SetNeedsForcedRedraw(true);
}
SchedulerStateMachine::Action expected_action;
- if (all_commit_states[i] !=
+ if (all_commit_states[i] ==
SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT) {
- expected_action =
- forced_draw ? SchedulerStateMachine::ACTION_DRAW_FORCED
- : SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE;
- } else {
expected_action = SchedulerStateMachine::ACTION_COMMIT;
+ } else if (request_readback) {
+ if (all_commit_states[i] ==
+ SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW)
+ expected_action = SchedulerStateMachine::ACTION_DRAW_AND_READBACK;
+ else
+ expected_action = SchedulerStateMachine::ACTION_NONE;
+ } else {
+ expected_action =
+ SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE;
}
// Case 1: needs_commit=false.
- EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread());
- EXPECT_EQ(expected_action, state.NextAction());
+ EXPECT_NE(state.BeginFrameNeededByImplThread(), request_readback)
+ << *state.AsValue();
+ EXPECT_EQ(expected_action, state.NextAction()) << *state.AsValue();
// Case 2: needs_commit=true.
state.SetNeedsCommit();
- EXPECT_TRUE(state.BeginFrameNeededToDrawByImplThread());
- EXPECT_EQ(expected_action, state.NextAction());
+ EXPECT_NE(state.BeginFrameNeededByImplThread(), request_readback)
+ << *state.AsValue();
+ EXPECT_EQ(expected_action, state.NextAction()) << *state.AsValue();
}
}
}
@@ -470,22 +490,24 @@ TEST(SchedulerStateMachineTest, TestNoCommitStatesRedrawWhenInvisible) {
StateMachine state(default_scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
- state.DidCreateAndInitializeOutputSurface();
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetCommitState(all_commit_states[i]);
state.SetVisible(false);
state.SetNeedsRedraw(true);
- state.SetNeedsForcedRedraw(false);
- if (j == 1)
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
+ if (j == 1) {
+ state.SetBeginFrameState(
+ SchedulerStateMachine::BEGIN_FRAME_STATE_INSIDE_DEADLINE);
+ }
// Case 1: needs_commit=false.
- EXPECT_NE(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE,
+ EXPECT_NE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE,
state.NextAction());
// Case 2: needs_commit=true.
state.SetNeedsCommit();
- EXPECT_NE(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE,
- state.NextAction());
+ EXPECT_NE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE,
+ state.NextAction())
+ << *state.AsValue();
}
}
}
@@ -501,16 +523,15 @@ TEST(SchedulerStateMachineTest, TestCanRedraw_StopsDraw) {
StateMachine state(default_scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
- state.DidCreateAndInitializeOutputSurface();
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetCommitState(all_commit_states[i]);
state.SetVisible(false);
state.SetNeedsRedraw(true);
- state.SetNeedsForcedRedraw(false);
if (j == 1)
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
state.SetCanDraw(false);
- EXPECT_NE(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE,
+ EXPECT_NE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE,
state.NextAction());
}
}
@@ -522,15 +543,25 @@ TEST(SchedulerStateMachineTest,
StateMachine state(default_scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
- state.DidCreateAndInitializeOutputSurface();
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
+
state.SetCommitState(
SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW);
+ state.SetActiveTreeNeedsFirstDraw(true);
state.SetNeedsCommit();
state.SetNeedsRedraw(true);
state.SetVisible(true);
state.SetCanDraw(false);
- EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD,
- state.NextAction());
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT);
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ state.FinishCommit();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
+ state.OnBeginFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
}
TEST(SchedulerStateMachineTest, TestsetNeedsCommitIsNotLost) {
@@ -538,15 +569,17 @@ TEST(SchedulerStateMachineTest, TestsetNeedsCommitIsNotLost) {
StateMachine state(default_scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
- state.DidCreateAndInitializeOutputSurface();
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetNeedsCommit();
state.SetVisible(true);
state.SetCanDraw(true);
+ EXPECT_TRUE(state.BeginFrameNeededByImplThread());
+
// Begin the frame.
- EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD,
- state.NextAction());
- state.UpdateState(state.NextAction());
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS,
state.CommitState());
@@ -560,23 +593,44 @@ TEST(SchedulerStateMachineTest, TestsetNeedsCommitIsNotLost) {
state.CommitState());
// Expect to commit regardless of BeginFrame state.
- state.DidLeaveBeginFrame();
+ EXPECT_EQ(SchedulerStateMachine::BEGIN_FRAME_STATE_BEGIN_FRAME_STARTING,
+ state.begin_frame_state());
EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction());
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
+
+ state.OnBeginFrameDeadlinePending();
+ EXPECT_EQ(SchedulerStateMachine::BEGIN_FRAME_STATE_INSIDE_BEGIN_FRAME,
+ state.begin_frame_state());
+ EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction());
+
+ state.OnBeginFrameDeadline();
+ EXPECT_EQ(SchedulerStateMachine::BEGIN_FRAME_STATE_INSIDE_DEADLINE,
+ state.begin_frame_state());
+ EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction());
+
+ state.OnBeginFrameIdle();
+ EXPECT_EQ(SchedulerStateMachine::BEGIN_FRAME_STATE_IDLE,
+ state.begin_frame_state());
+ EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction());
+
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_EQ(SchedulerStateMachine::BEGIN_FRAME_STATE_BEGIN_FRAME_STARTING,
+ state.begin_frame_state());
EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction());
// Commit and make sure we draw on next BeginFrame
- state.UpdateState(SchedulerStateMachine::ACTION_COMMIT);
- EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ state.OnBeginFrameDeadline();
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW,
state.CommitState());
- state.UpdateState(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE);
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
state.DidDrawIfPossibleCompleted(true);
- // Verify that another commit will begin.
- state.DidLeaveBeginFrame();
- EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD,
- state.NextAction());
+ // Verify that another commit will start immediately after draw.
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
}
TEST(SchedulerStateMachineTest, TestFullCycle) {
@@ -584,49 +638,46 @@ TEST(SchedulerStateMachineTest, TestFullCycle) {
StateMachine state(default_scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
- state.DidCreateAndInitializeOutputSurface();
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetVisible(true);
state.SetCanDraw(true);
// Start clean and set commit.
state.SetNeedsCommit();
- EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD,
- state.NextAction());
// Begin the frame.
- state.UpdateState(
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS,
state.CommitState());
EXPECT_FALSE(state.NeedsCommit());
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Tell the scheduler the frame finished.
state.FinishCommit();
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT,
state.CommitState());
- EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction());
// Commit.
- state.UpdateState(SchedulerStateMachine::ACTION_COMMIT);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW,
state.CommitState());
- EXPECT_TRUE(state.NeedsRedraw());
+ EXPECT_TRUE(state.needs_redraw());
- // Expect to do nothing until BeginFrame.
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ // Expect to do nothing until BeginFrame deadline
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
- // At BeginFrame, draw.
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction());
- state.UpdateState(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE);
+ // At BeginFrame deadline, draw.
+ state.OnBeginFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
state.DidDrawIfPossibleCompleted(true);
- state.DidLeaveBeginFrame();
// Should be synchronized, no draw needed, no action needed.
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_IDLE, state.CommitState());
- EXPECT_FALSE(state.NeedsRedraw());
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ EXPECT_FALSE(state.needs_redraw());
}
TEST(SchedulerStateMachineTest, TestFullCycleWithCommitRequestInbetween) {
@@ -634,54 +685,55 @@ TEST(SchedulerStateMachineTest, TestFullCycleWithCommitRequestInbetween) {
StateMachine state(default_scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
- state.DidCreateAndInitializeOutputSurface();
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetVisible(true);
state.SetCanDraw(true);
// Start clean and set commit.
state.SetNeedsCommit();
- EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD,
- state.NextAction());
// Begin the frame.
- state.UpdateState(
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS,
state.CommitState());
EXPECT_FALSE(state.NeedsCommit());
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Request another commit while the commit is in flight.
state.SetNeedsCommit();
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Tell the scheduler the frame finished.
state.FinishCommit();
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT,
state.CommitState());
- EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction());
- // Commit.
- state.UpdateState(SchedulerStateMachine::ACTION_COMMIT);
+ // First commit.
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW,
state.CommitState());
- EXPECT_TRUE(state.NeedsRedraw());
+ EXPECT_TRUE(state.needs_redraw());
- // Expect to do nothing until BeginFrame.
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ // Expect to do nothing until BeginFrame deadline.
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
- // At BeginFrame, draw.
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction());
- state.UpdateState(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE);
+ // At BeginFrame deadline, draw.
+ state.OnBeginFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
state.DidDrawIfPossibleCompleted(true);
- state.DidLeaveBeginFrame();
// Should be synchronized, no draw needed, no action needed.
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_IDLE, state.CommitState());
- EXPECT_FALSE(state.NeedsRedraw());
- EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD,
- state.NextAction());
+ EXPECT_FALSE(state.needs_redraw());
+
+ // Next BeginFrame should initiate second commit.
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
}
TEST(SchedulerStateMachineTest, TestRequestCommitInvisible) {
@@ -689,9 +741,9 @@ TEST(SchedulerStateMachineTest, TestRequestCommitInvisible) {
StateMachine state(default_scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
- state.DidCreateAndInitializeOutputSurface();
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetNeedsCommit();
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
}
TEST(SchedulerStateMachineTest, TestGoesInvisibleBeforeFinishCommit) {
@@ -699,31 +751,33 @@ TEST(SchedulerStateMachineTest, TestGoesInvisibleBeforeFinishCommit) {
StateMachine state(default_scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
- state.DidCreateAndInitializeOutputSurface();
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetVisible(true);
state.SetCanDraw(true);
// Start clean and set commit.
state.SetNeedsCommit();
- EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD,
- state.NextAction());
// Begin the frame while visible.
- state.UpdateState(
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS,
state.CommitState());
EXPECT_FALSE(state.NeedsCommit());
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Become invisible and abort the main thread's begin frame.
state.SetVisible(false);
state.BeginFrameAbortedByMainThread(false);
- // We should now be back in the idle state as if we didn't start a frame at
- // all.
+ // We should now be back in the idle state as if we never started the frame.
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_IDLE, state.CommitState());
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+
+ // We shouldn't do anything on the BeginFrame deadline.
+ state.OnBeginFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Become visible again.
state.SetVisible(true);
@@ -735,16 +789,14 @@ TEST(SchedulerStateMachineTest, TestGoesInvisibleBeforeFinishCommit) {
EXPECT_TRUE(state.NeedsCommit());
// Start a new frame.
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD,
- state.NextAction());
-
- // Begin the frame.
- state.UpdateState(state.NextAction());
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
// We should be starting the commit now.
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS,
state.CommitState());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
}
TEST(SchedulerStateMachineTest, AbortBeginFrameAndCancelCommit) {
@@ -758,9 +810,8 @@ TEST(SchedulerStateMachineTest, AbortBeginFrameAndCancelCommit) {
// Get into a begin frame / commit state.
state.SetNeedsCommit();
- EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD,
- state.NextAction());
- state.UpdateState(
+
+ EXPECT_ACTION_UPDATE_STATE(
SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS,
state.CommitState());
@@ -777,9 +828,11 @@ TEST(SchedulerStateMachineTest, AbortBeginFrameAndCancelCommit) {
// Start a new frame; draw because this is the first frame since output
// surface init'd.
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction());
- state.DidLeaveBeginFrame();
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ state.OnBeginFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
// Verify another commit doesn't start on another frame either.
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_IDLE, state.CommitState());
@@ -800,15 +853,22 @@ TEST(SchedulerStateMachineTest, TestFirstContextCreation) {
state.SetVisible(true);
state.SetCanDraw(true);
- EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION,
- state.NextAction());
- state.UpdateState(state.NextAction());
- state.DidCreateAndInitializeOutputSurface();
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION);
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Check that the first init does not SetNeedsCommit.
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ state.OnBeginFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+
+ // Check that a needs commit initiates a BeginFrame to the main thread.
state.SetNeedsCommit();
- EXPECT_NE(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
}
TEST(SchedulerStateMachineTest, TestContextLostWhenCompletelyIdle) {
@@ -816,7 +876,7 @@ TEST(SchedulerStateMachineTest, TestContextLostWhenCompletelyIdle) {
StateMachine state(default_scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
- state.DidCreateAndInitializeOutputSurface();
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetVisible(true);
state.SetCanDraw(true);
@@ -830,15 +890,15 @@ TEST(SchedulerStateMachineTest, TestContextLostWhenCompletelyIdle) {
state.UpdateState(state.NextAction());
// Once context recreation begins, nothing should happen.
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Recreate the context.
- state.DidCreateAndInitializeOutputSurface();
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
// When the context is recreated, we should begin a commit.
- EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD,
- state.NextAction());
- state.UpdateState(state.NextAction());
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
}
TEST(SchedulerStateMachineTest,
@@ -847,7 +907,7 @@ TEST(SchedulerStateMachineTest,
StateMachine state(default_scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
- state.DidCreateAndInitializeOutputSurface();
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetVisible(true);
state.SetCanDraw(true);
@@ -855,65 +915,84 @@ TEST(SchedulerStateMachineTest,
state.NextAction());
state.DidLoseOutputSurface();
- EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION,
- state.NextAction());
- state.UpdateState(state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Once context recreation begins, nothing should happen.
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ state.OnBeginFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// While context is recreating, commits shouldn't begin.
state.SetNeedsCommit();
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ state.OnBeginFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Recreate the context
state.DidCreateAndInitializeOutputSurface();
EXPECT_FALSE(state.RedrawPending());
// When the context is recreated, we should begin a commit
- EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD,
- state.NextAction());
- state.UpdateState(state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS,
state.CommitState());
state.FinishCommit();
- EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction());
- state.UpdateState(state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Finishing the first commit after initializing an output surface should
// automatically cause a redraw.
EXPECT_TRUE(state.RedrawPending());
// Once the context is recreated, whether we draw should be based on
// SetCanDraw.
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction());
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ state.OnBeginFrameDeadline();
+ EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE,
+ state.NextAction());
state.SetCanDraw(false);
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT,
+ state.NextAction());
state.SetCanDraw(true);
- state.DidLeaveBeginFrame();
+ EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE,
+ state.NextAction());
}
-TEST(SchedulerStateMachineTest, TestContextLostWhileCommitInProgress) {
- SchedulerSettings default_scheduler_settings;
- StateMachine state(default_scheduler_settings);
+void TestContextLostWhileCommitInProgress(bool deadline_scheduling_enabled) {
+ SchedulerSettings scheduler_settings;
+ scheduler_settings.deadline_scheduling_enabled = deadline_scheduling_enabled;
+ StateMachine state(scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
- state.DidCreateAndInitializeOutputSurface();
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetVisible(true);
state.SetCanDraw(true);
// Get a commit in flight.
state.SetNeedsCommit();
- EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD,
- state.NextAction());
- state.UpdateState(state.NextAction());
+ if (!deadline_scheduling_enabled) {
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
+ }
// Set damage and expect a draw.
state.SetNeedsRedraw(true);
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction());
- state.UpdateState(state.NextAction());
- state.DidLeaveBeginFrame();
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ if (deadline_scheduling_enabled) {
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
+ }
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ state.OnBeginFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Cause a lost context while the begin frame is in flight
// for the main thread.
@@ -925,47 +1004,81 @@ TEST(SchedulerStateMachineTest, TestContextLostWhileCommitInProgress) {
// Finish the frame, and commit.
state.FinishCommit();
- EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction());
- state.UpdateState(state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
+ // We will abort the draw when the output surface is lost if we are
+ // waiting for the first draw to unblock the main thread.
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW,
state.CommitState());
-
- EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction());
- state.UpdateState(state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT);
// Expect to be told to begin context recreation, independent of
// BeginFrame state.
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_EQ(SchedulerStateMachine::BEGIN_FRAME_STATE_IDLE,
+ state.begin_frame_state());
EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION,
state.NextAction());
- state.DidLeaveBeginFrame();
+
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_EQ(SchedulerStateMachine::BEGIN_FRAME_STATE_BEGIN_FRAME_STARTING,
+ state.begin_frame_state());
+ EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION,
+ state.NextAction());
+
+ state.OnBeginFrameDeadlinePending();
+ EXPECT_EQ(SchedulerStateMachine::BEGIN_FRAME_STATE_INSIDE_BEGIN_FRAME,
+ state.begin_frame_state());
+ EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION,
+ state.NextAction());
+
+ state.OnBeginFrameDeadline();
+ EXPECT_EQ(SchedulerStateMachine::BEGIN_FRAME_STATE_INSIDE_DEADLINE,
+ state.begin_frame_state());
EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION,
state.NextAction());
}
-TEST(SchedulerStateMachineTest,
- TestContextLostWhileCommitInProgressAndAnotherCommitRequested) {
- SchedulerSettings default_scheduler_settings;
- StateMachine state(default_scheduler_settings);
+TEST(SchedulerStateMachineTest, TestContextLostWhileCommitInProgress) {
+ bool deadline_scheduling_enabled = false;
+ TestContextLostWhileCommitInProgress(deadline_scheduling_enabled);
+}
+
+TEST(SchedulerStateMachineTest, TestContextLostWhileCommitInProgress_Deadline) {
+ bool deadline_scheduling_enabled = true;
+ TestContextLostWhileCommitInProgress(deadline_scheduling_enabled);
+}
+
+void TestContextLostWhileCommitInProgressAndAnotherCommitRequested(
+ bool deadline_scheduling_enabled) {
+ SchedulerSettings scheduler_settings;
+ scheduler_settings.deadline_scheduling_enabled = deadline_scheduling_enabled;
+ StateMachine state(scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
- state.DidCreateAndInitializeOutputSurface();
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetVisible(true);
state.SetCanDraw(true);
// Get a commit in flight.
state.SetNeedsCommit();
- EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD,
- state.NextAction());
- state.UpdateState(state.NextAction());
+ if (!deadline_scheduling_enabled) {
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
+ }
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Set damage and expect a draw.
state.SetNeedsRedraw(true);
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction());
- state.UpdateState(state.NextAction());
- state.DidLeaveBeginFrame();
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ if (deadline_scheduling_enabled) {
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
+ }
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ state.OnBeginFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Cause a lost context while the begin frame is in flight
// for the main thread.
@@ -974,27 +1087,72 @@ TEST(SchedulerStateMachineTest,
// Ask for another draw and also set needs commit. Expect nothing happens.
state.SetNeedsRedraw(true);
state.SetNeedsCommit();
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Finish the frame, and commit.
state.FinishCommit();
- EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction());
- state.UpdateState(state.NextAction());
-
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW,
state.CommitState());
- EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.NextAction());
- state.UpdateState(state.NextAction());
+ // Because the output surface is missing, we expect the draw to abort.
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT);
// Expect to be told to begin context recreation, independent of
// BeginFrame state
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_EQ(SchedulerStateMachine::BEGIN_FRAME_STATE_IDLE,
+ state.begin_frame_state());
+ EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION,
+ state.NextAction());
+
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_EQ(SchedulerStateMachine::BEGIN_FRAME_STATE_BEGIN_FRAME_STARTING,
+ state.begin_frame_state());
+ EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION,
+ state.NextAction());
+
+ state.OnBeginFrameDeadlinePending();
+ EXPECT_EQ(SchedulerStateMachine::BEGIN_FRAME_STATE_INSIDE_BEGIN_FRAME,
+ state.begin_frame_state());
EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION,
state.NextAction());
- state.DidLeaveBeginFrame();
+
+ state.OnBeginFrameDeadline();
+ EXPECT_EQ(SchedulerStateMachine::BEGIN_FRAME_STATE_INSIDE_DEADLINE,
+ state.begin_frame_state());
EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION,
state.NextAction());
+
+ // After we get a new output surface, the commit flow should start.
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION);
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
+ state.OnBeginFrameIdle();
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ state.FinishCommit();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+ state.OnBeginFrameDeadline();
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+}
+
+TEST(SchedulerStateMachineTest,
+ TestContextLostWhileCommitInProgressAndAnotherCommitRequested) {
+ bool deadline_scheduling_enabled = false;
+ TestContextLostWhileCommitInProgressAndAnotherCommitRequested(
+ deadline_scheduling_enabled);
+}
+
+TEST(SchedulerStateMachineTest,
+ TestContextLostWhileCommitInProgressAndAnotherCommitRequested_Deadline) {
+ bool deadline_scheduling_enabled = true;
+ TestContextLostWhileCommitInProgressAndAnotherCommitRequested(
+ deadline_scheduling_enabled);
}
TEST(SchedulerStateMachineTest, TestFinishAllRenderingWhileContextLost) {
@@ -1002,33 +1160,47 @@ TEST(SchedulerStateMachineTest, TestFinishAllRenderingWhileContextLost) {
StateMachine state(default_scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
- state.DidCreateAndInitializeOutputSurface();
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetVisible(true);
state.SetCanDraw(true);
// Cause a lost context lost.
state.DidLoseOutputSurface();
- // Ask a forced redraw and verify it ocurrs.
- state.SetNeedsForcedRedraw(true);
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_FORCED, state.NextAction());
- state.DidLeaveBeginFrame();
+ // Ask a forced redraw for readback and verify it ocurrs.
+ state.SetCommitState(
+ SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW);
+ state.SetNeedsForcedRedrawForReadback();
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_READBACK);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+
+ // Forced redraws for readbacks need to be followed by a new commit
+ // to replace the readback commit.
+ EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS,
+ state.CommitState());
+ state.FinishCommit();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
- // Clear the forced redraw bit.
- state.SetNeedsForcedRedraw(false);
+ // We don't yet have an output surface, so we the draw and swap should abort.
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT);
// Expect to be told to begin context recreation, independent of
// BeginFrame state
+ EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_IDLE, state.CommitState());
EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION,
state.NextAction());
- state.UpdateState(state.NextAction());
- // Ask a forced redraw and verify it ocurrs.
- state.SetNeedsForcedRedraw(true);
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_FORCED, state.NextAction());
- state.DidLeaveBeginFrame();
+ state.OnBeginFrameDeadline();
+ EXPECT_EQ(SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION,
+ state.NextAction());
+
+ // Ask a readback and verify it occurs.
+ state.SetCommitState(
+ SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW);
+ state.SetNeedsForcedRedrawForReadback();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_READBACK);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
}
TEST(SchedulerStateMachineTest, DontDrawBeforeCommitAfterLostOutputSurface) {
@@ -1036,7 +1208,7 @@ TEST(SchedulerStateMachineTest, DontDrawBeforeCommitAfterLostOutputSurface) {
StateMachine state(default_scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
- state.DidCreateAndInitializeOutputSurface();
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetVisible(true);
state.SetCanDraw(true);
@@ -1050,6 +1222,7 @@ TEST(SchedulerStateMachineTest, DontDrawBeforeCommitAfterLostOutputSurface) {
state.DidCreateAndInitializeOutputSurface();
EXPECT_FALSE(state.RedrawPending());
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD,
state.NextAction());
}
@@ -1060,10 +1233,10 @@ TEST(SchedulerStateMachineTest,
StateMachine state(default_scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
- state.DidCreateAndInitializeOutputSurface();
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetVisible(false);
state.SetNeedsCommit();
- state.SetNeedsForcedCommit();
+ state.SetNeedsForcedCommitForReadback();
EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD,
state.NextAction());
}
@@ -1075,7 +1248,7 @@ TEST(SchedulerStateMachineTest,
state.SetVisible(true);
state.SetCanDraw(true);
state.SetNeedsCommit();
- state.SetNeedsForcedCommit();
+ state.SetNeedsForcedCommitForReadback();
EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD,
state.NextAction());
}
@@ -1085,7 +1258,7 @@ TEST(SchedulerStateMachineTest, TestFinishCommitWhenCommitInProgress) {
StateMachine state(default_scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
- state.DidCreateAndInitializeOutputSurface();
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetVisible(false);
state.SetCommitState(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS);
state.SetNeedsCommit();
@@ -1096,8 +1269,7 @@ TEST(SchedulerStateMachineTest, TestFinishCommitWhenCommitInProgress) {
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW,
state.CommitState());
-
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT);
}
TEST(SchedulerStateMachineTest, TestFinishCommitWhenForcedCommitInProgress) {
@@ -1105,37 +1277,64 @@ TEST(SchedulerStateMachineTest, TestFinishCommitWhenForcedCommitInProgress) {
StateMachine state(default_scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
- state.DidCreateAndInitializeOutputSurface();
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetVisible(false);
state.SetCommitState(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS);
state.SetNeedsCommit();
- state.SetNeedsForcedCommit();
+ state.SetNeedsForcedCommitForReadback();
+ // The commit for readback interupts the normal commit.
state.FinishCommit();
- EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction());
- state.UpdateState(state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
- EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW,
+ EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW,
state.CommitState());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_READBACK);
- // If we are waiting for forced draw then we know a begin frame is already
- // in flight for the main thread.
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ // When the readback interrupts the normal commit, we should not get
+ // another BeginFrame on the impl thread when the readback completes.
+ EXPECT_NE(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD,
+ state.NextAction());
+
+ // The normal commit can then proceed.
+ state.FinishCommit();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
}
-TEST(SchedulerStateMachineTest, TestSendBeginFrameToMainThreadWhenContextLost) {
+TEST(SchedulerStateMachineTest, TestInitialActionsWhenContextLost) {
SchedulerSettings default_scheduler_settings;
StateMachine state(default_scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
- state.DidCreateAndInitializeOutputSurface();
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetVisible(true);
state.SetCanDraw(true);
state.SetNeedsCommit();
- state.SetNeedsForcedCommit();
state.DidLoseOutputSurface();
+
+ // When we are visible, we normally want to begin output surface creation
+ // as soon as possible.
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION);
+
+ state.DidCreateAndInitializeOutputSurface();
+ EXPECT_EQ(state.output_surface_state(),
+ SchedulerStateMachine::OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT);
+
+ // We should not send a BeginFrame to them main thread when we are invisible,
+ // even if we've lost the output surface and are trying to get the first
+ // commit, since the main thread will just abort anyway.
+ state.SetVisible(false);
+ EXPECT_EQ(SchedulerStateMachine::ACTION_NONE,
+ state.NextAction()) << *state.AsValue();
+
+ // If there is a forced commit, however, we could be blocking a readback
+ // on the main thread, so we need to unblock it before we can get our
+ // output surface, even if we are not visible.
+ state.SetNeedsForcedCommitForReadback();
EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD,
- state.NextAction());
+ state.NextAction())
+ << *state.AsValue();
}
TEST(SchedulerStateMachineTest, TestImmediateFinishCommit) {
@@ -1143,112 +1342,130 @@ TEST(SchedulerStateMachineTest, TestImmediateFinishCommit) {
StateMachine state(default_scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
- state.DidCreateAndInitializeOutputSurface();
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetVisible(true);
state.SetCanDraw(true);
- // Schedule a forced frame, commit it, draw it.
+ // Schedule a readback, commit it, draw it.
state.SetNeedsCommit();
- state.SetNeedsForcedCommit();
- state.UpdateState(state.NextAction());
+ state.SetNeedsForcedCommitForReadback();
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
state.FinishCommit();
- EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction());
+
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT,
state.CommitState());
- state.UpdateState(state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
- EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW,
+ EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW,
state.CommitState());
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
- state.SetNeedsForcedRedraw(true);
- EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_FORCED, state.NextAction());
- state.UpdateState(state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_READBACK);
state.DidDrawIfPossibleCompleted(true);
- state.DidLeaveBeginFrame();
+
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Should be waiting for the normal begin frame from the main thread.
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS,
state.CommitState());
}
-TEST(SchedulerStateMachineTest, TestImmediateFinishCommitDuringCommit) {
- SchedulerSettings default_scheduler_settings;
- StateMachine state(default_scheduler_settings);
+void TestImmediateFinishCommitDuringCommit(bool deadline_scheduling_enabled) {
+ SchedulerSettings scheduler_settings;
+ scheduler_settings.deadline_scheduling_enabled = deadline_scheduling_enabled;
+ StateMachine state(scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
- state.DidCreateAndInitializeOutputSurface();
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetVisible(true);
state.SetCanDraw(true);
// Start a normal commit.
state.SetNeedsCommit();
- state.UpdateState(state.NextAction());
+ if (!deadline_scheduling_enabled) {
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
+ }
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
- // Schedule a forced frame, commit it, draw it.
- state.SetNeedsCommit();
- state.SetNeedsForcedCommit();
- state.UpdateState(state.NextAction());
+ // Schedule a readback, commit it, draw it.
+ state.SetNeedsForcedCommitForReadback();
+ if (deadline_scheduling_enabled) {
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
+ }
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
state.FinishCommit();
- EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction());
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT,
state.CommitState());
- state.UpdateState(state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
- EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW,
+ EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW,
state.CommitState());
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
- state.SetNeedsForcedRedraw(true);
- EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_FORCED, state.NextAction());
- state.UpdateState(state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_READBACK);
state.DidDrawIfPossibleCompleted(true);
- state.DidLeaveBeginFrame();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Should be waiting for the normal begin frame from the main thread.
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS,
- state.CommitState()) << state.ToString();
+ state.CommitState())
+ << *state.AsValue();
}
TEST(SchedulerStateMachineTest,
- ImmediateBeginFrameAbortedByMainThreadWhileInvisible) {
- SchedulerSettings default_scheduler_settings;
- StateMachine state(default_scheduler_settings);
+ TestImmediateFinishCommitDuringCommit) {
+ bool deadline_scheduling_enabled = false;
+ TestImmediateFinishCommitDuringCommit(deadline_scheduling_enabled);
+}
+
+TEST(SchedulerStateMachineTest,
+ TestImmediateFinishCommitDuringCommit_Deadline) {
+ bool deadline_scheduling_enabled = true;
+ TestImmediateFinishCommitDuringCommit(deadline_scheduling_enabled);
+}
+
+void ImmediateBeginFrameAbortedByMainThreadWhileInvisible(
+ bool deadline_scheduling_enabled) {
+ SchedulerSettings scheduler_settings;
+ scheduler_settings.deadline_scheduling_enabled = deadline_scheduling_enabled;
+ StateMachine state(scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
- state.DidCreateAndInitializeOutputSurface();
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetVisible(true);
state.SetCanDraw(true);
state.SetNeedsCommit();
- state.UpdateState(state.NextAction());
+ if (!deadline_scheduling_enabled) {
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
+ }
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
state.SetNeedsCommit();
- state.SetNeedsForcedCommit();
- state.UpdateState(state.NextAction());
+ state.SetNeedsForcedCommitForReadback();
+ if (deadline_scheduling_enabled) {
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
+ }
state.FinishCommit();
- EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction());
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT,
state.CommitState());
- state.UpdateState(state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
- EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW,
+ EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW,
state.CommitState());
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
- state.SetNeedsForcedRedraw(true);
- EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_FORCED, state.NextAction());
- state.UpdateState(state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_READBACK);
state.DidDrawIfPossibleCompleted(true);
- state.DidLeaveBeginFrame();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Should be waiting for the main thread's begin frame.
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS,
- state.CommitState()) << state.ToString();
+ state.CommitState())
+ << *state.AsValue();
// Become invisible and abort the main thread's begin frame.
state.SetVisible(false);
@@ -1259,12 +1476,26 @@ TEST(SchedulerStateMachineTest,
EXPECT_TRUE(state.NeedsCommit());
}
+TEST(SchedulerStateMachineTest,
+ ImmediateBeginFrameAbortedByMainThreadWhileInvisible) {
+ bool deadline_scheduling_enabled = false;
+ ImmediateBeginFrameAbortedByMainThreadWhileInvisible(
+ deadline_scheduling_enabled);
+}
+
+TEST(SchedulerStateMachineTest,
+ ImmediateBeginFrameAbortedByMainThreadWhileInvisible_Deadline) {
+ bool deadline_scheduling_enabled = true;
+ ImmediateBeginFrameAbortedByMainThreadWhileInvisible(
+ deadline_scheduling_enabled);
+}
+
TEST(SchedulerStateMachineTest, ImmediateFinishCommitWhileCantDraw) {
SchedulerSettings default_scheduler_settings;
StateMachine state(default_scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
- state.DidCreateAndInitializeOutputSurface();
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetVisible(true);
state.SetCanDraw(false);
@@ -1272,92 +1503,87 @@ TEST(SchedulerStateMachineTest, ImmediateFinishCommitWhileCantDraw) {
state.UpdateState(state.NextAction());
state.SetNeedsCommit();
- state.SetNeedsForcedCommit();
+ state.SetNeedsForcedCommitForReadback();
state.UpdateState(state.NextAction());
state.FinishCommit();
- EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction());
EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT,
state.CommitState());
- state.UpdateState(state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
- EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW,
+ EXPECT_EQ(SchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW,
state.CommitState());
- state.DidEnterBeginFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
- state.SetNeedsForcedRedraw(true);
- EXPECT_EQ(SchedulerStateMachine::ACTION_DRAW_FORCED, state.NextAction());
- state.UpdateState(state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_READBACK);
state.DidDrawIfPossibleCompleted(true);
- state.DidLeaveBeginFrame();
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
}
TEST(SchedulerStateMachineTest, ReportIfNotDrawing) {
SchedulerSettings default_scheduler_settings;
- SchedulerStateMachine state(default_scheduler_settings);
+ StateMachine state(default_scheduler_settings);
+ state.SetCanStart();
+ state.UpdateState(state.NextAction());
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetCanDraw(true);
state.SetVisible(true);
- EXPECT_FALSE(state.DrawSuspendedUntilCommit());
+ EXPECT_FALSE(state.PendingDrawsShouldBeAborted());
state.SetCanDraw(false);
state.SetVisible(true);
- EXPECT_TRUE(state.DrawSuspendedUntilCommit());
+ EXPECT_TRUE(state.PendingDrawsShouldBeAborted());
state.SetCanDraw(true);
state.SetVisible(false);
- EXPECT_TRUE(state.DrawSuspendedUntilCommit());
+ EXPECT_TRUE(state.PendingDrawsShouldBeAborted());
state.SetCanDraw(false);
state.SetVisible(false);
- EXPECT_TRUE(state.DrawSuspendedUntilCommit());
+ EXPECT_TRUE(state.PendingDrawsShouldBeAborted());
state.SetCanDraw(true);
state.SetVisible(true);
- EXPECT_FALSE(state.DrawSuspendedUntilCommit());
+ EXPECT_FALSE(state.PendingDrawsShouldBeAborted());
}
TEST(SchedulerStateMachineTest, ReportIfNotDrawingFromAcquiredTextures) {
SchedulerSettings default_scheduler_settings;
- SchedulerStateMachine state(default_scheduler_settings);
+ StateMachine state(default_scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
- state.DidCreateAndInitializeOutputSurface();
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
state.SetCanDraw(true);
state.SetVisible(true);
- EXPECT_FALSE(state.DrawSuspendedUntilCommit());
+ EXPECT_FALSE(state.PendingDrawsShouldBeAborted());
state.SetMainThreadNeedsLayerTextures();
- EXPECT_EQ(
- SchedulerStateMachine::ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD,
- state.NextAction());
- state.UpdateState(state.NextAction());
- EXPECT_TRUE(state.DrawSuspendedUntilCommit());
-
- EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD);
+ EXPECT_TRUE(state.PendingDrawsShouldBeAborted());
+ EXPECT_TRUE(state.PendingActivationsShouldBeForced());
state.SetNeedsCommit();
- EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD,
- state.NextAction());
-
- state.UpdateState(state.NextAction());
- EXPECT_TRUE(state.DrawSuspendedUntilCommit());
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
+ EXPECT_TRUE(state.PendingDrawsShouldBeAborted());
+ EXPECT_TRUE(state.PendingActivationsShouldBeForced());
EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
state.FinishCommit();
- EXPECT_TRUE(state.DrawSuspendedUntilCommit());
+ EXPECT_TRUE(state.PendingDrawsShouldBeAborted());
EXPECT_EQ(SchedulerStateMachine::ACTION_COMMIT, state.NextAction());
state.UpdateState(state.NextAction());
- EXPECT_FALSE(state.DrawSuspendedUntilCommit());
+ EXPECT_FALSE(state.PendingDrawsShouldBeAborted());
}
TEST(SchedulerStateMachineTest, AcquireTexturesWithAbort) {
SchedulerSettings default_scheduler_settings;
- SchedulerStateMachine state(default_scheduler_settings);
+ StateMachine state(default_scheduler_settings);
state.SetCanStart();
state.UpdateState(state.NextAction());
state.DidCreateAndInitializeOutputSurface();
@@ -1369,7 +1595,7 @@ TEST(SchedulerStateMachineTest, AcquireTexturesWithAbort) {
SchedulerStateMachine::ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD,
state.NextAction());
state.UpdateState(state.NextAction());
- EXPECT_TRUE(state.DrawSuspendedUntilCommit());
+ EXPECT_TRUE(state.PendingDrawsShouldBeAborted());
EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
@@ -1377,14 +1603,75 @@ TEST(SchedulerStateMachineTest, AcquireTexturesWithAbort) {
EXPECT_EQ(SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD,
state.NextAction());
state.UpdateState(state.NextAction());
- EXPECT_TRUE(state.DrawSuspendedUntilCommit());
+ EXPECT_TRUE(state.PendingDrawsShouldBeAborted());
EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
state.BeginFrameAbortedByMainThread(true);
EXPECT_EQ(SchedulerStateMachine::ACTION_NONE, state.NextAction());
- EXPECT_FALSE(state.DrawSuspendedUntilCommit());
+ EXPECT_FALSE(state.PendingDrawsShouldBeAborted());
+}
+
+TEST(SchedulerStateMachineTest,
+ TestTriggerDeadlineEarlyAfterAbortedCommit) {
+ SchedulerSettings settings;
+ settings.deadline_scheduling_enabled = true;
+ settings.impl_side_painting = true;
+ StateMachine state(settings);
+ state.SetCanStart();
+ state.UpdateState(state.NextAction());
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
+ state.SetVisible(true);
+ state.SetCanDraw(true);
+
+ // This test mirrors what happens during the first frame of a scroll gesture.
+ // First we get the input event and a BeginFrame.
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+
+ // As a response the compositor requests a redraw and a commit to tell the
+ // main thread about the new scroll offset.
+ state.SetNeedsRedraw(true);
+ state.SetNeedsCommit();
+
+ // We should start the commit normally.
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+
+ // Since only the scroll offset changed, the main thread will abort the
+ // commit.
+ state.BeginFrameAbortedByMainThread(true);
+
+ // Since the commit was aborted, we should draw right away instead of waiting
+ // for the deadline.
+ EXPECT_TRUE(state.ShouldTriggerBeginFrameDeadlineEarly());
+}
+
+TEST(SchedulerStateMachineTest, TestTriggerDeadlineEarlyForSmoothness) {
+ SchedulerSettings settings;
+ settings.deadline_scheduling_enabled = true;
+ settings.impl_side_painting = true;
+ StateMachine state(settings);
+ state.SetCanStart();
+ state.UpdateState(state.NextAction());
+ state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
+ state.SetVisible(true);
+ state.SetCanDraw(true);
+
+ // This test ensures that impl-draws are prioritized over main thread updates
+ // in prefer smoothness mode.
+ state.OnBeginFrame(BeginFrameArgs::CreateForTesting());
+ state.SetNeedsRedraw(true);
+ state.SetNeedsCommit();
+ EXPECT_ACTION_UPDATE_STATE(
+ SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD);
+ EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+
+ // The deadline is not triggered early until we enter prefer smoothness mode.
+ EXPECT_FALSE(state.ShouldTriggerBeginFrameDeadlineEarly());
+ state.SetSmoothnessTakesPriority(true);
+ EXPECT_TRUE(state.ShouldTriggerBeginFrameDeadlineEarly());
}
} // namespace
diff --git a/chromium/cc/scheduler/scheduler_unittest.cc b/chromium/cc/scheduler/scheduler_unittest.cc
index 930111c1b2a..a1da822cff1 100644
--- a/chromium/cc/scheduler/scheduler_unittest.cc
+++ b/chromium/cc/scheduler/scheduler_unittest.cc
@@ -1,13 +1,13 @@
// Copyright 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-
#include "cc/scheduler/scheduler.h"
#include <string>
#include <vector>
#include "base/logging.h"
+#include "base/memory/scoped_vector.h"
#include "cc/test/scheduler_test_common.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -28,6 +28,18 @@
namespace cc {
namespace {
+void InitializeOutputSurfaceAndFirstCommit(Scheduler* scheduler) {
+ scheduler->DidCreateAndInitializeOutputSurface();
+ scheduler->SetNeedsCommit();
+ scheduler->FinishCommit();
+ // Go through the motions to draw the commit.
+ scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ scheduler->OnBeginFrameDeadline();
+ // We need another BeginFrame so Scheduler calls SetNeedsBeginFrame(false).
+ scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ scheduler->OnBeginFrameDeadline();
+}
+
class FakeSchedulerClient : public SchedulerClient {
public:
FakeSchedulerClient()
@@ -52,13 +64,17 @@ class FakeSchedulerClient : public SchedulerClient {
int num_draws() const { return num_draws_; }
int num_actions_() const { return static_cast<int>(actions_.size()); }
const char* Action(int i) const { return actions_[i]; }
- std::string StateForAction(int i) const { return states_[i]; }
+ base::Value& StateForAction(int i) const { return *states_[i]; }
- bool HasAction(const char* action) const {
+ int ActionIndex(const char* action) const {
for (size_t i = 0; i < actions_.size(); i++)
if (!strcmp(actions_[i], action))
- return true;
- return false;
+ return i;
+ return -1;
+ }
+
+ bool HasAction(const char* action) const {
+ return ActionIndex(action) >= 0;
}
void SetDrawWillHappen(bool draw_will_happen) {
@@ -71,48 +87,63 @@ class FakeSchedulerClient : public SchedulerClient {
// Scheduler Implementation.
virtual void SetNeedsBeginFrameOnImplThread(bool enable) OVERRIDE {
actions_.push_back("SetNeedsBeginFrameOnImplThread");
- states_.push_back(scheduler_->StateAsStringForTesting());
+ states_.push_back(scheduler_->StateAsValue().release());
needs_begin_frame_ = enable;
}
virtual void ScheduledActionSendBeginFrameToMainThread() OVERRIDE {
actions_.push_back("ScheduledActionSendBeginFrameToMainThread");
- states_.push_back(scheduler_->StateAsStringForTesting());
+ states_.push_back(scheduler_->StateAsValue().release());
}
- virtual ScheduledActionDrawAndSwapResult
- ScheduledActionDrawAndSwapIfPossible() OVERRIDE {
+ virtual DrawSwapReadbackResult ScheduledActionDrawAndSwapIfPossible()
+ OVERRIDE {
actions_.push_back("ScheduledActionDrawAndSwapIfPossible");
- states_.push_back(scheduler_->StateAsStringForTesting());
+ states_.push_back(scheduler_->StateAsValue().release());
num_draws_++;
- return ScheduledActionDrawAndSwapResult(draw_will_happen_,
- draw_will_happen_ &&
- swap_will_happen_if_draw_happens_);
+ bool did_readback = false;
+ return DrawSwapReadbackResult(
+ draw_will_happen_,
+ draw_will_happen_ && swap_will_happen_if_draw_happens_,
+ did_readback);
}
- virtual ScheduledActionDrawAndSwapResult ScheduledActionDrawAndSwapForced()
- OVERRIDE {
+ virtual DrawSwapReadbackResult ScheduledActionDrawAndSwapForced() OVERRIDE {
actions_.push_back("ScheduledActionDrawAndSwapForced");
- states_.push_back(scheduler_->StateAsStringForTesting());
- return ScheduledActionDrawAndSwapResult(true,
- swap_will_happen_if_draw_happens_);
+ states_.push_back(scheduler_->StateAsValue().release());
+ bool did_draw = true;
+ bool did_swap = swap_will_happen_if_draw_happens_;
+ bool did_readback = false;
+ return DrawSwapReadbackResult(did_draw, did_swap, did_readback);
+ }
+ virtual DrawSwapReadbackResult ScheduledActionDrawAndReadback() OVERRIDE {
+ actions_.push_back("ScheduledActionDrawAndReadback");
+ states_.push_back(scheduler_->StateAsValue().release());
+ bool did_draw = true;
+ bool did_swap = false;
+ bool did_readback = true;
+ return DrawSwapReadbackResult(did_draw, did_swap, did_readback);
}
virtual void ScheduledActionCommit() OVERRIDE {
actions_.push_back("ScheduledActionCommit");
- states_.push_back(scheduler_->StateAsStringForTesting());
+ states_.push_back(scheduler_->StateAsValue().release());
}
virtual void ScheduledActionUpdateVisibleTiles() OVERRIDE {
actions_.push_back("ScheduledActionUpdateVisibleTiles");
- states_.push_back(scheduler_->StateAsStringForTesting());
+ states_.push_back(scheduler_->StateAsValue().release());
}
- virtual void ScheduledActionActivatePendingTreeIfNeeded() OVERRIDE {
- actions_.push_back("ScheduledActionActivatePendingTreeIfNeeded");
- states_.push_back(scheduler_->StateAsStringForTesting());
+ virtual void ScheduledActionActivatePendingTree() OVERRIDE {
+ actions_.push_back("ScheduledActionActivatePendingTree");
+ states_.push_back(scheduler_->StateAsValue().release());
}
virtual void ScheduledActionBeginOutputSurfaceCreation() OVERRIDE {
actions_.push_back("ScheduledActionBeginOutputSurfaceCreation");
- states_.push_back(scheduler_->StateAsStringForTesting());
+ states_.push_back(scheduler_->StateAsValue().release());
}
virtual void ScheduledActionAcquireLayerTexturesForMainThread() OVERRIDE {
actions_.push_back("ScheduledActionAcquireLayerTexturesForMainThread");
- states_.push_back(scheduler_->StateAsStringForTesting());
+ states_.push_back(scheduler_->StateAsValue().release());
+ }
+ virtual void ScheduledActionManageTiles() OVERRIDE {
+ actions_.push_back("ScheduledActionManageTiles");
+ states_.push_back(scheduler_->StateAsValue().release());
}
virtual void DidAnticipatedDrawTimeChange(base::TimeTicks) OVERRIDE {}
virtual base::TimeDelta DrawDurationEstimate() OVERRIDE {
@@ -125,13 +156,21 @@ class FakeSchedulerClient : public SchedulerClient {
return base::TimeDelta();
}
+ virtual void PostBeginFrameDeadline(const base::Closure& closure,
+ base::TimeTicks deadline) OVERRIDE {
+ actions_.push_back("PostBeginFrameDeadlineTask");
+ states_.push_back(scheduler_->StateAsValue().release());
+ }
+
+ virtual void DidBeginFrameDeadlineOnImplThread() OVERRIDE {}
+
protected:
bool needs_begin_frame_;
bool draw_will_happen_;
bool swap_will_happen_if_draw_happens_;
int num_draws_;
std::vector<const char*> actions_;
- std::vector<std::string> states_;
+ ScopedVector<base::Value> states_;
scoped_ptr<Scheduler> scheduler_;
};
@@ -149,94 +188,205 @@ TEST(SchedulerTest, InitializeOutputSurfaceDoesNotBeginFrame) {
EXPECT_EQ(0, client.num_actions_());
}
-TEST(SchedulerTest, RequestCommit) {
+void RequestCommit(bool deadline_scheduling_enabled) {
FakeSchedulerClient client;
- SchedulerSettings default_scheduler_settings;
- Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
+ SchedulerSettings scheduler_settings;
+ scheduler_settings.deadline_scheduling_enabled = deadline_scheduling_enabled;
+ Scheduler* scheduler = client.CreateScheduler(scheduler_settings);
scheduler->SetCanStart();
scheduler->SetVisible(true);
scheduler->SetCanDraw(true);
EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client);
- client.Reset();
- scheduler->DidCreateAndInitializeOutputSurface();
+ InitializeOutputSurfaceAndFirstCommit(scheduler);
- // SetNeedsCommit should begin the frame.
+ // SetNeedsCommit should begin the frame on the next BeginFrame.
+ client.Reset();
scheduler->SetNeedsCommit();
- EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2);
- EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2);
+ EXPECT_TRUE(client.needs_begin_frame());
+ if (deadline_scheduling_enabled) {
+ EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client);
+ } else {
+ EXPECT_EQ(client.num_actions_(), 2);
+ EXPECT_TRUE(client.HasAction("ScheduledActionSendBeginFrameToMainThread"));
+ EXPECT_TRUE(client.HasAction("SetNeedsBeginFrameOnImplThread"));
+ }
+ client.Reset();
+
+ scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ if (deadline_scheduling_enabled) {
+ EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2);
+ EXPECT_ACTION("PostBeginFrameDeadlineTask", client, 1, 2);
+ } else {
+ EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client);
+ }
+ EXPECT_TRUE(client.needs_begin_frame());
+ client.Reset();
+
+ // If we don't swap on the deadline, we need to request another BeginFrame.
+ scheduler->OnBeginFrameDeadline();
+ EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client);
EXPECT_TRUE(client.needs_begin_frame());
client.Reset();
// FinishCommit should commit
scheduler->FinishCommit();
- EXPECT_ACTION("ScheduledActionCommit", client, 0, 2);
- EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2);
+ EXPECT_SINGLE_ACTION("ScheduledActionCommit", client);
EXPECT_TRUE(client.needs_begin_frame());
client.Reset();
- // BeginFrame should draw.
+ // BeginFrame should prepare the draw.
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client);
+ EXPECT_TRUE(client.needs_begin_frame());
+ client.Reset();
+
+ // BeginFrame deadline should draw.
+ scheduler->OnBeginFrameDeadline();
EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2);
EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2);
+ EXPECT_TRUE(client.needs_begin_frame());
+ client.Reset();
+
+ // The following BeginFrame deadline should SetNeedsBeginFrame(false) to avoid
+ // excessive toggles.
+ scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client);
+ client.Reset();
+
+ scheduler->OnBeginFrameDeadline();
+ EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client);
EXPECT_FALSE(client.needs_begin_frame());
client.Reset();
}
-TEST(SchedulerTest, RequestCommitAfterBeginFrameSentToMainThread) {
+TEST(SchedulerTest, RequestCommit) {
+ bool deadline_scheduling_enabled = false;
+ RequestCommit(deadline_scheduling_enabled);
+}
+
+TEST(SchedulerTest, RequestCommit_Deadline) {
+ bool deadline_scheduling_enabled = true;
+ RequestCommit(deadline_scheduling_enabled);
+}
+
+void RequestCommitAfterBeginFrameSentToMainThread(
+ bool deadline_scheduling_enabled) {
FakeSchedulerClient client;
- SchedulerSettings default_scheduler_settings;
- Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
+ SchedulerSettings scheduler_settings;
+ scheduler_settings.deadline_scheduling_enabled = deadline_scheduling_enabled;
+ Scheduler* scheduler = client.CreateScheduler(scheduler_settings);
scheduler->SetCanStart();
scheduler->SetVisible(true);
scheduler->SetCanDraw(true);
EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client);
+ InitializeOutputSurfaceAndFirstCommit(scheduler);
client.Reset();
- scheduler->DidCreateAndInitializeOutputSurface();
- // SetNedsCommit should begin the frame.
+ // SetNeedsCommit should begin the frame.
scheduler->SetNeedsCommit();
- EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2);
- EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2);
+ if (deadline_scheduling_enabled) {
+ EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client);
+ } else {
+ EXPECT_EQ(client.num_actions_(), 2);
+ EXPECT_TRUE(client.HasAction("SetNeedsBeginFrameOnImplThread"));
+ EXPECT_TRUE(client.HasAction("ScheduledActionSendBeginFrameToMainThread"));
+ }
+
+ client.Reset();
+ scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ if (deadline_scheduling_enabled) {
+ EXPECT_EQ(client.num_actions_(), 2);
+ EXPECT_TRUE(client.HasAction("ScheduledActionSendBeginFrameToMainThread"));
+ EXPECT_TRUE(client.HasAction("PostBeginFrameDeadlineTask"));
+ } else {
+ EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client);
+ }
+
+ EXPECT_TRUE(client.needs_begin_frame());
client.Reset();
- // Now SetNeedsCommit again. Calling here means we need a second frame.
+ // Now SetNeedsCommit again. Calling here means we need a second commit.
scheduler->SetNeedsCommit();
- EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 0, 1);
+ EXPECT_EQ(client.num_actions_(), 0);
client.Reset();
- // Since another commit is needed, FinishCommit should commit,
- // then begin another frame.
+ // Finish the first commit.
scheduler->FinishCommit();
EXPECT_ACTION("ScheduledActionCommit", client, 0, 2);
- EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2);
+ EXPECT_ACTION("PostBeginFrameDeadlineTask", client, 1, 2);
client.Reset();
+ scheduler->OnBeginFrameDeadline();
+ if (deadline_scheduling_enabled) {
+ EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2);
+ EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2);
+ } else {
+ EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 3);
+ EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 1, 3);
+ EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 2, 3);
+ }
- // Tick should draw but then begin another frame.
- scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ // Because we just swapped, the Scheduler should also request the next
+ // BeginFrame from the OutputSurface.
EXPECT_TRUE(client.needs_begin_frame());
- EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2);
- EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 1, 2);
client.Reset();
- // Go back to quiescent state and verify we no longer request BeginFrames.
+ // Since another commit is needed, the next BeginFrame should initiate
+ // the second commit.
+ scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ if (deadline_scheduling_enabled) {
+ EXPECT_EQ(client.num_actions_(), 2);
+ EXPECT_TRUE(client.HasAction("ScheduledActionSendBeginFrameToMainThread"));
+ EXPECT_TRUE(client.HasAction("PostBeginFrameDeadlineTask"));
+ } else {
+ EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client);
+ }
+ client.Reset();
+
+ // Finishing the commit before the deadline should post a new deadline task
+ // to trigger the deadline early.
scheduler->FinishCommit();
+ EXPECT_ACTION("ScheduledActionCommit", client, 0, 2);
+ EXPECT_ACTION("PostBeginFrameDeadlineTask", client, 1, 2);
+ client.Reset();
+ scheduler->OnBeginFrameDeadline();
+ EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2);
+ EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2);
+ EXPECT_TRUE(client.needs_begin_frame());
+ client.Reset();
+
+ // On the next BeginFrame, verify we go back to a quiescent state and
+ // no longer request BeginFrames.
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ scheduler->OnBeginFrameDeadline();
EXPECT_FALSE(client.needs_begin_frame());
+ client.Reset();
}
-TEST(SchedulerTest, TextureAcquisitionCausesCommitInsteadOfDraw) {
+TEST(SchedulerTest, RequestCommitAfterBeginFrameSentToMainThread) {
+ bool deadline_scheduling_enabled = false;
+ RequestCommitAfterBeginFrameSentToMainThread(deadline_scheduling_enabled);
+}
+
+TEST(SchedulerTest, RequestCommitAfterBeginFrameSentToMainThread_Deadline) {
+ bool deadline_scheduling_enabled = true;
+ RequestCommitAfterBeginFrameSentToMainThread(deadline_scheduling_enabled);
+}
+
+void TextureAcquisitionCausesCommitInsteadOfDraw(
+ bool deadline_scheduling_enabled) {
FakeSchedulerClient client;
- SchedulerSettings default_scheduler_settings;
- Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
+ SchedulerSettings scheduler_settings;
+ scheduler_settings.deadline_scheduling_enabled = deadline_scheduling_enabled;
+ Scheduler* scheduler = client.CreateScheduler(scheduler_settings);
scheduler->SetCanStart();
scheduler->SetVisible(true);
scheduler->SetCanDraw(true);
EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client);
+ InitializeOutputSurfaceAndFirstCommit(scheduler);
client.Reset();
- scheduler->DidCreateAndInitializeOutputSurface();
scheduler->SetNeedsRedraw();
EXPECT_TRUE(scheduler->RedrawPending());
EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client);
@@ -244,9 +394,21 @@ TEST(SchedulerTest, TextureAcquisitionCausesCommitInsteadOfDraw) {
client.Reset();
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client);
+ client.Reset();
+ scheduler->OnBeginFrameDeadline();
EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2);
EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2);
EXPECT_FALSE(scheduler->RedrawPending());
+ EXPECT_TRUE(client.needs_begin_frame());
+
+ client.Reset();
+ scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client);
+ client.Reset();
+ scheduler->OnBeginFrameDeadline();
+ EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client);
+ EXPECT_FALSE(scheduler->RedrawPending());
EXPECT_FALSE(client.needs_begin_frame());
client.Reset();
@@ -264,54 +426,103 @@ TEST(SchedulerTest, TextureAcquisitionCausesCommitInsteadOfDraw) {
// No draw happens since the textures are acquired by the main thread.
client.Reset();
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client);
+ client.Reset();
+ scheduler->OnBeginFrameDeadline();
EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client);
EXPECT_TRUE(scheduler->RedrawPending());
EXPECT_TRUE(client.needs_begin_frame());
+ client.Reset();
scheduler->SetNeedsCommit();
- EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 0, 2);
- EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 1, 2);
- EXPECT_TRUE(client.needs_begin_frame());
+ if (deadline_scheduling_enabled) {
+ EXPECT_EQ(0, client.num_actions_());
+ } else {
+ EXPECT_SINGLE_ACTION("ScheduledActionSendBeginFrameToMainThread", client);
+ }
+
+ client.Reset();
+ scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ if (deadline_scheduling_enabled) {
+ EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2);
+ EXPECT_ACTION("PostBeginFrameDeadlineTask", client, 1, 2);
+ } else {
+ EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client);
+ }
// Commit will release the texture.
client.Reset();
scheduler->FinishCommit();
- EXPECT_SINGLE_ACTION("ScheduledActionCommit", client);
+ EXPECT_ACTION("ScheduledActionCommit", client, 0, 2);
+ EXPECT_ACTION("PostBeginFrameDeadlineTask", client, 1, 2);
EXPECT_TRUE(scheduler->RedrawPending());
- EXPECT_TRUE(client.needs_begin_frame());
// Now we can draw again after the commit happens.
client.Reset();
- scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ scheduler->OnBeginFrameDeadline();
EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2);
EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2);
EXPECT_FALSE(scheduler->RedrawPending());
- EXPECT_FALSE(client.needs_begin_frame());
+ EXPECT_TRUE(client.needs_begin_frame());
+
+ // Make sure we stop requesting BeginFrames if we don't swap.
+ client.Reset();
+ scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client);
client.Reset();
+ scheduler->OnBeginFrameDeadline();
+ EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client);
+ EXPECT_FALSE(client.needs_begin_frame());
}
-TEST(SchedulerTest, TextureAcquisitionCollision) {
+TEST(SchedulerTest, TextureAcquisitionCausesCommitInsteadOfDraw) {
+ bool deadline_scheduling_enabled = false;
+ TextureAcquisitionCausesCommitInsteadOfDraw(deadline_scheduling_enabled);
+}
+
+TEST(SchedulerTest, TextureAcquisitionCausesCommitInsteadOfDraw_Deadline) {
+ bool deadline_scheduling_enabled = true;
+ TextureAcquisitionCausesCommitInsteadOfDraw(deadline_scheduling_enabled);
+}
+
+void TextureAcquisitionCollision(bool deadline_scheduling_enabled) {
FakeSchedulerClient client;
- SchedulerSettings default_scheduler_settings;
- Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
+ SchedulerSettings scheduler_settings;
+ scheduler_settings.deadline_scheduling_enabled = deadline_scheduling_enabled;
+ Scheduler* scheduler = client.CreateScheduler(scheduler_settings);
scheduler->SetCanStart();
scheduler->SetVisible(true);
scheduler->SetCanDraw(true);
EXPECT_SINGLE_ACTION("ScheduledActionBeginOutputSurfaceCreation", client);
- client.Reset();
- scheduler->DidCreateAndInitializeOutputSurface();
+ InitializeOutputSurfaceAndFirstCommit(scheduler);
+ client.Reset();
scheduler->SetNeedsCommit();
+if (deadline_scheduling_enabled) {
+ EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client);
+ } else {
+ EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2);
+ EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2);
+ }
+
+ client.Reset();
scheduler->SetMainThreadNeedsLayerTextures();
- EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 4);
- EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 4);
- EXPECT_ACTION("ScheduledActionAcquireLayerTexturesForMainThread",
- client,
- 2,
- 4);
- EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 3, 4);
+ EXPECT_SINGLE_ACTION(
+ "ScheduledActionAcquireLayerTexturesForMainThread", client);
+
+ client.Reset();
+ scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ if (deadline_scheduling_enabled) {
+ EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2);
+ EXPECT_ACTION("PostBeginFrameDeadlineTask", client, 1, 2);
+ } else {
+ EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client);
+ }
+
client.Reset();
+ scheduler->OnBeginFrameDeadline();
+ EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client);
// Although the compositor cannot draw because textures are locked by main
// thread, we continue requesting SetNeedsBeginFrame in anticipation of the
@@ -321,43 +532,88 @@ TEST(SchedulerTest, TextureAcquisitionCollision) {
// Trigger the commit
scheduler->FinishCommit();
EXPECT_TRUE(client.needs_begin_frame());
- client.Reset();
// Between commit and draw, texture acquisition for main thread delayed,
// and main thread blocks.
+ client.Reset();
scheduler->SetMainThreadNeedsLayerTextures();
EXPECT_EQ(0, client.num_actions_());
- client.Reset();
// No implicit commit is expected.
+ client.Reset();
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client);
+
+ client.Reset();
+ scheduler->OnBeginFrameDeadline();
EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 3);
- EXPECT_ACTION("ScheduledActionAcquireLayerTexturesForMainThread",
- client,
- 1,
- 3);
+ EXPECT_ACTION(
+ "ScheduledActionAcquireLayerTexturesForMainThread", client, 1, 3);
EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 2, 3);
- client.Reset();
+ EXPECT_TRUE(client.needs_begin_frame());
- // Compositor not scheduled to draw because textures are locked by main
+ // The compositor should not draw because textures are locked by main
// thread.
+ client.Reset();
+ scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client);
+ client.Reset();
+ scheduler->OnBeginFrameDeadline();
+ EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client);
EXPECT_FALSE(client.needs_begin_frame());
- // Needs an explicit commit from the main thread.
+ // The impl thread need an explicit commit from the main thread to lock
+ // the textures.
+ client.Reset();
scheduler->SetNeedsCommit();
- EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2);
- EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2);
+ if (deadline_scheduling_enabled) {
+ EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client);
+ } else {
+ EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2);
+ EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2);
+ }
+ EXPECT_TRUE(client.needs_begin_frame());
+
+ client.Reset();
+ scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ if (deadline_scheduling_enabled) {
+ EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2);
+ EXPECT_ACTION("PostBeginFrameDeadlineTask", client, 1, 2);
+ } else {
+ EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client);
+ }
client.Reset();
- // Trigger the commit
+ // Trigger the commit, which will trigger the deadline task early.
scheduler->FinishCommit();
+ EXPECT_ACTION("ScheduledActionCommit", client, 0, 2);
+ EXPECT_ACTION("PostBeginFrameDeadlineTask", client, 1, 2);
EXPECT_TRUE(client.needs_begin_frame());
+ client.Reset();
+
+ // Verify we draw on the next BeginFrame deadline
+ scheduler->OnBeginFrameDeadline();
+ EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client, 0, 2);
+ EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2);
+ EXPECT_TRUE(client.needs_begin_frame());
+ client.Reset();
}
-TEST(SchedulerTest, VisibilitySwitchWithTextureAcquisition) {
+TEST(SchedulerTest, TextureAcquisitionCollision) {
+ bool deadline_scheduling_enabled = false;
+ TextureAcquisitionCollision(deadline_scheduling_enabled);
+}
+
+TEST(SchedulerTest, TextureAcquisitionCollision_Deadline) {
+ bool deadline_scheduling_enabled = true;
+ TextureAcquisitionCollision(deadline_scheduling_enabled);
+}
+
+void VisibilitySwitchWithTextureAcquisition(bool deadline_scheduling_enabled) {
FakeSchedulerClient client;
- SchedulerSettings default_scheduler_settings;
- Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
+ SchedulerSettings scheduler_settings;
+ scheduler_settings.deadline_scheduling_enabled = deadline_scheduling_enabled;
+ Scheduler* scheduler = client.CreateScheduler(scheduler_settings);
scheduler->SetCanStart();
scheduler->SetVisible(true);
scheduler->SetCanDraw(true);
@@ -367,6 +623,10 @@ TEST(SchedulerTest, VisibilitySwitchWithTextureAcquisition) {
scheduler->DidCreateAndInitializeOutputSurface();
scheduler->SetNeedsCommit();
+ if (deadline_scheduling_enabled) {
+ scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ scheduler->OnBeginFrameDeadline();
+ }
scheduler->FinishCommit();
scheduler->SetMainThreadNeedsLayerTextures();
scheduler->SetNeedsCommit();
@@ -376,36 +636,48 @@ TEST(SchedulerTest, VisibilitySwitchWithTextureAcquisition) {
scheduler->SetVisible(false);
EXPECT_SINGLE_ACTION("ScheduledActionAcquireLayerTexturesForMainThread",
client);
- client.Reset();
- // Already sent a begin frame on this current frame, so wait.
+ client.Reset();
scheduler->SetVisible(true);
EXPECT_EQ(0, client.num_actions_());
- client.Reset();
+ EXPECT_TRUE(client.needs_begin_frame());
// Regaining visibility with textures acquired by main thread while
// compositor is waiting for first draw should result in a request
// for a new frame in order to escape a deadlock.
+ client.Reset();
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
EXPECT_ACTION("ScheduledActionSendBeginFrameToMainThread", client, 0, 2);
- EXPECT_ACTION("SetNeedsBeginFrameOnImplThread", client, 1, 2);
+ EXPECT_ACTION("PostBeginFrameDeadlineTask", client, 1, 2);
+}
+
+TEST(SchedulerTest, VisibilitySwitchWithTextureAcquisition) {
+ bool deadline_scheduling_enabled = false;
+ VisibilitySwitchWithTextureAcquisition(deadline_scheduling_enabled);
+}
+
+TEST(SchedulerTest, VisibilitySwitchWithTextureAcquisition_Deadline) {
+ bool deadline_scheduling_enabled = true;
+ VisibilitySwitchWithTextureAcquisition(deadline_scheduling_enabled);
}
class SchedulerClientThatsetNeedsDrawInsideDraw : public FakeSchedulerClient {
public:
virtual void ScheduledActionSendBeginFrameToMainThread() OVERRIDE {}
- virtual ScheduledActionDrawAndSwapResult
- ScheduledActionDrawAndSwapIfPossible() OVERRIDE {
+ virtual DrawSwapReadbackResult ScheduledActionDrawAndSwapIfPossible()
+ OVERRIDE {
// Only SetNeedsRedraw the first time this is called
if (!num_draws_)
scheduler_->SetNeedsRedraw();
return FakeSchedulerClient::ScheduledActionDrawAndSwapIfPossible();
}
- virtual ScheduledActionDrawAndSwapResult ScheduledActionDrawAndSwapForced()
- OVERRIDE {
+ virtual DrawSwapReadbackResult ScheduledActionDrawAndSwapForced() OVERRIDE {
NOTREACHED();
- return ScheduledActionDrawAndSwapResult(true, true);
+ bool did_draw = true;
+ bool did_swap = true;
+ bool did_readback = false;
+ return DrawSwapReadbackResult(did_draw, did_swap, did_readback);
}
virtual void ScheduledActionCommit() OVERRIDE {}
@@ -424,7 +696,8 @@ TEST(SchedulerTest, RequestRedrawInsideDraw) {
scheduler->SetCanStart();
scheduler->SetVisible(true);
scheduler->SetCanDraw(true);
- scheduler->DidCreateAndInitializeOutputSurface();
+ InitializeOutputSurfaceAndFirstCommit(scheduler);
+ client.Reset();
scheduler->SetNeedsRedraw();
EXPECT_TRUE(scheduler->RedrawPending());
@@ -432,11 +705,20 @@ TEST(SchedulerTest, RequestRedrawInsideDraw) {
EXPECT_EQ(0, client.num_draws());
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ scheduler->OnBeginFrameDeadline();
EXPECT_EQ(1, client.num_draws());
EXPECT_TRUE(scheduler->RedrawPending());
EXPECT_TRUE(client.needs_begin_frame());
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ scheduler->OnBeginFrameDeadline();
+ EXPECT_EQ(2, client.num_draws());
+ EXPECT_FALSE(scheduler->RedrawPending());
+ EXPECT_TRUE(client.needs_begin_frame());
+
+ // We stop requesting BeginFrames after a BeginFrame where we don't swap.
+ scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ scheduler->OnBeginFrameDeadline();
EXPECT_EQ(2, client.num_draws());
EXPECT_FALSE(scheduler->RedrawPending());
EXPECT_FALSE(client.needs_begin_frame());
@@ -450,7 +732,8 @@ TEST(SchedulerTest, RequestRedrawInsideFailedDraw) {
scheduler->SetCanStart();
scheduler->SetVisible(true);
scheduler->SetCanDraw(true);
- scheduler->DidCreateAndInitializeOutputSurface();
+ InitializeOutputSurfaceAndFirstCommit(scheduler);
+ client.Reset();
client.SetDrawWillHappen(false);
@@ -461,6 +744,7 @@ TEST(SchedulerTest, RequestRedrawInsideFailedDraw) {
// Fail the draw.
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ scheduler->OnBeginFrameDeadline();
EXPECT_EQ(1, client.num_draws());
// We have a commit pending and the draw failed, and we didn't lose the redraw
@@ -471,6 +755,7 @@ TEST(SchedulerTest, RequestRedrawInsideFailedDraw) {
// Fail the draw again.
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ scheduler->OnBeginFrameDeadline();
EXPECT_EQ(2, client.num_draws());
EXPECT_TRUE(scheduler->CommitPending());
EXPECT_TRUE(scheduler->RedrawPending());
@@ -479,58 +764,86 @@ TEST(SchedulerTest, RequestRedrawInsideFailedDraw) {
// Draw successfully.
client.SetDrawWillHappen(true);
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ scheduler->OnBeginFrameDeadline();
EXPECT_EQ(3, client.num_draws());
EXPECT_TRUE(scheduler->CommitPending());
EXPECT_FALSE(scheduler->RedrawPending());
EXPECT_TRUE(client.needs_begin_frame());
}
-class SchedulerClientThatsetNeedsCommitInsideDraw : public FakeSchedulerClient {
+class SchedulerClientThatSetNeedsCommitInsideDraw : public FakeSchedulerClient {
public:
+ SchedulerClientThatSetNeedsCommitInsideDraw()
+ : set_needs_commit_on_next_draw_(false) {}
+
virtual void ScheduledActionSendBeginFrameToMainThread() OVERRIDE {}
- virtual ScheduledActionDrawAndSwapResult
- ScheduledActionDrawAndSwapIfPossible() OVERRIDE {
+ virtual DrawSwapReadbackResult ScheduledActionDrawAndSwapIfPossible()
+ OVERRIDE {
// Only SetNeedsCommit the first time this is called
- if (!num_draws_)
+ if (set_needs_commit_on_next_draw_) {
scheduler_->SetNeedsCommit();
+ set_needs_commit_on_next_draw_ = false;
+ }
return FakeSchedulerClient::ScheduledActionDrawAndSwapIfPossible();
}
- virtual ScheduledActionDrawAndSwapResult ScheduledActionDrawAndSwapForced()
- OVERRIDE {
+ virtual DrawSwapReadbackResult ScheduledActionDrawAndSwapForced() OVERRIDE {
NOTREACHED();
- return ScheduledActionDrawAndSwapResult(true, true);
+ bool did_draw = true;
+ bool did_swap = false;
+ bool did_readback = false;
+ return DrawSwapReadbackResult(did_draw, did_swap, did_readback);
}
virtual void ScheduledActionCommit() OVERRIDE {}
virtual void ScheduledActionBeginOutputSurfaceCreation() OVERRIDE {}
virtual void DidAnticipatedDrawTimeChange(base::TimeTicks) OVERRIDE {}
+
+ void SetNeedsCommitOnNextDraw() { set_needs_commit_on_next_draw_ = true; }
+
+ private:
+ bool set_needs_commit_on_next_draw_;
};
// Tests for the scheduler infinite-looping on SetNeedsCommit requests that
// happen inside a ScheduledActionDrawAndSwap
TEST(SchedulerTest, RequestCommitInsideDraw) {
- SchedulerClientThatsetNeedsCommitInsideDraw client;
+ SchedulerClientThatSetNeedsCommitInsideDraw client;
SchedulerSettings default_scheduler_settings;
Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
scheduler->SetCanStart();
scheduler->SetVisible(true);
scheduler->SetCanDraw(true);
- scheduler->DidCreateAndInitializeOutputSurface();
+ InitializeOutputSurfaceAndFirstCommit(scheduler);
+ client.Reset();
+ EXPECT_FALSE(client.needs_begin_frame());
scheduler->SetNeedsRedraw();
EXPECT_TRUE(scheduler->RedrawPending());
EXPECT_EQ(0, client.num_draws());
EXPECT_TRUE(client.needs_begin_frame());
+ client.SetNeedsCommitOnNextDraw();
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ client.SetNeedsCommitOnNextDraw();
+ scheduler->OnBeginFrameDeadline();
EXPECT_EQ(1, client.num_draws());
EXPECT_TRUE(scheduler->CommitPending());
EXPECT_TRUE(client.needs_begin_frame());
scheduler->FinishCommit();
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
- EXPECT_EQ(2, client.num_draws());;
+ scheduler->OnBeginFrameDeadline();
+ EXPECT_EQ(2, client.num_draws());
+
+ EXPECT_FALSE(scheduler->RedrawPending());
+ EXPECT_FALSE(scheduler->CommitPending());
+ EXPECT_TRUE(client.needs_begin_frame());
+
+ // We stop requesting BeginFrames after a BeginFrame where we don't swap.
+ scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ scheduler->OnBeginFrameDeadline();
+ EXPECT_EQ(2, client.num_draws());
EXPECT_FALSE(scheduler->RedrawPending());
EXPECT_FALSE(scheduler->CommitPending());
EXPECT_FALSE(client.needs_begin_frame());
@@ -544,7 +857,8 @@ TEST(SchedulerTest, RequestCommitInsideFailedDraw) {
scheduler->SetCanStart();
scheduler->SetVisible(true);
scheduler->SetCanDraw(true);
- scheduler->DidCreateAndInitializeOutputSurface();
+ InitializeOutputSurfaceAndFirstCommit(scheduler);
+ client.Reset();
client.SetDrawWillHappen(false);
@@ -555,6 +869,7 @@ TEST(SchedulerTest, RequestCommitInsideFailedDraw) {
// Fail the draw.
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ scheduler->OnBeginFrameDeadline();
EXPECT_EQ(1, client.num_draws());
// We have a commit pending and the draw failed, and we didn't lose the commit
@@ -565,6 +880,7 @@ TEST(SchedulerTest, RequestCommitInsideFailedDraw) {
// Fail the draw again.
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ scheduler->OnBeginFrameDeadline();
EXPECT_EQ(2, client.num_draws());
EXPECT_TRUE(scheduler->CommitPending());
EXPECT_TRUE(scheduler->RedrawPending());
@@ -573,6 +889,7 @@ TEST(SchedulerTest, RequestCommitInsideFailedDraw) {
// Draw successfully.
client.SetDrawWillHappen(true);
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ scheduler->OnBeginFrameDeadline();
EXPECT_EQ(3, client.num_draws());
EXPECT_TRUE(scheduler->CommitPending());
EXPECT_FALSE(scheduler->RedrawPending());
@@ -580,13 +897,14 @@ TEST(SchedulerTest, RequestCommitInsideFailedDraw) {
}
TEST(SchedulerTest, NoSwapWhenDrawFails) {
- SchedulerClientThatsetNeedsCommitInsideDraw client;
+ SchedulerClientThatSetNeedsCommitInsideDraw client;
SchedulerSettings default_scheduler_settings;
Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
scheduler->SetCanStart();
scheduler->SetVisible(true);
scheduler->SetCanDraw(true);
- scheduler->DidCreateAndInitializeOutputSurface();
+ InitializeOutputSurfaceAndFirstCommit(scheduler);
+ client.Reset();
scheduler->SetNeedsRedraw();
EXPECT_TRUE(scheduler->RedrawPending());
@@ -594,7 +912,9 @@ TEST(SchedulerTest, NoSwapWhenDrawFails) {
EXPECT_EQ(0, client.num_draws());
// Draw successfully, this starts a new frame.
+ client.SetNeedsCommitOnNextDraw();
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ scheduler->OnBeginFrameDeadline();
EXPECT_EQ(1, client.num_draws());
scheduler->SetNeedsRedraw();
@@ -603,7 +923,9 @@ TEST(SchedulerTest, NoSwapWhenDrawFails) {
// Fail to draw, this should not start a frame.
client.SetDrawWillHappen(false);
+ client.SetNeedsCommitOnNextDraw();
scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ scheduler->OnBeginFrameDeadline();
EXPECT_EQ(2, client.num_draws());
}
@@ -616,11 +938,142 @@ TEST(SchedulerTest, NoSwapWhenSwapFailsDuringForcedCommit) {
client.SetDrawWillHappen(true);
client.SetSwapWillHappenIfDrawHappens(false);
- // Get the compositor to do a ScheduledActionDrawAndSwapForced.
+ // Get the compositor to do a ScheduledActionDrawAndReadback.
scheduler->SetCanDraw(true);
scheduler->SetNeedsRedraw();
- scheduler->SetNeedsForcedRedraw();
- EXPECT_TRUE(client.HasAction("ScheduledActionDrawAndSwapForced"));
+ scheduler->SetNeedsForcedCommitForReadback();
+ scheduler->FinishCommit();
+ EXPECT_TRUE(client.HasAction("ScheduledActionDrawAndReadback"));
+}
+
+TEST(SchedulerTest, BackToBackReadbackAllowed) {
+ // Some clients call readbacks twice in a row before the replacement
+ // commit comes in. Make sure it is allowed.
+ FakeSchedulerClient client;
+ SchedulerSettings default_scheduler_settings;
+ Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
+
+ // Get the compositor to do 2 ScheduledActionDrawAndReadbacks before
+ // the replacement commit comes in.
+ scheduler->SetCanDraw(true);
+ scheduler->SetNeedsRedraw();
+ scheduler->SetNeedsForcedCommitForReadback();
+ scheduler->FinishCommit();
+ EXPECT_TRUE(client.HasAction("ScheduledActionDrawAndReadback"));
+
+ client.Reset();
+ scheduler->SetNeedsForcedCommitForReadback();
+ scheduler->FinishCommit();
+ EXPECT_TRUE(client.HasAction("ScheduledActionDrawAndReadback"));
+
+ // The replacement commit comes in after 2 readbacks.
+ client.Reset();
+ scheduler->FinishCommit();
+}
+
+
+class SchedulerClientNeedsManageTilesInDraw : public FakeSchedulerClient {
+ public:
+ virtual DrawSwapReadbackResult ScheduledActionDrawAndSwapIfPossible()
+ OVERRIDE {
+ scheduler_->SetNeedsManageTiles();
+ return FakeSchedulerClient::ScheduledActionDrawAndSwapIfPossible();
+ }
+};
+
+// Test manage tiles is independant of draws.
+TEST(SchedulerTest, ManageTiles) {
+ SchedulerClientNeedsManageTilesInDraw client;
+ SchedulerSettings default_scheduler_settings;
+ Scheduler* scheduler = client.CreateScheduler(default_scheduler_settings);
+ scheduler->SetCanStart();
+ scheduler->SetVisible(true);
+ scheduler->SetCanDraw(true);
+ InitializeOutputSurfaceAndFirstCommit(scheduler);
+
+ // Request both draw and manage tiles. ManageTiles shouldn't
+ // be trigged until BeginFrame.
+ client.Reset();
+ scheduler->SetNeedsManageTiles();
+ scheduler->SetNeedsRedraw();
+ EXPECT_TRUE(scheduler->RedrawPending());
+ EXPECT_TRUE(scheduler->ManageTilesPending());
+ EXPECT_TRUE(client.needs_begin_frame());
+ EXPECT_EQ(0, client.num_draws());
+ EXPECT_FALSE(client.HasAction("ScheduledActionManageTiles"));
+ EXPECT_FALSE(client.HasAction("ScheduledActionDrawAndSwapIfPossible"));
+
+ // We have no immediate actions to perform, so the BeginFrame should post
+ // the deadline task.
+ client.Reset();
+ scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client);
+
+ // On the deadline, he actions should have occured in the right order.
+ client.Reset();
+ scheduler->OnBeginFrameDeadline();
+ EXPECT_EQ(1, client.num_draws());
+ EXPECT_TRUE(client.HasAction("ScheduledActionDrawAndSwapIfPossible"));
+ EXPECT_TRUE(client.HasAction("ScheduledActionManageTiles"));
+ EXPECT_LT(client.ActionIndex("ScheduledActionDrawAndSwapIfPossible"),
+ client.ActionIndex("ScheduledActionManageTiles"));
+ EXPECT_FALSE(scheduler->RedrawPending());
+ EXPECT_FALSE(scheduler->ManageTilesPending());
+
+ // Request a draw. We don't need a ManageTiles yet.
+ client.Reset();
+ scheduler->SetNeedsRedraw();
+ EXPECT_TRUE(scheduler->RedrawPending());
+ EXPECT_FALSE(scheduler->ManageTilesPending());
+ EXPECT_TRUE(client.needs_begin_frame());
+ EXPECT_EQ(0, client.num_draws());
+
+ // We have no immediate actions to perform, so the BeginFrame should post
+ // the deadline task.
+ client.Reset();
+ scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client);
+
+ // Draw. The draw will trigger SetNeedsManageTiles, and
+ // then the ManageTiles action will be triggered after the Draw.
+ // Afterwards, neither a draw nor ManageTiles are pending.
+ client.Reset();
+ scheduler->OnBeginFrameDeadline();
+ EXPECT_EQ(1, client.num_draws());
+ EXPECT_TRUE(client.HasAction("ScheduledActionDrawAndSwapIfPossible"));
+ EXPECT_TRUE(client.HasAction("ScheduledActionManageTiles"));
+ EXPECT_LT(client.ActionIndex("ScheduledActionDrawAndSwapIfPossible"),
+ client.ActionIndex("ScheduledActionManageTiles"));
+ EXPECT_FALSE(scheduler->RedrawPending());
+ EXPECT_FALSE(scheduler->ManageTilesPending());
+
+ // We need a BeginFrame where we don't swap to go idle.
+ client.Reset();
+ scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client);
+ client.Reset();
+ scheduler->OnBeginFrameDeadline();
+ EXPECT_SINGLE_ACTION("SetNeedsBeginFrameOnImplThread", client);;
+ EXPECT_EQ(0, client.num_draws());
+
+ // Now trigger a ManageTiles outside of a draw. We will then need
+ // a begin-frame for the ManageTiles, but we don't need a draw.
+ client.Reset();
+ EXPECT_FALSE(client.needs_begin_frame());
+ scheduler->SetNeedsManageTiles();
+ EXPECT_TRUE(client.needs_begin_frame());
+ EXPECT_TRUE(scheduler->ManageTilesPending());
+ EXPECT_FALSE(scheduler->RedrawPending());
+
+ // BeginFrame. There will be no draw, only ManageTiles.
+ client.Reset();
+ scheduler->BeginFrame(BeginFrameArgs::CreateForTesting());
+ EXPECT_SINGLE_ACTION("PostBeginFrameDeadlineTask", client);
+ client.Reset();
+ scheduler->OnBeginFrameDeadline();
+ EXPECT_EQ(0, client.num_draws());
+ EXPECT_FALSE(client.HasAction("ScheduledActionDrawAndSwapIfPossible"));
+ EXPECT_TRUE(client.HasAction("ScheduledActionManageTiles"));
}
} // namespace
diff --git a/chromium/cc/scheduler/texture_uploader.cc b/chromium/cc/scheduler/texture_uploader.cc
index 920c38e6692..677029081fc 100644
--- a/chromium/cc/scheduler/texture_uploader.cc
+++ b/chromium/cc/scheduler/texture_uploader.cc
@@ -7,7 +7,6 @@
#include <algorithm>
#include <vector>
-#include "base/debug/alias.h"
#include "base/debug/trace_event.h"
#include "base/metrics/histogram.h"
#include "cc/base/util.h"
@@ -135,7 +134,7 @@ void TextureUploader::Upload(const uint8* image,
gfx::Rect image_rect,
gfx::Rect source_rect,
gfx::Vector2d dest_offset,
- GLenum format,
+ ResourceFormat format,
gfx::Size size) {
CHECK(image_rect.Contains(source_rect));
@@ -178,39 +177,23 @@ void TextureUploader::UploadWithTexSubImage(const uint8* image,
gfx::Rect image_rect,
gfx::Rect source_rect,
gfx::Vector2d dest_offset,
- GLenum format) {
- // Instrumentation to debug issue 156107
- int source_rect_x = source_rect.x();
- int source_rect_y = source_rect.y();
- int source_rect_width = source_rect.width();
- int source_rect_height = source_rect.height();
- int image_rect_x = image_rect.x();
- int image_rect_y = image_rect.y();
- int image_rect_width = image_rect.width();
- int image_rect_height = image_rect.height();
- int dest_offset_x = dest_offset.x();
- int dest_offset_y = dest_offset.y();
- base::debug::Alias(&image);
- base::debug::Alias(&source_rect_x);
- base::debug::Alias(&source_rect_y);
- base::debug::Alias(&source_rect_width);
- base::debug::Alias(&source_rect_height);
- base::debug::Alias(&image_rect_x);
- base::debug::Alias(&image_rect_y);
- base::debug::Alias(&image_rect_width);
- base::debug::Alias(&image_rect_height);
- base::debug::Alias(&dest_offset_x);
- base::debug::Alias(&dest_offset_y);
+ ResourceFormat format) {
TRACE_EVENT0("cc", "TextureUploader::UploadWithTexSubImage");
+ // Early-out if this is a no-op, and assert that |image| be valid if this is
+ // not a no-op.
+ if (source_rect.IsEmpty())
+ return;
+ DCHECK(image);
+
// Offset from image-rect to source-rect.
gfx::Vector2d offset(source_rect.origin() - image_rect.origin());
const uint8* pixel_source;
- unsigned int bytes_per_pixel = Resource::BytesPerPixel(format);
+ unsigned bytes_per_pixel = ResourceProvider::BytesPerPixel(format);
// Use 4-byte row alignment (OpenGL default) for upload performance.
// Assuming that GL_UNPACK_ALIGNMENT has not changed from default.
- unsigned int upload_image_stride =
+ unsigned upload_image_stride =
RoundUp(bytes_per_pixel * source_rect.width(), 4u);
if (upload_image_stride == image_rect.width() * bytes_per_pixel &&
@@ -239,8 +222,8 @@ void TextureUploader::UploadWithTexSubImage(const uint8* image,
dest_offset.y(),
source_rect.width(),
source_rect.height(),
- format,
- GL_UNSIGNED_BYTE,
+ ResourceProvider::GetGLDataFormat(format),
+ ResourceProvider::GetGLDataType(format),
pixel_source);
}
@@ -248,39 +231,22 @@ void TextureUploader::UploadWithMapTexSubImage(const uint8* image,
gfx::Rect image_rect,
gfx::Rect source_rect,
gfx::Vector2d dest_offset,
- GLenum format) {
- // Instrumentation to debug issue 156107
- int source_rect_x = source_rect.x();
- int source_rect_y = source_rect.y();
- int source_rect_width = source_rect.width();
- int source_rect_height = source_rect.height();
- int image_rect_x = image_rect.x();
- int image_rect_y = image_rect.y();
- int image_rect_width = image_rect.width();
- int image_rect_height = image_rect.height();
- int dest_offset_x = dest_offset.x();
- int dest_offset_y = dest_offset.y();
- base::debug::Alias(&image);
- base::debug::Alias(&source_rect_x);
- base::debug::Alias(&source_rect_y);
- base::debug::Alias(&source_rect_width);
- base::debug::Alias(&source_rect_height);
- base::debug::Alias(&image_rect_x);
- base::debug::Alias(&image_rect_y);
- base::debug::Alias(&image_rect_width);
- base::debug::Alias(&image_rect_height);
- base::debug::Alias(&dest_offset_x);
- base::debug::Alias(&dest_offset_y);
-
+ ResourceFormat format) {
TRACE_EVENT0("cc", "TextureUploader::UploadWithMapTexSubImage");
+ // Early-out if this is a no-op, and assert that |image| be valid if this is
+ // not a no-op.
+ if (source_rect.IsEmpty())
+ return;
+ DCHECK(image);
+
// Offset from image-rect to source-rect.
gfx::Vector2d offset(source_rect.origin() - image_rect.origin());
- unsigned int bytes_per_pixel = Resource::BytesPerPixel(format);
+ unsigned bytes_per_pixel = ResourceProvider::BytesPerPixel(format);
// Use 4-byte row alignment (OpenGL default) for upload performance.
// Assuming that GL_UNPACK_ALIGNMENT has not changed from default.
- unsigned int upload_image_stride =
+ unsigned upload_image_stride =
RoundUp(bytes_per_pixel * source_rect.width(), 4u);
// Upload tile data via a mapped transfer buffer
@@ -291,8 +257,10 @@ void TextureUploader::UploadWithMapTexSubImage(const uint8* image,
dest_offset.y(),
source_rect.width(),
source_rect.height(),
- format,
- GL_UNSIGNED_BYTE,
+ ResourceProvider::GetGLDataFormat(
+ format),
+ ResourceProvider::GetGLDataType(
+ format),
GL_WRITE_ONLY));
if (!pixel_dest) {
diff --git a/chromium/cc/scheduler/texture_uploader.h b/chromium/cc/scheduler/texture_uploader.h
index 1457bedb727..a131ba04f9e 100644
--- a/chromium/cc/scheduler/texture_uploader.h
+++ b/chromium/cc/scheduler/texture_uploader.h
@@ -11,7 +11,7 @@
#include "base/memory/scoped_ptr.h"
#include "cc/base/cc_export.h"
#include "cc/base/scoped_ptr_deque.h"
-#include "third_party/khronos/GLES2/gl2.h"
+#include "cc/resources/resource_provider.h"
namespace WebKit { class WebGraphicsContext3D; }
@@ -46,7 +46,7 @@ class CC_EXPORT TextureUploader {
gfx::Rect content_rect,
gfx::Rect source_rect,
gfx::Vector2d dest_offset,
- GLenum format,
+ ResourceFormat format,
gfx::Size size);
void Flush();
@@ -97,12 +97,12 @@ class CC_EXPORT TextureUploader {
gfx::Rect image_rect,
gfx::Rect source_rect,
gfx::Vector2d dest_offset,
- GLenum format);
+ ResourceFormat format);
void UploadWithMapTexSubImage(const uint8* image,
gfx::Rect image_rect,
gfx::Rect source_rect,
gfx::Vector2d dest_offset,
- GLenum format);
+ ResourceFormat format);
WebKit::WebGraphicsContext3D* context_;
ScopedPtrDeque<Query> pending_queries_;
diff --git a/chromium/cc/scheduler/texture_uploader_unittest.cc b/chromium/cc/scheduler/texture_uploader_unittest.cc
index f4b4ce69b2e..68413df92e1 100644
--- a/chromium/cc/scheduler/texture_uploader_unittest.cc
+++ b/chromium/cc/scheduler/texture_uploader_unittest.cc
@@ -5,8 +5,8 @@
#include "cc/scheduler/texture_uploader.h"
#include "cc/base/util.h"
+#include "cc/debug/test_web_graphics_context_3d.h"
#include "cc/resources/prioritized_resource.h"
-#include "cc/test/test_web_graphics_context_3d.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/khronos/GLES2/gl2.h"
@@ -151,7 +151,7 @@ class TestWebGraphicsContext3DTextureUpload : public TestWebGraphicsContext3D {
};
void UploadTexture(TextureUploader* uploader,
- WGC3Denum format,
+ ResourceFormat format,
gfx::Size size,
const uint8* data) {
uploader->Upload(data,
@@ -170,17 +170,17 @@ TEST(TextureUploaderTest, NumBlockingUploads) {
fake_context->SetResultAvailable(0);
EXPECT_EQ(0u, uploader->NumBlockingUploads());
- UploadTexture(uploader.get(), GL_RGBA, gfx::Size(), NULL);
+ UploadTexture(uploader.get(), RGBA_8888, gfx::Size(), NULL);
EXPECT_EQ(1u, uploader->NumBlockingUploads());
- UploadTexture(uploader.get(), GL_RGBA, gfx::Size(), NULL);
+ UploadTexture(uploader.get(), RGBA_8888, gfx::Size(), NULL);
EXPECT_EQ(2u, uploader->NumBlockingUploads());
fake_context->SetResultAvailable(1);
EXPECT_EQ(0u, uploader->NumBlockingUploads());
- UploadTexture(uploader.get(), GL_RGBA, gfx::Size(), NULL);
+ UploadTexture(uploader.get(), RGBA_8888, gfx::Size(), NULL);
EXPECT_EQ(0u, uploader->NumBlockingUploads());
- UploadTexture(uploader.get(), GL_RGBA, gfx::Size(), NULL);
- UploadTexture(uploader.get(), GL_RGBA, gfx::Size(), NULL);
+ UploadTexture(uploader.get(), RGBA_8888, gfx::Size(), NULL);
+ UploadTexture(uploader.get(), RGBA_8888, gfx::Size(), NULL);
EXPECT_EQ(0u, uploader->NumBlockingUploads());
}
@@ -192,18 +192,18 @@ TEST(TextureUploaderTest, MarkPendingUploadsAsNonBlocking) {
fake_context->SetResultAvailable(0);
EXPECT_EQ(0u, uploader->NumBlockingUploads());
- UploadTexture(uploader.get(), GL_RGBA, gfx::Size(), NULL);
- UploadTexture(uploader.get(), GL_RGBA, gfx::Size(), NULL);
+ UploadTexture(uploader.get(), RGBA_8888, gfx::Size(), NULL);
+ UploadTexture(uploader.get(), RGBA_8888, gfx::Size(), NULL);
EXPECT_EQ(2u, uploader->NumBlockingUploads());
uploader->MarkPendingUploadsAsNonBlocking();
EXPECT_EQ(0u, uploader->NumBlockingUploads());
- UploadTexture(uploader.get(), GL_RGBA, gfx::Size(), NULL);
+ UploadTexture(uploader.get(), RGBA_8888, gfx::Size(), NULL);
EXPECT_EQ(1u, uploader->NumBlockingUploads());
fake_context->SetResultAvailable(1);
EXPECT_EQ(0u, uploader->NumBlockingUploads());
- UploadTexture(uploader.get(), GL_RGBA, gfx::Size(), NULL);
+ UploadTexture(uploader.get(), RGBA_8888, gfx::Size(), NULL);
uploader->MarkPendingUploadsAsNonBlocking();
EXPECT_EQ(0u, uploader->NumBlockingUploads());
}
@@ -222,7 +222,7 @@ TEST(TextureUploaderTest, UploadContentsTest) {
buffer[i * 4 * 256] = 0x1;
buffer[(i + 1) * 4 * 256 - 1] = 0x2;
}
- UploadTexture(uploader.get(), GL_RGBA, gfx::Size(256, 256), buffer);
+ UploadTexture(uploader.get(), RGBA_8888, gfx::Size(256, 256), buffer);
// Upload a tightly packed 41x43 RGBA texture.
memset(buffer, 0, sizeof(buffer));
@@ -231,7 +231,7 @@ TEST(TextureUploaderTest, UploadContentsTest) {
buffer[i * 4 * 41] = 0x1;
buffer[(i + 1) * 4 * 41 - 1] = 0x2;
}
- UploadTexture(uploader.get(), GL_RGBA, gfx::Size(41, 43), buffer);
+ UploadTexture(uploader.get(), RGBA_8888, gfx::Size(41, 43), buffer);
// Upload a tightly packed 82x86 LUMINANCE texture.
memset(buffer, 0, sizeof(buffer));
@@ -240,7 +240,7 @@ TEST(TextureUploaderTest, UploadContentsTest) {
buffer[i * 1 * 82] = 0x1;
buffer[(i + 1) * 82 - 1] = 0x2;
}
- UploadTexture(uploader.get(), GL_LUMINANCE, gfx::Size(82, 86), buffer);
+ UploadTexture(uploader.get(), LUMINANCE_8, gfx::Size(82, 86), buffer);
}
} // namespace
diff --git a/chromium/cc/scheduler/time_source.h b/chromium/cc/scheduler/time_source.h
index c02d4901a97..e45ffbb95aa 100644
--- a/chromium/cc/scheduler/time_source.h
+++ b/chromium/cc/scheduler/time_source.h
@@ -27,7 +27,12 @@ class TimeSourceClient {
class CC_EXPORT TimeSource : public base::RefCounted<TimeSource> {
public:
virtual void SetClient(TimeSourceClient* client) = 0;
- virtual void SetActive(bool active) = 0;
+
+ // If transitioning from not active to active, SetActive will return the
+ // timestamp of the most recenly missed tick that did not have OnTimerTick
+ // called.
+ virtual base::TimeTicks SetActive(bool active) = 0;
+
virtual bool Active() const = 0;
virtual void SetTimebaseAndInterval(base::TimeTicks timebase,
base::TimeDelta interval) = 0;
diff --git a/chromium/cc/trees/blocking_task_runner.cc b/chromium/cc/trees/blocking_task_runner.cc
new file mode 100644
index 00000000000..576aa9b1bf9
--- /dev/null
+++ b/chromium/cc/trees/blocking_task_runner.cc
@@ -0,0 +1,105 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/trees/blocking_task_runner.h"
+
+#include <utility>
+
+#include "base/logging.h"
+#include "base/memory/singleton.h"
+#include "base/message_loop/message_loop_proxy.h"
+
+namespace cc {
+
+typedef std::pair<base::SingleThreadTaskRunner*,
+ scoped_refptr<BlockingTaskRunner> > TaskRunnerPair;
+
+struct TaskRunnerPairs {
+ static TaskRunnerPairs* GetInstance() {
+ return Singleton<TaskRunnerPairs>::get();
+ }
+
+ base::Lock lock;
+ std::vector<TaskRunnerPair> pairs;
+
+ private:
+ friend struct DefaultSingletonTraits<TaskRunnerPairs>;
+};
+
+// static
+scoped_refptr<BlockingTaskRunner> BlockingTaskRunner::current() {
+ TaskRunnerPairs* task_runners = TaskRunnerPairs::GetInstance();
+
+ base::AutoLock lock(task_runners->lock);
+
+ for (size_t i = 0; i < task_runners->pairs.size(); ++i) {
+ if (task_runners->pairs[i].first->HasOneRef()) {
+ // The SingleThreadTaskRunner is kept alive by its MessageLoop, and we
+ // hold a second reference in the TaskRunnerPairs array. If the
+ // SingleThreadTaskRunner has one ref, then it is being held alive only
+ // by the BlockingTaskRunner and the MessageLoop is gone, so drop the
+ // BlockingTaskRunner from the TaskRunnerPairs array along with the
+ // SingleThreadTaskRunner.
+ task_runners->pairs.erase(task_runners->pairs.begin() + i);
+ --i;
+ }
+ }
+
+ scoped_refptr<base::SingleThreadTaskRunner> current =
+ base::MessageLoopProxy::current();
+ for (size_t i = 0; i < task_runners->pairs.size(); ++i) {
+ if (task_runners->pairs[i].first == current.get())
+ return task_runners->pairs[i].second.get();
+ }
+
+ scoped_refptr<BlockingTaskRunner> runner = new BlockingTaskRunner(current);
+ task_runners->pairs.push_back(TaskRunnerPair(current, runner));
+ return runner;
+}
+
+BlockingTaskRunner::BlockingTaskRunner(
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+ : task_runner_(task_runner), capture_(0) {}
+
+BlockingTaskRunner::~BlockingTaskRunner() {}
+
+bool BlockingTaskRunner::PostTask(const tracked_objects::Location& from_here,
+ const base::Closure& task) {
+ base::AutoLock lock(lock_);
+ if (!capture_)
+ return task_runner_->PostTask(from_here, task);
+ captured_tasks_.push_back(task);
+ return true;
+}
+
+void BlockingTaskRunner::SetCapture(bool capture) {
+ DCHECK(BelongsToCurrentThread());
+
+ std::vector<base::Closure> tasks;
+
+ {
+ base::AutoLock lock(lock_);
+ capture_ += capture ? 1 : -1;
+ DCHECK_GE(capture_, 0);
+
+ if (capture_)
+ return;
+
+ // We're done capturing, so grab all the captured tasks and run them.
+ tasks.swap(captured_tasks_);
+ }
+ for (size_t i = 0; i < tasks.size(); ++i)
+ tasks[i].Run();
+}
+
+BlockingTaskRunner::CapturePostTasks::CapturePostTasks()
+ : blocking_runner_(BlockingTaskRunner::current()) {
+ blocking_runner_->SetCapture(true);
+}
+
+BlockingTaskRunner::CapturePostTasks::~CapturePostTasks() {
+ blocking_runner_->SetCapture(false);
+}
+
+} // namespace cc
diff --git a/chromium/cc/trees/blocking_task_runner.h b/chromium/cc/trees/blocking_task_runner.h
new file mode 100644
index 00000000000..28633171c20
--- /dev/null
+++ b/chromium/cc/trees/blocking_task_runner.h
@@ -0,0 +1,94 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_TREES_BLOCKING_TASK_RUNNER_H_
+#define CC_TREES_BLOCKING_TASK_RUNNER_H_
+
+#include <vector>
+
+#include "base/location.h"
+#include "base/memory/ref_counted.h"
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/lock.h"
+#include "cc/base/cc_export.h"
+
+namespace cc {
+
+// This class wraps a SingleThreadTaskRunner but allows posted tasks to be
+// run without a round trip through the message loop. This shortcutting
+// removes guarantees about ordering. Tasks posted while the
+// BlockingTaskRunner is in a capturing state will run in order, and tasks
+// posted while the BlockingTaskRunner is /not/ in a capturing state will
+// run in order, but the two sets of tasks will *not* run in order relative
+// to when they were posted.
+//
+// To use this class, post tasks to the task runner returned by
+// BlockingTaskRunner::current() on the thread you want the tasks to run.
+// Hold a reference to the BlockingTaskRunner as long as you intend to
+// post tasks to it.
+//
+// Then, on the thread from which the BlockingTaskRunner was created, you
+// may instantiate a BlockingTaskRunner::CapturePostTasks. While this object
+// exists, the task runner will collect any PostTasks called on it, posting
+// tasks to that thread from anywhere. This CapturePostTasks object provides
+// a window in time where tasks can shortcut past the MessageLoop. As soon
+// as the CapturePostTasks object is destroyed (goes out of scope), all
+// tasks that had been posted to the thread during the window will be exectuted
+// immediately.
+//
+// Beware of re-entrancy, make sure the CapturePostTasks object is destroyed at
+// a time when it makes sense for the embedder to call arbitrary things.
+class CC_EXPORT BlockingTaskRunner
+ : public base::RefCountedThreadSafe<BlockingTaskRunner> {
+ public:
+ // Returns the BlockingTaskRunner for the current thread, creating one if
+ // necessary.
+ static scoped_refptr<BlockingTaskRunner> current();
+
+ // While an object of this type is held alive on a thread, any tasks
+ // posted to the thread will be captured and run as soon as the object
+ // is destroyed, shortcutting past the MessageLoop.
+ class CC_EXPORT CapturePostTasks {
+ public:
+ CapturePostTasks();
+ ~CapturePostTasks();
+
+ private:
+ scoped_refptr<BlockingTaskRunner> blocking_runner_;
+
+ DISALLOW_COPY_AND_ASSIGN(CapturePostTasks);
+ };
+
+ // True if tasks posted to the BlockingTaskRunner will run on the current
+ // thread.
+ bool BelongsToCurrentThread() {
+ return task_runner_->BelongsToCurrentThread();
+ }
+
+ // Posts a task using the contained SingleThreadTaskRunner unless |capture_|
+ // is true. When |capture_| is true, tasks posted will be caught and stored
+ // until the capturing stops. At that time the tasks will be run directly
+ // instead of being posted to the SingleThreadTaskRunner.
+ bool PostTask(const tracked_objects::Location& from_here,
+ const base::Closure& task);
+
+ private:
+ friend class base::RefCountedThreadSafe<BlockingTaskRunner>;
+
+ explicit BlockingTaskRunner(
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+ virtual ~BlockingTaskRunner();
+
+ void SetCapture(bool capture);
+
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
+ base::Lock lock_;
+ int capture_;
+ std::vector<base::Closure> captured_tasks_;
+};
+
+} // namespace cc
+
+#endif // CC_TREES_BLOCKING_TASK_RUNNER_H_
diff --git a/chromium/cc/trees/damage_tracker_unittest.cc b/chromium/cc/trees/damage_tracker_unittest.cc
index 9e4537974d9..8a862074410 100644
--- a/chromium/cc/trees/damage_tracker_unittest.cc
+++ b/chromium/cc/trees/damage_tracker_unittest.cc
@@ -340,14 +340,12 @@ TEST_F(DamageTrackerTest, VerifyDamageForTransformedLayer) {
// should increase the size of the expected rect by sqrt(2), centered around
// (100, 100). The old exposed region should be fully contained in the new
// region.
- double expected_width = 30.0 * sqrt(2.0);
- double expected_position = 100.0 - 0.5 * expected_width;
- gfx::RectF expected_rect(expected_position,
- expected_position,
- expected_width,
- expected_width);
+ float expected_width = 30.f * sqrt(2.f);
+ float expected_position = 100.f - 0.5f * expected_width;
+ gfx::RectF expected_rect(
+ expected_position, expected_position, expected_width, expected_width);
root_damage_rect =
- root->render_surface()->damage_tracker()->current_damage_rect();
+ root->render_surface()->damage_tracker()->current_damage_rect();
EXPECT_FLOAT_RECT_EQ(expected_rect, root_damage_rect);
}
diff --git a/chromium/cc/trees/layer_tree_host.cc b/chromium/cc/trees/layer_tree_host.cc
index 9956f4c7359..f8f0d614440 100644
--- a/chromium/cc/trees/layer_tree_host.cc
+++ b/chromium/cc/trees/layer_tree_host.cc
@@ -26,8 +26,8 @@
#include "cc/layers/heads_up_display_layer_impl.h"
#include "cc/layers/layer.h"
#include "cc/layers/layer_iterator.h"
+#include "cc/layers/painted_scrollbar_layer.h"
#include "cc/layers/render_surface.h"
-#include "cc/layers/scrollbar_layer.h"
#include "cc/resources/prioritized_resource_manager.h"
#include "cc/resources/ui_resource_client.h"
#include "cc/trees/layer_tree_host_client.h"
@@ -47,7 +47,7 @@ static int s_num_layer_tree_instances;
namespace cc {
RendererCapabilities::RendererCapabilities()
- : best_texture_format(0),
+ : best_texture_format(RGBA_8888),
using_partial_swap(false),
using_set_visibility(false),
using_egl_image(false),
@@ -56,12 +56,36 @@ RendererCapabilities::RendererCapabilities()
max_texture_size(0),
avoid_pow2_textures(false),
using_map_image(false),
- using_shared_memory_resources(false) {}
+ using_shared_memory_resources(false),
+ using_discard_framebuffer(false) {}
RendererCapabilities::~RendererCapabilities() {}
-UIResourceRequest::UIResourceRequest()
- : type(UIResourceInvalidRequest), id(0), bitmap(NULL) {}
+UIResourceRequest::UIResourceRequest(UIResourceRequestType type,
+ UIResourceId id)
+ : type_(type), id_(id) {}
+
+UIResourceRequest::UIResourceRequest(UIResourceRequestType type,
+ UIResourceId id,
+ const UIResourceBitmap& bitmap)
+ : type_(type), id_(id), bitmap_(new UIResourceBitmap(bitmap)) {}
+
+UIResourceRequest::UIResourceRequest(const UIResourceRequest& request) {
+ (*this) = request;
+}
+
+UIResourceRequest& UIResourceRequest::operator=(
+ const UIResourceRequest& request) {
+ type_ = request.type_;
+ id_ = request.id_;
+ if (request.bitmap_) {
+ bitmap_ = make_scoped_ptr(new UIResourceBitmap(*request.bitmap_.get()));
+ } else {
+ bitmap_.reset();
+ }
+
+ return *this;
+}
UIResourceRequest::~UIResourceRequest() {}
@@ -142,6 +166,9 @@ bool LayerTreeHost::InitializeProxy(scoped_ptr<Proxy> proxy) {
LayerTreeHost::~LayerTreeHost() {
TRACE_EVENT0("cc", "LayerTreeHost::~LayerTreeHost");
+
+ overhang_ui_resource_.reset();
+
if (root_layer_.get())
root_layer_->SetLayerTreeHost(NULL);
@@ -193,7 +220,7 @@ LayerTreeHost::OnCreateAndInitializeOutputSurfaceAttempted(bool success) {
contents_texture_manager_ =
PrioritizedResourceManager::Create(proxy_.get());
surface_memory_placeholder_ =
- contents_texture_manager_->CreateTexture(gfx::Size(), GL_RGBA);
+ contents_texture_manager_->CreateTexture(gfx::Size(), RGBA_8888);
}
client_->DidInitializeOutputSurface(true);
@@ -326,6 +353,19 @@ void LayerTreeHost::FinishCommitOnImplThread(LayerTreeHostImpl* host_impl) {
sync_tree->FindRootScrollLayer();
+ // TODO(wjmaclean) For now, not all LTH clients will register viewports, so
+ // only set them when available..
+ if (page_scale_layer_) {
+ DCHECK(inner_viewport_scroll_layer_);
+ sync_tree->SetViewportLayersFromIds(
+ page_scale_layer_->id(),
+ inner_viewport_scroll_layer_->id(),
+ outer_viewport_scroll_layer_ ? outer_viewport_scroll_layer_->id()
+ : Layer::INVALID_ID);
+ } else {
+ sync_tree->ClearViewportLayers();
+ }
+
float page_scale_delta, sent_page_scale_delta;
if (settings_.impl_side_painting) {
// Update the delta from the active tree, which may have
@@ -356,7 +396,6 @@ void LayerTreeHost::FinishCommitOnImplThread(LayerTreeHostImpl* host_impl) {
pending_page_scale_animation_->target_offset,
pending_page_scale_animation_->use_anchor,
pending_page_scale_animation_->scale,
- base::TimeTicks::Now(),
pending_page_scale_animation_->duration);
pending_page_scale_animation_.reset();
}
@@ -369,6 +408,11 @@ void LayerTreeHost::FinishCommitOnImplThread(LayerTreeHostImpl* host_impl) {
if (!settings_.impl_side_painting)
sync_tree->ProcessUIResourceRequestQueue();
}
+ if (overhang_ui_resource_) {
+ host_impl->SetOverhangUIResource(
+ overhang_ui_resource_->id(),
+ GetUIResourceSize(overhang_ui_resource_->id()));
+ }
DCHECK(!sync_tree->ViewportSizeInvalid());
@@ -435,8 +479,6 @@ void LayerTreeHost::DidLoseOutputSurface() {
if (output_surface_lost_)
return;
- DidLoseUIResources();
-
num_failed_recreate_attempts_ = 0;
output_surface_lost_ = true;
SetNeedsCommit();
@@ -518,6 +560,10 @@ bool LayerTreeHost::CommitRequested() const {
return proxy_->CommitRequested();
}
+void LayerTreeHost::SetNextCommitWaitsForActivation() {
+ proxy_->SetNextCommitWaitsForActivation();
+}
+
void LayerTreeHost::SetAnimationEvents(scoped_ptr<AnimationEventsVector> events,
base::Time wall_clock_time) {
DCHECK(proxy_->IsMainThread());
@@ -545,9 +591,6 @@ void LayerTreeHost::SetAnimationEvents(scoped_ptr<AnimationEventsVector> events,
case AnimationEvent::PropertyUpdate:
(*iter).second->NotifyAnimationPropertyUpdate((*events)[event_index]);
break;
-
- default:
- NOTREACHED();
}
}
}
@@ -622,6 +665,22 @@ void LayerTreeHost::SetPageScaleFactorAndLimits(float page_scale_factor,
SetNeedsCommit();
}
+void LayerTreeHost::SetOverhangBitmap(const SkBitmap& bitmap) {
+ DCHECK(bitmap.width() && bitmap.height());
+ DCHECK_EQ(bitmap.bytesPerPixel(), 4);
+
+ SkBitmap bitmap_copy;
+ if (bitmap.isImmutable()) {
+ bitmap_copy = bitmap;
+ } else {
+ bitmap.copyTo(&bitmap_copy, bitmap.config());
+ bitmap_copy.setImmutable();
+ }
+
+ overhang_ui_resource_ = ScopedUIResource::Create(
+ this, UIResourceBitmap(bitmap_copy, UIResourceBitmap::REPEAT));
+}
+
void LayerTreeHost::SetVisible(bool visible) {
if (visible_ == visible)
return;
@@ -673,8 +732,7 @@ bool LayerTreeHost::InitializeOutputSurfaceIfNeeded() {
return !output_surface_lost_;
}
-bool LayerTreeHost::UpdateLayers(ResourceUpdateQueue* queue,
- size_t memory_allocation_limit_bytes) {
+bool LayerTreeHost::UpdateLayers(ResourceUpdateQueue* queue) {
DCHECK(!output_surface_lost_);
if (!root_layer())
@@ -682,11 +740,6 @@ bool LayerTreeHost::UpdateLayers(ResourceUpdateQueue* queue,
DCHECK(!root_layer()->parent());
- if (contents_texture_manager_ && memory_allocation_limit_bytes) {
- contents_texture_manager_->SetMaxMemoryLimitBytes(
- memory_allocation_limit_bytes);
- }
-
return UpdateLayers(root_layer(), queue);
}
@@ -734,6 +787,9 @@ bool LayerTreeHost::UpdateLayers(Layer* root_layer,
UpdateHudLayer();
Layer* root_scroll = FindFirstScrollableLayer(root_layer);
+ Layer* page_scale_layer = page_scale_layer_;
+ if (!page_scale_layer && root_scroll)
+ page_scale_layer = root_scroll->parent();
if (hud_layer_) {
hud_layer_->PrepareForCalculateDrawProperties(
@@ -747,7 +803,7 @@ bool LayerTreeHost::UpdateLayers(Layer* root_layer,
gfx::Transform(),
device_scale_factor_,
page_scale_factor_,
- root_scroll ? root_scroll->parent() : NULL,
+ page_scale_layer,
GetRendererCapabilities().max_texture_size,
settings_.can_use_lcd_text,
settings_.layer_transforms_should_scale_layer_contents,
@@ -890,7 +946,7 @@ size_t LayerTreeHost::CalculateMemoryForRenderSurfaces(
size_t bytes =
Resource::MemorySizeBytes(render_surface->content_rect().size(),
- GL_RGBA);
+ RGBA_8888);
contents_texture_bytes += bytes;
if (render_surface_layer->background_filters().IsEmpty())
@@ -900,7 +956,7 @@ size_t LayerTreeHost::CalculateMemoryForRenderSurfaces(
max_background_texture_bytes = bytes;
if (!readback_bytes) {
readback_bytes = Resource::MemorySizeBytes(device_viewport_size_,
- GL_RGBA);
+ RGBA_8888);
}
}
return readback_bytes + max_background_texture_bytes + contents_texture_bytes;
@@ -1092,12 +1148,6 @@ void LayerTreeHost::UpdateTopControlsState(TopControlsState constraints,
animate));
}
-bool LayerTreeHost::BlocksPendingCommit() const {
- if (!root_layer_.get())
- return false;
- return root_layer_->BlocksPendingCommitRecursive();
-}
-
scoped_ptr<base::Value> LayerTreeHost::AsValue() const {
scoped_ptr<base::DictionaryValue> state(new base::DictionaryValue());
state->Set("proxy", proxy_->AsValue().release());
@@ -1128,54 +1178,66 @@ void LayerTreeHost::AnimateLayers(base::TimeTicks time) {
UIResourceId LayerTreeHost::CreateUIResource(UIResourceClient* client) {
DCHECK(client);
- UIResourceRequest request;
- bool resource_lost = false;
- request.type = UIResourceRequest::UIResourceCreate;
- request.id = next_ui_resource_id_++;
-
- DCHECK(ui_resource_client_map_.find(request.id) ==
+ UIResourceId next_id = next_ui_resource_id_++;
+ DCHECK(ui_resource_client_map_.find(next_id) ==
ui_resource_client_map_.end());
- request.bitmap = client->GetBitmap(request.id, resource_lost);
+ bool resource_lost = false;
+ UIResourceRequest request(UIResourceRequest::UIResourceCreate,
+ next_id,
+ client->GetBitmap(next_id, resource_lost));
ui_resource_request_queue_.push_back(request);
- ui_resource_client_map_[request.id] = client;
- return request.id;
-}
-// Deletes a UI resource. May safely be called more than once.
-void LayerTreeHost::DeleteUIResource(UIResourceId uid) {
- UIResourceClientMap::iterator iter = ui_resource_client_map_.find(uid);
- if (iter == ui_resource_client_map_.end())
- return;
+ UIResourceClientData data;
+ data.client = client;
+ data.size = request.GetBitmap().GetSize();
- UIResourceRequest request;
- request.type = UIResourceRequest::UIResourceDelete;
- request.id = uid;
- ui_resource_request_queue_.push_back(request);
- ui_resource_client_map_.erase(uid);
+ ui_resource_client_map_[request.GetId()] = data;
+ return request.GetId();
}
-void LayerTreeHost::UIResourceLost(UIResourceId uid) {
+// Deletes a UI resource. May safely be called more than once.
+void LayerTreeHost::DeleteUIResource(UIResourceId uid) {
UIResourceClientMap::iterator iter = ui_resource_client_map_.find(uid);
if (iter == ui_resource_client_map_.end())
return;
- UIResourceRequest request;
- bool resource_lost = true;
- request.type = UIResourceRequest::UIResourceCreate;
- request.id = uid;
- request.bitmap = iter->second->GetBitmap(uid, resource_lost);
- DCHECK(request.bitmap.get());
+ UIResourceRequest request(UIResourceRequest::UIResourceDelete, uid);
ui_resource_request_queue_.push_back(request);
+ ui_resource_client_map_.erase(iter);
}
-void LayerTreeHost::DidLoseUIResources() {
- // When output surface is lost, we need to recreate the resource.
+void LayerTreeHost::RecreateUIResources() {
for (UIResourceClientMap::iterator iter = ui_resource_client_map_.begin();
iter != ui_resource_client_map_.end();
++iter) {
- UIResourceLost(iter->first);
+ UIResourceId uid = iter->first;
+ const UIResourceClientData& data = iter->second;
+ bool resource_lost = true;
+ UIResourceRequest request(UIResourceRequest::UIResourceCreate,
+ uid,
+ data.client->GetBitmap(uid, resource_lost));
+ ui_resource_request_queue_.push_back(request);
}
}
+// Returns the size of a resource given its id.
+gfx::Size LayerTreeHost::GetUIResourceSize(UIResourceId uid) const {
+ UIResourceClientMap::const_iterator iter = ui_resource_client_map_.find(uid);
+ if (iter == ui_resource_client_map_.end())
+ return gfx::Size();
+
+ const UIResourceClientData& data = iter->second;
+ return data.size;
+}
+
+void LayerTreeHost::RegisterViewportLayers(
+ scoped_refptr<Layer> page_scale_layer,
+ scoped_refptr<Layer> inner_viewport_scroll_layer,
+ scoped_refptr<Layer> outer_viewport_scroll_layer) {
+ page_scale_layer_ = page_scale_layer;
+ inner_viewport_scroll_layer_ = inner_viewport_scroll_layer;
+ outer_viewport_scroll_layer_ = outer_viewport_scroll_layer;
+}
+
} // namespace cc
diff --git a/chromium/cc/trees/layer_tree_host.h b/chromium/cc/trees/layer_tree_host.h
index 619c18e6574..72a5628a9ad 100644
--- a/chromium/cc/trees/layer_tree_host.h
+++ b/chromium/cc/trees/layer_tree_host.h
@@ -24,6 +24,8 @@
#include "cc/input/top_controls_state.h"
#include "cc/layers/layer_lists.h"
#include "cc/output/output_surface.h"
+#include "cc/resources/resource_format.h"
+#include "cc/resources/scoped_ui_resource.h"
#include "cc/resources/ui_resource_bitmap.h"
#include "cc/resources/ui_resource_client.h"
#include "cc/scheduler/rate_limiter.h"
@@ -33,7 +35,7 @@
#include "cc/trees/occlusion_tracker.h"
#include "cc/trees/proxy.h"
#include "third_party/skia/include/core/SkColor.h"
-#include "ui/base/latency_info.h"
+#include "ui/events/latency_info.h"
#include "ui/gfx/rect.h"
namespace WebKit { class WebGraphicsContext3D; }
@@ -72,7 +74,7 @@ struct CC_EXPORT RendererCapabilities {
RendererCapabilities();
~RendererCapabilities();
- unsigned best_texture_format;
+ ResourceFormat best_texture_format;
bool using_partial_swap;
bool using_set_visibility;
bool using_egl_image;
@@ -82,20 +84,38 @@ struct CC_EXPORT RendererCapabilities {
bool avoid_pow2_textures;
bool using_map_image;
bool using_shared_memory_resources;
+ bool using_discard_framebuffer;
};
-struct CC_EXPORT UIResourceRequest {
+class CC_EXPORT UIResourceRequest {
+ public:
enum UIResourceRequestType {
UIResourceCreate,
UIResourceDelete,
UIResourceInvalidRequest
};
- UIResourceRequest();
+ UIResourceRequest(UIResourceRequestType type, UIResourceId id);
+ UIResourceRequest(UIResourceRequestType type,
+ UIResourceId id,
+ const UIResourceBitmap& bitmap);
+ UIResourceRequest(const UIResourceRequest& request);
+
~UIResourceRequest();
- UIResourceRequestType type;
- UIResourceId id;
- scoped_refptr<UIResourceBitmap> bitmap;
+
+ UIResourceRequestType GetType() const { return type_; }
+ UIResourceId GetId() const { return id_; }
+ UIResourceBitmap GetBitmap() const {
+ DCHECK(bitmap_);
+ return *bitmap_.get();
+ }
+
+ UIResourceRequest& operator=(const UIResourceRequest& request);
+
+ private:
+ UIResourceRequestType type_;
+ UIResourceId id_;
+ scoped_ptr<UIResourceBitmap> bitmap_;
};
class CC_EXPORT LayerTreeHost : NON_EXPORTED_BASE(public RateLimiterClient) {
@@ -144,8 +164,7 @@ class CC_EXPORT LayerTreeHost : NON_EXPORTED_BASE(public RateLimiterClient) {
virtual void AcquireLayerTextures();
// Returns false if we should abort this frame due to initialization failure.
bool InitializeOutputSurfaceIfNeeded();
- bool UpdateLayers(ResourceUpdateQueue* queue,
- size_t contents_memory_limit_bytes);
+ bool UpdateLayers(ResourceUpdateQueue* queue);
LayerTreeHostClient* client() { return client_; }
const base::WeakPtr<InputHandler>& GetInputHandler() {
@@ -191,12 +210,18 @@ class CC_EXPORT LayerTreeHost : NON_EXPORTED_BASE(public RateLimiterClient) {
void SetNeedsRedrawRect(gfx::Rect damage_rect);
bool CommitRequested() const;
+ void SetNextCommitWaitsForActivation();
+
void SetAnimationEvents(scoped_ptr<AnimationEventsVector> events,
base::Time wall_clock_time);
void SetRootLayer(scoped_refptr<Layer> root_layer);
Layer* root_layer() { return root_layer_.get(); }
const Layer* root_layer() const { return root_layer_.get(); }
+ void RegisterViewportLayers(
+ scoped_refptr<Layer> page_scale_layer,
+ scoped_refptr<Layer> inner_viewport_scroll_layer,
+ scoped_refptr<Layer> outer_viewport_scroll_layer);
const LayerTreeSettings& settings() const { return settings_; }
@@ -222,6 +247,8 @@ class CC_EXPORT LayerTreeHost : NON_EXPORTED_BASE(public RateLimiterClient) {
has_transparent_background_ = transparent;
}
+ void SetOverhangBitmap(const SkBitmap& bitmap);
+
PrioritizedResourceManager* contents_texture_manager() const {
return contents_texture_manager_.get();
}
@@ -266,8 +293,6 @@ class CC_EXPORT LayerTreeHost : NON_EXPORTED_BASE(public RateLimiterClient) {
return animation_registrar_.get();
}
- bool BlocksPendingCommit() const;
-
// Obtains a thorough dump of the LayerTreeHost as a value.
scoped_ptr<base::Value> AsValue() const;
@@ -282,6 +307,11 @@ class CC_EXPORT LayerTreeHost : NON_EXPORTED_BASE(public RateLimiterClient) {
virtual UIResourceId CreateUIResource(UIResourceClient* client);
// Deletes a UI resource. May safely be called more than once.
virtual void DeleteUIResource(UIResourceId id);
+ // Put the recreation of all UI resources into the resource queue after they
+ // were evicted on the impl thread.
+ void RecreateUIResources();
+
+ virtual gfx::Size GetUIResourceSize(UIResourceId id) const;
bool UsingSharedMemoryResources();
int id() const { return tree_id_; }
@@ -319,11 +349,13 @@ class CC_EXPORT LayerTreeHost : NON_EXPORTED_BASE(public RateLimiterClient) {
bool AnimateLayersRecursive(Layer* current, base::TimeTicks time);
- void UIResourceLost(UIResourceId id);
-
- void DidLoseUIResources();
+ struct UIResourceClientData {
+ UIResourceClient* client;
+ gfx::Size size;
+ };
- typedef base::hash_map<UIResourceId, UIResourceClient*> UIResourceClientMap;
+ typedef base::hash_map<UIResourceId, UIResourceClientData>
+ UIResourceClientMap;
UIResourceClientMap ui_resource_client_map_;
int next_ui_resource_id_;
@@ -379,6 +411,10 @@ class CC_EXPORT LayerTreeHost : NON_EXPORTED_BASE(public RateLimiterClient) {
SkColor background_color_;
bool has_transparent_background_;
+ // If set, this texture is used to fill in the parts of the screen not
+ // covered by layers.
+ scoped_ptr<ScopedUIResource> overhang_ui_resource_;
+
typedef ScopedPtrVector<PrioritizedResource> TextureList;
size_t partial_texture_update_requests_;
@@ -412,6 +448,10 @@ class CC_EXPORT LayerTreeHost : NON_EXPORTED_BASE(public RateLimiterClient) {
LCDTextMetrics lcd_text_metrics_;
int tree_id_;
+ scoped_refptr<Layer> page_scale_layer_;
+ scoped_refptr<Layer> inner_viewport_scroll_layer_;
+ scoped_refptr<Layer> outer_viewport_scroll_layer_;
+
DISALLOW_COPY_AND_ASSIGN(LayerTreeHost);
};
diff --git a/chromium/cc/trees/layer_tree_host_common.cc b/chromium/cc/trees/layer_tree_host_common.cc
index 68dadd5f9b9..8017fa9a933 100644
--- a/chromium/cc/trees/layer_tree_host_common.cc
+++ b/chromium/cc/trees/layer_tree_host_common.cc
@@ -40,6 +40,29 @@ static void SortLayers(LayerImplList::iterator first,
layer_sorter->Sort(first, end);
}
+template <typename LayerType>
+static gfx::Vector2dF GetEffectiveScrollDelta(LayerType* layer) {
+ gfx::Vector2dF scroll_delta = layer->ScrollDelta();
+ // The scroll parent's scroll delta is the amount we've scrolled on the
+ // compositor thread since the commit for this layer tree's source frame.
+ // we last reported to the main thread. I.e., it's the discrepancy between
+ // a scroll parent's scroll delta and offset, so we must add it here.
+ if (layer->scroll_parent())
+ scroll_delta += layer->scroll_parent()->ScrollDelta();
+ return scroll_delta;
+}
+
+template <typename LayerType>
+static gfx::Vector2dF GetEffectiveTotalScrollOffset(LayerType* layer) {
+ gfx::Vector2dF offset = layer->TotalScrollOffset();
+ // The scroll parent's total scroll offset (scroll offset + scroll delta)
+ // can't be used because its scroll offset has already been applied to the
+ // scroll children's positions by the main thread layer positioning code.
+ if (layer->scroll_parent())
+ offset += layer->scroll_parent()->ScrollDelta();
+ return offset;
+}
+
inline gfx::Rect CalculateVisibleRectWithCachedLayerRect(
gfx::Rect target_surface_rect,
gfx::Rect layer_bound_rect,
@@ -58,6 +81,9 @@ inline gfx::Rect CalculateVisibleRectWithCachedLayerRect(
gfx::Rect minimal_surface_rect = target_surface_rect;
minimal_surface_rect.Intersect(layer_rect_in_target_space);
+ if (minimal_surface_rect.IsEmpty())
+ return gfx::Rect();
+
// Project the corners of the target surface rect into the layer space.
// This bounding rectangle may be larger than it needs to be (being
// axis-aligned), but is a reasonable filter on the space to consider.
@@ -65,11 +91,10 @@ inline gfx::Rect CalculateVisibleRectWithCachedLayerRect(
gfx::Transform surface_to_layer(gfx::Transform::kSkipInitialization);
if (!transform.GetInverse(&surface_to_layer)) {
- // TODO(shawnsingh): Some uninvertible transforms may be visible, but
- // their behaviour is undefined thoughout the compositor. Make their
- // behaviour well-defined and allow the visible content rect to be non-
- // empty when needed.
- return gfx::Rect();
+ // Because we cannot use the surface bounds to determine what portion of
+ // the layer is visible, we must conservatively assume the full layer is
+ // visible.
+ return layer_bound_rect;
}
gfx::Rect layer_rect = gfx::ToEnclosingRect(MathUtil::ProjectClippedRect(
@@ -88,6 +113,201 @@ gfx::Rect LayerTreeHostCommon::CalculateVisibleRect(
target_surface_rect, layer_bound_rect, layer_in_surface_space, transform);
}
+template <typename LayerType>
+static LayerType* NextTargetSurface(LayerType* layer) {
+ return layer->parent() ? layer->parent()->render_target() : 0;
+}
+
+// Given two layers, this function finds their respective render targets and,
+// computes a change of basis translation. It does this by accumulating the
+// translation components of the draw transforms of each target between the
+// ancestor and descendant. These transforms must be 2D translations, and this
+// requirement is enforced at every step.
+template <typename LayerType, typename RenderSurfaceType>
+static gfx::Vector2dF ComputeChangeOfBasisTranslation(
+ const LayerType& ancestor_layer,
+ const LayerType& descendant_layer) {
+ DCHECK(descendant_layer.HasAncestor(&ancestor_layer));
+ const LayerType* descendant_target = descendant_layer.render_target();
+ DCHECK(descendant_target);
+ const LayerType* ancestor_target = ancestor_layer.render_target();
+ DCHECK(ancestor_target);
+
+ gfx::Vector2dF translation;
+ for (const LayerType* target = descendant_target; target != ancestor_target;
+ target = NextTargetSurface(target)) {
+ const gfx::Transform& trans = target->render_surface()->draw_transform();
+ // Ensure that this translation is truly 2d.
+ DCHECK(trans.IsIdentityOrTranslation());
+ DCHECK_EQ(0.f, trans.matrix().get(2, 3));
+ translation += trans.To2dTranslation();
+ }
+
+ return translation;
+}
+
+enum TranslateRectDirection {
+ TranslateRectDirectionToAncestor,
+ TranslateRectDirectionToDescendant
+};
+
+template <typename LayerType, typename RenderSurfaceType>
+static gfx::Rect TranslateRectToTargetSpace(const LayerType& ancestor_layer,
+ const LayerType& descendant_layer,
+ gfx::Rect rect,
+ TranslateRectDirection direction) {
+ gfx::Vector2dF translation =
+ ComputeChangeOfBasisTranslation<LayerType, RenderSurfaceType>(
+ ancestor_layer, descendant_layer);
+ if (direction == TranslateRectDirectionToDescendant)
+ translation.Scale(-1.f);
+ return gfx::ToEnclosingRect(
+ gfx::RectF(rect.origin() + translation, rect.size()));
+}
+
+// Attempts to update the clip rects for the given layer. If the layer has a
+// clip_parent, it may not inherit its immediate ancestor's clip.
+template <typename LayerType, typename RenderSurfaceType>
+static void UpdateClipRectsForClipChild(
+ const LayerType* layer,
+ gfx::Rect* clip_rect_in_parent_target_space,
+ bool* subtree_should_be_clipped) {
+ // If the layer has no clip_parent, or the ancestor is the same as its actual
+ // parent, then we don't need special clip rects. Bail now and leave the out
+ // parameters untouched.
+ const LayerType* clip_parent = layer->clip_parent();
+ if (!clip_parent || clip_parent == layer->parent())
+ return;
+
+ // The root layer is never a clip child.
+ DCHECK(layer->parent());
+
+ // Grab the cached values.
+ *clip_rect_in_parent_target_space = clip_parent->clip_rect();
+ *subtree_should_be_clipped = clip_parent->is_clipped();
+
+ // We may have to project the clip rect into our parent's target space. Note,
+ // it must be our parent's target space, not ours. For one, we haven't
+ // computed our transforms, so we couldn't put it in our space yet even if we
+ // wanted to. But more importantly, this matches the expectations of
+ // CalculateDrawPropertiesInternal. If we, say, create a render surface, these
+ // clip rects will want to be in its target space, not ours.
+ *clip_rect_in_parent_target_space =
+ TranslateRectToTargetSpace<LayerType, RenderSurfaceType>(
+ *clip_parent,
+ *layer->parent(),
+ *clip_rect_in_parent_target_space,
+ TranslateRectDirectionToDescendant);
+}
+
+// We collect an accumulated drawable content rect per render surface.
+// Typically, a layer will contribute to only one surface, the surface
+// associated with its render target. Clip children, however, may affect
+// several surfaces since there may be several surfaces between the clip child
+// and its parent.
+//
+// NB: we accumulate the layer's *clipped* drawable content rect.
+template <typename LayerType>
+struct AccumulatedSurfaceState {
+ explicit AccumulatedSurfaceState(LayerType* render_target)
+ : render_target(render_target) {}
+
+ // The accumulated drawable content rect for the surface associated with the
+ // given |render_target|.
+ gfx::Rect drawable_content_rect;
+
+ // The target owning the surface. (We hang onto the target rather than the
+ // surface so that we can DCHECK that the surface's draw transform is simply
+ // a translation when |render_target| reports that it has no unclipped
+ // descendants).
+ LayerType* render_target;
+};
+
+template <typename LayerType, typename RenderSurfaceType>
+void UpdateAccumulatedSurfaceState(
+ LayerType* layer,
+ gfx::Rect drawable_content_rect,
+ std::vector<AccumulatedSurfaceState<LayerType> >*
+ accumulated_surface_state) {
+ if (IsRootLayer(layer))
+ return;
+
+ // We will apply our drawable content rect to the accumulated rects for all
+ // surfaces between us and |render_target| (inclusive). This is either our
+ // clip parent's target if we are a clip child, or else simply our parent's
+ // target. We use our parent's target because we're either the owner of a
+ // render surface and we'll want to add our rect to our *surface's* target, or
+ // we're not and our target is the same as our parent's. In both cases, the
+ // parent's target gives us what we want.
+ LayerType* render_target = layer->clip_parent()
+ ? layer->clip_parent()->render_target()
+ : layer->parent()->render_target();
+
+ // If the layer owns a surface, then the content rect is in the wrong space.
+ // Instead, we will use the surface's DrawableContentRect which is in target
+ // space as required.
+ gfx::Rect target_rect = drawable_content_rect;
+ if (layer->render_surface()) {
+ target_rect =
+ gfx::ToEnclosedRect(layer->render_surface()->DrawableContentRect());
+ }
+
+ if (render_target->is_clipped()) {
+ gfx::Rect clip_rect = render_target->clip_rect();
+ // If the layer has a clip parent, the clip rect may be in the wrong space,
+ // so we'll need to transform it before it is applied.
+ if (layer->clip_parent()) {
+ clip_rect = TranslateRectToTargetSpace<LayerType, RenderSurfaceType>(
+ *layer->clip_parent(),
+ *layer,
+ clip_rect,
+ TranslateRectDirectionToDescendant);
+ }
+ target_rect.Intersect(clip_rect);
+ }
+
+ // We must have at least one entry in the vector for the root.
+ DCHECK_LT(0ul, accumulated_surface_state->size());
+
+ typedef typename std::vector<AccumulatedSurfaceState<LayerType> >
+ AccumulatedSurfaceStateVector;
+ typedef typename AccumulatedSurfaceStateVector::reverse_iterator
+ AccumulatedSurfaceStateIterator;
+ AccumulatedSurfaceStateIterator current_state =
+ accumulated_surface_state->rbegin();
+
+ // Add this rect to the accumulated content rect for all surfaces until we
+ // reach the target surface.
+ bool found_render_target = false;
+ for (; current_state != accumulated_surface_state->rend(); ++current_state) {
+ current_state->drawable_content_rect.Union(target_rect);
+
+ // If we've reached |render_target| our work is done and we can bail.
+ if (current_state->render_target == render_target) {
+ found_render_target = true;
+ break;
+ }
+
+ // Transform rect from the current target's space to the next.
+ LayerType* current_target = current_state->render_target;
+ DCHECK(current_target->render_surface());
+ const gfx::Transform& current_draw_transform =
+ current_target->render_surface()->draw_transform();
+
+ // If we have unclipped descendants, the draw transform is a translation.
+ DCHECK(current_target->num_unclipped_descendants() == 0 ||
+ current_draw_transform.IsIdentityOrTranslation());
+
+ target_rect = gfx::ToEnclosingRect(
+ MathUtil::MapClippedRect(current_draw_transform, target_rect));
+ }
+
+ // It is an error to not reach |render_target|. If this happens, it means that
+ // either the clip parent is not an ancestor of the clip child or the surface
+ // state vector is empty, both of which should be impossible.
+ DCHECK(found_render_target);
+}
+
template <typename LayerType> static inline bool IsRootLayer(LayerType* layer) {
return !layer->parent();
}
@@ -414,10 +634,6 @@ static bool SubtreeShouldRenderToSeparateSurface(
return false;
}
-static LayerImpl* NextTargetSurface(LayerImpl* layer) {
- return layer->parent() ? layer->parent()->render_target() : 0;
-}
-
// This function returns a translation matrix that can be applied on a vector
// that's in the layer's target surface coordinate, while the position offset is
// specified in some ancestor layer's coordinate.
@@ -526,7 +742,8 @@ void ApplyPositionAdjustment(
gfx::Transform ComputeScrollCompensationForThisLayer(
LayerImpl* scrolling_layer,
- const gfx::Transform& parent_matrix) {
+ const gfx::Transform& parent_matrix,
+ gfx::Vector2dF scroll_delta) {
// For every layer that has non-zero scroll_delta, we have to compute a
// transform that can undo the scroll_delta translation. In particular, we
// want this matrix to premultiply a fixed-position layer's parent_matrix, so
@@ -549,8 +766,8 @@ gfx::Transform ComputeScrollCompensationForThisLayer(
gfx::Transform scroll_compensation_for_this_layer = parent_matrix; // Step 3
scroll_compensation_for_this_layer.Translate(
- scrolling_layer->ScrollDelta().x(),
- scrolling_layer->ScrollDelta().y()); // Step 2
+ scroll_delta.x(),
+ scroll_delta.y()); // Step 2
gfx::Transform inverse_parent_matrix(gfx::Transform::kSkipInitialization);
if (!parent_matrix.GetInverse(&inverse_parent_matrix)) {
@@ -565,7 +782,8 @@ gfx::Transform ComputeScrollCompensationForThisLayer(
gfx::Transform ComputeScrollCompensationMatrixForChildren(
Layer* current_layer,
const gfx::Transform& current_parent_matrix,
- const gfx::Transform& current_scroll_compensation) {
+ const gfx::Transform& current_scroll_compensation,
+ gfx::Vector2dF scroll_delta) {
// The main thread (i.e. Layer) does not need to worry about scroll
// compensation. So we can just return an identity matrix here.
return gfx::Transform();
@@ -574,7 +792,8 @@ gfx::Transform ComputeScrollCompensationMatrixForChildren(
gfx::Transform ComputeScrollCompensationMatrixForChildren(
LayerImpl* layer,
const gfx::Transform& parent_matrix,
- const gfx::Transform& current_scroll_compensation_matrix) {
+ const gfx::Transform& current_scroll_compensation_matrix,
+ gfx::Vector2dF scroll_delta) {
// "Total scroll compensation" is the transform needed to cancel out all
// scroll_delta translations that occurred since the nearest container layer,
// even if there are render_surfaces in-between.
@@ -600,7 +819,7 @@ gfx::Transform ComputeScrollCompensationMatrixForChildren(
// initialization/copy) if we know that the scroll compensation doesn't need
// to be reset or adjusted.
if (!layer->IsContainerForFixedPositionLayers() &&
- layer->ScrollDelta().IsZero() && !layer->render_surface())
+ scroll_delta.IsZero() && !layer->render_surface())
return current_scroll_compensation_matrix;
// Start as identity matrix.
@@ -614,10 +833,10 @@ gfx::Transform ComputeScrollCompensationMatrixForChildren(
// If the current layer has a non-zero scroll_delta, then we should compute
// its local scroll compensation and accumulate it to the
// next_scroll_compensation_matrix.
- if (!layer->ScrollDelta().IsZero()) {
+ if (!scroll_delta.IsZero()) {
gfx::Transform scroll_compensation_for_this_layer =
ComputeScrollCompensationForThisLayer(
- layer, parent_matrix);
+ layer, parent_matrix, scroll_delta);
next_scroll_compensation_matrix.PreconcatTransform(
scroll_compensation_for_this_layer);
}
@@ -783,13 +1002,17 @@ static inline void RemoveSurfaceForEarlyExit(
struct PreCalculateMetaInformationRecursiveData {
bool layer_or_descendant_has_copy_request;
+ int num_unclipped_descendants;
PreCalculateMetaInformationRecursiveData()
- : layer_or_descendant_has_copy_request(false) {}
+ : layer_or_descendant_has_copy_request(false),
+ num_unclipped_descendants(0) {}
void Merge(const PreCalculateMetaInformationRecursiveData& data) {
layer_or_descendant_has_copy_request |=
data.layer_or_descendant_has_copy_request;
+ num_unclipped_descendants +=
+ data.num_unclipped_descendants;
}
};
@@ -812,6 +1035,9 @@ static void PreCalculateMetaInformation(
descendants_can_clip_selves = false;
}
+ if (layer->clip_parent())
+ recursive_data->num_unclipped_descendants++;
+
for (size_t i = 0; i < layer->children().size(); ++i) {
LayerType* child_layer =
LayerTreeHostCommon::get_child_as_raw_ptr(layer->children(), i);
@@ -837,11 +1063,19 @@ static void PreCalculateMetaInformation(
recursive_data->Merge(data_for_child);
}
+ if (layer->clip_children()) {
+ int num_clip_children = layer->clip_children()->size();
+ DCHECK_GE(recursive_data->num_unclipped_descendants, num_clip_children);
+ recursive_data->num_unclipped_descendants -= num_clip_children;
+ }
+
if (layer->HasCopyRequest())
recursive_data->layer_or_descendant_has_copy_request = true;
layer->draw_properties().num_descendants_that_draw_content =
num_descendants_that_draw_content;
+ layer->draw_properties().num_unclipped_descendants =
+ recursive_data->num_unclipped_descendants;
layer->draw_properties().descendants_can_clip_selves =
descendants_can_clip_selves;
layer->draw_properties().layer_or_descendant_has_copy_request =
@@ -915,7 +1149,8 @@ static void CalculateDrawPropertiesInternal(
const DataForRecursion<LayerType, RenderSurfaceType>& data_from_ancestor,
LayerListType* render_surface_layer_list,
LayerListType* layer_list,
- gfx::Rect* drawable_content_rect_of_subtree) {
+ std::vector<AccumulatedSurfaceState<LayerType> >*
+ accumulated_surface_state) {
// This function computes the new matrix transformations recursively for this
// layer and all its descendants. It also computes the appropriate render
// surfaces.
@@ -1043,10 +1278,6 @@ static void CalculateDrawPropertiesInternal(
DCHECK(globals.page_scale_application_layer ||
(globals.page_scale_factor == 1.f));
- // If we early-exit anywhere in this function, the drawable_content_rect of
- // this subtree should be considered empty.
- *drawable_content_rect_of_subtree = gfx::Rect();
-
DataForRecursion<LayerType, RenderSurfaceType> data_for_children;
RenderSurfaceType* nearest_ancestor_surface_that_moves_pixels =
data_from_ancestor.nearest_ancestor_surface_that_moves_pixels;
@@ -1065,8 +1296,25 @@ static void CalculateDrawPropertiesInternal(
layer_is_visible = true;
// The root layer cannot skip CalcDrawProperties.
- if (!IsRootLayer(layer) && SubtreeShouldBeSkipped(layer, layer_is_visible))
+ if (!IsRootLayer(layer) && SubtreeShouldBeSkipped(layer, layer_is_visible)) {
+ if (layer->render_surface())
+ layer->ClearRenderSurface();
return;
+ }
+
+ // We need to circumvent the normal recursive flow of information for clip
+ // children (they don't inherit their direct ancestor's clip information).
+ // This is unfortunate, and would be unnecessary if we were to formally
+ // separate the clipping hierarchy from the layer hierarchy.
+ bool ancestor_clips_subtree = data_from_ancestor.ancestor_clips_subtree;
+ gfx::Rect ancestor_clip_rect_in_target_space =
+ data_from_ancestor.clip_rect_in_target_space;
+
+ // Update our clipping state. If we have a clip parent we will need to pull
+ // from the clip state cache rather than using the clip state passed from our
+ // immediate ancestor.
+ UpdateClipRectsForClipChild<LayerType, RenderSurfaceType>(
+ layer, &ancestor_clip_rect_in_target_space, &ancestor_clips_subtree);
// As this function proceeds, these are the properties for the current
// layer that actually get computed. To avoid unnecessary copies
@@ -1106,7 +1354,8 @@ static void CalculateDrawPropertiesInternal(
gfx::Size bounds = layer->bounds();
gfx::PointF anchor_point = layer->anchor_point();
- gfx::PointF position = layer->position() - layer->TotalScrollOffset();
+ gfx::Vector2dF scroll_offset = GetEffectiveTotalScrollOffset(layer);
+ gfx::PointF position = layer->position() - scroll_offset;
gfx::Transform combined_transform = data_from_ancestor.parent_matrix;
if (!layer->transform().IsIdentity()) {
@@ -1125,12 +1374,19 @@ static void CalculateDrawPropertiesInternal(
combined_transform.Translate(position.x(), position.y());
}
+ gfx::Vector2dF effective_scroll_delta = GetEffectiveScrollDelta(layer);
if (!animating_transform_to_target && layer->scrollable() &&
combined_transform.IsScaleOrTranslation()) {
// Align the scrollable layer's position to screen space pixels to avoid
// blurriness. To avoid side-effects, do this only if the transform is
// simple.
+ gfx::Vector2dF previous_translation = combined_transform.To2dTranslation();
RoundTranslationComponents(&combined_transform);
+ gfx::Vector2dF current_translation = combined_transform.To2dTranslation();
+
+ // This rounding changes the scroll delta, and so must be included
+ // in the scroll compensation matrix.
+ effective_scroll_delta -= current_translation - previous_translation;
}
// Apply adjustment from position constraints.
@@ -1166,8 +1422,9 @@ static void CalculateDrawPropertiesInternal(
// case, the render_surface re-parents the transforms.
layer_draw_properties.target_space_transform = combined_transform;
// M[draw] = M[parent] * LT * S[layer2content]
- layer_draw_properties.target_space_transform.Scale
- (1.f / layer->contents_scale_x(), 1.f / layer->contents_scale_y());
+ layer_draw_properties.target_space_transform.Scale(
+ SK_MScalar1 / layer->contents_scale_x(),
+ SK_MScalar1 / layer->contents_scale_y());
// The layer's screen_space_transform represents the transform between root
// layer's "screen space" and local content space.
@@ -1212,8 +1469,10 @@ static void CalculateDrawPropertiesInternal(
// Check back-face visibility before continuing with this surface and its
// subtree
if (!layer->double_sided() && TransformToParentIsKnown(layer) &&
- IsSurfaceBackFaceVisible(layer, combined_transform))
+ IsSurfaceBackFaceVisible(layer, combined_transform)) {
+ layer->ClearRenderSurface();
return;
+ }
RenderSurfaceType* render_surface = CreateOrReuseRenderSurface(layer);
@@ -1283,13 +1542,6 @@ static void CalculateDrawPropertiesInternal(
data_for_children.full_hierarchy_matrix.PreconcatTransform(
render_surface->draw_transform());
- // The new render_surface here will correctly clip the entire subtree. So,
- // we do not need to continue propagating the clipping state further down
- // the tree. This way, we can avoid transforming clip rects from ancestor
- // target surface space to current target surface space that could cause
- // more w < 0 headaches.
- layer_or_ancestor_clips_descendants = false;
-
if (layer->mask_layer()) {
DrawProperties<LayerType, RenderSurfaceType>& mask_layer_draw_properties =
layer->mask_layer()->draw_properties();
@@ -1312,14 +1564,15 @@ static void CalculateDrawPropertiesInternal(
if (layer->filters().HasFilterThatMovesPixels() || layer->filter())
nearest_ancestor_surface_that_moves_pixels = render_surface;
- // The render surface clip rect is expressed in the space where this surface
- // draws, i.e. the same space as
- // data_from_ancestor.clip_rect_in_target_space.
- render_surface->SetIsClipped(data_from_ancestor.ancestor_clips_subtree);
- if (data_from_ancestor.ancestor_clips_subtree) {
- render_surface->SetClipRect(
- data_from_ancestor.clip_rect_in_target_space);
+ render_surface->SetNearestAncestorThatMovesPixels(
+ nearest_ancestor_surface_that_moves_pixels);
+ layer_or_ancestor_clips_descendants = false;
+ bool subtree_is_clipped_by_surface_bounds = false;
+ if (ancestor_clips_subtree) {
+ // It may be the layer or the surface doing the clipping of the subtree,
+ // but in either case, we'll be clipping to the projected clip rect of our
+ // ancestor.
gfx::Transform inverse_surface_draw_transform(
gfx::Transform::kSkipInitialization);
if (!render_surface->draw_transform().GetInverse(
@@ -1327,18 +1580,48 @@ static void CalculateDrawPropertiesInternal(
// TODO(shawnsingh): Either we need to handle uninvertible transforms
// here, or DCHECK that the transform is invertible.
}
- clip_rect_of_target_surface_in_target_space =
- gfx::ToEnclosingRect(MathUtil::ProjectClippedRect(
- inverse_surface_draw_transform, render_surface->clip_rect()));
- } else {
+
+ gfx::Rect projected_surface_rect = gfx::ToEnclosingRect(
+ MathUtil::ProjectClippedRect(inverse_surface_draw_transform,
+ ancestor_clip_rect_in_target_space));
+
+ if (layer_draw_properties.num_unclipped_descendants > 0) {
+ // If we have unclipped descendants, we cannot count on the render
+ // surface's bounds clipping our subtree: the unclipped descendants
+ // could cause us to expand our bounds. In this case, we must rely on
+ // layer clipping for correctess. NB: since we can only encounter
+ // translations between a clip child and its clip parent, clipping is
+ // guaranteed to be exact in this case.
+ layer_or_ancestor_clips_descendants = true;
+ clip_rect_in_target_space = projected_surface_rect;
+ } else {
+ // The new render_surface here will correctly clip the entire subtree.
+ // So, we do not need to continue propagating the clipping state further
+ // down the tree. This way, we can avoid transforming clip rects from
+ // ancestor target surface space to current target surface space that
+ // could cause more w < 0 headaches. The render surface clip rect is
+ // expressed in the space where this surface draws, i.e. the same space
+ // as clip_rect_from_ancestor_in_ancestor_target_space.
+ render_surface->SetClipRect(ancestor_clip_rect_in_target_space);
+ clip_rect_of_target_surface_in_target_space = projected_surface_rect;
+ subtree_is_clipped_by_surface_bounds = true;
+ }
+ }
+
+ DCHECK(layer->render_surface());
+ DCHECK(!layer->parent() || layer->parent()->render_target() ==
+ accumulated_surface_state->back().render_target);
+
+ accumulated_surface_state->push_back(
+ AccumulatedSurfaceState<LayerType>(layer));
+
+ render_surface->SetIsClipped(subtree_is_clipped_by_surface_bounds);
+ if (!subtree_is_clipped_by_surface_bounds) {
render_surface->SetClipRect(gfx::Rect());
clip_rect_of_target_surface_in_target_space =
data_from_ancestor.clip_rect_of_target_surface_in_target_space;
}
- render_surface->SetNearestAncestorThatMovesPixels(
- nearest_ancestor_surface_that_moves_pixels);
-
// If the new render surface is drawn translucent or with a non-integral
// translation then the subtree that gets drawn on this render surface
// cannot use LCD text.
@@ -1364,11 +1647,10 @@ static void CalculateDrawPropertiesInternal(
// Layers without render_surfaces directly inherit the ancestor's clip
// status.
- layer_or_ancestor_clips_descendants =
- data_from_ancestor.ancestor_clips_subtree;
- if (data_from_ancestor.ancestor_clips_subtree) {
+ layer_or_ancestor_clips_descendants = ancestor_clips_subtree;
+ if (ancestor_clips_subtree) {
clip_rect_in_target_space =
- data_from_ancestor.clip_rect_in_target_space;
+ ancestor_clip_rect_in_target_space;
}
// The surface's cached clip rect value propagates regardless of what
@@ -1404,37 +1686,28 @@ static void CalculateDrawPropertiesInternal(
if (LayerClipsSubtree(layer)) {
layer_or_ancestor_clips_descendants = true;
- if (data_from_ancestor.ancestor_clips_subtree && !layer->render_surface()) {
+ if (ancestor_clips_subtree && !layer->render_surface()) {
// A layer without render surface shares the same target as its ancestor.
clip_rect_in_target_space =
- data_from_ancestor.clip_rect_in_target_space;
+ ancestor_clip_rect_in_target_space;
clip_rect_in_target_space.Intersect(rect_in_target_space);
} else {
clip_rect_in_target_space = rect_in_target_space;
}
}
- if (layer == globals.page_scale_application_layer) {
- data_for_children.parent_matrix.Scale(
- globals.page_scale_factor,
- globals.page_scale_factor);
- data_for_children.in_subtree_of_page_scale_application_layer = true;
- }
-
- // Flatten to 2D if the layer doesn't preserve 3D.
- if (!layer->preserves_3d())
- data_for_children.parent_matrix.FlattenTo2d();
-
- // Apply the sublayer transform at the anchor point of the layer.
- if (!layer->sublayer_transform().IsIdentity()) {
- data_for_children.parent_matrix.Translate(
- layer->anchor_point().x() * bounds.width(),
- layer->anchor_point().y() * bounds.height());
- data_for_children.parent_matrix.PreconcatTransform(
- layer->sublayer_transform());
- data_for_children.parent_matrix.Translate(
- -layer->anchor_point().x() * bounds.width(),
- -layer->anchor_point().y() * bounds.height());
+ // Tell the layer the rect that it's clipped by. In theory we could use a
+ // tighter clip rect here (drawable_content_rect), but that actually does not
+ // reduce how much would be drawn, and instead it would create unnecessary
+ // changes to scissor state affecting GPU performance. Our clip information
+ // is used in the recursion below, so we must set it beforehand.
+ layer_draw_properties.is_clipped = layer_or_ancestor_clips_descendants;
+ if (layer_or_ancestor_clips_descendants) {
+ layer_draw_properties.clip_rect = clip_rect_in_target_space;
+ } else {
+ // Initialize the clip rect to a safe value that will not clip the
+ // layer, just in case clipping is still accidentally used.
+ layer_draw_properties.clip_rect = rect_in_target_space;
}
LayerListType& descendants =
@@ -1448,25 +1721,50 @@ static void CalculateDrawPropertiesInternal(
if (!LayerShouldBeSkipped(layer, layer_is_visible))
descendants.push_back(layer);
- data_for_children.scroll_compensation_matrix =
- ComputeScrollCompensationMatrixForChildren(
- layer,
- data_from_ancestor.parent_matrix,
- data_from_ancestor.scroll_compensation_matrix);
- data_for_children.fixed_container =
- layer->IsContainerForFixedPositionLayers() ?
- layer : data_from_ancestor.fixed_container;
-
- data_for_children.clip_rect_in_target_space = clip_rect_in_target_space;
- data_for_children.clip_rect_of_target_surface_in_target_space =
- clip_rect_of_target_surface_in_target_space;
- data_for_children.ancestor_clips_subtree =
- layer_or_ancestor_clips_descendants;
- data_for_children.nearest_ancestor_surface_that_moves_pixels =
- nearest_ancestor_surface_that_moves_pixels;
- data_for_children.subtree_is_visible_from_ancestor = layer_is_visible;
-
- gfx::Rect accumulated_drawable_content_rect_of_children;
+ if (!layer->children().empty()) {
+ if (layer == globals.page_scale_application_layer) {
+ data_for_children.parent_matrix.Scale(
+ globals.page_scale_factor,
+ globals.page_scale_factor);
+ data_for_children.in_subtree_of_page_scale_application_layer = true;
+ }
+
+ // Flatten to 2D if the layer doesn't preserve 3D.
+ if (!layer->preserves_3d())
+ data_for_children.parent_matrix.FlattenTo2d();
+
+ // Apply the sublayer transform at the anchor point of the layer.
+ if (!layer->sublayer_transform().IsIdentity()) {
+ data_for_children.parent_matrix.Translate(
+ layer->anchor_point().x() * bounds.width(),
+ layer->anchor_point().y() * bounds.height());
+ data_for_children.parent_matrix.PreconcatTransform(
+ layer->sublayer_transform());
+ data_for_children.parent_matrix.Translate(
+ -layer->anchor_point().x() * bounds.width(),
+ -layer->anchor_point().y() * bounds.height());
+ }
+
+ data_for_children.scroll_compensation_matrix =
+ ComputeScrollCompensationMatrixForChildren(
+ layer,
+ data_from_ancestor.parent_matrix,
+ data_from_ancestor.scroll_compensation_matrix,
+ effective_scroll_delta);
+ data_for_children.fixed_container =
+ layer->IsContainerForFixedPositionLayers() ?
+ layer : data_from_ancestor.fixed_container;
+
+ data_for_children.clip_rect_in_target_space = clip_rect_in_target_space;
+ data_for_children.clip_rect_of_target_surface_in_target_space =
+ clip_rect_of_target_surface_in_target_space;
+ data_for_children.ancestor_clips_subtree =
+ layer_or_ancestor_clips_descendants;
+ data_for_children.nearest_ancestor_surface_that_moves_pixels =
+ nearest_ancestor_surface_that_moves_pixels;
+ data_for_children.subtree_is_visible_from_ancestor = layer_is_visible;
+ }
+
for (size_t i = 0; i < layer->children().size(); ++i) {
LayerType* child =
LayerTreeHostCommon::get_child_as_raw_ptr(layer->children(), i);
@@ -1480,29 +1778,34 @@ static void CalculateDrawPropertiesInternal(
data_for_children,
render_surface_layer_list,
&descendants,
- &drawable_content_rect_of_child_subtree);
- if (!drawable_content_rect_of_child_subtree.IsEmpty()) {
- accumulated_drawable_content_rect_of_children.Union(
- drawable_content_rect_of_child_subtree);
- if (child->render_surface())
- descendants.push_back(child);
+ accumulated_surface_state);
+ if (child->render_surface() &&
+ !child->render_surface()->content_rect().IsEmpty()) {
+ descendants.push_back(child);
}
}
+ // Compute the total drawable_content_rect for this subtree (the rect is in
+ // target surface space).
+ gfx::Rect local_drawable_content_rect_of_subtree =
+ accumulated_surface_state->back().drawable_content_rect;
+ if (layer->render_surface()) {
+ DCHECK(accumulated_surface_state->back().render_target == layer);
+ accumulated_surface_state->pop_back();
+ }
+
if (layer->render_surface() && !IsRootLayer(layer) &&
layer->render_surface()->layer_list().empty()) {
RemoveSurfaceForEarlyExit(layer, render_surface_layer_list);
return;
}
- // Compute the total drawable_content_rect for this subtree (the rect is in
- // target surface space).
- gfx::Rect local_drawable_content_rect_of_subtree =
- accumulated_drawable_content_rect_of_children;
- if (layer->DrawsContent())
- local_drawable_content_rect_of_subtree.Union(rect_in_target_space);
- if (layer_or_ancestor_clips_descendants)
- local_drawable_content_rect_of_subtree.Intersect(clip_rect_in_target_space);
+ if (layer->DrawsContent()) {
+ gfx::Rect local_drawable_content_rect = rect_in_target_space;
+ if (layer_or_ancestor_clips_descendants)
+ local_drawable_content_rect.Intersect(clip_rect_in_target_space);
+ local_drawable_content_rect_of_subtree.Union(local_drawable_content_rect);
+ }
// Compute the layer's drawable content rect (the rect is in target surface
// space).
@@ -1512,19 +1815,6 @@ static void CalculateDrawPropertiesInternal(
Intersect(clip_rect_in_target_space);
}
- // Tell the layer the rect that is clipped by. In theory we could use a
- // tighter clip rect here (drawable_content_rect), but that actually does not
- // reduce how much would be drawn, and instead it would create unnecessary
- // changes to scissor state affecting GPU performance.
- layer_draw_properties.is_clipped = layer_or_ancestor_clips_descendants;
- if (layer_or_ancestor_clips_descendants) {
- layer_draw_properties.clip_rect = clip_rect_in_target_space;
- } else {
- // Initialize the clip rect to a safe value that will not clip the
- // layer, just in case clipping is still accidentally used.
- layer_draw_properties.clip_rect = rect_in_target_space;
- }
-
// Compute the layer's visible content rect (the rect is in content space).
layer_draw_properties.visible_content_rect = CalculateVisibleContentRect(
layer, clip_rect_of_target_surface_in_target_space, rect_in_target_space);
@@ -1535,7 +1825,7 @@ static void CalculateDrawPropertiesInternal(
// The root layer's surface's content_rect is always the entire viewport.
DCHECK(layer->render_surface());
layer->render_surface()->SetContentRect(
- data_from_ancestor.clip_rect_in_target_space);
+ ancestor_clip_rect_in_target_space);
} else if (layer->render_surface() && !IsRootLayer(layer)) {
RenderSurfaceType* render_surface = layer->render_surface();
gfx::Rect clipped_content_rect = local_drawable_content_rect_of_subtree;
@@ -1548,8 +1838,7 @@ static void CalculateDrawPropertiesInternal(
// Note, it is correct to use data_from_ancestor.ancestor_clips_subtree
// here, because we are looking at this layer's render_surface, not the
// layer itself.
- if (data_from_ancestor.ancestor_clips_subtree &&
- !clipped_content_rect.IsEmpty()) {
+ if (render_surface->is_clipped() && !clipped_content_rect.IsEmpty()) {
gfx::Rect surface_clip_rect = LayerTreeHostCommon::CalculateVisibleRect(
render_surface->clip_rect(),
clipped_content_rect,
@@ -1620,8 +1909,10 @@ static void CalculateDrawPropertiesInternal(
SavePaintPropertiesLayer(layer);
// If neither this layer nor any of its children were added, early out.
- if (sorting_start_index == descendants.size())
+ if (sorting_start_index == descendants.size()) {
+ DCHECK(!layer->render_surface() || IsRootLayer(layer));
return;
+ }
// If preserves-3d then sort all the descendants in 3D so that they can be
// drawn from back to front. If the preserves-3d property is also set on the
@@ -1634,12 +1925,8 @@ static void CalculateDrawPropertiesInternal(
globals.layer_sorter);
}
- if (layer->render_surface()) {
- *drawable_content_rect_of_subtree =
- gfx::ToEnclosingRect(layer->render_surface()->DrawableContentRect());
- } else {
- *drawable_content_rect_of_subtree = local_drawable_content_rect_of_subtree;
- }
+ UpdateAccumulatedSurfaceState<LayerType, RenderSurfaceType>(
+ layer, local_drawable_content_rect_of_subtree, accumulated_surface_state);
if (layer->HasContributingDelegatedRenderPasses()) {
layer->render_target()->render_surface()->
@@ -1652,7 +1939,6 @@ void LayerTreeHostCommon::CalculateDrawProperties(
DCHECK(inputs->root_layer);
DCHECK(IsRootLayer(inputs->root_layer));
DCHECK(inputs->render_surface_layer_list);
- gfx::Rect total_drawable_content_rect;
gfx::Transform identity_matrix;
gfx::Transform scaled_device_transform = inputs->device_transform;
scaled_device_transform.Scale(inputs->device_scale_factor,
@@ -1687,14 +1973,14 @@ void LayerTreeHostCommon::CalculateDrawProperties(
PreCalculateMetaInformationRecursiveData recursive_data;
PreCalculateMetaInformation(inputs->root_layer, &recursive_data);
-
+ std::vector<AccumulatedSurfaceState<Layer> > accumulated_surface_state;
CalculateDrawPropertiesInternal<Layer, RenderSurfaceLayerList, RenderSurface>(
inputs->root_layer,
globals,
data_for_recursion,
inputs->render_surface_layer_list,
&dummy_layer_list,
- &total_drawable_content_rect);
+ &accumulated_surface_state);
// The dummy layer list should not have been used.
DCHECK_EQ(0u, dummy_layer_list.size());
@@ -1709,7 +1995,6 @@ void LayerTreeHostCommon::CalculateDrawProperties(
DCHECK(IsRootLayer(inputs->root_layer));
DCHECK(inputs->render_surface_layer_list);
- gfx::Rect total_drawable_content_rect;
gfx::Transform identity_matrix;
gfx::Transform scaled_device_transform = inputs->device_transform;
scaled_device_transform.Scale(inputs->device_scale_factor,
@@ -1745,14 +2030,15 @@ void LayerTreeHostCommon::CalculateDrawProperties(
PreCalculateMetaInformationRecursiveData recursive_data;
PreCalculateMetaInformation(inputs->root_layer, &recursive_data);
-
+ std::vector<AccumulatedSurfaceState<LayerImpl> >
+ accumulated_surface_state;
CalculateDrawPropertiesInternal<LayerImpl, LayerImplList, RenderSurfaceImpl>(
inputs->root_layer,
globals,
data_for_recursion,
inputs->render_surface_layer_list,
&dummy_layer_list,
- &total_drawable_content_rect);
+ &accumulated_surface_state);
// The dummy layer list should not have been used.
DCHECK_EQ(0u, dummy_layer_list.size());
diff --git a/chromium/cc/trees/layer_tree_host_common_unittest.cc b/chromium/cc/trees/layer_tree_host_common_unittest.cc
index fa5450d45f4..a5546192365 100644
--- a/chromium/cc/trees/layer_tree_host_common_unittest.cc
+++ b/chromium/cc/trees/layer_tree_host_common_unittest.cc
@@ -164,6 +164,10 @@ class LayerTreeHostCommonTestBase {
false);
}
+ RenderSurfaceLayerList* render_surface_layer_list() const {
+ return render_surface_layer_list_.get();
+ }
+
private:
scoped_ptr<RenderSurfaceLayerList> render_surface_layer_list_;
};
@@ -1368,29 +1372,25 @@ TEST_F(LayerTreeHostCommonTest, TransformsForRenderSurfaceHierarchy) {
// Sanity check. If these fail there is probably a bug in the test itself. It
// is expected that we correctly set up transforms so that the y-component of
// the screen-space transform encodes the "depth" of the layer in the tree.
- EXPECT_FLOAT_EQ(1.0,
- parent->screen_space_transform().matrix().getDouble(1, 3));
- EXPECT_FLOAT_EQ(
- 2.0, child_of_root->screen_space_transform().matrix().getDouble(1, 3));
+ EXPECT_FLOAT_EQ(1.0, parent->screen_space_transform().matrix().get(1, 3));
+ EXPECT_FLOAT_EQ(2.0,
+ child_of_root->screen_space_transform().matrix().get(1, 3));
EXPECT_FLOAT_EQ(
- 3.0,
- grand_child_of_root->screen_space_transform().matrix().getDouble(1, 3));
+ 3.0, grand_child_of_root->screen_space_transform().matrix().get(1, 3));
+ EXPECT_FLOAT_EQ(2.0,
+ render_surface1->screen_space_transform().matrix().get(1, 3));
+ EXPECT_FLOAT_EQ(3.0,
+ child_of_rs1->screen_space_transform().matrix().get(1, 3));
EXPECT_FLOAT_EQ(
- 2.0, render_surface1->screen_space_transform().matrix().getDouble(1, 3));
- EXPECT_FLOAT_EQ(
- 3.0, child_of_rs1->screen_space_transform().matrix().getDouble(1, 3));
- EXPECT_FLOAT_EQ(
- 4.0,
- grand_child_of_rs1->screen_space_transform().matrix().getDouble(1, 3));
+ 4.0, grand_child_of_rs1->screen_space_transform().matrix().get(1, 3));
+ EXPECT_FLOAT_EQ(3.0,
+ render_surface2->screen_space_transform().matrix().get(1, 3));
+ EXPECT_FLOAT_EQ(4.0,
+ child_of_rs2->screen_space_transform().matrix().get(1, 3));
EXPECT_FLOAT_EQ(
- 3.0, render_surface2->screen_space_transform().matrix().getDouble(1, 3));
- EXPECT_FLOAT_EQ(
- 4.0, child_of_rs2->screen_space_transform().matrix().getDouble(1, 3));
- EXPECT_FLOAT_EQ(
- 5.0,
- grand_child_of_rs2->screen_space_transform().matrix().getDouble(1, 3));
+ 5.0, grand_child_of_rs2->screen_space_transform().matrix().get(1, 3));
}
TEST_F(LayerTreeHostCommonTest, TransformsForFlatteningLayer) {
@@ -1660,8 +1660,10 @@ TEST_F(LayerTreeHostCommonTest, TransformAboveRootLayer) {
compositeSquared.ConcatTransform(composite);
gfx::Transform compositeCubed = compositeSquared;
compositeCubed.ConcatTransform(composite);
- EXPECT_EQ(compositeSquared, root->draw_properties().target_space_transform);
- EXPECT_EQ(compositeCubed, child->draw_properties().target_space_transform);
+ EXPECT_TRANSFORMATION_MATRIX_EQ(
+ compositeSquared, root->draw_properties().target_space_transform);
+ EXPECT_TRANSFORMATION_MATRIX_EQ(
+ compositeCubed, child->draw_properties().target_space_transform);
EXPECT_EQ(identity_matrix, root->render_surface()->draw_transform());
}
}
@@ -2634,29 +2636,25 @@ TEST_F(LayerTreeHostCommonTest, AnimationsForRenderSurfaceHierarchy) {
// Sanity check. If these fail there is probably a bug in the test itself.
// It is expected that we correctly set up transforms so that the y-component
// of the screen-space transform encodes the "depth" of the layer in the tree.
- EXPECT_FLOAT_EQ(1.0,
- parent->screen_space_transform().matrix().getDouble(1, 3));
- EXPECT_FLOAT_EQ(
- 2.0, child_of_root->screen_space_transform().matrix().getDouble(1, 3));
+ EXPECT_FLOAT_EQ(1.0, parent->screen_space_transform().matrix().get(1, 3));
+ EXPECT_FLOAT_EQ(2.0,
+ child_of_root->screen_space_transform().matrix().get(1, 3));
EXPECT_FLOAT_EQ(
- 3.0,
- grand_child_of_root->screen_space_transform().matrix().getDouble(1, 3));
+ 3.0, grand_child_of_root->screen_space_transform().matrix().get(1, 3));
+ EXPECT_FLOAT_EQ(2.0,
+ render_surface1->screen_space_transform().matrix().get(1, 3));
+ EXPECT_FLOAT_EQ(3.0,
+ child_of_rs1->screen_space_transform().matrix().get(1, 3));
EXPECT_FLOAT_EQ(
- 2.0, render_surface1->screen_space_transform().matrix().getDouble(1, 3));
- EXPECT_FLOAT_EQ(
- 3.0, child_of_rs1->screen_space_transform().matrix().getDouble(1, 3));
- EXPECT_FLOAT_EQ(
- 4.0,
- grand_child_of_rs1->screen_space_transform().matrix().getDouble(1, 3));
+ 4.0, grand_child_of_rs1->screen_space_transform().matrix().get(1, 3));
+ EXPECT_FLOAT_EQ(3.0,
+ render_surface2->screen_space_transform().matrix().get(1, 3));
+ EXPECT_FLOAT_EQ(4.0,
+ child_of_rs2->screen_space_transform().matrix().get(1, 3));
EXPECT_FLOAT_EQ(
- 3.0, render_surface2->screen_space_transform().matrix().getDouble(1, 3));
- EXPECT_FLOAT_EQ(
- 4.0, child_of_rs2->screen_space_transform().matrix().getDouble(1, 3));
- EXPECT_FLOAT_EQ(
- 5.0,
- grand_child_of_rs2->screen_space_transform().matrix().getDouble(1, 3));
+ 5.0, grand_child_of_rs2->screen_space_transform().matrix().get(1, 3));
}
TEST_F(LayerTreeHostCommonTest, VisibleRectForIdentityTransform) {
@@ -2792,7 +2790,8 @@ TEST_F(LayerTreeHostCommonTest, VisibleRectFor3dOrthographicTransform) {
// degrees, but shifted to the side so only the right-half the layer would be
// visible on the surface.
// 100 is the un-rotated layer width; divided by sqrt(2) is the rotated width.
- double half_width_of_rotated_layer = (100.0 / sqrt(2.0)) * 0.5;
+ SkMScalar half_width_of_rotated_layer =
+ SkDoubleToMScalar((100.0 / sqrt(2.0)) * 0.5);
layer_to_surface_transform.MakeIdentity();
layer_to_surface_transform.Translate(-half_width_of_rotated_layer, 0.0);
layer_to_surface_transform.RotateAboutYAxis(45.0); // Rotates about the left
@@ -3185,8 +3184,10 @@ TEST_F(LayerTreeHostCommonTest,
scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create();
host->SetRootLayer(root);
+ // Case 1: a truly degenerate matrix
gfx::Transform identity_matrix;
gfx::Transform uninvertible_matrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
+ ASSERT_FALSE(uninvertible_matrix.IsInvertible());
SetLayerPropertiesForTesting(root.get(),
identity_matrix,
@@ -3207,6 +3208,48 @@ TEST_F(LayerTreeHostCommonTest,
EXPECT_TRUE(child->visible_content_rect().IsEmpty());
EXPECT_TRUE(child->drawable_content_rect().IsEmpty());
+
+ // Case 2: a matrix with flattened z, technically uninvertible but still
+ // drawable and visible. In this case, we must assume that the entire layer
+ // bounds are visible since there is no way to inverse-project the surface
+ // bounds to intersect.
+ uninvertible_matrix.MakeIdentity();
+ uninvertible_matrix.matrix().set(2, 2, 0.0);
+ ASSERT_FALSE(uninvertible_matrix.IsInvertible());
+
+ SetLayerPropertiesForTesting(child.get(),
+ uninvertible_matrix,
+ identity_matrix,
+ gfx::PointF(),
+ gfx::PointF(5.f, 5.f),
+ gfx::Size(50, 50),
+ false);
+
+ ExecuteCalculateDrawProperties(root.get());
+
+ EXPECT_RECT_EQ(gfx::Rect(0, 0, 50, 50), child->visible_content_rect());
+ EXPECT_RECT_EQ(gfx::Rect(5, 5, 50, 50), child->drawable_content_rect());
+
+ // Case 3: a matrix with flattened z, technically uninvertible but still
+ // drawable, but not visible. In this case, we don't need to conservatively
+ // assume that the whole layer is visible.
+ uninvertible_matrix.MakeIdentity();
+ uninvertible_matrix.Translate(500.0, 0.0);
+ uninvertible_matrix.matrix().set(2, 2, 0.0);
+ ASSERT_FALSE(uninvertible_matrix.IsInvertible());
+
+ SetLayerPropertiesForTesting(child.get(),
+ uninvertible_matrix,
+ identity_matrix,
+ gfx::PointF(),
+ gfx::PointF(5.f, 5.f),
+ gfx::Size(50, 50),
+ false);
+
+ ExecuteCalculateDrawProperties(root.get());
+
+ EXPECT_TRUE(child->visible_content_rect().IsEmpty());
+ EXPECT_RECT_EQ(gfx::Rect(505, 5, 50, 50), child->drawable_content_rect());
}
TEST_F(LayerTreeHostCommonTest,
@@ -3460,10 +3503,10 @@ TEST_F(LayerTreeHostCommonTest,
// regions of the subtree.
int diagonal_radius = ceil(sqrt(2.0) * 25.0);
gfx::Rect expected_surface_drawable_content =
- gfx::Rect(50.0 - diagonal_radius,
- 50.0 - diagonal_radius,
- diagonal_radius * 2.0,
- diagonal_radius * 2.0);
+ gfx::Rect(50 - diagonal_radius,
+ 50 - diagonal_radius,
+ diagonal_radius * 2,
+ diagonal_radius * 2);
EXPECT_RECT_EQ(expected_surface_drawable_content,
render_surface1->render_surface()->DrawableContentRect());
@@ -3522,10 +3565,10 @@ TEST_F(LayerTreeHostCommonTest,
// The clipped surface clamps the DrawableContentRect that encloses the
// rotated layer.
int diagonal_radius = ceil(sqrt(2.0) * 25.0);
- gfx::Rect unclipped_surface_content = gfx::Rect(50.0 - diagonal_radius,
- 50.0 - diagonal_radius,
- diagonal_radius * 2.0,
- diagonal_radius * 2.0);
+ gfx::Rect unclipped_surface_content = gfx::Rect(50 - diagonal_radius,
+ 50 - diagonal_radius,
+ diagonal_radius * 2,
+ diagonal_radius * 2);
gfx::Rect expected_surface_drawable_content =
gfx::IntersectRects(unclipped_surface_content, gfx::Rect(0, 0, 50, 50));
EXPECT_RECT_EQ(expected_surface_drawable_content,
@@ -3534,6 +3577,8 @@ TEST_F(LayerTreeHostCommonTest,
// On the clipped surface, only a quarter of the child1 is visible, but when
// rotating it back to child1's content space, the actual enclosing rect ends
// up covering the full left half of child1.
+ //
+ // Given the floating point math, this number is a little bit fuzzy.
EXPECT_RECT_EQ(gfx::Rect(0, 0, 26, 50), child1->visible_content_rect());
// The child's DrawableContentRect is unclipped.
@@ -4420,10 +4465,10 @@ TEST_F(LayerTreeHostCommonTest, HitTestingForUninvertibleTransform) {
LayerImpl::Create(host_impl.active_tree(), 12345);
gfx::Transform uninvertible_transform;
- uninvertible_transform.matrix().setDouble(0, 0, 0.0);
- uninvertible_transform.matrix().setDouble(1, 1, 0.0);
- uninvertible_transform.matrix().setDouble(2, 2, 0.0);
- uninvertible_transform.matrix().setDouble(3, 3, 0.0);
+ uninvertible_transform.matrix().set(0, 0, 0.0);
+ uninvertible_transform.matrix().set(1, 1, 0.0);
+ uninvertible_transform.matrix().set(2, 2, 0.0);
+ uninvertible_transform.matrix().set(3, 3, 0.0);
ASSERT_FALSE(uninvertible_transform.IsInvertible());
gfx::Transform identity_matrix;
@@ -5006,7 +5051,7 @@ TEST_F(LayerTreeHostCommonTest, HitTestingForMultiClippedRotatedLayer) {
// Around the middle, just to the right and up, would have hit the layer
// except that that area should be clipped away by the parent.
- test_point = gfx::Point(51, 51);
+ test_point = gfx::Point(51, 49);
result_layer = LayerTreeHostCommon::FindLayerThatIsHitByPoint(
test_point, render_surface_layer_list);
EXPECT_FALSE(result_layer);
@@ -5519,10 +5564,10 @@ TEST_F(LayerTreeHostCommonTest,
LayerImpl::Create(host_impl.active_tree(), 12345);
gfx::Transform uninvertible_transform;
- uninvertible_transform.matrix().setDouble(0, 0, 0.0);
- uninvertible_transform.matrix().setDouble(1, 1, 0.0);
- uninvertible_transform.matrix().setDouble(2, 2, 0.0);
- uninvertible_transform.matrix().setDouble(3, 3, 0.0);
+ uninvertible_transform.matrix().set(0, 0, 0.0);
+ uninvertible_transform.matrix().set(1, 1, 0.0);
+ uninvertible_transform.matrix().set(2, 2, 0.0);
+ uninvertible_transform.matrix().set(3, 3, 0.0);
ASSERT_FALSE(uninvertible_transform.IsInvertible());
gfx::Transform identity_matrix;
@@ -6422,11 +6467,11 @@ TEST_F(LayerTreeHostCommonTest, ContentsScale) {
gfx::Transform identity_matrix;
gfx::Transform parent_scale_matrix;
- double initial_parent_scale = 1.75;
+ SkMScalar initial_parent_scale = 1.75;
parent_scale_matrix.Scale(initial_parent_scale, initial_parent_scale);
gfx::Transform child_scale_matrix;
- double initial_child_scale = 1.25;
+ SkMScalar initial_child_scale = 1.25;
child_scale_matrix.Scale(initial_child_scale, initial_child_scale);
scoped_refptr<Layer> root = Layer::Create();
@@ -6507,22 +6552,18 @@ TEST_F(LayerTreeHostCommonTest, ContentsScale) {
// child that can scale its contents should also not need to scale during
// draw. This shouldn't change if the child has empty bounds. The other
// children should.
- EXPECT_FLOAT_EQ(1.0, parent->draw_transform().matrix().getDouble(0, 0));
- EXPECT_FLOAT_EQ(1.0, parent->draw_transform().matrix().getDouble(1, 1));
- EXPECT_FLOAT_EQ(1.0,
- child_scale->draw_transform().matrix().getDouble(0, 0));
- EXPECT_FLOAT_EQ(1.0,
- child_scale->draw_transform().matrix().getDouble(1, 1));
- EXPECT_FLOAT_EQ(1.0,
- child_empty->draw_transform().matrix().getDouble(0, 0));
- EXPECT_FLOAT_EQ(1.0,
- child_empty->draw_transform().matrix().getDouble(1, 1));
+ EXPECT_FLOAT_EQ(1.0, parent->draw_transform().matrix().get(0, 0));
+ EXPECT_FLOAT_EQ(1.0, parent->draw_transform().matrix().get(1, 1));
+ EXPECT_FLOAT_EQ(1.0, child_scale->draw_transform().matrix().get(0, 0));
+ EXPECT_FLOAT_EQ(1.0, child_scale->draw_transform().matrix().get(1, 1));
+ EXPECT_FLOAT_EQ(1.0, child_empty->draw_transform().matrix().get(0, 0));
+ EXPECT_FLOAT_EQ(1.0, child_empty->draw_transform().matrix().get(1, 1));
EXPECT_FLOAT_EQ(device_scale_factor * page_scale_factor *
initial_parent_scale * initial_child_scale,
- child_no_scale->draw_transform().matrix().getDouble(0, 0));
+ child_no_scale->draw_transform().matrix().get(0, 0));
EXPECT_FLOAT_EQ(device_scale_factor * page_scale_factor *
- initial_parent_scale * initial_child_scale,
- child_no_scale->draw_transform().matrix().getDouble(1, 1));
+ initial_parent_scale * initial_child_scale,
+ child_no_scale->draw_transform().matrix().get(1, 1));
}
// If the device_scale_factor or page_scale_factor changes, then it should be
@@ -6552,7 +6593,7 @@ TEST_F(LayerTreeHostCommonTest, ContentsScale) {
}
// If the transform changes, we expect the raster scale to be reset to 1.0.
- double second_child_scale = 1.75;
+ SkMScalar second_child_scale = 1.75;
child_scale_matrix.Scale(second_child_scale / initial_child_scale,
second_child_scale / initial_child_scale);
child_scale->SetTransform(child_scale_matrix);
@@ -6610,11 +6651,11 @@ TEST_F(LayerTreeHostCommonTest,
gfx::Transform identity_matrix;
gfx::Transform parent_scale_matrix;
- double initial_parent_scale = 1.75;
+ SkMScalar initial_parent_scale = 1.75;
parent_scale_matrix.Scale(initial_parent_scale, initial_parent_scale);
gfx::Transform child_scale_matrix;
- double initial_child_scale = 1.25;
+ SkMScalar initial_child_scale = 1.25;
child_scale_matrix.Scale(initial_child_scale, initial_child_scale);
scoped_refptr<Layer> root = Layer::Create();
@@ -6690,23 +6731,23 @@ TEST_F(LayerTreeHostCommonTest,
// Since the transform scale does not affect contents scale, it should affect
// the draw transform instead.
EXPECT_FLOAT_EQ(initial_parent_scale,
- parent->draw_transform().matrix().getDouble(0, 0));
+ parent->draw_transform().matrix().get(0, 0));
EXPECT_FLOAT_EQ(initial_parent_scale,
- parent->draw_transform().matrix().getDouble(1, 1));
+ parent->draw_transform().matrix().get(1, 1));
EXPECT_FLOAT_EQ(initial_parent_scale * initial_child_scale,
- child_scale->draw_transform().matrix().getDouble(0, 0));
+ child_scale->draw_transform().matrix().get(0, 0));
EXPECT_FLOAT_EQ(initial_parent_scale * initial_child_scale,
- child_scale->draw_transform().matrix().getDouble(1, 1));
+ child_scale->draw_transform().matrix().get(1, 1));
EXPECT_FLOAT_EQ(initial_parent_scale * initial_child_scale,
- child_empty->draw_transform().matrix().getDouble(0, 0));
+ child_empty->draw_transform().matrix().get(0, 0));
EXPECT_FLOAT_EQ(initial_parent_scale * initial_child_scale,
- child_empty->draw_transform().matrix().getDouble(1, 1));
+ child_empty->draw_transform().matrix().get(1, 1));
EXPECT_FLOAT_EQ(device_scale_factor * page_scale_factor *
- initial_parent_scale * initial_child_scale,
- child_no_scale->draw_transform().matrix().getDouble(0, 0));
+ initial_parent_scale * initial_child_scale,
+ child_no_scale->draw_transform().matrix().get(0, 0));
EXPECT_FLOAT_EQ(device_scale_factor * page_scale_factor *
- initial_parent_scale * initial_child_scale,
- child_no_scale->draw_transform().matrix().getDouble(1, 1));
+ initial_parent_scale * initial_child_scale,
+ child_no_scale->draw_transform().matrix().get(1, 1));
}
TEST_F(LayerTreeHostCommonTest, SmallContentsScale) {
@@ -6714,11 +6755,11 @@ TEST_F(LayerTreeHostCommonTest, SmallContentsScale) {
gfx::Transform identity_matrix;
gfx::Transform parent_scale_matrix;
- double initial_parent_scale = 1.75;
+ SkMScalar initial_parent_scale = 1.75;
parent_scale_matrix.Scale(initial_parent_scale, initial_parent_scale);
gfx::Transform child_scale_matrix;
- double initial_child_scale = 0.25;
+ SkMScalar initial_child_scale = 0.25;
child_scale_matrix.Scale(initial_child_scale, initial_child_scale);
scoped_refptr<Layer> root = Layer::Create();
@@ -6775,7 +6816,7 @@ TEST_F(LayerTreeHostCommonTest, SmallContentsScale) {
// When chilld's total scale becomes >= 1, we should save and use that scale
// factor.
child_scale_matrix.MakeIdentity();
- double final_child_scale = 0.75;
+ SkMScalar final_child_scale = 0.75;
child_scale_matrix.Scale(final_child_scale, final_child_scale);
child_scale->SetTransform(child_scale_matrix);
@@ -6803,11 +6844,11 @@ TEST_F(LayerTreeHostCommonTest, ContentsScaleForSurfaces) {
gfx::Transform identity_matrix;
gfx::Transform parent_scale_matrix;
- double initial_parent_scale = 2.0;
+ SkMScalar initial_parent_scale = 2.0;
parent_scale_matrix.Scale(initial_parent_scale, initial_parent_scale);
gfx::Transform child_scale_matrix;
- double initial_child_scale = 3.0;
+ SkMScalar initial_child_scale = 3.0;
child_scale_matrix.Scale(initial_child_scale, initial_child_scale);
scoped_refptr<Layer> root = Layer::Create();
@@ -6898,8 +6939,8 @@ TEST_F(LayerTreeHostCommonTest, ContentsScaleForSurfaces) {
scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create();
host->SetRootLayer(root);
- double device_scale_factor = 5;
- double page_scale_factor = 7;
+ SkMScalar device_scale_factor = 5;
+ SkMScalar page_scale_factor = 7;
RenderSurfaceLayerList render_surface_layer_list;
LayerTreeHostCommon::CalcDrawPropsMainInputsForTesting inputs(
@@ -6928,86 +6969,74 @@ TEST_F(LayerTreeHostCommonTest, ContentsScaleForSurfaces) {
EXPECT_CONTENTS_SCALE_EQ(1, surface_no_scale_child_no_scale);
// The parent is scaled up and shouldn't need to scale during draw.
- EXPECT_FLOAT_EQ(1.0, parent->draw_transform().matrix().getDouble(0, 0));
- EXPECT_FLOAT_EQ(1.0, parent->draw_transform().matrix().getDouble(1, 1));
+ EXPECT_FLOAT_EQ(1.0, parent->draw_transform().matrix().get(0, 0));
+ EXPECT_FLOAT_EQ(1.0, parent->draw_transform().matrix().get(1, 1));
// RenderSurfaces should always be 1:1 with their target.
EXPECT_FLOAT_EQ(
1.0,
- surface_scale->render_surface()->draw_transform().matrix().getDouble(0,
- 0));
+ surface_scale->render_surface()->draw_transform().matrix().get(0, 0));
EXPECT_FLOAT_EQ(
1.0,
- surface_scale->render_surface()->draw_transform().matrix().getDouble(1,
- 1));
+ surface_scale->render_surface()->draw_transform().matrix().get(1, 1));
// The surface_scale can apply contents scale so the layer shouldn't need to
// scale during draw.
- EXPECT_FLOAT_EQ(1.0,
- surface_scale->draw_transform().matrix().getDouble(0, 0));
- EXPECT_FLOAT_EQ(1.0,
- surface_scale->draw_transform().matrix().getDouble(1, 1));
+ EXPECT_FLOAT_EQ(1.0, surface_scale->draw_transform().matrix().get(0, 0));
+ EXPECT_FLOAT_EQ(1.0, surface_scale->draw_transform().matrix().get(1, 1));
// The surface_scale_child_scale can apply contents scale so it shouldn't need
// to scale during draw.
EXPECT_FLOAT_EQ(
- 1.0,
- surface_scale_child_scale->draw_transform().matrix().getDouble(0, 0));
+ 1.0, surface_scale_child_scale->draw_transform().matrix().get(0, 0));
EXPECT_FLOAT_EQ(
- 1.0,
- surface_scale_child_scale->draw_transform().matrix().getDouble(1, 1));
+ 1.0, surface_scale_child_scale->draw_transform().matrix().get(1, 1));
// The surface_scale_child_no_scale can not apply contents scale, so it needs
// to be scaled during draw.
EXPECT_FLOAT_EQ(
device_scale_factor * page_scale_factor * initial_parent_scale *
- initial_child_scale * initial_child_scale,
- surface_scale_child_no_scale->draw_transform().matrix().getDouble(0, 0));
+ initial_child_scale * initial_child_scale,
+ surface_scale_child_no_scale->draw_transform().matrix().get(0, 0));
EXPECT_FLOAT_EQ(
device_scale_factor * page_scale_factor * initial_parent_scale *
- initial_child_scale * initial_child_scale,
- surface_scale_child_no_scale->draw_transform().matrix().getDouble(1, 1));
+ initial_child_scale * initial_child_scale,
+ surface_scale_child_no_scale->draw_transform().matrix().get(1, 1));
// RenderSurfaces should always be 1:1 with their target.
EXPECT_FLOAT_EQ(
1.0,
- surface_no_scale->render_surface()->draw_transform().matrix().getDouble(
- 0, 0));
+ surface_no_scale->render_surface()->draw_transform().matrix().get(0, 0));
EXPECT_FLOAT_EQ(
1.0,
- surface_no_scale->render_surface()->draw_transform().matrix().getDouble(
- 1, 1));
+ surface_no_scale->render_surface()->draw_transform().matrix().get(1, 1));
// The surface_no_scale layer can not apply contents scale, so it needs to be
// scaled during draw.
EXPECT_FLOAT_EQ(device_scale_factor * page_scale_factor *
- initial_parent_scale * initial_child_scale,
- surface_no_scale->draw_transform().matrix().getDouble(0, 0));
+ initial_parent_scale * initial_child_scale,
+ surface_no_scale->draw_transform().matrix().get(0, 0));
EXPECT_FLOAT_EQ(device_scale_factor * page_scale_factor *
- initial_parent_scale * initial_child_scale,
- surface_no_scale->draw_transform().matrix().getDouble(1, 1));
+ initial_parent_scale * initial_child_scale,
+ surface_no_scale->draw_transform().matrix().get(1, 1));
// The surface_scale_child_scale can apply contents scale so it shouldn't need
// to scale during draw.
EXPECT_FLOAT_EQ(
- 1.0,
- surface_no_scale_child_scale->draw_transform().matrix().getDouble(0, 0));
+ 1.0, surface_no_scale_child_scale->draw_transform().matrix().get(0, 0));
EXPECT_FLOAT_EQ(
- 1.0,
- surface_no_scale_child_scale->draw_transform().matrix().getDouble(1, 1));
+ 1.0, surface_no_scale_child_scale->draw_transform().matrix().get(1, 1));
// The surface_scale_child_no_scale can not apply contents scale, so it needs
// to be scaled during draw.
EXPECT_FLOAT_EQ(
device_scale_factor * page_scale_factor * initial_parent_scale *
- initial_child_scale * initial_child_scale,
- surface_no_scale_child_no_scale->draw_transform().matrix().getDouble(0,
- 0));
+ initial_child_scale * initial_child_scale,
+ surface_no_scale_child_no_scale->draw_transform().matrix().get(0, 0));
EXPECT_FLOAT_EQ(
device_scale_factor * page_scale_factor * initial_parent_scale *
- initial_child_scale * initial_child_scale,
- surface_no_scale_child_no_scale->draw_transform().matrix().getDouble(1,
- 1));
+ initial_child_scale * initial_child_scale,
+ surface_no_scale_child_no_scale->draw_transform().matrix().get(1, 1));
}
TEST_F(LayerTreeHostCommonTest,
@@ -7016,11 +7045,11 @@ TEST_F(LayerTreeHostCommonTest,
gfx::Transform identity_matrix;
gfx::Transform parent_scale_matrix;
- double initial_parent_scale = 2.0;
+ SkMScalar initial_parent_scale = 2.0;
parent_scale_matrix.Scale(initial_parent_scale, initial_parent_scale);
gfx::Transform child_scale_matrix;
- double initial_child_scale = 3.0;
+ SkMScalar initial_child_scale = 3.0;
child_scale_matrix.Scale(initial_child_scale, initial_child_scale);
scoped_refptr<Layer> root = Layer::Create();
@@ -7113,8 +7142,8 @@ TEST_F(LayerTreeHostCommonTest,
RenderSurfaceLayerList render_surface_layer_list;
- double device_scale_factor = 5.0;
- double page_scale_factor = 7.0;
+ SkMScalar device_scale_factor = 5.0;
+ SkMScalar page_scale_factor = 7.0;
LayerTreeHostCommon::CalcDrawPropsMainInputsForTesting inputs(
root.get(), root->bounds(), &render_surface_layer_list);
inputs.device_scale_factor = device_scale_factor;
@@ -7137,88 +7166,80 @@ TEST_F(LayerTreeHostCommonTest,
// The parent is scaled up during draw, since its contents are not scaled by
// the transform hierarchy.
EXPECT_FLOAT_EQ(initial_parent_scale,
- parent->draw_transform().matrix().getDouble(0, 0));
+ parent->draw_transform().matrix().get(0, 0));
EXPECT_FLOAT_EQ(initial_parent_scale,
- parent->draw_transform().matrix().getDouble(1, 1));
+ parent->draw_transform().matrix().get(1, 1));
// The child surface is scaled up during draw since its subtree is not scaled
// by the transform hierarchy.
EXPECT_FLOAT_EQ(
initial_parent_scale * initial_child_scale,
- surface_scale->render_surface()->draw_transform().matrix().getDouble(0,
- 0));
+ surface_scale->render_surface()->draw_transform().matrix().get(0, 0));
EXPECT_FLOAT_EQ(
initial_parent_scale * initial_child_scale,
- surface_scale->render_surface()->draw_transform().matrix().getDouble(1,
- 1));
+ surface_scale->render_surface()->draw_transform().matrix().get(1, 1));
// The surface_scale's RenderSurface is scaled during draw, so the layer does
// not need to be scaled when drawing into its surface.
- EXPECT_FLOAT_EQ(1.0,
- surface_scale->draw_transform().matrix().getDouble(0, 0));
- EXPECT_FLOAT_EQ(1.0,
- surface_scale->draw_transform().matrix().getDouble(1, 1));
+ EXPECT_FLOAT_EQ(1.0, surface_scale->draw_transform().matrix().get(0, 0));
+ EXPECT_FLOAT_EQ(1.0, surface_scale->draw_transform().matrix().get(1, 1));
// The surface_scale_child_scale is scaled when drawing into its surface,
// since its content bounds are not scaled by the transform hierarchy.
EXPECT_FLOAT_EQ(
initial_child_scale,
- surface_scale_child_scale->draw_transform().matrix().getDouble(0, 0));
+ surface_scale_child_scale->draw_transform().matrix().get(0, 0));
EXPECT_FLOAT_EQ(
initial_child_scale,
- surface_scale_child_scale->draw_transform().matrix().getDouble(1, 1));
+ surface_scale_child_scale->draw_transform().matrix().get(1, 1));
// The surface_scale_child_no_scale has a fixed contents scale of 1, so it
// needs to be scaled by the device and page scale factors, along with the
// transform hierarchy.
EXPECT_FLOAT_EQ(
device_scale_factor * page_scale_factor * initial_child_scale,
- surface_scale_child_no_scale->draw_transform().matrix().getDouble(0, 0));
+ surface_scale_child_no_scale->draw_transform().matrix().get(0, 0));
EXPECT_FLOAT_EQ(
device_scale_factor * page_scale_factor * initial_child_scale,
- surface_scale_child_no_scale->draw_transform().matrix().getDouble(1, 1));
+ surface_scale_child_no_scale->draw_transform().matrix().get(1, 1));
// The child surface is scaled up during draw since its subtree is not scaled
// by the transform hierarchy.
EXPECT_FLOAT_EQ(
initial_parent_scale * initial_child_scale,
- surface_no_scale->render_surface()->draw_transform().matrix().getDouble(
- 0, 0));
+ surface_no_scale->render_surface()->draw_transform().matrix().get(0, 0));
EXPECT_FLOAT_EQ(
initial_parent_scale * initial_child_scale,
- surface_no_scale->render_surface()->draw_transform().matrix().getDouble(
- 1, 1));
+ surface_no_scale->render_surface()->draw_transform().matrix().get(1, 1));
// The surface_no_scale layer has a fixed contents scale of 1, so it needs to
// be scaled by the device and page scale factors. Its surface is already
// scaled by the transform hierarchy so those don't need to scale the layer's
// drawing.
EXPECT_FLOAT_EQ(device_scale_factor * page_scale_factor,
- surface_no_scale->draw_transform().matrix().getDouble(0, 0));
+ surface_no_scale->draw_transform().matrix().get(0, 0));
EXPECT_FLOAT_EQ(device_scale_factor * page_scale_factor,
- surface_no_scale->draw_transform().matrix().getDouble(1, 1));
+ surface_no_scale->draw_transform().matrix().get(1, 1));
// The surface_no_scale_child_scale has its contents scaled by the page and
// device scale factors, but needs to be scaled by the transform hierarchy
// when drawing.
EXPECT_FLOAT_EQ(
initial_child_scale,
- surface_no_scale_child_scale->draw_transform().matrix().getDouble(0, 0));
+ surface_no_scale_child_scale->draw_transform().matrix().get(0, 0));
EXPECT_FLOAT_EQ(
initial_child_scale,
- surface_no_scale_child_scale->draw_transform().matrix().getDouble(1, 1));
+ surface_no_scale_child_scale->draw_transform().matrix().get(1, 1));
// The surface_no_scale_child_no_scale has a fixed contents scale of 1, so it
// needs to be scaled by the device and page scale factors. It also needs to
// be scaled by any transform heirarchy below its target surface.
EXPECT_FLOAT_EQ(
device_scale_factor * page_scale_factor * initial_child_scale,
- surface_no_scale_child_no_scale->draw_transform().matrix().getDouble(0,
- 0));
+ surface_no_scale_child_no_scale->draw_transform().matrix().get(0, 0));
EXPECT_FLOAT_EQ(
device_scale_factor * page_scale_factor * initial_child_scale,
- surface_no_scale_child_no_scale->draw_transform().matrix().getDouble(1,
- 1));
+ surface_no_scale_child_no_scale->draw_transform().matrix().get(1, 1));
}
TEST_F(LayerTreeHostCommonTest, ContentsScaleForAnimatingLayer) {
@@ -7226,11 +7247,11 @@ TEST_F(LayerTreeHostCommonTest, ContentsScaleForAnimatingLayer) {
gfx::Transform identity_matrix;
gfx::Transform parent_scale_matrix;
- double initial_parent_scale = 1.75;
+ SkMScalar initial_parent_scale = 1.75;
parent_scale_matrix.Scale(initial_parent_scale, initial_parent_scale);
gfx::Transform child_scale_matrix;
- double initial_child_scale = 1.25;
+ SkMScalar initial_child_scale = 1.25;
child_scale_matrix.Scale(initial_child_scale, initial_child_scale);
scoped_refptr<Layer> root = Layer::Create();
@@ -7412,17 +7433,17 @@ TEST_F(LayerTreeHostCommonTest, RenderSurfaceTransformsInHighDPI) {
child->render_surface()->screen_space_transform());
gfx::Transform expected_replica_draw_transform;
- expected_replica_draw_transform.matrix().setDouble(1, 1, -1.0);
- expected_replica_draw_transform.matrix().setDouble(0, 3, 6.0);
- expected_replica_draw_transform.matrix().setDouble(1, 3, 6.0);
+ expected_replica_draw_transform.matrix().set(1, 1, -1.0);
+ expected_replica_draw_transform.matrix().set(0, 3, 6.0);
+ expected_replica_draw_transform.matrix().set(1, 3, 6.0);
EXPECT_TRANSFORMATION_MATRIX_EQ(
expected_replica_draw_transform,
child->render_surface()->replica_draw_transform());
gfx::Transform expected_replica_screen_space_transform;
- expected_replica_screen_space_transform.matrix().setDouble(1, 1, -1.0);
- expected_replica_screen_space_transform.matrix().setDouble(0, 3, 6.0);
- expected_replica_screen_space_transform.matrix().setDouble(1, 3, 6.0);
+ expected_replica_screen_space_transform.matrix().set(1, 1, -1.0);
+ expected_replica_screen_space_transform.matrix().set(0, 3, 6.0);
+ expected_replica_screen_space_transform.matrix().set(1, 3, 6.0);
EXPECT_TRANSFORMATION_MATRIX_EQ(
expected_replica_screen_space_transform,
child->render_surface()->replica_screen_space_transform());
@@ -7522,13 +7543,13 @@ TEST_F(LayerTreeHostCommonTest,
identity_transform, child->render_surface()->screen_space_transform());
gfx::Transform expected_replica_draw_transform;
- expected_replica_draw_transform.matrix().setDouble(1, 1, -1.0);
+ expected_replica_draw_transform.matrix().set(1, 1, -1.0);
EXPECT_TRANSFORMATION_MATRIX_EQ(
expected_replica_draw_transform,
child->render_surface()->replica_draw_transform());
gfx::Transform expected_replica_screen_space_transform;
- expected_replica_screen_space_transform.matrix().setDouble(1, 1, -1.0);
+ expected_replica_screen_space_transform.matrix().set(1, 1, -1.0);
EXPECT_TRANSFORMATION_MATRIX_EQ(
expected_replica_screen_space_transform,
child->render_surface()->replica_screen_space_transform());
@@ -8306,5 +8327,736 @@ TEST_F(LayerTreeHostCommonTest, VisibleContentRectInsideSurface) {
surface_child->visible_content_rect().ToString());
}
+TEST_F(LayerTreeHostCommonTest, TransformedClipParent) {
+ // Ensure that a transform between the layer and its render surface is not a
+ // problem. Constructs the following layer tree.
+ //
+ // root (a render surface)
+ // + render_surface
+ // + clip_parent (scaled)
+ // + intervening_clipping_layer
+ // + clip_child
+ //
+ // The render surface should be resized correctly and the clip child should
+ // inherit the right clip rect.
+ scoped_refptr<Layer> root = Layer::Create();
+ scoped_refptr<Layer> render_surface = Layer::Create();
+ scoped_refptr<Layer> clip_parent = Layer::Create();
+ scoped_refptr<Layer> intervening = Layer::Create();
+ scoped_refptr<LayerWithForcedDrawsContent> clip_child =
+ make_scoped_refptr(new LayerWithForcedDrawsContent);
+
+ root->AddChild(render_surface);
+ render_surface->AddChild(clip_parent);
+ clip_parent->AddChild(intervening);
+ intervening->AddChild(clip_child);
+
+ clip_child->SetClipParent(clip_parent.get());
+
+ intervening->SetMasksToBounds(true);
+ clip_parent->SetMasksToBounds(true);
+
+ render_surface->SetForceRenderSurface(true);
+
+ gfx::Transform scale_transform;
+ scale_transform.Scale(2, 2);
+
+ gfx::Transform identity_transform;
+
+ SetLayerPropertiesForTesting(root.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(),
+ gfx::Size(50, 50),
+ false);
+ SetLayerPropertiesForTesting(render_surface.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(),
+ gfx::Size(10, 10),
+ false);
+ SetLayerPropertiesForTesting(clip_parent.get(),
+ scale_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(1.f, 1.f),
+ gfx::Size(10, 10),
+ false);
+ SetLayerPropertiesForTesting(intervening.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(1.f, 1.f),
+ gfx::Size(5, 5),
+ false);
+ SetLayerPropertiesForTesting(clip_child.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(1.f, 1.f),
+ gfx::Size(10, 10),
+ false);
+
+ scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create();
+ host->SetRootLayer(root);
+
+ ExecuteCalculateDrawProperties(root.get());
+
+ ASSERT_TRUE(root->render_surface());
+ ASSERT_TRUE(render_surface->render_surface());
+
+ // Ensure that we've inherited our clip parent's clip and weren't affected
+ // by the intervening clip layer.
+ ASSERT_EQ(gfx::Rect(1, 1, 20, 20).ToString(),
+ clip_parent->clip_rect().ToString());
+ ASSERT_EQ(clip_parent->clip_rect().ToString(),
+ clip_child->clip_rect().ToString());
+ ASSERT_EQ(gfx::Rect(3, 3, 10, 10).ToString(),
+ intervening->clip_rect().ToString());
+
+ // Ensure that the render surface reports a content rect that has been grown
+ // to accomodate for the clip child.
+ ASSERT_EQ(gfx::Rect(5, 5, 16, 16).ToString(),
+ render_surface->render_surface()->content_rect().ToString());
+
+ // The above check implies the two below, but they nicely demonstrate that
+ // we've grown, despite the intervening layer's clip.
+ ASSERT_TRUE(clip_parent->clip_rect().Contains(
+ render_surface->render_surface()->content_rect()));
+ ASSERT_FALSE(intervening->clip_rect().Contains(
+ render_surface->render_surface()->content_rect()));
+}
+
+TEST_F(LayerTreeHostCommonTest, ClipParentWithInterveningRenderSurface) {
+ // Ensure that intervening render surfaces are not a problem in the basic
+ // case. In the following tree, both render surfaces should be resized to
+ // accomodate for the clip child, despite an intervening clip.
+ //
+ // root (a render surface)
+ // + clip_parent (masks to bounds)
+ // + render_surface1 (sets opacity)
+ // + intervening (masks to bounds)
+ // + render_surface2 (also sets opacity)
+ // + clip_child
+ //
+ scoped_refptr<Layer> root = Layer::Create();
+ scoped_refptr<Layer> clip_parent = Layer::Create();
+ scoped_refptr<Layer> render_surface1 = Layer::Create();
+ scoped_refptr<Layer> intervening = Layer::Create();
+ scoped_refptr<Layer> render_surface2 = Layer::Create();
+ scoped_refptr<LayerWithForcedDrawsContent> clip_child =
+ make_scoped_refptr(new LayerWithForcedDrawsContent);
+
+ root->AddChild(clip_parent);
+ clip_parent->AddChild(render_surface1);
+ render_surface1->AddChild(intervening);
+ intervening->AddChild(render_surface2);
+ render_surface2->AddChild(clip_child);
+
+ clip_child->SetClipParent(clip_parent.get());
+
+ intervening->SetMasksToBounds(true);
+ clip_parent->SetMasksToBounds(true);
+
+ render_surface1->SetForceRenderSurface(true);
+ render_surface2->SetForceRenderSurface(true);
+
+ gfx::Transform translation_transform;
+ translation_transform.Translate(2, 2);
+
+ gfx::Transform identity_transform;
+ SetLayerPropertiesForTesting(root.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(),
+ gfx::Size(50, 50),
+ false);
+ SetLayerPropertiesForTesting(clip_parent.get(),
+ translation_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(1.f, 1.f),
+ gfx::Size(40, 40),
+ false);
+ SetLayerPropertiesForTesting(render_surface1.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(),
+ gfx::Size(10, 10),
+ false);
+ SetLayerPropertiesForTesting(intervening.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(1.f, 1.f),
+ gfx::Size(5, 5),
+ false);
+ SetLayerPropertiesForTesting(render_surface2.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(),
+ gfx::Size(10, 10),
+ false);
+ SetLayerPropertiesForTesting(clip_child.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(-10.f, -10.f),
+ gfx::Size(60, 60),
+ false);
+
+ scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create();
+ host->SetRootLayer(root);
+
+ ExecuteCalculateDrawProperties(root.get());
+
+ EXPECT_TRUE(root->render_surface());
+ EXPECT_TRUE(render_surface1->render_surface());
+ EXPECT_TRUE(render_surface2->render_surface());
+
+ // Since the render surfaces could have expanded, they should not clip (their
+ // bounds would no longer be reliable). We should resort to layer clipping
+ // in this case.
+ EXPECT_EQ(gfx::Rect(0, 0, 0, 0).ToString(),
+ render_surface1->render_surface()->clip_rect().ToString());
+ EXPECT_FALSE(render_surface1->render_surface()->is_clipped());
+ EXPECT_EQ(gfx::Rect(0, 0, 0, 0).ToString(),
+ render_surface2->render_surface()->clip_rect().ToString());
+ EXPECT_FALSE(render_surface2->render_surface()->is_clipped());
+
+ // NB: clip rects are in target space.
+ EXPECT_EQ(gfx::Rect(0, 0, 40, 40).ToString(),
+ render_surface1->clip_rect().ToString());
+ EXPECT_TRUE(render_surface1->is_clipped());
+
+ // This value is inherited from the clipping ancestor layer, 'intervening'.
+ EXPECT_EQ(gfx::Rect(0, 0, 5, 5).ToString(),
+ render_surface2->clip_rect().ToString());
+ EXPECT_TRUE(render_surface2->is_clipped());
+
+ // The content rects of both render surfaces should both have expanded to
+ // contain the clip child.
+ EXPECT_EQ(gfx::Rect(0, 0, 40, 40).ToString(),
+ render_surface1->render_surface()->content_rect().ToString());
+ EXPECT_EQ(gfx::Rect(-1, -1, 40, 40).ToString(),
+ render_surface2->render_surface()->content_rect().ToString());
+
+ // The clip child should have inherited the clip parent's clip (projected to
+ // the right space, of course), and should have the correctly sized visible
+ // content rect.
+ EXPECT_EQ(gfx::Rect(-1, -1, 40, 40).ToString(),
+ clip_child->clip_rect().ToString());
+ EXPECT_EQ(gfx::Rect(9, 9, 40, 40).ToString(),
+ clip_child->visible_content_rect().ToString());
+ EXPECT_TRUE(clip_child->is_clipped());
+}
+
+TEST_F(LayerTreeHostCommonTest, ClipParentScrolledInterveningLayer) {
+ // Ensure that intervening render surfaces are not a problem, even if there
+ // is a scroll involved. Note, we do _not_ have to consider any other sort
+ // of transform.
+ //
+ // root (a render surface)
+ // + clip_parent (masks to bounds)
+ // + render_surface1 (sets opacity)
+ // + intervening (masks to bounds AND scrolls)
+ // + render_surface2 (also sets opacity)
+ // + clip_child
+ //
+ scoped_refptr<Layer> root = Layer::Create();
+ scoped_refptr<Layer> clip_parent = Layer::Create();
+ scoped_refptr<Layer> render_surface1 = Layer::Create();
+ scoped_refptr<Layer> intervening = Layer::Create();
+ scoped_refptr<Layer> render_surface2 = Layer::Create();
+ scoped_refptr<LayerWithForcedDrawsContent> clip_child =
+ make_scoped_refptr(new LayerWithForcedDrawsContent);
+
+ root->AddChild(clip_parent);
+ clip_parent->AddChild(render_surface1);
+ render_surface1->AddChild(intervening);
+ intervening->AddChild(render_surface2);
+ render_surface2->AddChild(clip_child);
+
+ clip_child->SetClipParent(clip_parent.get());
+
+ intervening->SetMasksToBounds(true);
+ clip_parent->SetMasksToBounds(true);
+ intervening->SetScrollable(true);
+ intervening->SetMaxScrollOffset(gfx::Vector2d(50, 50));
+ intervening->SetScrollOffset(gfx::Vector2d(3, 3));
+
+ render_surface1->SetForceRenderSurface(true);
+ render_surface2->SetForceRenderSurface(true);
+
+ gfx::Transform translation_transform;
+ translation_transform.Translate(2, 2);
+
+ gfx::Transform identity_transform;
+ SetLayerPropertiesForTesting(root.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(),
+ gfx::Size(50, 50),
+ false);
+ SetLayerPropertiesForTesting(clip_parent.get(),
+ translation_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(1.f, 1.f),
+ gfx::Size(40, 40),
+ false);
+ SetLayerPropertiesForTesting(render_surface1.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(),
+ gfx::Size(10, 10),
+ false);
+ SetLayerPropertiesForTesting(intervening.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(1.f, 1.f),
+ gfx::Size(5, 5),
+ false);
+ SetLayerPropertiesForTesting(render_surface2.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(),
+ gfx::Size(10, 10),
+ false);
+ SetLayerPropertiesForTesting(clip_child.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(-10.f, -10.f),
+ gfx::Size(60, 60),
+ false);
+
+ scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create();
+ host->SetRootLayer(root);
+
+ ExecuteCalculateDrawProperties(root.get());
+
+ EXPECT_TRUE(root->render_surface());
+ EXPECT_TRUE(render_surface1->render_surface());
+ EXPECT_TRUE(render_surface2->render_surface());
+
+ // Since the render surfaces could have expanded, they should not clip (their
+ // bounds would no longer be reliable). We should resort to layer clipping
+ // in this case.
+ EXPECT_EQ(gfx::Rect(0, 0, 0, 0).ToString(),
+ render_surface1->render_surface()->clip_rect().ToString());
+ EXPECT_FALSE(render_surface1->render_surface()->is_clipped());
+ EXPECT_EQ(gfx::Rect(0, 0, 0, 0).ToString(),
+ render_surface2->render_surface()->clip_rect().ToString());
+ EXPECT_FALSE(render_surface2->render_surface()->is_clipped());
+
+ // NB: clip rects are in target space.
+ EXPECT_EQ(gfx::Rect(0, 0, 40, 40).ToString(),
+ render_surface1->clip_rect().ToString());
+ EXPECT_TRUE(render_surface1->is_clipped());
+
+ // This value is inherited from the clipping ancestor layer, 'intervening'.
+ EXPECT_EQ(gfx::Rect(2, 2, 3, 3).ToString(),
+ render_surface2->clip_rect().ToString());
+ EXPECT_TRUE(render_surface2->is_clipped());
+
+ // The content rects of both render surfaces should both have expanded to
+ // contain the clip child.
+ EXPECT_EQ(gfx::Rect(0, 0, 40, 40).ToString(),
+ render_surface1->render_surface()->content_rect().ToString());
+ EXPECT_EQ(gfx::Rect(2, 2, 40, 40).ToString(),
+ render_surface2->render_surface()->content_rect().ToString());
+
+ // The clip child should have inherited the clip parent's clip (projected to
+ // the right space, of course), and should have the correctly sized visible
+ // content rect.
+ EXPECT_EQ(gfx::Rect(2, 2, 40, 40).ToString(),
+ clip_child->clip_rect().ToString());
+ EXPECT_EQ(gfx::Rect(12, 12, 40, 40).ToString(),
+ clip_child->visible_content_rect().ToString());
+ EXPECT_TRUE(clip_child->is_clipped());
+}
+
+TEST_F(LayerTreeHostCommonTest, DescendantsOfClipChildren) {
+ // Ensures that descendants of the clip child inherit the correct clip.
+ //
+ // root (a render surface)
+ // + clip_parent (masks to bounds)
+ // + intervening (masks to bounds)
+ // + clip_child
+ // + child
+ //
+ scoped_refptr<Layer> root = Layer::Create();
+ scoped_refptr<Layer> clip_parent = Layer::Create();
+ scoped_refptr<Layer> intervening = Layer::Create();
+ scoped_refptr<Layer> clip_child = Layer::Create();
+ scoped_refptr<LayerWithForcedDrawsContent> child =
+ make_scoped_refptr(new LayerWithForcedDrawsContent);
+
+ root->AddChild(clip_parent);
+ clip_parent->AddChild(intervening);
+ intervening->AddChild(clip_child);
+ clip_child->AddChild(child);
+
+ clip_child->SetClipParent(clip_parent.get());
+
+ intervening->SetMasksToBounds(true);
+ clip_parent->SetMasksToBounds(true);
+
+ gfx::Transform identity_transform;
+ SetLayerPropertiesForTesting(root.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(),
+ gfx::Size(50, 50),
+ false);
+ SetLayerPropertiesForTesting(clip_parent.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(),
+ gfx::Size(40, 40),
+ false);
+ SetLayerPropertiesForTesting(intervening.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(),
+ gfx::Size(5, 5),
+ false);
+ SetLayerPropertiesForTesting(clip_child.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(),
+ gfx::Size(60, 60),
+ false);
+ SetLayerPropertiesForTesting(child.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(),
+ gfx::Size(60, 60),
+ false);
+
+ scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create();
+ host->SetRootLayer(root);
+
+ ExecuteCalculateDrawProperties(root.get());
+
+ EXPECT_TRUE(root->render_surface());
+
+ // Neither the clip child nor its descendant should have inherited the clip
+ // from |intervening|.
+ EXPECT_EQ(gfx::Rect(0, 0, 40, 40).ToString(),
+ clip_child->clip_rect().ToString());
+ EXPECT_TRUE(clip_child->is_clipped());
+ EXPECT_EQ(gfx::Rect(0, 0, 40, 40).ToString(),
+ child->visible_content_rect().ToString());
+ EXPECT_TRUE(child->is_clipped());
+}
+
+TEST_F(LayerTreeHostCommonTest,
+ SurfacesShouldBeUnaffectedByNonDescendantClipChildren) {
+ // Ensures that non-descendant clip children in the tree do not affect
+ // render surfaces.
+ //
+ // root (a render surface)
+ // + clip_parent (masks to bounds)
+ // + render_surface1
+ // + clip_child
+ // + render_surface2
+ // + non_clip_child
+ //
+ // In this example render_surface2 should be unaffected by clip_child.
+ scoped_refptr<Layer> root = Layer::Create();
+ scoped_refptr<Layer> clip_parent = Layer::Create();
+ scoped_refptr<Layer> render_surface1 = Layer::Create();
+ scoped_refptr<LayerWithForcedDrawsContent> clip_child =
+ make_scoped_refptr(new LayerWithForcedDrawsContent);
+ scoped_refptr<Layer> render_surface2 = Layer::Create();
+ scoped_refptr<LayerWithForcedDrawsContent> non_clip_child =
+ make_scoped_refptr(new LayerWithForcedDrawsContent);
+
+ root->AddChild(clip_parent);
+ clip_parent->AddChild(render_surface1);
+ render_surface1->AddChild(clip_child);
+ clip_parent->AddChild(render_surface2);
+ render_surface2->AddChild(non_clip_child);
+
+ clip_child->SetClipParent(clip_parent.get());
+
+ clip_parent->SetMasksToBounds(true);
+ render_surface1->SetMasksToBounds(true);
+
+ gfx::Transform identity_transform;
+ SetLayerPropertiesForTesting(root.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(),
+ gfx::Size(15, 15),
+ false);
+ SetLayerPropertiesForTesting(clip_parent.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(),
+ gfx::Size(10, 10),
+ false);
+ SetLayerPropertiesForTesting(render_surface1.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(5, 5),
+ gfx::Size(5, 5),
+ false);
+ SetLayerPropertiesForTesting(render_surface2.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(),
+ gfx::Size(5, 5),
+ false);
+ SetLayerPropertiesForTesting(clip_child.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(-1, 1),
+ gfx::Size(10, 10),
+ false);
+ SetLayerPropertiesForTesting(non_clip_child.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(),
+ gfx::Size(5, 5),
+ false);
+
+ render_surface1->SetForceRenderSurface(true);
+ render_surface2->SetForceRenderSurface(true);
+
+ scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create();
+ host->SetRootLayer(root);
+
+ ExecuteCalculateDrawProperties(root.get());
+
+ EXPECT_TRUE(root->render_surface());
+ EXPECT_TRUE(render_surface1->render_surface());
+ EXPECT_TRUE(render_surface2->render_surface());
+
+ EXPECT_EQ(gfx::Rect(0, 0, 5, 5).ToString(),
+ render_surface1->clip_rect().ToString());
+ EXPECT_TRUE(render_surface1->is_clipped());
+
+ // The render surface should not clip (it has unclipped descendants), instead
+ // it should rely on layer clipping.
+ EXPECT_EQ(gfx::Rect(0, 0, 0, 0).ToString(),
+ render_surface1->render_surface()->clip_rect().ToString());
+ EXPECT_FALSE(render_surface1->render_surface()->is_clipped());
+
+ // That said, it should have grown to accomodate the unclipped descendant.
+ EXPECT_EQ(gfx::Rect(-1, 1, 6, 4).ToString(),
+ render_surface1->render_surface()->content_rect().ToString());
+
+ // This render surface should clip. It has no unclipped descendants.
+ EXPECT_EQ(gfx::Rect(0, 0, 5, 5).ToString(),
+ render_surface2->clip_rect().ToString());
+ EXPECT_TRUE(render_surface2->render_surface()->is_clipped());
+
+ // It also shouldn't have grown to accomodate the clip child.
+ EXPECT_EQ(gfx::Rect(0, 0, 5, 5).ToString(),
+ render_surface2->render_surface()->content_rect().ToString());
+
+ // Sanity check our num_unclipped_descendants values.
+ EXPECT_EQ(1, render_surface1->num_unclipped_descendants());
+ EXPECT_EQ(0, render_surface2->num_unclipped_descendants());
+}
+
+TEST_F(LayerTreeHostCommonTest, DoNotIncludeBackfaceInvisibleSurfaces) {
+ scoped_refptr<Layer> root = Layer::Create();
+ scoped_refptr<Layer> render_surface = Layer::Create();
+ scoped_refptr<LayerWithForcedDrawsContent> child =
+ make_scoped_refptr(new LayerWithForcedDrawsContent);
+
+ root->AddChild(render_surface);
+ render_surface->AddChild(child);
+
+ gfx::Transform identity_transform;
+ SetLayerPropertiesForTesting(root.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(),
+ gfx::Size(50, 50),
+ false);
+ SetLayerPropertiesForTesting(render_surface.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(),
+ gfx::Size(30, 30),
+ false);
+ SetLayerPropertiesForTesting(child.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(),
+ gfx::Size(20, 20),
+ false);
+
+ root->SetPreserves3d(true);
+ render_surface->SetDoubleSided(false);
+ render_surface->SetForceRenderSurface(true);
+
+ scoped_ptr<FakeLayerTreeHost> host = FakeLayerTreeHost::Create();
+ host->SetRootLayer(root);
+
+ ExecuteCalculateDrawProperties(root.get());
+
+ EXPECT_EQ(2u, render_surface_layer_list()->size());
+ EXPECT_EQ(1u,
+ render_surface_layer_list()->at(0)
+ ->render_surface()->layer_list().size());
+ EXPECT_EQ(1u,
+ render_surface_layer_list()->at(1)
+ ->render_surface()->layer_list().size());
+
+ gfx::Transform rotation_transform = identity_transform;
+ rotation_transform.RotateAboutXAxis(180.0);
+
+ render_surface->SetTransform(rotation_transform);
+
+ ExecuteCalculateDrawProperties(root.get());
+
+ EXPECT_EQ(1u, render_surface_layer_list()->size());
+ EXPECT_EQ(0u,
+ render_surface_layer_list()->at(0)
+ ->render_surface()->layer_list().size());
+}
+
+TEST_F(LayerTreeHostCommonTest, ScrollCompensationWithRounding) {
+ // This test verifies that a scrolling layer that gets snapped to
+ // integer coordinates doesn't move a fixed position child.
+ //
+ // + root
+ // + container
+ // + scroller
+ // + fixed
+ //
+ FakeImplProxy proxy;
+ FakeLayerTreeHostImpl host_impl(&proxy);
+ host_impl.CreatePendingTree();
+ scoped_ptr<LayerImpl> root = LayerImpl::Create(host_impl.active_tree(), 1);
+ scoped_ptr<LayerImpl> container =
+ LayerImpl::Create(host_impl.active_tree(), 2);
+ LayerImpl* container_layer = container.get();
+ scoped_ptr<LayerImpl> scroller =
+ LayerImpl::Create(host_impl.active_tree(), 3);
+ LayerImpl* scroll_layer = scroller.get();
+ scoped_ptr<LayerImpl> fixed = LayerImpl::Create(host_impl.active_tree(), 4);
+ LayerImpl* fixed_layer = fixed.get();
+
+ container->SetIsContainerForFixedPositionLayers(true);
+
+ LayerPositionConstraint constraint;
+ constraint.set_is_fixed_position(true);
+ fixed->SetPositionConstraint(constraint);
+
+ scroller->SetScrollable(true);
+
+ gfx::Transform identity_transform;
+ gfx::Transform container_transform;
+ container_transform.Translate3d(10.0, 20.0, 0.0);
+ gfx::Vector2dF container_offset = container_transform.To2dTranslation();
+
+ SetLayerPropertiesForTesting(root.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(),
+ gfx::Size(50, 50),
+ false);
+ SetLayerPropertiesForTesting(container.get(),
+ container_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(),
+ gfx::Size(40, 40),
+ false);
+ SetLayerPropertiesForTesting(scroller.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(),
+ gfx::Size(30, 30),
+ false);
+ SetLayerPropertiesForTesting(fixed.get(),
+ identity_transform,
+ identity_transform,
+ gfx::PointF(),
+ gfx::PointF(),
+ gfx::Size(50, 50),
+ false);
+
+ scroller->AddChild(fixed.Pass());
+ container->AddChild(scroller.Pass());
+ root->AddChild(container.Pass());
+
+ // Rounded to integers already.
+ {
+ gfx::Vector2dF scroll_delta(3.0, 5.0);
+ scroll_layer->SetScrollDelta(scroll_delta);
+
+ LayerImplList render_surface_layer_list;
+ LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs(
+ root.get(), root->bounds(), &render_surface_layer_list);
+ LayerTreeHostCommon::CalculateDrawProperties(&inputs);
+
+ EXPECT_TRANSFORMATION_MATRIX_EQ(
+ container_layer->draw_properties().screen_space_transform,
+ fixed_layer->draw_properties().screen_space_transform);
+ EXPECT_VECTOR_EQ(
+ fixed_layer->draw_properties().screen_space_transform.To2dTranslation(),
+ container_offset);
+ EXPECT_VECTOR_EQ(scroll_layer->draw_properties()
+ .screen_space_transform.To2dTranslation(),
+ container_offset - scroll_delta);
+ }
+
+ // Scroll delta requiring rounding.
+ {
+ gfx::Vector2dF scroll_delta(4.1f, 8.1f);
+ scroll_layer->SetScrollDelta(scroll_delta);
+
+ gfx::Vector2dF rounded_scroll_delta(4.f, 8.f);
+
+ LayerImplList render_surface_layer_list;
+ LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs(
+ root.get(), root->bounds(), &render_surface_layer_list);
+ LayerTreeHostCommon::CalculateDrawProperties(&inputs);
+
+ EXPECT_TRANSFORMATION_MATRIX_EQ(
+ container_layer->draw_properties().screen_space_transform,
+ fixed_layer->draw_properties().screen_space_transform);
+ EXPECT_VECTOR_EQ(
+ fixed_layer->draw_properties().screen_space_transform.To2dTranslation(),
+ container_offset);
+ EXPECT_VECTOR_EQ(scroll_layer->draw_properties()
+ .screen_space_transform.To2dTranslation(),
+ container_offset - rounded_scroll_delta);
+ }
+}
+
} // namespace
} // namespace cc
diff --git a/chromium/cc/trees/layer_tree_host_impl.cc b/chromium/cc/trees/layer_tree_host_impl.cc
index 9519e745af4..a854dff2894 100644
--- a/chromium/cc/trees/layer_tree_host_impl.cc
+++ b/chromium/cc/trees/layer_tree_host_impl.cc
@@ -5,6 +5,7 @@
#include "cc/trees/layer_tree_host_impl.h"
#include <algorithm>
+#include <limits>
#include "base/basictypes.h"
#include "base/containers/hash_tables.h"
@@ -28,8 +29,8 @@
#include "cc/layers/heads_up_display_layer_impl.h"
#include "cc/layers/layer_impl.h"
#include "cc/layers/layer_iterator.h"
+#include "cc/layers/painted_scrollbar_layer_impl.h"
#include "cc/layers/render_surface_impl.h"
-#include "cc/layers/scrollbar_layer_impl.h"
#include "cc/output/compositor_frame_metadata.h"
#include "cc/output/copy_output_request.h"
#include "cc/output/delegating_renderer.h"
@@ -38,9 +39,11 @@
#include "cc/quads/render_pass_draw_quad.h"
#include "cc/quads/shared_quad_state.h"
#include "cc/quads/solid_color_draw_quad.h"
+#include "cc/quads/texture_draw_quad.h"
#include "cc/resources/memory_history.h"
#include "cc/resources/picture_layer_tiling.h"
#include "cc/resources/prioritized_resource_manager.h"
+#include "cc/resources/texture_mailbox_deleter.h"
#include "cc/resources/ui_resource_bitmap.h"
#include "cc/scheduler/delay_based_time_source.h"
#include "cc/scheduler/texture_uploader.h"
@@ -69,6 +72,27 @@ void DidVisibilityChange(cc::LayerTreeHostImpl* id, bool visible) {
TRACE_EVENT_ASYNC_END0("webkit", "LayerTreeHostImpl::SetVisible", id);
}
+size_t GetMaxTransferBufferUsageBytes(cc::ContextProvider* context_provider) {
+ if (context_provider) {
+ // We want to make sure the default transfer buffer size is equal to the
+ // amount of data that can be uploaded by the compositor to avoid stalling
+ // the pipeline.
+ // For reference Chromebook Pixel can upload 1MB in about 0.5ms.
+ const size_t kMaxBytesUploadedPerMs = 1024 * 1024 * 2;
+ // Assuming a two frame deep pipeline between CPU and GPU and we are
+ // drawing 60 frames per second which would require us to draw one
+ // frame in 16 milliseconds.
+ const size_t kMaxTransferBufferUsageBytes = 16 * 2 * kMaxBytesUploadedPerMs;
+ return std::min(
+ context_provider->ContextCapabilities().max_transfer_buffer_usage_bytes,
+ kMaxTransferBufferUsageBytes);
+ } else {
+ // Software compositing should not use this value in production. Just use a
+ // default value when testing uploads with the software compositor.
+ return std::numeric_limits<size_t>::max();
+ }
+}
+
} // namespace
namespace cc {
@@ -110,12 +134,8 @@ class LayerTreeHostImplTimeSourceAdapter : public TimeSourceClient {
// TODO(enne): This should probably happen post-animate.
if (layer_tree_host_impl_->pending_tree()) {
- layer_tree_host_impl_->ActivatePendingTreeIfNeeded();
-
- if (layer_tree_host_impl_->pending_tree()) {
- layer_tree_host_impl_->pending_tree()->UpdateDrawProperties();
- layer_tree_host_impl_->ManageTiles();
- }
+ layer_tree_host_impl_->pending_tree()->UpdateDrawProperties();
+ layer_tree_host_impl_->ManageTiles();
}
layer_tree_host_impl_->Animate(
@@ -132,6 +152,8 @@ class LayerTreeHostImplTimeSourceAdapter : public TimeSourceClient {
time_source_->SetActive(active);
}
+ bool Active() const { return time_source_->Active(); }
+
private:
LayerTreeHostImplTimeSourceAdapter(
LayerTreeHostImpl* layer_tree_host_impl,
@@ -175,7 +197,7 @@ LayerTreeHostImpl::LayerTreeHostImpl(
did_lock_scrolling_layer_(false),
should_bubble_scrolls_(false),
wheel_scrolling_(false),
- manage_tiles_needed_(false),
+ tile_priorities_dirty_(false),
root_layer_scroll_offset_delegate_(NULL),
settings_(settings),
visible_(true),
@@ -190,13 +212,16 @@ LayerTreeHostImpl::LayerTreeHostImpl(
paint_time_counter_(PaintTimeCounter::Create()),
memory_history_(MemoryHistory::Create()),
debug_rect_history_(DebugRectHistory::Create()),
+ texture_mailbox_deleter_(new TextureMailboxDeleter),
max_memory_needed_bytes_(0),
last_sent_memory_visible_bytes_(0),
last_sent_memory_visible_and_nearby_bytes_(0),
last_sent_memory_use_bytes_(0),
zero_budget_(false),
device_scale_factor_(1.f),
+ overhang_ui_resource_id_(0),
overdraw_bottom_height_(0.f),
+ device_viewport_valid_for_tile_management_(true),
external_stencil_test_enabled_(false),
animation_registrar_(AnimationRegistrar::Create()),
rendering_stats_instrumentation_(rendering_stats_instrumentation),
@@ -246,16 +271,22 @@ void LayerTreeHostImpl::BeginCommit() {}
void LayerTreeHostImpl::CommitComplete() {
TRACE_EVENT0("cc", "LayerTreeHostImpl::CommitComplete");
- // Impl-side painting needs an update immediately post-commit to have the
- // opportunity to create tilings. Other paths can call UpdateDrawProperties
- // more lazily when needed prior to drawing.
if (settings_.impl_side_painting) {
+ // Impl-side painting needs an update immediately post-commit to have the
+ // opportunity to create tilings. Other paths can call UpdateDrawProperties
+ // more lazily when needed prior to drawing.
+ pending_tree()->ApplyScrollDeltasSinceBeginFrame();
pending_tree_->set_needs_update_draw_properties();
pending_tree_->UpdateDrawProperties();
// Start working on newly created tiles immediately if needed.
- ManageTiles();
+ if (!tile_manager_ || !tile_priorities_dirty_)
+ NotifyReadyToActivate();
+ else
+ ManageTiles();
} else {
active_tree_->set_needs_update_draw_properties();
+ if (time_source_client_adapter_ && time_source_client_adapter_->Active())
+ DCHECK(active_tree_->root_layer());
}
client_->SendManagedMemoryStats();
@@ -287,7 +318,7 @@ bool LayerTreeHostImpl::CanDraw() const {
if (output_surface_->capabilities().draw_and_swap_full_viewport_every_frame)
return true;
- if (device_viewport_size_.IsEmpty()) {
+ if (DrawViewportSize().IsEmpty()) {
TRACE_EVENT_INSTANT0("cc", "LayerTreeHostImpl::CanDraw empty viewport",
TRACE_EVENT_SCOPE_THREAD);
return false;
@@ -304,6 +335,12 @@ bool LayerTreeHostImpl::CanDraw() const {
TRACE_EVENT_SCOPE_THREAD);
return false;
}
+ if (EvictedUIResourcesExist()) {
+ TRACE_EVENT_INSTANT0(
+ "cc", "LayerTreeHostImpl::CanDraw UI resources evicted not recreated",
+ TRACE_EVENT_SCOPE_THREAD);
+ return false;
+ }
return true;
}
@@ -320,16 +357,21 @@ void LayerTreeHostImpl::Animate(base::TimeTicks monotonic_time,
void LayerTreeHostImpl::ManageTiles() {
if (!tile_manager_)
return;
- if (!manage_tiles_needed_)
+ if (!tile_priorities_dirty_)
+ return;
+ if (!device_viewport_valid_for_tile_management_)
return;
- manage_tiles_needed_ = false;
+
+ tile_priorities_dirty_ = false;
tile_manager_->ManageTiles();
size_t memory_required_bytes;
size_t memory_nice_to_have_bytes;
+ size_t memory_allocated_bytes;
size_t memory_used_bytes;
tile_manager_->GetMemoryStats(&memory_required_bytes,
&memory_nice_to_have_bytes,
+ &memory_allocated_bytes,
&memory_used_bytes);
SendManagedMemoryStats(memory_required_bytes,
memory_nice_to_have_bytes,
@@ -339,7 +381,6 @@ void LayerTreeHostImpl::ManageTiles() {
void LayerTreeHostImpl::StartPageScaleAnimation(gfx::Vector2d target_offset,
bool anchor_point,
float page_scale,
- base::TimeTicks start_time,
base::TimeDelta duration) {
if (!RootScrollLayer())
return;
@@ -347,9 +388,7 @@ void LayerTreeHostImpl::StartPageScaleAnimation(gfx::Vector2d target_offset,
gfx::Vector2dF scroll_total =
RootScrollLayer()->scroll_offset() + RootScrollLayer()->ScrollDelta();
gfx::SizeF scaled_scrollable_size = active_tree_->ScrollableSize();
- gfx::SizeF viewport_size = VisibleViewportSize();
-
- double start_time_seconds = (start_time - base::TimeTicks()).InSecondsF();
+ gfx::SizeF viewport_size = UnscaledScrollableViewportSize();
// Easing constants experimentally determined.
scoped_ptr<TimingFunction> timing_function =
@@ -360,7 +399,6 @@ void LayerTreeHostImpl::StartPageScaleAnimation(gfx::Vector2d target_offset,
active_tree_->total_page_scale_factor(),
viewport_size,
scaled_scrollable_size,
- start_time_seconds,
timing_function.Pass());
if (anchor_point) {
@@ -466,7 +504,7 @@ void LayerTreeHostImpl::FrameData::AppendRenderPass(
static DrawMode GetDrawMode(OutputSurface* output_surface) {
if (output_surface->ForcedDrawToSoftwareDevice()) {
return DRAW_MODE_RESOURCELESS_SOFTWARE;
- } else if (output_surface->context3d()) {
+ } else if (output_surface->context_provider()) {
return DRAW_MODE_HARDWARE;
} else {
DCHECK(output_surface->software_device());
@@ -519,6 +557,8 @@ static void AppendQuadsForRenderSurfaceLayer(
}
static void AppendQuadsToFillScreen(
+ ResourceProvider::ResourceId resource_id,
+ gfx::SizeF resource_scaled_size,
RenderPass* target_render_pass,
LayerImpl* root_layer,
SkColor screen_background_color,
@@ -567,11 +607,31 @@ static void AppendQuadsToFillScreen(
// no perspective, so mapping is sufficient (as opposed to projecting).
gfx::Rect layer_rect =
MathUtil::MapClippedRect(transform_to_layer_space, fill_rects.rect());
- // Skip the quad culler and just append the quads directly to avoid
- // occlusion checks.
- scoped_ptr<SolidColorDrawQuad> quad = SolidColorDrawQuad::Create();
- quad->SetNew(shared_quad_state, layer_rect, screen_background_color, false);
- quad_culler.Append(quad.PassAs<DrawQuad>(), &append_quads_data);
+ if (resource_id) {
+ scoped_ptr<TextureDrawQuad> tex_quad = TextureDrawQuad::Create();
+ const float vertex_opacity[4] = {1.f, 1.f, 1.f, 1.f};
+ tex_quad->SetNew(
+ shared_quad_state,
+ layer_rect,
+ layer_rect,
+ resource_id,
+ false,
+ gfx::PointF(layer_rect.x() / resource_scaled_size.width(),
+ layer_rect.y() / resource_scaled_size.height()),
+ gfx::PointF(layer_rect.right() / resource_scaled_size.width(),
+ layer_rect.bottom() / resource_scaled_size.height()),
+ screen_background_color,
+ vertex_opacity,
+ false);
+ quad_culler.Append(tex_quad.PassAs<DrawQuad>(), &append_quads_data);
+ } else {
+ // Skip the quad culler and just append the quads directly to avoid
+ // occlusion checks.
+ scoped_ptr<SolidColorDrawQuad> quad = SolidColorDrawQuad::Create();
+ quad->SetNew(
+ shared_quad_state, layer_rect, screen_background_color, false);
+ quad_culler.Append(quad.PassAs<DrawQuad>(), &append_quads_data);
+ }
}
}
@@ -783,7 +843,10 @@ bool LayerTreeHostImpl::CalculateRenderPasses(FrameData* frame) {
if (!active_tree_->has_transparent_background()) {
frame->render_passes.back()->has_transparent_background = false;
- AppendQuadsToFillScreen(frame->render_passes.back(),
+ AppendQuadsToFillScreen(ResourceIdForUIResource(overhang_ui_resource_id_),
+ gfx::ScaleSize(overhang_ui_resource_size_,
+ device_scale_factor_),
+ frame->render_passes.back(),
active_tree_->root_layer(),
active_tree_->background_color(),
occlusion_tracker);
@@ -823,6 +886,8 @@ void LayerTreeHostImpl::MainThreadHasStoppedFlinging() {
void LayerTreeHostImpl::UpdateBackgroundAnimateTicking(
bool should_background_tick) {
DCHECK(proxy_->IsImplThread());
+ if (should_background_tick)
+ DCHECK(active_tree_->root_layer());
bool enabled = should_background_tick &&
!animation_registrar_->active_animation_controllers().empty();
@@ -985,11 +1050,11 @@ bool LayerTreeHostImpl::PrepareToDraw(FrameData* frame,
"SourceFrameNumber",
active_tree_->source_frame_number());
- if (need_to_update_visible_tiles_before_draw_) {
- DCHECK(tile_manager_);
- if (tile_manager_->UpdateVisibleTiles())
- DidInitializeVisibleTile();
+ if (need_to_update_visible_tiles_before_draw_ &&
+ tile_manager_ && tile_manager_->UpdateVisibleTiles()) {
+ DidInitializeVisibleTile();
}
+ need_to_update_visible_tiles_before_draw_ = true;
active_tree_->UpdateDrawProperties();
@@ -1023,6 +1088,14 @@ void LayerTreeHostImpl::EvictTexturesForTesting() {
EnforceManagedMemoryPolicy(ManagedMemoryPolicy(0));
}
+void LayerTreeHostImpl::BlockNotifyReadyToActivateForTesting(bool block) {
+ NOTREACHED();
+}
+
+void LayerTreeHostImpl::DidInitializeVisibleTileForTesting() {
+ DidInitializeVisibleTile();
+}
+
void LayerTreeHostImpl::EnforceManagedMemoryPolicy(
const ManagedMemoryPolicy& policy) {
@@ -1065,31 +1138,28 @@ void LayerTreeHostImpl::UpdateTileManagerMemoryPolicy(
policy.priority_cutoff_when_visible :
policy.priority_cutoff_when_not_visible);
new_state.num_resources_limit = policy.num_resources_limit;
+
tile_manager_->SetGlobalState(new_state);
- manage_tiles_needed_ = true;
+ DidModifyTilePriorities();
}
-bool LayerTreeHostImpl::HasImplThread() const {
- return proxy_->HasImplThread();
+void LayerTreeHostImpl::DidModifyTilePriorities() {
+ DCHECK(settings_.impl_side_painting);
+ // Mark priorities as dirty and schedule a ManageTiles().
+ tile_priorities_dirty_ = true;
+ client_->SetNeedsManageTilesOnImplThread();
}
void LayerTreeHostImpl::DidInitializeVisibleTile() {
// TODO(reveman): Determine tiles that changed and only damage
// what's necessary.
SetFullRootLayerDamage();
- if (client_)
+ if (client_ && !client_->IsInsideDraw())
client_->DidInitializeVisibleTileOnImplThread();
}
void LayerTreeHostImpl::NotifyReadyToActivate() {
- if (pending_tree_) {
- need_to_update_visible_tiles_before_draw_ = true;
- ActivatePendingTree();
- }
-}
-
-bool LayerTreeHostImpl::ShouldClearRootRenderPass() const {
- return settings_.should_clear_root_render_pass;
+ client_->NotifyReadyToActivate();
}
void LayerTreeHostImpl::SetMemoryPolicy(const ManagedMemoryPolicy& policy) {
@@ -1151,13 +1221,13 @@ void LayerTreeHostImpl::SetManagedMemoryPolicy(
void LayerTreeHostImpl::SetExternalDrawConstraints(
const gfx::Transform& transform,
- gfx::Rect viewport) {
+ gfx::Rect viewport,
+ gfx::Rect clip,
+ bool valid_for_tile_management) {
external_transform_ = transform;
external_viewport_ = viewport;
-}
-
-void LayerTreeHostImpl::SetExternalStencilTest(bool enabled) {
- external_stencil_test_enabled_ = enabled;
+ external_clip_ = clip;
+ device_viewport_valid_for_tile_management_ = valid_for_tile_management;
}
void LayerTreeHostImpl::SetNeedsRedrawRect(gfx::Rect damage_rect) {
@@ -1168,14 +1238,15 @@ void LayerTreeHostImpl::BeginFrame(const BeginFrameArgs& args) {
client_->BeginFrameOnImplThread(args);
}
-void LayerTreeHostImpl::OnSwapBuffersComplete(
- const CompositorFrameAck* ack) {
+void LayerTreeHostImpl::OnSwapBuffersComplete() {
+ client_->OnSwapBuffersCompleteOnImplThread();
+}
+
+void LayerTreeHostImpl::ReclaimResources(const CompositorFrameAck* ack) {
// TODO(piman): We may need to do some validation on this ack before
// processing it.
- if (ack && renderer_)
+ if (renderer_)
renderer_->ReceiveSwapBuffersAck(*ack);
-
- client_->OnSwapBuffersCompleteOnImplThread();
}
void LayerTreeHostImpl::OnCanDrawStateChangedForTree() {
@@ -1207,17 +1278,6 @@ CompositorFrameMetadata LayerTreeHostImpl::MakeCompositorFrameMetadata() const {
return metadata;
}
-bool LayerTreeHostImpl::AllowPartialSwap() const {
- // We don't track damage on the HUD layer (it interacts with damage tracking
- // visualizations), so disable partial swaps to make the HUD layer display
- // properly.
- return !debug_state_.ShowHudRects();
-}
-
-bool LayerTreeHostImpl::ExternalStencilTestEnabled() const {
- return external_stencil_test_enabled_;
-}
-
static void LayerTreeHostImplDidBeginTracingCallback(LayerImpl* layer) {
layer->DidBeginTracing();
}
@@ -1236,12 +1296,15 @@ void LayerTreeHostImpl::DrawLayers(FrameData* frame,
DCHECK(!frame->render_passes.empty());
- fps_counter_->SaveTimeStamp(frame_begin_time);
+ int old_dropped_frame_count = fps_counter_->dropped_frame_count();
+ fps_counter_->SaveTimeStamp(frame_begin_time,
+ !output_surface_->context_provider());
- rendering_stats_instrumentation_->SetScreenFrameCount(
- fps_counter_->current_frame_number());
- rendering_stats_instrumentation_->SetDroppedFrameCount(
- fps_counter_->dropped_frame_count());
+ bool on_main_thread = false;
+ rendering_stats_instrumentation_->IncrementScreenFrameCount(
+ 1, on_main_thread);
+ rendering_stats_instrumentation_->IncrementDroppedFrameCount(
+ fps_counter_->dropped_frame_count() - old_dropped_frame_count);
if (tile_manager_) {
memory_history_->SaveEntry(
@@ -1260,7 +1323,7 @@ void LayerTreeHostImpl::DrawLayers(FrameData* frame,
if (!settings_.impl_side_painting && debug_state_.continuous_painting) {
const RenderingStats& stats =
rendering_stats_instrumentation_->GetRenderingStats();
- paint_time_counter_->SavePaintTime(stats.total_paint_time);
+ paint_time_counter_->SavePaintTime(stats.main_stats.paint_time);
}
bool is_new_trace;
@@ -1288,11 +1351,22 @@ void LayerTreeHostImpl::DrawLayers(FrameData* frame,
active_tree_->hud_layer()->UpdateHudTexture(resource_provider_.get());
if (output_surface_->ForcedDrawToSoftwareDevice()) {
+ bool allow_partial_swap = false;
+
scoped_ptr<SoftwareRenderer> temp_software_renderer =
- SoftwareRenderer::Create(this, output_surface_.get(), NULL);
- temp_software_renderer->DrawFrame(&frame->render_passes);
+ SoftwareRenderer::Create(this, &settings_, output_surface_.get(), NULL);
+ temp_software_renderer->DrawFrame(
+ &frame->render_passes, NULL, device_scale_factor_, allow_partial_swap);
} else {
- renderer_->DrawFrame(&frame->render_passes);
+ // We don't track damage on the HUD layer (it interacts with damage tracking
+ // visualizations), so disable partial swaps to make the HUD layer display
+ // properly.
+ bool allow_partial_swap = !debug_state_.ShowHudRects();
+
+ renderer_->DrawFrame(&frame->render_passes,
+ offscreen_context_provider_.get(),
+ device_scale_factor_,
+ allow_partial_swap);
}
// The render passes should be consumed by the renderer.
DCHECK(frame->render_passes.empty());
@@ -1300,11 +1374,16 @@ void LayerTreeHostImpl::DrawLayers(FrameData* frame,
// The next frame should start by assuming nothing has changed, and changes
// are noted as they occur.
+ // TODO(boliu): If we did a temporary software renderer frame, propogate the
+ // damage forward to the next frame.
for (size_t i = 0; i < frame->render_surface_layer_list->size(); i++) {
(*frame->render_surface_layer_list)[i]->render_surface()->damage_tracker()->
DidDrawDamagedArea();
}
active_tree_->root_layer()->ResetAllChangeTrackingForSubtree();
+
+ rendering_stats_instrumentation_->IssueTraceEventForImplThreadStats();
+ rendering_stats_instrumentation_->AccumulateAndClearImplThreadStats();
}
void LayerTreeHostImpl::DidDrawAllLayers(const FrameData& frame) {
@@ -1343,22 +1422,18 @@ void LayerTreeHostImpl::SetNeedsBeginFrame(bool enable) {
output_surface_->SetNeedsBeginFrame(enable);
}
-float LayerTreeHostImpl::DeviceScaleFactor() const {
- return device_scale_factor_;
-}
-
-gfx::SizeF LayerTreeHostImpl::VisibleViewportSize() const {
+gfx::SizeF LayerTreeHostImpl::UnscaledScrollableViewportSize() const {
// The container layer bounds should be used if non-overlay scrollbars may
// exist since it adjusts for them.
LayerImpl* container_layer = active_tree_->RootContainerLayer();
- if (!Settings().solid_color_scrollbars && container_layer) {
+ if (!settings_.solid_color_scrollbars && container_layer) {
DCHECK(!top_controls_manager_);
DCHECK_EQ(0, overdraw_bottom_height_);
return container_layer->bounds();
}
gfx::SizeF dip_size =
- gfx::ScaleSize(device_viewport_size(), 1.f / device_scale_factor());
+ gfx::ScaleSize(device_viewport_size_, 1.f / device_scale_factor());
float top_offset =
top_controls_manager_ ? top_controls_manager_->content_top_offset() : 0.f;
@@ -1366,10 +1441,6 @@ gfx::SizeF LayerTreeHostImpl::VisibleViewportSize() const {
dip_size.height() - top_offset - overdraw_bottom_height_);
}
-const LayerTreeSettings& LayerTreeHostImpl::Settings() const {
- return settings();
-}
-
void LayerTreeHostImpl::DidLoseOutputSurface() {
// TODO(jamesr): The renderer_ check is needed to make some of the
// LayerTreeHostContextTest tests pass, but shouldn't be necessary (or
@@ -1425,49 +1496,23 @@ void LayerTreeHostImpl::CreatePendingTree() {
else
pending_tree_ = LayerTreeImpl::create(this);
client_->OnCanDrawStateChanged(CanDraw());
- client_->OnHasPendingTreeStateChanged(pending_tree_);
TRACE_EVENT_ASYNC_BEGIN0("cc", "PendingTree", pending_tree_.get());
TRACE_EVENT_ASYNC_STEP0("cc",
"PendingTree", pending_tree_.get(), "waiting");
}
void LayerTreeHostImpl::UpdateVisibleTiles() {
- DCHECK(!client_->IsInsideDraw()) <<
- "Updating visible tiles within a draw may trigger "
- "spurious redraws.";
if (tile_manager_ && tile_manager_->UpdateVisibleTiles())
DidInitializeVisibleTile();
-
need_to_update_visible_tiles_before_draw_ = false;
}
-void LayerTreeHostImpl::ActivatePendingTreeIfNeeded() {
- DCHECK(pending_tree_);
- CHECK(settings_.impl_side_painting);
-
- if (!pending_tree_)
- return;
-
- // The tile manager is usually responsible for notifying activation.
- // If there is no tile manager, then we need to manually activate.
- if (!tile_manager_ || tile_manager_->AreTilesRequiredForActivationReady()) {
- ActivatePendingTree();
- return;
- }
-
- // Manage tiles in case state affecting tile priority has changed.
- ManageTiles();
-
- TRACE_EVENT_ASYNC_STEP1(
- "cc",
- "PendingTree", pending_tree_.get(), "activate",
- "state", TracedValue::FromValue(ActivationStateAsValue().release()));
-}
-
void LayerTreeHostImpl::ActivatePendingTree() {
CHECK(pending_tree_);
TRACE_EVENT_ASYNC_END0("cc", "PendingTree", pending_tree_.get());
+ need_to_update_visible_tiles_before_draw_ = true;
+
active_tree_->SetRootLayerScrollOffsetDelegate(NULL);
active_tree_->PushPersistedState(pending_tree_.get());
if (pending_tree_->needs_full_tree_sync()) {
@@ -1501,7 +1546,6 @@ void LayerTreeHostImpl::ActivatePendingTree() {
client_->ReduceWastedContentsTextureMemoryOnImplThread();
client_->OnCanDrawStateChanged(CanDraw());
- client_->OnHasPendingTreeStateChanged(pending_tree_);
client_->SetNeedsRedrawOnImplThread();
client_->RenewTreePriority();
@@ -1509,13 +1553,16 @@ void LayerTreeHostImpl::ActivatePendingTree() {
const RenderingStats& stats =
rendering_stats_instrumentation_->GetRenderingStats();
paint_time_counter_->SavePaintTime(
- stats.total_paint_time + stats.total_record_time +
- stats.total_rasterize_time_for_now_bins_on_pending_tree);
+ stats.main_stats.paint_time + stats.main_stats.record_time +
+ stats.impl_stats.rasterize_time_for_now_bins_on_pending_tree);
}
client_->DidActivatePendingTree();
if (!tree_activation_callback_.is_null())
tree_activation_callback_.Run();
+
+ if (time_source_client_adapter_ && time_source_client_adapter_->Active())
+ DCHECK(active_tree_->root_layer());
}
void LayerTreeHostImpl::SetVisible(bool visible) {
@@ -1527,6 +1574,9 @@ void LayerTreeHostImpl::SetVisible(bool visible) {
DidVisibilityChange(this, visible_);
EnforceManagedMemoryPolicy(ActualManagedMemoryPolicy());
+ if (!visible_)
+ EvictAllUIResources();
+
// Evict tiles immediately if invisible since this tab may never get another
// draw or timer tick.
if (!visible_)
@@ -1559,6 +1609,11 @@ size_t LayerTreeHostImpl::memory_allocation_limit_bytes() const {
return ActualManagedMemoryPolicy().bytes_limit_when_visible;
}
+int LayerTreeHostImpl::memory_allocation_priority_cutoff() const {
+ return ManagedMemoryPolicy::PriorityCutoffToValue(
+ ActualManagedMemoryPolicy().priority_cutoff_when_visible);
+}
+
void LayerTreeHostImpl::ReleaseTreeResources() {
if (active_tree_->root_layer())
SendReleaseResourcesRecursive(active_tree_->root_layer());
@@ -1567,8 +1622,7 @@ void LayerTreeHostImpl::ReleaseTreeResources() {
if (recycle_tree_ && recycle_tree_->root_layer())
SendReleaseResourcesRecursive(recycle_tree_->root_layer());
- // Remove all existing maps from UIResourceId to ResourceId.
- ui_resource_map_.clear();
+ EvictAllUIResources();
}
void LayerTreeHostImpl::CreateAndSetRenderer(
@@ -1577,35 +1631,48 @@ void LayerTreeHostImpl::CreateAndSetRenderer(
bool skip_gl_renderer) {
DCHECK(!renderer_);
if (output_surface->capabilities().delegated_rendering) {
- renderer_ =
- DelegatingRenderer::Create(this, output_surface, resource_provider);
- } else if (output_surface->context3d() && !skip_gl_renderer) {
+ renderer_ = DelegatingRenderer::Create(
+ this, &settings_, output_surface, resource_provider);
+ } else if (output_surface->context_provider() && !skip_gl_renderer) {
renderer_ = GLRenderer::Create(this,
+ &settings_,
output_surface,
resource_provider,
+ texture_mailbox_deleter_.get(),
settings_.highp_threshold_min,
settings_.force_direct_layer_drawing);
} else if (output_surface->software_device()) {
- renderer_ =
- SoftwareRenderer::Create(this, output_surface, resource_provider);
+ renderer_ = SoftwareRenderer::Create(
+ this, &settings_, output_surface, resource_provider);
}
if (renderer_) {
renderer_->SetVisible(visible_);
SetFullRootLayerDamage();
+
+ // See note in LayerTreeImpl::UpdateDrawProperties. Renderer needs to be
+ // initialized to get max texture size. Also, after releasing resources,
+ // trees need another update to generate new ones.
+ active_tree_->set_needs_update_draw_properties();
+ if (pending_tree_)
+ pending_tree_->set_needs_update_draw_properties();
}
}
void LayerTreeHostImpl::CreateAndSetTileManager(
ResourceProvider* resource_provider,
+ ContextProvider* context_provider,
bool using_map_image) {
DCHECK(settings_.impl_side_painting);
DCHECK(resource_provider);
- tile_manager_ = TileManager::Create(this,
- resource_provider,
- settings_.num_raster_threads,
- rendering_stats_instrumentation_,
- using_map_image);
+ tile_manager_ =
+ TileManager::Create(this,
+ resource_provider,
+ settings_.num_raster_threads,
+ rendering_stats_instrumentation_,
+ using_map_image,
+ GetMaxTransferBufferUsageBytes(context_provider));
+
UpdateTileManagerMemoryPolicy(ActualManagedMemoryPolicy());
need_to_update_visible_tiles_before_draw_ = false;
}
@@ -1633,7 +1700,9 @@ bool LayerTreeHostImpl::InitializeRenderer(
return false;
scoped_ptr<ResourceProvider> resource_provider = ResourceProvider::Create(
- output_surface.get(), settings_.highp_threshold_min);
+ output_surface.get(),
+ settings_.highp_threshold_min,
+ settings_.use_rgba_4444_textures);
if (!resource_provider)
return false;
@@ -1649,6 +1718,7 @@ bool LayerTreeHostImpl::InitializeRenderer(
if (settings_.impl_side_painting) {
CreateAndSetTileManager(resource_provider.get(),
+ output_surface->context_provider().get(),
GetRendererCapabilities().using_map_image);
}
@@ -1676,12 +1746,6 @@ bool LayerTreeHostImpl::InitializeRenderer(
client_->OnCanDrawStateChanged(CanDraw());
- // See note in LayerTreeImpl::UpdateDrawProperties. Renderer needs
- // to be initialized to get max texture size.
- active_tree_->set_needs_update_draw_properties();
- if (pending_tree_)
- pending_tree_->set_needs_update_draw_properties();
-
return true;
}
@@ -1690,22 +1754,56 @@ bool LayerTreeHostImpl::DeferredInitialize(
DCHECK(output_surface_->capabilities().deferred_gl_initialization);
DCHECK(settings_.impl_side_painting);
DCHECK(settings_.solid_color_scrollbars);
- DCHECK(output_surface_->context3d());
+ DCHECK(output_surface_->context_provider());
ReleaseTreeResources();
renderer_.reset();
- resource_provider_->InitializeGL();
- bool skip_gl_renderer = false;
- CreateAndSetRenderer(
- output_surface_.get(), resource_provider_.get(), skip_gl_renderer);
- bool success = !!renderer_.get();
- client_->DidTryInitializeRendererOnImplThread(success,
- offscreen_context_provider);
+ bool resource_provider_success = resource_provider_->InitializeGL();
+
+ bool success = resource_provider_success;
+ if (success) {
+ bool skip_gl_renderer = false;
+ CreateAndSetRenderer(
+ output_surface_.get(), resource_provider_.get(), skip_gl_renderer);
+ if (!renderer_)
+ success = false;
+ }
+
+ if (success) {
+ if (offscreen_context_provider.get() &&
+ !offscreen_context_provider->BindToCurrentThread())
+ success = false;
+ }
+
if (success) {
EnforceZeroBudget(false);
client_->SetNeedsCommitOnImplThread();
+ } else {
+ if (offscreen_context_provider.get()) {
+ if (offscreen_context_provider->BindToCurrentThread())
+ offscreen_context_provider->VerifyContexts();
+ offscreen_context_provider = NULL;
+ }
+
+ client_->DidLoseOutputSurfaceOnImplThread();
+
+ if (resource_provider_success) {
+ // If this fails the context provider will be dropped from the output
+ // surface and destroyed. But the GLRenderer expects the output surface
+ // to stick around - and hold onto the context3d - as long as it is alive.
+ // TODO(danakj): Remove the need for this code path: crbug.com/276411
+ renderer_.reset();
+
+ // The resource provider can't stay in GL mode or it tries to clean up GL
+ // stuff, but the context provider is going away on the output surface
+ // which contradicts being in GL mode.
+ // TODO(danakj): Remove the need for this code path: crbug.com/276411
+ resource_provider_->InitializeSoftware();
+ }
}
+
+ SetOffscreenContextProvider(offscreen_context_provider);
return success;
}
@@ -1713,7 +1811,7 @@ void LayerTreeHostImpl::ReleaseGL() {
DCHECK(output_surface_->capabilities().deferred_gl_initialization);
DCHECK(settings_.impl_side_painting);
DCHECK(settings_.solid_color_scrollbars);
- DCHECK(output_surface_->context3d());
+ DCHECK(output_surface_->context_provider());
ReleaseTreeResources();
renderer_.reset();
@@ -1727,12 +1825,12 @@ void LayerTreeHostImpl::ReleaseGL() {
EnforceZeroBudget(true);
CreateAndSetTileManager(resource_provider_.get(),
+ NULL,
GetRendererCapabilities().using_map_image);
DCHECK(tile_manager_);
- bool success = true;
- client_->DidTryInitializeRendererOnImplThread(
- success, scoped_refptr<ContextProvider>());
+ SetOffscreenContextProvider(NULL);
+
client_->SetNeedsCommitOnImplThread();
}
@@ -1740,7 +1838,7 @@ void LayerTreeHostImpl::SetViewportSize(gfx::Size device_viewport_size) {
if (device_viewport_size == device_viewport_size_)
return;
- if (pending_tree_ && device_viewport_size_ != device_viewport_size)
+ if (pending_tree_)
active_tree_->SetViewportSizeInvalid();
device_viewport_size_ = device_viewport_size;
@@ -1763,6 +1861,13 @@ void LayerTreeHostImpl::SetOverdrawBottomHeight(float overdraw_bottom_height) {
SetFullRootLayerDamage();
}
+void LayerTreeHostImpl::SetOverhangUIResource(
+ UIResourceId overhang_ui_resource_id,
+ gfx::Size overhang_ui_resource_size) {
+ overhang_ui_resource_id_ = overhang_ui_resource_id;
+ overhang_ui_resource_size_ = overhang_ui_resource_size;
+}
+
void LayerTreeHostImpl::SetDeviceScaleFactor(float device_scale_factor) {
if (device_scale_factor == device_scale_factor_)
return;
@@ -1775,6 +1880,10 @@ void LayerTreeHostImpl::SetDeviceScaleFactor(float device_scale_factor) {
SetFullRootLayerDamage();
}
+gfx::Size LayerTreeHostImpl::DrawViewportSize() const {
+ return DeviceViewport().size();
+}
+
gfx::Rect LayerTreeHostImpl::DeviceViewport() const {
if (external_viewport_.IsEmpty())
return gfx::Rect(device_viewport_size_);
@@ -1782,7 +1891,14 @@ gfx::Rect LayerTreeHostImpl::DeviceViewport() const {
return external_viewport_;
}
-const gfx::Transform& LayerTreeHostImpl::DeviceTransform() const {
+gfx::Rect LayerTreeHostImpl::DeviceClip() const {
+ if (external_clip_.IsEmpty())
+ return DeviceViewport();
+
+ return external_clip_;
+}
+
+const gfx::Transform& LayerTreeHostImpl::DrawTransform() const {
return external_transform_;
}
@@ -1806,6 +1922,12 @@ void LayerTreeHostImpl::BindToClient(InputHandlerClient* client) {
input_handler_client_ = client;
}
+static LayerImpl* NextScrollLayer(LayerImpl* layer) {
+ if (LayerImpl* scroll_parent = layer->scroll_parent())
+ return scroll_parent;
+ return layer->parent();
+}
+
InputHandler::ScrollStatus LayerTreeHostImpl::ScrollBegin(
gfx::Point viewport_point, InputHandler::ScrollInputType type) {
TRACE_EVENT0("cc", "LayerTreeHostImpl::ScrollBegin");
@@ -1829,7 +1951,7 @@ InputHandler::ScrollStatus LayerTreeHostImpl::ScrollBegin(
// Walk up the hierarchy and look for a scrollable layer.
LayerImpl* potentially_scrolling_layer_impl = 0;
- for (; layer_impl; layer_impl = layer_impl->parent()) {
+ for (; layer_impl; layer_impl = NextScrollLayer(layer_impl)) {
// The content layer can also block attempts to scroll outside the main
// thread.
ScrollStatus status = layer_impl->TryScroll(device_viewport_point, type);
@@ -2229,7 +2351,7 @@ scoped_ptr<ScrollAndScaleSet> LayerTreeHostImpl::ProcessScrollDeltas() {
}
void LayerTreeHostImpl::SetFullRootLayerDamage() {
- SetViewportDamage(gfx::Rect(device_viewport_size_));
+ SetViewportDamage(gfx::Rect(DrawViewportSize()));
}
void LayerTreeHostImpl::AnimatePageScale(base::TimeTicks time) {
@@ -2240,6 +2362,9 @@ void LayerTreeHostImpl::AnimatePageScale(base::TimeTicks time) {
gfx::Vector2dF scroll_total = RootScrollLayer()->scroll_offset() +
RootScrollLayer()->ScrollDelta();
+ if (!page_scale_animation_->IsAnimationStarted())
+ page_scale_animation_->StartAnimation(monotonic_time);
+
active_tree_->SetPageScaleDelta(
page_scale_animation_->PageScaleFactorAtTime(monotonic_time) /
active_tree_->page_scale_factor());
@@ -2327,6 +2452,21 @@ void LayerTreeHostImpl::SendReleaseResourcesRecursive(LayerImpl* current) {
SendReleaseResourcesRecursive(current->children()[i]);
}
+void LayerTreeHostImpl::SetOffscreenContextProvider(
+ const scoped_refptr<ContextProvider>& offscreen_context_provider) {
+ if (!offscreen_context_provider.get()) {
+ offscreen_context_provider_ = NULL;
+ return;
+ }
+
+ if (!offscreen_context_provider->BindToCurrentThread()) {
+ offscreen_context_provider_ = NULL;
+ return;
+ }
+
+ offscreen_context_provider_ = offscreen_context_provider;
+}
+
std::string LayerTreeHostImpl::LayerTreeAsJson() const {
std::string str;
if (active_tree_->root_layer()) {
@@ -2426,7 +2566,7 @@ void LayerTreeHostImpl::SetTreePriority(TreePriority priority) {
new_state.tree_priority = priority;
tile_manager_->SetGlobalState(new_state);
- manage_tiles_needed_ = true;
+ DidModifyTilePriorities();
}
void LayerTreeHostImpl::ResetCurrentFrameTimeForNextFrame() {
@@ -2493,11 +2633,20 @@ void LayerTreeHostImpl::SetDebugState(
SetFullRootLayerDamage();
}
-void LayerTreeHostImpl::CreateUIResource(
- UIResourceId uid,
- scoped_refptr<UIResourceBitmap> bitmap) {
+void LayerTreeHostImpl::CreateUIResource(UIResourceId uid,
+ const UIResourceBitmap& bitmap) {
DCHECK_GT(uid, 0);
- DCHECK_EQ(bitmap->GetFormat(), UIResourceBitmap::RGBA8);
+ DCHECK_EQ(bitmap.GetFormat(), UIResourceBitmap::RGBA8);
+
+ GLint wrap_mode = 0;
+ switch (bitmap.GetWrapMode()) {
+ case UIResourceBitmap::CLAMP_TO_EDGE:
+ wrap_mode = GL_CLAMP_TO_EDGE;
+ break;
+ case UIResourceBitmap::REPEAT:
+ wrap_mode = GL_REPEAT;
+ break;
+ }
// Allow for multiple creation requests with the same UIResourceId. The
// previous resource is simply deleted.
@@ -2505,14 +2654,20 @@ void LayerTreeHostImpl::CreateUIResource(
if (id)
DeleteUIResource(uid);
id = resource_provider_->CreateResource(
- bitmap->GetSize(), GL_RGBA, ResourceProvider::TextureUsageAny);
+ bitmap.GetSize(),
+ wrap_mode,
+ ResourceProvider::TextureUsageAny,
+ resource_provider_->best_texture_format());
ui_resource_map_[uid] = id;
+
+ AutoLockUIResourceBitmap bitmap_lock(bitmap);
resource_provider_->SetPixels(id,
- reinterpret_cast<uint8_t*>(bitmap->GetPixels()),
- gfx::Rect(bitmap->GetSize()),
- gfx::Rect(bitmap->GetSize()),
+ bitmap_lock.GetPixels(),
+ gfx::Rect(bitmap.GetSize()),
+ gfx::Rect(bitmap.GetSize()),
gfx::Vector2d(0, 0));
+ MarkUIResourceNotEvicted(uid);
}
void LayerTreeHostImpl::DeleteUIResource(UIResourceId uid) {
@@ -2521,6 +2676,24 @@ void LayerTreeHostImpl::DeleteUIResource(UIResourceId uid) {
resource_provider_->DeleteResource(id);
ui_resource_map_.erase(uid);
}
+ MarkUIResourceNotEvicted(uid);
+}
+
+void LayerTreeHostImpl::EvictAllUIResources() {
+ if (ui_resource_map_.empty())
+ return;
+
+ for (UIResourceMap::const_iterator iter = ui_resource_map_.begin();
+ iter != ui_resource_map_.end();
+ ++iter) {
+ evicted_ui_resources_.insert(iter->first);
+ resource_provider_->DeleteResource(iter->second);
+ }
+ ui_resource_map_.clear();
+
+ client_->SetNeedsCommitOnImplThread();
+ client_->OnCanDrawStateChanged(CanDraw());
+ client_->RenewTreePriority();
}
ResourceProvider::ResourceId LayerTreeHostImpl::ResourceIdForUIResource(
@@ -2531,4 +2704,18 @@ ResourceProvider::ResourceId LayerTreeHostImpl::ResourceIdForUIResource(
return 0;
}
+bool LayerTreeHostImpl::EvictedUIResourcesExist() const {
+ return !evicted_ui_resources_.empty();
+}
+
+void LayerTreeHostImpl::MarkUIResourceNotEvicted(UIResourceId uid) {
+ std::set<UIResourceId>::iterator found_in_evicted =
+ evicted_ui_resources_.find(uid);
+ if (found_in_evicted == evicted_ui_resources_.end())
+ return;
+ evicted_ui_resources_.erase(found_in_evicted);
+ if (evicted_ui_resources_.empty())
+ client_->OnCanDrawStateChanged(CanDraw());
+}
+
} // namespace cc
diff --git a/chromium/cc/trees/layer_tree_host_impl.h b/chromium/cc/trees/layer_tree_host_impl.h
index 31870ac48bd..6d969012deb 100644
--- a/chromium/cc/trees/layer_tree_host_impl.h
+++ b/chromium/cc/trees/layer_tree_host_impl.h
@@ -6,6 +6,7 @@
#define CC_TREES_LAYER_TREE_HOST_IMPL_H_
#include <list>
+#include <set>
#include <string>
#include <vector>
@@ -46,26 +47,25 @@ class PaintTimeCounter;
class MemoryHistory;
class RenderingStatsInstrumentation;
class RenderPassDrawQuad;
+class TextureMailboxDeleter;
class TopControlsManager;
class UIResourceBitmap;
+class UIResourceRequest;
struct RendererCapabilities;
-struct UIResourceRequest;
// LayerTreeHost->Proxy callback interface.
class LayerTreeHostImplClient {
public:
- virtual void DidTryInitializeRendererOnImplThread(
- bool success,
- scoped_refptr<ContextProvider> offscreen_context_provider) = 0;
virtual void DidLoseOutputSurfaceOnImplThread() = 0;
virtual void OnSwapBuffersCompleteOnImplThread() = 0;
virtual void BeginFrameOnImplThread(const BeginFrameArgs& args) = 0;
virtual void OnCanDrawStateChanged(bool can_draw) = 0;
- virtual void OnHasPendingTreeStateChanged(bool has_pending_tree) = 0;
+ virtual void NotifyReadyToActivate() = 0;
virtual void SetNeedsRedrawOnImplThread() = 0;
virtual void SetNeedsRedrawRectOnImplThread(gfx::Rect damage_rect) = 0;
virtual void DidInitializeVisibleTileOnImplThread() = 0;
virtual void SetNeedsCommitOnImplThread() = 0;
+ virtual void SetNeedsManageTilesOnImplThread() = 0;
virtual void PostAnimationEventsToMainThreadOnImplThread(
scoped_ptr<AnimationEventsVector> events,
base::Time wall_clock_time) = 0;
@@ -123,7 +123,6 @@ class CC_EXPORT LayerTreeHostImpl
virtual void StartPageScaleAnimation(gfx::Vector2d target_offset,
bool anchor_point,
float page_scale,
- base::TimeTicks start_time,
base::TimeDelta duration) OVERRIDE;
virtual void ScheduleAnimation() OVERRIDE;
virtual bool HaveTouchEventHandlersAt(gfx::Point viewport_port) OVERRIDE;
@@ -163,7 +162,7 @@ class CC_EXPORT LayerTreeHostImpl
void UpdateBackgroundAnimateTicking(bool should_background_tick);
void SetViewportDamage(gfx::Rect damage_rect);
- void ManageTiles();
+ virtual void ManageTiles();
// Returns false if problems occured preparing the frame, and we should try
// to avoid displaying the frame. If PrepareToDraw is called, DidDrawAllLayers
@@ -177,25 +176,39 @@ class CC_EXPORT LayerTreeHostImpl
const LayerTreeSettings& settings() const { return settings_; }
- // Returns the currently visible viewport size in DIP. This value excludes
- // the URL bar and non-overlay scrollbars.
- gfx::SizeF VisibleViewportSize() const;
-
// Evict all textures by enforcing a memory policy with an allocation of 0.
void EvictTexturesForTesting();
+ // When blocking, this prevents client_->NotifyReadyToActivate() from being
+ // called. When disabled, it calls client_->NotifyReadyToActivate()
+ // immediately if any notifications had been blocked while blocking.
+ virtual void BlockNotifyReadyToActivateForTesting(bool block);
+
+ // This allows us to inject DidInitializeVisibleTile events for testing.
+ void DidInitializeVisibleTileForTesting();
+
+ bool device_viewport_valid_for_tile_management() const {
+ return device_viewport_valid_for_tile_management_;
+ }
+
+ // Viewport size in draw space: this size is in physical pixels and is used
+ // for draw properties, tilings, quads and render passes.
+ gfx::Size DrawViewportSize() const;
+
+ // Viewport size for scrolling and fixed-position compensation. This value
+ // excludes the URL bar and non-overlay scrollbars and is in DIP (and
+ // invariant relative to page scale).
+ gfx::SizeF UnscaledScrollableViewportSize() const;
+
// RendererClient implementation
+
+ // Viewport rectangle and clip in nonflipped window space. These rects
+ // should only be used by Renderer subclasses to populate glViewport/glClip
+ // and their software-mode equivalents.
virtual gfx::Rect DeviceViewport() const OVERRIDE;
- private:
- virtual float DeviceScaleFactor() const OVERRIDE;
- virtual const LayerTreeSettings& Settings() const OVERRIDE;
- public:
+ virtual gfx::Rect DeviceClip() const OVERRIDE;
virtual void SetFullRootLayerDamage() OVERRIDE;
- virtual bool HasImplThread() const OVERRIDE;
- virtual bool ShouldClearRootRenderPass() const OVERRIDE;
virtual CompositorFrameMetadata MakeCompositorFrameMetadata() const OVERRIDE;
- virtual bool AllowPartialSwap() const OVERRIDE;
- virtual bool ExternalStencilTestEnabled() const OVERRIDE;
// TileManagerClient implementation.
virtual void NotifyReadyToActivate() OVERRIDE;
@@ -206,11 +219,14 @@ class CC_EXPORT LayerTreeHostImpl
virtual void ReleaseGL() OVERRIDE;
virtual void SetNeedsRedrawRect(gfx::Rect rect) OVERRIDE;
virtual void BeginFrame(const BeginFrameArgs& args) OVERRIDE;
- virtual void SetExternalDrawConstraints(const gfx::Transform& transform,
- gfx::Rect viewport) OVERRIDE;
- virtual void SetExternalStencilTest(bool enabled) OVERRIDE;
+ virtual void SetExternalDrawConstraints(
+ const gfx::Transform& transform,
+ gfx::Rect viewport,
+ gfx::Rect clip,
+ bool valid_for_tile_management) OVERRIDE;
virtual void DidLoseOutputSurface() OVERRIDE;
- virtual void OnSwapBuffersComplete(const CompositorFrameAck* ack) OVERRIDE;
+ virtual void OnSwapBuffersComplete() OVERRIDE;
+ virtual void ReclaimResources(const CompositorFrameAck* ack) OVERRIDE;
virtual void SetMemoryPolicy(const ManagedMemoryPolicy& policy) OVERRIDE;
virtual void SetDiscardBackBufferWhenNotVisible(bool discard) OVERRIDE;
virtual void SetTreeActivationCallback(const base::Closure& callback)
@@ -219,10 +235,16 @@ class CC_EXPORT LayerTreeHostImpl
// Called from LayerTreeImpl.
void OnCanDrawStateChangedForTree();
- // Implementation
+ // Implementation.
bool CanDraw() const;
OutputSurface* output_surface() const { return output_surface_.get(); }
+ void SetOffscreenContextProvider(
+ const scoped_refptr<ContextProvider>& offscreen_context_provider);
+ ContextProvider* offscreen_context_provider() const {
+ return offscreen_context_provider_.get();
+ }
+
std::string LayerTreeAsJson() const;
void FinishAllRendering();
@@ -236,7 +258,7 @@ class CC_EXPORT LayerTreeHostImpl
virtual bool SwapBuffers(const FrameData& frame);
void SetNeedsBeginFrame(bool enable);
- void SetNeedsManageTiles() { manage_tiles_needed_ = true; }
+ void DidModifyTilePriorities();
void Readback(void* pixels, gfx::Rect rect_in_device_viewport);
@@ -246,8 +268,8 @@ class CC_EXPORT LayerTreeHostImpl
const LayerTreeImpl* pending_tree() const { return pending_tree_.get(); }
const LayerTreeImpl* recycle_tree() const { return recycle_tree_.get(); }
virtual void CreatePendingTree();
- void UpdateVisibleTiles();
- virtual void ActivatePendingTreeIfNeeded();
+ virtual void UpdateVisibleTiles();
+ virtual void ActivatePendingTree();
// Shortcuts to layers on the active tree.
LayerImpl* RootLayer() const;
@@ -263,17 +285,20 @@ class CC_EXPORT LayerTreeHostImpl
ManagedMemoryPolicy ActualManagedMemoryPolicy() const;
size_t memory_allocation_limit_bytes() const;
+ int memory_allocation_priority_cutoff() const;
void SetViewportSize(gfx::Size device_viewport_size);
- gfx::Size device_viewport_size() const { return device_viewport_size_; }
void SetOverdrawBottomHeight(float overdraw_bottom_height);
float overdraw_bottom_height() const { return overdraw_bottom_height_; }
+ void SetOverhangUIResource(UIResourceId overhang_ui_resource_id,
+ gfx::Size overhang_ui_resource_size);
+
void SetDeviceScaleFactor(float device_scale_factor);
float device_scale_factor() const { return device_scale_factor_; }
- const gfx::Transform& DeviceTransform() const;
+ const gfx::Transform& DrawTransform() const;
scoped_ptr<ScrollAndScaleSet> ProcessScrollDeltas();
@@ -367,7 +392,7 @@ class CC_EXPORT LayerTreeHostImpl
void SetTreePriority(TreePriority priority);
void ResetCurrentFrameTimeForNextFrame();
- base::TimeTicks CurrentFrameTimeTicks();
+ virtual base::TimeTicks CurrentFrameTimeTicks();
base::Time CurrentFrameTime();
virtual base::TimeTicks CurrentPhysicalTimeTicks() const;
@@ -378,12 +403,15 @@ class CC_EXPORT LayerTreeHostImpl
bool page_scale_animation_active() const { return !!page_scale_animation_; }
- void CreateUIResource(UIResourceId uid,
- scoped_refptr<UIResourceBitmap> bitmap);
+ virtual void CreateUIResource(UIResourceId uid,
+ const UIResourceBitmap& bitmap);
// Deletes a UI resource. May safely be called more than once.
- void DeleteUIResource(UIResourceId uid);
+ virtual void DeleteUIResource(UIResourceId uid);
+ void EvictAllUIResources();
+ bool EvictedUIResourcesExist() const;
- ResourceProvider::ResourceId ResourceIdForUIResource(UIResourceId uid) const;
+ virtual ResourceProvider::ResourceId ResourceIdForUIResource(
+ UIResourceId uid) const;
protected:
LayerTreeHostImpl(
@@ -391,7 +419,6 @@ class CC_EXPORT LayerTreeHostImpl
LayerTreeHostImplClient* client,
Proxy* proxy,
RenderingStatsInstrumentation* rendering_stats_instrumentation);
- virtual void ActivatePendingTree();
// Virtual for testing.
virtual void AnimateLayers(base::TimeTicks monotonic_time,
@@ -405,14 +432,18 @@ class CC_EXPORT LayerTreeHostImpl
return animation_registrar_->active_animation_controllers();
}
+ bool manage_tiles_needed() const { return tile_priorities_dirty_; }
+
LayerTreeHostImplClient* client_;
Proxy* proxy_;
private:
- void CreateAndSetRenderer(OutputSurface* output_surface,
- ResourceProvider* resource_provider,
- bool skip_gl_renderer);
+ void CreateAndSetRenderer(
+ OutputSurface* output_surface,
+ ResourceProvider* resource_provider,
+ bool skip_gl_renderer);
void CreateAndSetTileManager(ResourceProvider* resource_provider,
+ ContextProvider* context_provider,
bool using_map_image);
void ReleaseTreeResources();
void EnforceZeroBudget(bool zero_budget);
@@ -455,11 +486,19 @@ class CC_EXPORT LayerTreeHostImpl
void DidInitializeVisibleTile();
+ void MarkUIResourceNotEvicted(UIResourceId uid);
+
typedef base::hash_map<UIResourceId, ResourceProvider::ResourceId>
UIResourceMap;
UIResourceMap ui_resource_map_;
+ // Resources that were evicted by EvictAllUIResources. Resources are removed
+ // from this when they are touched by a create or destroy from the UI resource
+ // request queue.
+ std::set<UIResourceId> evicted_ui_resources_;
+
scoped_ptr<OutputSurface> output_surface_;
+ scoped_refptr<ContextProvider> offscreen_context_provider_;
// |resource_provider_| and |tile_manager_| can be NULL, e.g. when using tile-
// free rendering - see OutputSurface::ForcedDrawToSoftwareDevice().
@@ -471,7 +510,7 @@ class CC_EXPORT LayerTreeHostImpl
scoped_ptr<LayerTreeImpl> active_tree_;
// In impl-side painting mode, tree with possibly incomplete rasterized
- // content. May be promoted to active by ActivatePendingTreeIfNeeded().
+ // content. May be promoted to active by ActivatePendingTree().
scoped_ptr<LayerTreeImpl> pending_tree_;
// In impl-side painting mode, inert tree with layers that can be recycled
@@ -483,7 +522,7 @@ class CC_EXPORT LayerTreeHostImpl
bool should_bubble_scrolls_;
bool wheel_scrolling_;
- bool manage_tiles_needed_;
+ bool tile_priorities_dirty_;
// The optional delegate for the root layer scroll offset.
LayerScrollOffsetDelegate* root_layer_scroll_offset_delegate_;
@@ -514,6 +553,8 @@ class CC_EXPORT LayerTreeHostImpl
scoped_ptr<MemoryHistory> memory_history_;
scoped_ptr<DebugRectHistory> debug_rect_history_;
+ scoped_ptr<TextureMailboxDeleter> texture_mailbox_deleter_;
+
// The maximum memory that would be used by the prioritized resource
// manager, if there were no limit on memory usage.
size_t max_memory_needed_bytes_;
@@ -523,25 +564,38 @@ class CC_EXPORT LayerTreeHostImpl
size_t last_sent_memory_use_bytes_;
bool zero_budget_;
- // Viewport size passed in from the main thread, in physical pixels.
+ // Viewport size passed in from the main thread, in physical pixels. This
+ // value is the default size for all concepts of physical viewport (draw
+ // viewport, scrolling viewport and device viewport), but it can be
+ // overridden.
gfx::Size device_viewport_size_;
// Conversion factor from CSS pixels to physical pixels when
// pageScaleFactor=1.
float device_scale_factor_;
+ // UI resource to use for drawing overhang gutters.
+ UIResourceId overhang_ui_resource_id_;
+ gfx::Size overhang_ui_resource_size_;
+
// Vertical amount of the viewport size that's known to covered by a
// browser-side UI element, such as an on-screen-keyboard. This affects
// scrollable size since we want to still be able to scroll to the bottom of
// the page when the keyboard is up.
float overdraw_bottom_height_;
- // Optional top-level constraints that can be set by the OutputSurface. The
- // external_viewport_'s size takes precedence over device_viewport_size_ for
- // DrawQuad generation and Renderer; however, device_viewport_size_ is still
- // used for scrollable size.
+ // Optional top-level constraints that can be set by the OutputSurface.
+ // - external_transform_ applies a transform above the root layer
+ // - external_viewport_ is used DrawProperties, tile management and
+ // glViewport/window projection matrix.
+ // - external_clip_ specifies a top-level clip rect
+ // - external_stencil_test_enabled_ tells CC to respect existing stencil bits
+ // (When these are specified, device_viewport_size_ remains used only for
+ // scrollable size.)
gfx::Transform external_transform_;
gfx::Rect external_viewport_;
+ gfx::Rect external_clip_;
+ bool device_viewport_valid_for_tile_management_;
bool external_stencil_test_enabled_;
gfx::Rect viewport_damage_rect_;
diff --git a/chromium/cc/trees/layer_tree_host_impl_unittest.cc b/chromium/cc/trees/layer_tree_host_impl_unittest.cc
index ab81c672b8c..6bd815b9424 100644
--- a/chromium/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/chromium/cc/trees/layer_tree_host_impl_unittest.cc
@@ -9,15 +9,17 @@
#include "base/bind.h"
#include "base/command_line.h"
#include "base/containers/hash_tables.h"
+#include "base/containers/scoped_ptr_hash_map.h"
#include "cc/base/math_util.h"
+#include "cc/debug/test_web_graphics_context_3d.h"
#include "cc/input/top_controls_manager.h"
#include "cc/layers/delegated_renderer_layer_impl.h"
#include "cc/layers/heads_up_display_layer_impl.h"
#include "cc/layers/io_surface_layer_impl.h"
#include "cc/layers/layer_impl.h"
+#include "cc/layers/painted_scrollbar_layer_impl.h"
#include "cc/layers/quad_sink.h"
#include "cc/layers/render_surface_impl.h"
-#include "cc/layers/scrollbar_layer_impl.h"
#include "cc/layers/solid_color_layer_impl.h"
#include "cc/layers/texture_layer_impl.h"
#include "cc/layers/tiled_layer_impl.h"
@@ -25,6 +27,8 @@
#include "cc/output/begin_frame_args.h"
#include "cc/output/compositor_frame_ack.h"
#include "cc/output/compositor_frame_metadata.h"
+#include "cc/output/copy_output_request.h"
+#include "cc/output/copy_output_result.h"
#include "cc/output/gl_renderer.h"
#include "cc/quads/render_pass_draw_quad.h"
#include "cc/quads/solid_color_draw_quad.h"
@@ -32,19 +36,23 @@
#include "cc/quads/tile_draw_quad.h"
#include "cc/resources/layer_tiling_data.h"
#include "cc/test/animation_test_common.h"
+#include "cc/test/fake_layer_tree_host_impl.h"
#include "cc/test/fake_output_surface.h"
+#include "cc/test/fake_output_surface_client.h"
+#include "cc/test/fake_picture_layer_impl.h"
+#include "cc/test/fake_picture_pile_impl.h"
#include "cc/test/fake_proxy.h"
#include "cc/test/fake_rendering_stats_instrumentation.h"
#include "cc/test/fake_video_frame_provider.h"
#include "cc/test/geometry_test_utils.h"
#include "cc/test/layer_test_common.h"
#include "cc/test/render_pass_test_common.h"
-#include "cc/test/test_web_graphics_context_3d.h"
#include "cc/trees/layer_tree_impl.h"
#include "cc/trees/single_thread_proxy.h"
#include "media/base/media.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/rect_conversions.h"
#include "ui/gfx/size_conversions.h"
#include "ui/gfx/vector2d_conversions.h"
@@ -65,12 +73,13 @@ class LayerTreeHostImplTest : public testing::Test,
: proxy_(),
always_impl_thread_(&proxy_),
always_main_thread_blocked_(&proxy_),
- did_try_initialize_renderer_(false),
on_can_draw_state_changed_called_(false),
- has_pending_tree_(false),
+ did_notify_ready_to_activate_(false),
did_request_commit_(false),
did_request_redraw_(false),
+ did_request_manage_tiles_(false),
did_upload_visible_tile_(false),
+ did_lose_output_surface_(false),
reduce_memory_result_(true),
current_limit_bytes_(0),
current_priority_cutoff_value_(0) {
@@ -93,20 +102,18 @@ class LayerTreeHostImplTest : public testing::Test,
virtual void TearDown() OVERRIDE {}
- virtual void DidTryInitializeRendererOnImplThread(
- bool success,
- scoped_refptr<ContextProvider> offscreen_context_provider) OVERRIDE {
- did_try_initialize_renderer_ = true;
+ virtual void DidLoseOutputSurfaceOnImplThread() OVERRIDE {
+ did_lose_output_surface_ = true;
}
- virtual void DidLoseOutputSurfaceOnImplThread() OVERRIDE {}
virtual void OnSwapBuffersCompleteOnImplThread() OVERRIDE {}
virtual void BeginFrameOnImplThread(const BeginFrameArgs& args)
OVERRIDE {}
virtual void OnCanDrawStateChanged(bool can_draw) OVERRIDE {
on_can_draw_state_changed_called_ = true;
}
- virtual void OnHasPendingTreeStateChanged(bool has_pending_tree) OVERRIDE {
- has_pending_tree_ = has_pending_tree;
+ virtual void NotifyReadyToActivate() OVERRIDE {
+ did_notify_ready_to_activate_ = true;
+ host_impl_->ActivatePendingTree();
}
virtual void SetNeedsRedrawOnImplThread() OVERRIDE {
did_request_redraw_ = true;
@@ -114,6 +121,9 @@ class LayerTreeHostImplTest : public testing::Test,
virtual void SetNeedsRedrawRectOnImplThread(gfx::Rect damage_rect) OVERRIDE {
did_request_redraw_ = true;
}
+ virtual void SetNeedsManageTilesOnImplThread() OVERRIDE {
+ did_request_manage_tiles_ = true;
+ }
virtual void DidInitializeVisibleTileOnImplThread() OVERRIDE {
did_upload_visible_tile_ = true;
}
@@ -350,12 +360,13 @@ class LayerTreeHostImplTest : public testing::Test,
scoped_ptr<LayerTreeHostImpl> host_impl_;
FakeRenderingStatsInstrumentation stats_instrumentation_;
- bool did_try_initialize_renderer_;
bool on_can_draw_state_changed_called_;
- bool has_pending_tree_;
+ bool did_notify_ready_to_activate_;
bool did_request_commit_;
bool did_request_redraw_;
+ bool did_request_manage_tiles_;
bool did_upload_visible_tile_;
+ bool did_lose_output_surface_;
bool reduce_memory_result_;
base::TimeDelta requested_scrollbar_animation_delay_;
size_t current_limit_bytes_;
@@ -372,20 +383,18 @@ TEST_F(LayerTreeHostImplTest, CanDrawIncompleteFrames) {
settings.impl_side_painting = true;
host_impl_ = LayerTreeHostImpl::Create(
settings, this, &proxy_, &stats_instrumentation_);
+
+ scoped_ptr<FakeOutputSurface> output_surface(
+ FakeOutputSurface::CreateAlwaysDrawAndSwap3d());
+
host_impl_->InitializeRenderer(
- FakeOutputSurface::CreateAlwaysDrawAndSwap3d().PassAs<OutputSurface>());
+ output_surface.PassAs<OutputSurface>());
host_impl_->SetViewportSize(gfx::Size(10, 10));
bool always_draw = true;
CheckNotifyCalledIfCanDrawChanged(always_draw);
}
-class TestWebGraphicsContext3DMakeCurrentFails
- : public TestWebGraphicsContext3D {
- public:
- virtual bool makeContextCurrent() OVERRIDE { return false; }
-};
-
TEST_F(LayerTreeHostImplTest, ScrollDeltaNoLayers) {
ASSERT_FALSE(host_impl_->active_tree()->root_layer());
@@ -480,12 +489,15 @@ TEST_F(LayerTreeHostImplTest, ScrollWithoutRenderer) {
this,
&proxy_,
&stats_instrumentation_);
+ scoped_ptr<TestWebGraphicsContext3D> context_owned =
+ TestWebGraphicsContext3D::Create();
+ context_owned->set_times_make_current_succeeds(0);
+
+ scoped_ptr<FakeOutputSurface> output_surface(FakeOutputSurface::Create3d(
+ context_owned.Pass()));
// Initialization will fail here.
- host_impl_->InitializeRenderer(FakeOutputSurface::Create3d(
- scoped_ptr<WebKit::WebGraphicsContext3D>(
- new TestWebGraphicsContext3DMakeCurrentFails))
- .PassAs<OutputSurface>());
+ host_impl_->InitializeRenderer(output_surface.PassAs<OutputSurface>());
host_impl_->SetViewportSize(gfx::Size(10, 10));
SetupScrollAndContentsLayers(gfx::Size(100, 100));
@@ -721,8 +733,8 @@ TEST_F(LayerTreeHostImplTest, ScrollVerticallyByPageReturnsCorrectValue) {
EXPECT_FALSE(host_impl_->ScrollVerticallyByPage(
gfx::Point(), SCROLL_BACKWARD));
- scoped_ptr<cc::ScrollbarLayerImpl> vertical_scrollbar(
- cc::ScrollbarLayerImpl::Create(
+ scoped_ptr<cc::PaintedScrollbarLayerImpl> vertical_scrollbar(
+ cc::PaintedScrollbarLayerImpl::Create(
host_impl_->active_tree(),
20,
VERTICAL));
@@ -982,13 +994,17 @@ TEST_F(LayerTreeHostImplTest, PageScaleAnimation) {
max_page_scale);
scroll_layer->SetScrollOffset(gfx::Vector2d(50, 50));
- host_impl_->StartPageScaleAnimation(gfx::Vector2d(),
- false,
- 2.f,
- start_time,
- duration);
+ host_impl_->StartPageScaleAnimation(gfx::Vector2d(), false, 2.f, duration);
+ did_request_redraw_ = false;
+ host_impl_->Animate(start_time, base::Time());
+ EXPECT_TRUE(did_request_redraw_);
+
+ did_request_redraw_ = false;
host_impl_->Animate(halfway_through_animation, base::Time());
EXPECT_TRUE(did_request_redraw_);
+
+ did_request_redraw_ = false;
+ did_request_commit_ = false;
host_impl_->Animate(end_time, base::Time());
EXPECT_TRUE(did_request_commit_);
@@ -1005,10 +1021,14 @@ TEST_F(LayerTreeHostImplTest, PageScaleAnimation) {
max_page_scale);
scroll_layer->SetScrollOffset(gfx::Vector2d(50, 50));
- host_impl_->StartPageScaleAnimation(gfx::Vector2d(25, 25),
- true,
- min_page_scale,
- start_time, duration);
+ host_impl_->StartPageScaleAnimation(
+ gfx::Vector2d(25, 25), true, min_page_scale, duration);
+ did_request_redraw_ = false;
+ host_impl_->Animate(start_time, base::Time());
+ EXPECT_TRUE(did_request_redraw_);
+
+ did_request_redraw_ = false;
+ did_request_commit_ = false;
host_impl_->Animate(end_time, base::Time());
EXPECT_TRUE(did_request_redraw_);
EXPECT_TRUE(did_request_commit_);
@@ -1044,11 +1064,8 @@ TEST_F(LayerTreeHostImplTest, PageScaleAnimationNoOp) {
max_page_scale);
scroll_layer->SetScrollOffset(gfx::Vector2d(50, 50));
- host_impl_->StartPageScaleAnimation(gfx::Vector2d(),
- true,
- 1.f,
- start_time,
- duration);
+ host_impl_->StartPageScaleAnimation(gfx::Vector2d(), true, 1.f, duration);
+ host_impl_->Animate(start_time, base::Time());
host_impl_->Animate(halfway_through_animation, base::Time());
EXPECT_TRUE(did_request_redraw_);
host_impl_->Animate(end_time, base::Time());
@@ -1086,9 +1103,9 @@ class LayerTreeHostImplOverridePhysicalTime : public LayerTreeHostImpl {
base::TimeTicks fake_current_physical_time_;
};
-TEST_F(LayerTreeHostImplTest, ScrollbarLinearFadeScheduling) {
+TEST_F(LayerTreeHostImplTest, DISABLED_ScrollbarLinearFadeScheduling) {
LayerTreeSettings settings;
- settings.use_linear_fade_scrollbar_animator = true;
+ settings.scrollbar_animator = LayerTreeSettings::LinearFade;
settings.scrollbar_linear_fade_delay_ms = 20;
settings.scrollbar_linear_fade_length_ms = 20;
@@ -1121,10 +1138,8 @@ TEST_F(LayerTreeHostImplTest, ScrollbarLinearFadeScheduling) {
contents->SetBounds(content_size);
contents->SetContentBounds(content_size);
- scoped_ptr<ScrollbarLayerImpl> scrollbar = ScrollbarLayerImpl::Create(
- host_impl_->active_tree(),
- 4,
- VERTICAL);
+ scoped_ptr<PaintedScrollbarLayerImpl> scrollbar =
+ PaintedScrollbarLayerImpl::Create(host_impl_->active_tree(), 4, VERTICAL);
scroll->SetVerticalScrollbarLayer(scrollbar.get());
scroll->AddChild(contents.Pass());
@@ -1144,9 +1159,19 @@ TEST_F(LayerTreeHostImplTest, ScrollbarLinearFadeScheduling) {
EXPECT_EQ(base::TimeDelta(), requested_scrollbar_animation_delay_);
EXPECT_FALSE(did_request_redraw_);
+ // If no scroll happened during a scroll gesture, StartScrollbarAnimation
+ // should have no effect.
+ host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel);
+ host_impl_->ScrollEnd();
+ host_impl_->StartScrollbarAnimation();
+ EXPECT_EQ(base::TimeDelta(), requested_scrollbar_animation_delay_);
+ EXPECT_FALSE(did_request_redraw_);
+
// After a scroll, a fade animation should be scheduled about 20ms from now.
host_impl_->ScrollBegin(gfx::Point(), InputHandler::Wheel);
+ host_impl_->ScrollBy(gfx::Point(), gfx::Vector2dF(5, 0));
host_impl_->ScrollEnd();
+ did_request_redraw_ = false;
host_impl_->StartScrollbarAnimation();
EXPECT_LT(base::TimeDelta::FromMilliseconds(19),
requested_scrollbar_animation_delay_);
@@ -1509,8 +1534,9 @@ class MissingTextureAnimatingLayer : public DidDrawCheckLayer {
if (!tile_missing) {
ResourceProvider::ResourceId resource =
resource_provider->CreateResource(gfx::Size(1, 1),
- GL_RGBA,
- ResourceProvider::TextureUsageAny);
+ GL_CLAMP_TO_EDGE,
+ ResourceProvider::TextureUsageAny,
+ RGBA_8888);
resource_provider->AllocateForTesting(resource);
PushTileProperties(0, 0, resource, gfx::Rect(), false);
}
@@ -2207,7 +2233,7 @@ TEST_F(LayerTreeHostImplTest, ScrollNonAxisAlignedRotatedLayer) {
// Scroll down in screen coordinates with a gesture.
gfx::Vector2d gesture_scroll_delta(0, 10);
EXPECT_EQ(InputHandler::ScrollStarted,
- host_impl_->ScrollBegin(gfx::Point(),
+ host_impl_->ScrollBegin(gfx::Point(1, 1),
InputHandler::Gesture));
host_impl_->ScrollBy(gfx::Point(), gesture_scroll_delta);
host_impl_->ScrollEnd();
@@ -2232,7 +2258,7 @@ TEST_F(LayerTreeHostImplTest, ScrollNonAxisAlignedRotatedLayer) {
gfx::Vector2dF());
gfx::Vector2d gesture_scroll_delta(10, 0);
EXPECT_EQ(InputHandler::ScrollStarted,
- host_impl_->ScrollBegin(gfx::Point(),
+ host_impl_->ScrollBegin(gfx::Point(1, 1),
InputHandler::Gesture));
host_impl_->ScrollBy(gfx::Point(), gesture_scroll_delta);
host_impl_->ScrollEnd();
@@ -2600,8 +2626,9 @@ class BlendStateCheckLayer : public LayerImpl {
quad_visible_rect_(5, 5, 5, 5),
resource_id_(resource_provider->CreateResource(
gfx::Size(1, 1),
- GL_RGBA,
- ResourceProvider::TextureUsageAny)) {
+ GL_CLAMP_TO_EDGE,
+ ResourceProvider::TextureUsageAny,
+ RGBA_8888)) {
resource_provider->AllocateForTesting(resource_id_);
SetAnchorPoint(gfx::PointF());
SetBounds(gfx::Size(10, 10));
@@ -2844,20 +2871,25 @@ TEST_F(LayerTreeHostImplTest, BlendingOffWhenDrawingOpaqueLayers) {
class LayerTreeHostImplViewportCoveredTest : public LayerTreeHostImplTest {
public:
+ LayerTreeHostImplViewportCoveredTest() :
+ gutter_quad_material_(DrawQuad::SOLID_COLOR),
+ child_(NULL),
+ did_activate_pending_tree_(false) {}
+
void CreateLayerTreeHostImpl(bool always_draw) {
LayerTreeSettings settings;
settings.minimum_occlusion_tracking_size = gfx::Size();
settings.impl_side_painting = true;
host_impl_ = LayerTreeHostImpl::Create(
settings, this, &proxy_, &stats_instrumentation_);
- scoped_ptr<OutputSurface> output_surface;
- if (always_draw) {
- output_surface = FakeOutputSurface::CreateAlwaysDrawAndSwap3d()
- .PassAs<OutputSurface>();
- } else {
- output_surface = CreateFakeOutputSurface();
- }
- host_impl_->InitializeRenderer(output_surface.Pass());
+
+ scoped_ptr<FakeOutputSurface> output_surface;
+ if (always_draw)
+ output_surface = FakeOutputSurface::CreateAlwaysDrawAndSwap3d().Pass();
+ else
+ output_surface = FakeOutputSurface::Create3d().Pass();
+
+ host_impl_->InitializeRenderer(output_surface.PassAs<OutputSurface>());
viewport_size_ = gfx::Size(1000, 1000);
}
@@ -2888,15 +2920,11 @@ class LayerTreeHostImplViewportCoveredTest : public LayerTreeHostImplTest {
EXPECT_TRUE(host_impl_->PrepareToDraw(&frame, gfx::Rect()));
ASSERT_EQ(1u, frame.render_passes.size());
- size_t num_gutter_quads = 0;
- for (size_t i = 0; i < frame.render_passes[0]->quad_list.size(); ++i)
- num_gutter_quads += (frame.render_passes[0]->quad_list[i]->material ==
- DrawQuad::SOLID_COLOR) ? 1 : 0;
- EXPECT_EQ(0u, num_gutter_quads);
+ EXPECT_EQ(0u, CountGutterQuads(frame.render_passes[0]->quad_list));
EXPECT_EQ(1u, frame.render_passes[0]->quad_list.size());
+ ValidateTextureDrawQuads(frame.render_passes[0]->quad_list);
- LayerTestCommon::VerifyQuadsExactlyCoverRect(
- frame.render_passes[0]->quad_list, gfx::Rect(viewport_size_));
+ VerifyQuadsExactlyCoverViewport(frame.render_passes[0]->quad_list);
host_impl_->DidDrawAllLayers(frame);
}
@@ -2913,15 +2941,11 @@ class LayerTreeHostImplViewportCoveredTest : public LayerTreeHostImplTest {
EXPECT_TRUE(host_impl_->PrepareToDraw(&frame, gfx::Rect()));
ASSERT_EQ(1u, frame.render_passes.size());
- size_t num_gutter_quads = 0;
- for (size_t i = 0; i < frame.render_passes[0]->quad_list.size(); ++i)
- num_gutter_quads += (frame.render_passes[0]->quad_list[i]->material ==
- DrawQuad::SOLID_COLOR) ? 1 : 0;
- EXPECT_EQ(1u, num_gutter_quads);
+ EXPECT_EQ(1u, CountGutterQuads(frame.render_passes[0]->quad_list));
EXPECT_EQ(1u, frame.render_passes[0]->quad_list.size());
+ ValidateTextureDrawQuads(frame.render_passes[0]->quad_list);
- LayerTestCommon::VerifyQuadsExactlyCoverRect(
- frame.render_passes[0]->quad_list, gfx::Rect(viewport_size_));
+ VerifyQuadsExactlyCoverViewport(frame.render_passes[0]->quad_list);
host_impl_->DidDrawAllLayers(frame);
}
@@ -2938,15 +2962,11 @@ class LayerTreeHostImplViewportCoveredTest : public LayerTreeHostImplTest {
EXPECT_TRUE(host_impl_->PrepareToDraw(&frame, gfx::Rect()));
ASSERT_EQ(1u, frame.render_passes.size());
- size_t num_gutter_quads = 0;
- for (size_t i = 0; i < frame.render_passes[0]->quad_list.size(); ++i)
- num_gutter_quads += (frame.render_passes[0]->quad_list[i]->material ==
- DrawQuad::SOLID_COLOR) ? 1 : 0;
- EXPECT_EQ(4u, num_gutter_quads);
+ EXPECT_EQ(4u, CountGutterQuads(frame.render_passes[0]->quad_list));
EXPECT_EQ(5u, frame.render_passes[0]->quad_list.size());
+ ValidateTextureDrawQuads(frame.render_passes[0]->quad_list);
- LayerTestCommon::VerifyQuadsExactlyCoverRect(
- frame.render_passes[0]->quad_list, gfx::Rect(viewport_size_));
+ VerifyQuadsExactlyCoverViewport(frame.render_passes[0]->quad_list);
host_impl_->DidDrawAllLayers(frame);
}
@@ -2964,12 +2984,9 @@ class LayerTreeHostImplViewportCoveredTest : public LayerTreeHostImplTest {
EXPECT_TRUE(host_impl_->PrepareToDraw(&frame, gfx::Rect()));
ASSERT_EQ(1u, frame.render_passes.size());
- size_t num_gutter_quads = 0;
- for (size_t i = 0; i < frame.render_passes[0]->quad_list.size(); ++i)
- num_gutter_quads += (frame.render_passes[0]->quad_list[i]->material ==
- DrawQuad::SOLID_COLOR) ? 1 : 0;
- EXPECT_EQ(0u, num_gutter_quads);
+ EXPECT_EQ(0u, CountGutterQuads(frame.render_passes[0]->quad_list));
EXPECT_EQ(1u, frame.render_passes[0]->quad_list.size());
+ ValidateTextureDrawQuads(frame.render_passes[0]->quad_list);
host_impl_->DidDrawAllLayers(frame);
}
@@ -2978,7 +2995,54 @@ class LayerTreeHostImplViewportCoveredTest : public LayerTreeHostImplTest {
did_activate_pending_tree_ = true;
}
+ void set_gutter_quad_material(DrawQuad::Material material) {
+ gutter_quad_material_ = material;
+ }
+ void set_gutter_texture_size(gfx::Size gutter_texture_size) {
+ gutter_texture_size_ = gutter_texture_size;
+ }
+
protected:
+ size_t CountGutterQuads(const QuadList& quad_list) {
+ size_t num_gutter_quads = 0;
+ for (size_t i = 0; i < quad_list.size(); ++i) {
+ num_gutter_quads += (quad_list[i]->material ==
+ gutter_quad_material_) ? 1 : 0;
+ }
+ return num_gutter_quads;
+ }
+
+ void VerifyQuadsExactlyCoverViewport(const QuadList& quad_list) {
+ LayerTestCommon::VerifyQuadsExactlyCoverRect(
+ quad_list, gfx::Rect(DipSizeToPixelSize(viewport_size_)));
+ }
+
+ // Make sure that the texture coordinates match their expectations.
+ void ValidateTextureDrawQuads(const QuadList& quad_list) {
+ for (size_t i = 0; i < quad_list.size(); ++i) {
+ if (quad_list[i]->material != DrawQuad::TEXTURE_CONTENT)
+ continue;
+ const TextureDrawQuad* quad = TextureDrawQuad::MaterialCast(quad_list[i]);
+ gfx::SizeF gutter_texture_size_pixels = gfx::ScaleSize(
+ gutter_texture_size_, host_impl_->device_scale_factor());
+ EXPECT_EQ(quad->uv_top_left.x(),
+ quad->rect.x() / gutter_texture_size_pixels.width());
+ EXPECT_EQ(quad->uv_top_left.y(),
+ quad->rect.y() / gutter_texture_size_pixels.height());
+ EXPECT_EQ(quad->uv_bottom_right.x(),
+ quad->rect.right() / gutter_texture_size_pixels.width());
+ EXPECT_EQ(quad->uv_bottom_right.y(),
+ quad->rect.bottom() / gutter_texture_size_pixels.height());
+ }
+ }
+
+ gfx::Size DipSizeToPixelSize(gfx::Size size) {
+ return gfx::ToRoundedSize(
+ gfx::ScaleSize(size, host_impl_->device_scale_factor()));
+ }
+
+ DrawQuad::Material gutter_quad_material_;
+ gfx::Size gutter_texture_size_;
gfx::Size viewport_size_;
BlendStateCheckLayer* child_;
bool did_activate_pending_tree_;
@@ -2988,7 +3052,7 @@ TEST_F(LayerTreeHostImplViewportCoveredTest, ViewportCovered) {
bool always_draw = false;
CreateLayerTreeHostImpl(always_draw);
- host_impl_->SetViewportSize(viewport_size_);
+ host_impl_->SetViewportSize(DipSizeToPixelSize(viewport_size_));
SetupActiveTreeLayers();
TestLayerCoversFullViewport();
TestEmptyLayer();
@@ -2996,13 +3060,70 @@ TEST_F(LayerTreeHostImplViewportCoveredTest, ViewportCovered) {
TestLayerIsLargerThanViewport();
}
+TEST_F(LayerTreeHostImplViewportCoveredTest, ViewportCoveredScaled) {
+ bool always_draw = false;
+ CreateLayerTreeHostImpl(always_draw);
+
+ host_impl_->SetDeviceScaleFactor(2.f);
+ host_impl_->SetViewportSize(DipSizeToPixelSize(viewport_size_));
+ SetupActiveTreeLayers();
+ TestLayerCoversFullViewport();
+ TestEmptyLayer();
+ TestLayerInMiddleOfViewport();
+ TestLayerIsLargerThanViewport();
+}
+
+TEST_F(LayerTreeHostImplViewportCoveredTest, ViewportCoveredOverhangBitmap) {
+ bool always_draw = false;
+ CreateLayerTreeHostImpl(always_draw);
+
+ host_impl_->SetViewportSize(DipSizeToPixelSize(viewport_size_));
+ SetupActiveTreeLayers();
+
+ SkBitmap skbitmap;
+ skbitmap.setConfig(SkBitmap::kARGB_8888_Config, 2, 2);
+ skbitmap.allocPixels();
+ skbitmap.setImmutable();
+
+ // Specify an overhang bitmap to use.
+ UIResourceBitmap ui_resource_bitmap(skbitmap, UIResourceBitmap::REPEAT);
+ UIResourceId ui_resource_id = 12345;
+ host_impl_->CreateUIResource(ui_resource_id, ui_resource_bitmap);
+ host_impl_->SetOverhangUIResource(ui_resource_id, gfx::Size(32, 32));
+ set_gutter_quad_material(DrawQuad::TEXTURE_CONTENT);
+ set_gutter_texture_size(gfx::Size(32, 32));
+
+ TestLayerCoversFullViewport();
+ TestEmptyLayer();
+ TestLayerInMiddleOfViewport();
+ TestLayerIsLargerThanViewport();
+
+ // Change the resource size.
+ host_impl_->SetOverhangUIResource(ui_resource_id, gfx::Size(128, 16));
+ set_gutter_texture_size(gfx::Size(128, 16));
+
+ TestLayerCoversFullViewport();
+ TestEmptyLayer();
+ TestLayerInMiddleOfViewport();
+ TestLayerIsLargerThanViewport();
+
+ // Change the device scale factor
+ host_impl_->SetDeviceScaleFactor(2.f);
+ host_impl_->SetViewportSize(DipSizeToPixelSize(viewport_size_));
+
+ TestLayerCoversFullViewport();
+ TestEmptyLayer();
+ TestLayerInMiddleOfViewport();
+ TestLayerIsLargerThanViewport();
+}
+
TEST_F(LayerTreeHostImplViewportCoveredTest, ActiveTreeGrowViewportInvalid) {
bool always_draw = true;
CreateLayerTreeHostImpl(always_draw);
// Pending tree to force active_tree size invalid. Not used otherwise.
host_impl_->CreatePendingTree();
- host_impl_->SetViewportSize(viewport_size_);
+ host_impl_->SetViewportSize(DipSizeToPixelSize(viewport_size_));
EXPECT_TRUE(host_impl_->active_tree()->ViewportSizeInvalid());
SetupActiveTreeLayers();
@@ -3019,16 +3140,15 @@ TEST_F(LayerTreeHostImplViewportCoveredTest, ActiveTreeShrinkViewportInvalid) {
host_impl_->CreatePendingTree();
gfx::Size larger_viewport(viewport_size_.width() + 100,
viewport_size_.height() + 100);
- host_impl_->SetViewportSize(larger_viewport);
+ host_impl_->SetViewportSize(DipSizeToPixelSize(larger_viewport));
EXPECT_TRUE(host_impl_->active_tree()->ViewportSizeInvalid());
- did_activate_pending_tree_ = false;
- host_impl_->ActivatePendingTreeIfNeeded();
+ host_impl_->ActivatePendingTree();
EXPECT_TRUE(did_activate_pending_tree_);
EXPECT_FALSE(host_impl_->active_tree()->ViewportSizeInvalid());
// Shrink pending tree viewport without activating.
host_impl_->CreatePendingTree();
- host_impl_->SetViewportSize(viewport_size_);
+ host_impl_->SetViewportSize(DipSizeToPixelSize(viewport_size_));
EXPECT_TRUE(host_impl_->active_tree()->ViewportSizeInvalid());
SetupActiveTreeLayers();
@@ -3081,11 +3201,11 @@ class FakeDrawableLayerImpl: public LayerImpl {
// can leave the window at the wrong size if we never draw and the proper
// viewport size is never set.
TEST_F(LayerTreeHostImplTest, ReshapeNotCalledUntilDraw) {
- scoped_ptr<OutputSurface> output_surface = FakeOutputSurface::Create3d(
- scoped_ptr<WebKit::WebGraphicsContext3D>(new ReshapeTrackerContext))
- .PassAs<OutputSurface>();
- ReshapeTrackerContext* reshape_tracker =
- static_cast<ReshapeTrackerContext*>(output_surface->context3d());
+ scoped_ptr<ReshapeTrackerContext> owned_reshape_tracker(
+ new ReshapeTrackerContext);
+ ReshapeTrackerContext* reshape_tracker = owned_reshape_tracker.get();
+ scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d(
+ owned_reshape_tracker.PassAs<TestWebGraphicsContext3D>()));
host_impl_->InitializeRenderer(output_surface.Pass());
scoped_ptr<LayerImpl> root =
@@ -3133,7 +3253,11 @@ TEST_F(LayerTreeHostImplTest, ReshapeNotCalledUntilDraw) {
class SwapTrackerContext : public TestWebGraphicsContext3D {
public:
- SwapTrackerContext() : last_update_type_(NoUpdate) {}
+ SwapTrackerContext()
+ : last_update_type_(NoUpdate) {
+ test_capabilities_.post_sub_buffer = true;
+ test_capabilities_.set_visibility = true;
+ }
virtual void prepareTexture() OVERRIDE {
update_rect_ = gfx::Rect(width_, height_);
@@ -3146,15 +3270,6 @@ class SwapTrackerContext : public TestWebGraphicsContext3D {
last_update_type_ = PostSubBuffer;
}
- virtual WebKit::WebString getString(WebKit::WGC3Denum name) OVERRIDE {
- if (name == GL_EXTENSIONS) {
- return WebKit::WebString(
- "GL_CHROMIUM_post_sub_buffer GL_CHROMIUM_set_visibility");
- }
-
- return WebKit::WebString();
- }
-
gfx::Rect update_rect() const { return update_rect_; }
enum UpdateType {
@@ -3175,11 +3290,11 @@ class SwapTrackerContext : public TestWebGraphicsContext3D {
// Make sure damage tracking propagates all the way to the graphics context,
// where it should request to swap only the sub-buffer that is damaged.
TEST_F(LayerTreeHostImplTest, PartialSwapReceivesDamageRect) {
- scoped_ptr<OutputSurface> output_surface =
- FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>(
- new SwapTrackerContext)).PassAs<OutputSurface>();
- SwapTrackerContext* swap_tracker =
- static_cast<SwapTrackerContext*>(output_surface->context3d());
+ scoped_ptr<SwapTrackerContext> context(new SwapTrackerContext);
+ SwapTrackerContext* swap_tracker = context.get();
+
+ scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d(
+ context.PassAs<TestWebGraphicsContext3D>()));
// This test creates its own LayerTreeHostImpl, so
// that we can force partial swap enabled.
@@ -3329,7 +3444,6 @@ class MockContext : public TestWebGraphicsContext3D {
WebKit::WGC3Dsizei count,
WebKit::WGC3Denum type,
WebKit::WGC3Dintptr offset));
- MOCK_METHOD1(getString, WebKit::WebString(WebKit::WGC3Denum name));
MOCK_METHOD0(getRequestableExtensionsCHROMIUM, WebKit::WebString());
MOCK_METHOD1(enable, void(WebKit::WGC3Denum cap));
MOCK_METHOD1(disable, void(WebKit::WGC3Denum cap));
@@ -3346,6 +3460,8 @@ class MockContextHarness {
public:
explicit MockContextHarness(MockContext* context)
: context_(context) {
+ context_->set_have_post_sub_buffer(true);
+
// Catch "uninteresting" calls
EXPECT_CALL(*context_, useProgram(_))
.Times(0);
@@ -3360,19 +3476,6 @@ class MockContextHarness {
EXPECT_CALL(*context_, uniform4f(_, _, _, _, _))
.WillRepeatedly(Return());
- // Any other strings are empty
- EXPECT_CALL(*context_, getString(_))
- .WillRepeatedly(Return(WebKit::WebString()));
-
- // Support for partial swap, if needed
- EXPECT_CALL(*context_, getString(GL_EXTENSIONS))
- .WillRepeatedly(Return(
- WebKit::WebString("GL_CHROMIUM_post_sub_buffer")));
-
- EXPECT_CALL(*context_, getRequestableExtensionsCHROMIUM())
- .WillRepeatedly(Return(
- WebKit::WebString("GL_CHROMIUM_post_sub_buffer")));
-
// Any un-sanctioned calls to enable() are OK
EXPECT_CALL(*context_, enable(_))
.WillRepeatedly(Return());
@@ -3414,11 +3517,11 @@ class MockContextHarness {
};
TEST_F(LayerTreeHostImplTest, NoPartialSwap) {
- scoped_ptr<OutputSurface> output_surface =
- FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>(
- new MockContext)).PassAs<OutputSurface>();
- MockContext* mock_context =
- static_cast<MockContext*>(output_surface->context3d());
+ scoped_ptr<MockContext> mock_context_owned(new MockContext);
+ MockContext* mock_context = mock_context_owned.get();
+
+ scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d(
+ mock_context_owned.PassAs<TestWebGraphicsContext3D>()));
MockContextHarness harness(mock_context);
// Run test case
@@ -3451,11 +3554,10 @@ TEST_F(LayerTreeHostImplTest, NoPartialSwap) {
}
TEST_F(LayerTreeHostImplTest, PartialSwap) {
- scoped_ptr<OutputSurface> output_surface =
- FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>(
- new MockContext)).PassAs<OutputSurface>();
- MockContext* mock_context =
- static_cast<MockContext*>(output_surface->context3d());
+ scoped_ptr<MockContext> context_owned(new MockContext);
+ MockContext* mock_context = context_owned.get();
+ scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d(
+ context_owned.PassAs<TestWebGraphicsContext3D>()));
MockContextHarness harness(mock_context);
CreateLayerTreeHost(true, output_surface.Pass());
@@ -3490,14 +3592,8 @@ TEST_F(LayerTreeHostImplTest, PartialSwap) {
class PartialSwapContext : public TestWebGraphicsContext3D {
public:
- virtual WebKit::WebString getString(WebKit::WGC3Denum name) OVERRIDE {
- if (name == GL_EXTENSIONS)
- return WebKit::WebString("GL_CHROMIUM_post_sub_buffer");
- return WebKit::WebString();
- }
-
- virtual WebKit::WebString getRequestableExtensionsCHROMIUM() OVERRIDE {
- return WebKit::WebString("GL_CHROMIUM_post_sub_buffer");
+ PartialSwapContext() {
+ test_capabilities_.post_sub_buffer = true;
}
// Unlimited texture size.
@@ -3515,9 +3611,8 @@ static scoped_ptr<LayerTreeHostImpl> SetupLayersForOpacity(
LayerTreeHostImplClient* client,
Proxy* proxy,
RenderingStatsInstrumentation* stats_instrumentation) {
- scoped_ptr<OutputSurface> output_surface =
- FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>(
- new PartialSwapContext)).PassAs<OutputSurface>();
+ scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d(
+ scoped_ptr<TestWebGraphicsContext3D>(new PartialSwapContext)));
LayerTreeSettings settings;
settings.partial_swap_enabled = partial_swap;
@@ -3634,7 +3729,10 @@ class TrackingWebGraphicsContext3D : public TestWebGraphicsContext3D {
public:
TrackingWebGraphicsContext3D()
: TestWebGraphicsContext3D(),
- num_textures_(0) {}
+ num_textures_(0) {
+ test_capabilities_.iosurface = true;
+ test_capabilities_.texture_rectangle = true;
+ }
virtual WebKit::WebGLId createTexture() OVERRIDE {
WebKit::WebGLId id = TestWebGraphicsContext3D::createTexture();
@@ -3652,15 +3750,6 @@ class TrackingWebGraphicsContext3D : public TestWebGraphicsContext3D {
--num_textures_;
}
- virtual WebKit::WebString getString(WebKit::WGC3Denum name) OVERRIDE {
- if (name == GL_EXTENSIONS) {
- return WebKit::WebString(
- "GL_CHROMIUM_iosurface GL_ARB_texture_rectangle");
- }
-
- return WebKit::WebString();
- }
-
unsigned num_textures() const { return num_textures_; }
private:
@@ -3672,8 +3761,8 @@ TEST_F(LayerTreeHostImplTest, LayersFreeTextures) {
scoped_ptr<TestWebGraphicsContext3D> context =
TestWebGraphicsContext3D::Create();
TestWebGraphicsContext3D* context3d = context.get();
- scoped_ptr<OutputSurface> output_surface = FakeOutputSurface::Create3d(
- context.PassAs<WebKit::WebGraphicsContext3D>()).PassAs<OutputSurface>();
+ scoped_ptr<OutputSurface> output_surface(
+ FakeOutputSurface::Create3d(context.Pass()));
host_impl_->InitializeRenderer(output_surface.Pass());
scoped_ptr<LayerImpl> root_layer =
@@ -3732,12 +3821,12 @@ class MockDrawQuadsToFillScreenContext : public TestWebGraphicsContext3D {
};
TEST_F(LayerTreeHostImplTest, HasTransparentBackground) {
- scoped_ptr<OutputSurface> output_surface =
- FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>(
- new MockDrawQuadsToFillScreenContext)).PassAs<OutputSurface>();
- MockDrawQuadsToFillScreenContext* mock_context =
- static_cast<MockDrawQuadsToFillScreenContext*>(
- output_surface->context3d());
+ scoped_ptr<MockDrawQuadsToFillScreenContext> mock_context_owned(
+ new MockDrawQuadsToFillScreenContext);
+ MockDrawQuadsToFillScreenContext* mock_context = mock_context_owned.get();
+
+ scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d(
+ mock_context_owned.PassAs<TestWebGraphicsContext3D>()));
// Run test case
CreateLayerTreeHost(false, output_surface.Pass());
@@ -3790,9 +3879,8 @@ static void SetupLayersForTextureCaching(
LayerImpl*& surface_layer_ptr,
LayerImpl*& child_ptr,
gfx::Size root_size) {
- scoped_ptr<OutputSurface> output_surface =
- FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>(
- new PartialSwapContext)).PassAs<OutputSurface>();
+ scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d(
+ scoped_ptr<TestWebGraphicsContext3D>(new PartialSwapContext)));
layer_tree_host_impl->InitializeRenderer(output_surface.Pass());
layer_tree_host_impl->SetViewportSize(root_size);
@@ -3869,9 +3957,8 @@ TEST_F(LayerTreeHostImplTest, TextureCachingWithOcclusion) {
LayerImpl* layer_s1_ptr;
LayerImpl* layer_s2_ptr;
- scoped_ptr<OutputSurface> output_surface =
- FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>(
- new PartialSwapContext)).PassAs<OutputSurface>();
+ scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d(
+ scoped_ptr<TestWebGraphicsContext3D>(new PartialSwapContext)));
gfx::Size root_size(1000, 1000);
@@ -3988,9 +4075,8 @@ TEST_F(LayerTreeHostImplTest, TextureCachingWithOcclusionEarlyOut) {
LayerImpl* layer_s1_ptr;
LayerImpl* layer_s2_ptr;
- scoped_ptr<OutputSurface> output_surface =
- FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>(
- new PartialSwapContext)).PassAs<OutputSurface>();
+ scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d(
+ scoped_ptr<TestWebGraphicsContext3D>(new PartialSwapContext)));
gfx::Size root_size(1000, 1000);
@@ -4109,9 +4195,8 @@ TEST_F(LayerTreeHostImplTest, TextureCachingWithOcclusionExternalOverInternal) {
LayerImpl* layer_s1_ptr;
LayerImpl* layer_s2_ptr;
- scoped_ptr<OutputSurface> output_surface =
- FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>(
- new PartialSwapContext)).PassAs<OutputSurface>();
+ scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d(
+ scoped_ptr<TestWebGraphicsContext3D>(new PartialSwapContext)));
gfx::Size root_size(1000, 1000);
@@ -4199,9 +4284,8 @@ TEST_F(LayerTreeHostImplTest, TextureCachingWithOcclusionExternalNotAligned) {
LayerImpl* root_ptr;
LayerImpl* layer_s1_ptr;
- scoped_ptr<OutputSurface> output_surface =
- FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>(
- new PartialSwapContext)).PassAs<OutputSurface>();
+ scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d(
+ scoped_ptr<TestWebGraphicsContext3D>(new PartialSwapContext)));
gfx::Size root_size(1000, 1000);
@@ -4290,9 +4374,8 @@ TEST_F(LayerTreeHostImplTest, TextureCachingWithOcclusionPartialSwap) {
LayerImpl* layer_s1_ptr;
LayerImpl* layer_s2_ptr;
- scoped_ptr<OutputSurface> output_surface =
- FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>(
- new PartialSwapContext)).PassAs<OutputSurface>();
+ scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d(
+ scoped_ptr<TestWebGraphicsContext3D>(new PartialSwapContext)));
gfx::Size root_size(1000, 1000);
@@ -4420,9 +4503,8 @@ TEST_F(LayerTreeHostImplTest, TextureCachingWithScissor) {
gfx::Rect child_rect(10, 10, 50, 50);
gfx::Rect grand_child_rect(5, 5, 150, 150);
- scoped_ptr<OutputSurface> output_surface =
- FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>(
- new PartialSwapContext)).PassAs<OutputSurface>();
+ scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d(
+ scoped_ptr<TestWebGraphicsContext3D>(new PartialSwapContext)));
my_host_impl->InitializeRenderer(output_surface.Pass());
root->SetAnchorPoint(gfx::PointF());
@@ -4924,18 +5006,18 @@ TEST_F(LayerTreeHostImplTest, ReleaseContentsTextureShouldTriggerCommit) {
}
struct RenderPassRemovalTestData : public LayerTreeHostImpl::FrameData {
- ScopedPtrHashMap<RenderPass::Id, TestRenderPass> render_pass_cache;
+ base::ScopedPtrHashMap<RenderPass::Id, TestRenderPass> render_pass_cache;
scoped_ptr<SharedQuadState> shared_quad_state;
};
class TestRenderer : public GLRenderer, public RendererClient {
public:
- static scoped_ptr<TestRenderer> Create(ResourceProvider* resource_provider,
+ static scoped_ptr<TestRenderer> Create(const LayerTreeSettings* settings,
+ ResourceProvider* resource_provider,
OutputSurface* output_surface,
Proxy* proxy) {
- scoped_ptr<TestRenderer> renderer(new TestRenderer(resource_provider,
- output_surface,
- proxy));
+ scoped_ptr<TestRenderer> renderer(
+ new TestRenderer(settings, resource_provider, output_surface, proxy));
if (!renderer->Initialize())
return scoped_ptr<TestRenderer>();
@@ -4956,27 +5038,19 @@ class TestRenderer : public GLRenderer, public RendererClient {
virtual gfx::Rect DeviceViewport() const OVERRIDE {
return gfx::Rect(viewport_size_);
}
- virtual float DeviceScaleFactor() const OVERRIDE {
- return 1.f;
- }
- virtual const LayerTreeSettings& Settings() const OVERRIDE {
- return settings_;
- }
+ virtual gfx::Rect DeviceClip() const OVERRIDE { return DeviceViewport(); }
virtual void SetFullRootLayerDamage() OVERRIDE {}
- virtual bool HasImplThread() const OVERRIDE { return false; }
- virtual bool ShouldClearRootRenderPass() const OVERRIDE { return true; }
- virtual CompositorFrameMetadata MakeCompositorFrameMetadata() const
- OVERRIDE { return CompositorFrameMetadata(); }
- virtual bool AllowPartialSwap() const OVERRIDE {
- return true;
+ virtual CompositorFrameMetadata MakeCompositorFrameMetadata() const OVERRIDE {
+ return CompositorFrameMetadata();
}
- virtual bool ExternalStencilTestEnabled() const OVERRIDE { return false; }
protected:
- TestRenderer(ResourceProvider* resource_provider,
+ TestRenderer(const LayerTreeSettings* settings,
+ ResourceProvider* resource_provider,
OutputSurface* output_surface,
Proxy* proxy)
- : GLRenderer(this, output_surface, resource_provider, 0) {}
+ : GLRenderer(this, settings, output_surface, resource_provider, NULL, 0) {
+ }
private:
LayerTreeSettings settings_;
@@ -5296,15 +5370,17 @@ static void VerifyRenderPassTestData(
}
TEST_F(LayerTreeHostImplTest, TestRemoveRenderPasses) {
+ LayerTreeSettings settings;
+ FakeOutputSurfaceClient output_surface_client;
scoped_ptr<OutputSurface> output_surface(CreateOutputSurface());
- ASSERT_TRUE(output_surface->context3d());
+ ASSERT_TRUE(output_surface->BindToClient(&output_surface_client));
+ ASSERT_TRUE(output_surface->context_provider());
+
scoped_ptr<ResourceProvider> resource_provider =
- ResourceProvider::Create(output_surface.get(), 0);
+ ResourceProvider::Create(output_surface.get(), 0, false);
- scoped_ptr<TestRenderer> renderer =
- TestRenderer::Create(resource_provider.get(),
- output_surface.get(),
- &proxy_);
+ scoped_ptr<TestRenderer> renderer = TestRenderer::Create(
+ &settings, resource_provider.get(), output_surface.get(), &proxy_);
int test_case_index = 0;
while (remove_render_passes_cases[test_case_index].name) {
@@ -5388,7 +5464,7 @@ TEST_F(LayerTreeHostImplTestWithDelegatingRenderer, FrameIncludesDamageRect) {
host_impl_->active_tree()->SetRootLayer(root.PassAs<LayerImpl>());
// Draw a frame. In the first frame, the entire viewport should be damaged.
- gfx::Rect full_frame_damage = gfx::Rect(host_impl_->device_viewport_size());
+ gfx::Rect full_frame_damage(host_impl_->DrawViewportSize());
DrawFrameAndTestDamage(full_frame_damage);
// The second frame has damage that doesn't touch the child layer. Its quads
@@ -6085,6 +6161,79 @@ TEST_F(LayerTreeHostImplTest, MaskLayerForSurfaceWithClippedLayer) {
}
}
+class GLRendererWithSetupQuadForAntialiasing : public GLRenderer {
+ public:
+ using GLRenderer::SetupQuadForAntialiasing;
+};
+
+TEST_F(LayerTreeHostImplTest, FarAwayQuadsDontNeedAA) {
+ // Due to precision issues (especially on Android), sometimes far
+ // away quads can end up thinking they need AA.
+ float device_scale_factor = 4.f / 3.f;
+ host_impl_->SetDeviceScaleFactor(device_scale_factor);
+ gfx::Size root_size(2000, 1000);
+ gfx::Size device_viewport_size =
+ gfx::ToCeiledSize(gfx::ScaleSize(root_size, device_scale_factor));
+ host_impl_->SetViewportSize(device_viewport_size);
+
+ host_impl_->CreatePendingTree();
+ host_impl_->pending_tree()
+ ->SetPageScaleFactorAndLimits(1.f, 1.f / 16.f, 16.f);
+
+ scoped_ptr<LayerImpl> scoped_root =
+ LayerImpl::Create(host_impl_->pending_tree(), 1);
+ LayerImpl* root = scoped_root.get();
+
+ host_impl_->pending_tree()->SetRootLayer(scoped_root.Pass());
+
+ scoped_ptr<LayerImpl> scoped_scrolling_layer =
+ LayerImpl::Create(host_impl_->pending_tree(), 2);
+ LayerImpl* scrolling_layer = scoped_scrolling_layer.get();
+ root->AddChild(scoped_scrolling_layer.Pass());
+
+ gfx::Size content_layer_bounds(100000, 100);
+ gfx::Size pile_tile_size(3000, 3000);
+ scoped_refptr<FakePicturePileImpl> pile(FakePicturePileImpl::CreateFilledPile(
+ pile_tile_size, content_layer_bounds));
+
+ scoped_ptr<FakePictureLayerImpl> scoped_content_layer =
+ FakePictureLayerImpl::CreateWithPile(host_impl_->pending_tree(), 3, pile);
+ LayerImpl* content_layer = scoped_content_layer.get();
+ scrolling_layer->AddChild(scoped_content_layer.PassAs<LayerImpl>());
+ content_layer->SetBounds(content_layer_bounds);
+ content_layer->SetDrawsContent(true);
+
+ root->SetBounds(root_size);
+
+ gfx::Vector2d scroll_offset(100000, 0);
+ scrolling_layer->SetScrollable(true);
+ scrolling_layer->SetMaxScrollOffset(scroll_offset);
+ scrolling_layer->SetScrollOffset(scroll_offset);
+
+ host_impl_->ActivatePendingTree();
+
+ host_impl_->active_tree()->UpdateDrawProperties();
+ ASSERT_EQ(1u, host_impl_->active_tree()->RenderSurfaceLayerList().size());
+
+ LayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(host_impl_->PrepareToDraw(&frame, gfx::Rect()));
+
+ ASSERT_EQ(1u, frame.render_passes.size());
+ ASSERT_LE(1u, frame.render_passes[0]->quad_list.size());
+ const DrawQuad* quad = frame.render_passes[0]->quad_list[0];
+
+ float edge[24];
+ gfx::QuadF device_layer_quad;
+ bool antialiased =
+ GLRendererWithSetupQuadForAntialiasing::SetupQuadForAntialiasing(
+ quad->quadTransform(), quad, &device_layer_quad, edge);
+ EXPECT_FALSE(antialiased);
+
+ host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
+ host_impl_->DidDrawAllLayers(frame);
+}
+
+
class CompositorFrameMetadataTest : public LayerTreeHostImplTest {
public:
CompositorFrameMetadataTest()
@@ -6106,7 +6255,8 @@ TEST_F(CompositorFrameMetadataTest, CompositorFrameAckCountsAsSwapComplete) {
host_impl_->DidDrawAllLayers(frame);
}
CompositorFrameAck ack;
- host_impl_->OnSwapBuffersComplete(&ack);
+ host_impl_->ReclaimResources(&ack);
+ host_impl_->OnSwapBuffersComplete();
EXPECT_EQ(swap_buffers_complete_, 1);
}
@@ -6187,55 +6337,152 @@ TEST_F(LayerTreeHostImplTest,
EXPECT_EQ(host_impl_->active_tree()->root_layer(), frame.will_draw_layers[0]);
}
-TEST_F(LayerTreeHostImplTest, DeferredInitializeSmoke) {
- set_reduce_memory_result(false);
- scoped_ptr<FakeOutputSurface> output_surface(
- FakeOutputSurface::CreateDeferredGL(
- scoped_ptr<SoftwareOutputDevice>(new CountingSoftwareDevice())));
- FakeOutputSurface* output_surface_ptr = output_surface.get();
- EXPECT_TRUE(
- host_impl_->InitializeRenderer(output_surface.PassAs<OutputSurface>()));
+class LayerTreeHostImplTestDeferredInitialize : public LayerTreeHostImplTest {
+ protected:
+ virtual void SetUp() OVERRIDE {
+ LayerTreeHostImplTest::SetUp();
+
+ set_reduce_memory_result(false);
+
+ scoped_ptr<FakeOutputSurface> output_surface(
+ FakeOutputSurface::CreateDeferredGL(
+ scoped_ptr<SoftwareOutputDevice>(new CountingSoftwareDevice())));
+ output_surface_ = output_surface.get();
+
+ EXPECT_TRUE(host_impl_->InitializeRenderer(
+ output_surface.PassAs<OutputSurface>()));
+
+ scoped_ptr<SolidColorLayerImpl> root_layer =
+ SolidColorLayerImpl::Create(host_impl_->active_tree(), 1);
+ SetupRootLayerImpl(root_layer.PassAs<LayerImpl>());
+
+ onscreen_context_provider_ = TestContextProvider::Create();
+ offscreen_context_provider_ = TestContextProvider::Create();
+ }
+
+ FakeOutputSurface* output_surface_;
+ scoped_refptr<TestContextProvider> onscreen_context_provider_;
+ scoped_refptr<TestContextProvider> offscreen_context_provider_;
+};
- // Add two layers.
- scoped_ptr<SolidColorLayerImpl> root_layer =
- SolidColorLayerImpl::Create(host_impl_->active_tree(), 1);
- FakeVideoFrameProvider provider;
- scoped_ptr<VideoLayerImpl> video_layer =
- VideoLayerImpl::Create(host_impl_->active_tree(), 2, &provider);
- video_layer->SetBounds(gfx::Size(10, 10));
- video_layer->SetContentBounds(gfx::Size(10, 10));
- video_layer->SetDrawsContent(true);
- root_layer->AddChild(video_layer.PassAs<LayerImpl>());
- SetupRootLayerImpl(root_layer.PassAs<LayerImpl>());
+TEST_F(LayerTreeHostImplTestDeferredInitialize, Success) {
// Software draw.
DrawFrame();
+ EXPECT_FALSE(host_impl_->output_surface()->context_provider());
+ EXPECT_FALSE(host_impl_->offscreen_context_provider());
+
// DeferredInitialize and hardware draw.
- EXPECT_FALSE(did_try_initialize_renderer_);
- EXPECT_TRUE(output_surface_ptr->SetAndInitializeContext3D(
- scoped_ptr<WebKit::WebGraphicsContext3D>(
- TestWebGraphicsContext3D::Create())));
- EXPECT_TRUE(did_try_initialize_renderer_);
+ EXPECT_TRUE(output_surface_->InitializeAndSetContext3d(
+ onscreen_context_provider_, offscreen_context_provider_));
+ EXPECT_EQ(onscreen_context_provider_,
+ host_impl_->output_surface()->context_provider());
+ EXPECT_EQ(offscreen_context_provider_,
+ host_impl_->offscreen_context_provider());
// Defer intialized GL draw.
DrawFrame();
// Revert back to software.
- did_try_initialize_renderer_ = false;
- output_surface_ptr->ReleaseGL();
- EXPECT_TRUE(did_try_initialize_renderer_);
+ output_surface_->ReleaseGL();
+ EXPECT_FALSE(host_impl_->output_surface()->context_provider());
+ EXPECT_FALSE(host_impl_->offscreen_context_provider());
+
+ // Software draw again.
DrawFrame();
}
-class ContextThatDoesNotSupportMemoryManagmentExtensions
- : public TestWebGraphicsContext3D {
- public:
- // WebGraphicsContext3D methods.
- virtual WebKit::WebString getString(WebKit::WGC3Denum name) {
- return WebKit::WebString();
- }
-};
+TEST_F(LayerTreeHostImplTestDeferredInitialize, Fails_OnscreenContext_0) {
+ // Software draw.
+ DrawFrame();
+
+ // Fail initialization of the onscreen context before the OutputSurface binds
+ // it to the thread.
+ onscreen_context_provider_->UnboundTestContext3d()
+ ->set_times_make_current_succeeds(0);
+
+ EXPECT_FALSE(host_impl_->output_surface()->context_provider());
+ EXPECT_FALSE(host_impl_->offscreen_context_provider());
+ EXPECT_FALSE(did_lose_output_surface_);
+
+ // DeferredInitialize fails.
+ EXPECT_FALSE(output_surface_->InitializeAndSetContext3d(
+ onscreen_context_provider_, offscreen_context_provider_));
+ EXPECT_FALSE(host_impl_->output_surface()->context_provider());
+ EXPECT_FALSE(host_impl_->offscreen_context_provider());
+
+ // Software draw again.
+ DrawFrame();
+}
+
+TEST_F(LayerTreeHostImplTestDeferredInitialize, Fails_OnscreenContext_1) {
+ // Software draw.
+ DrawFrame();
+
+ // Fail initialization of the onscreen context after the OutputSurface binds
+ // it to the thread.
+ onscreen_context_provider_->UnboundTestContext3d()
+ ->set_times_make_current_succeeds(2);
+
+ EXPECT_FALSE(host_impl_->output_surface()->context_provider());
+ EXPECT_FALSE(host_impl_->offscreen_context_provider());
+ EXPECT_FALSE(did_lose_output_surface_);
+
+ // DeferredInitialize fails.
+ EXPECT_FALSE(output_surface_->InitializeAndSetContext3d(
+ onscreen_context_provider_, offscreen_context_provider_));
+ EXPECT_FALSE(host_impl_->output_surface()->context_provider());
+ EXPECT_FALSE(host_impl_->offscreen_context_provider());
+
+ // We lose the output surface.
+ EXPECT_TRUE(did_lose_output_surface_);
+}
+
+TEST_F(LayerTreeHostImplTestDeferredInitialize, Fails_OnscreenContext_2) {
+ // Software draw.
+ DrawFrame();
+
+ // Fail initialization of the onscreen context after the OutputSurface binds
+ // it to the thread and during renderer initialization.
+ onscreen_context_provider_->UnboundTestContext3d()
+ ->set_times_make_current_succeeds(1);
+
+ EXPECT_FALSE(host_impl_->output_surface()->context_provider());
+ EXPECT_FALSE(host_impl_->offscreen_context_provider());
+ EXPECT_FALSE(did_lose_output_surface_);
+
+ // DeferredInitialize fails.
+ EXPECT_FALSE(output_surface_->InitializeAndSetContext3d(
+ onscreen_context_provider_, offscreen_context_provider_));
+ EXPECT_FALSE(host_impl_->output_surface()->context_provider());
+ EXPECT_FALSE(host_impl_->offscreen_context_provider());
+
+ // We lose the output surface.
+ EXPECT_TRUE(did_lose_output_surface_);
+}
+
+TEST_F(LayerTreeHostImplTestDeferredInitialize, Fails_OffscreenContext) {
+ // Software draw.
+ DrawFrame();
+
+ // Fail initialization of the offscreen context.
+ offscreen_context_provider_->UnboundTestContext3d()
+ ->set_times_make_current_succeeds(0);
+
+ EXPECT_FALSE(host_impl_->output_surface()->context_provider());
+ EXPECT_FALSE(host_impl_->offscreen_context_provider());
+ EXPECT_FALSE(did_lose_output_surface_);
+
+ // DeferredInitialize fails.
+ EXPECT_FALSE(output_surface_->InitializeAndSetContext3d(
+ onscreen_context_provider_, offscreen_context_provider_));
+ EXPECT_FALSE(host_impl_->output_surface()->context_provider());
+ EXPECT_FALSE(host_impl_->offscreen_context_provider());
+
+ // We lose the output surface.
+ EXPECT_TRUE(did_lose_output_surface_);
+}
// Checks that we have a non-0 default allocation if we pass a context that
// doesn't support memory management extensions.
@@ -6246,10 +6493,9 @@ TEST_F(LayerTreeHostImplTest, DefaultMemoryAllocation) {
&proxy_,
&stats_instrumentation_);
- host_impl_->InitializeRenderer(FakeOutputSurface::Create3d(
- scoped_ptr<WebKit::WebGraphicsContext3D>(
- new ContextThatDoesNotSupportMemoryManagmentExtensions))
- .PassAs<OutputSurface>());
+ scoped_ptr<OutputSurface> output_surface(
+ FakeOutputSurface::Create3d(TestWebGraphicsContext3D::Create()));
+ host_impl_->InitializeRenderer(output_surface.Pass());
EXPECT_LT(0ul, host_impl_->memory_allocation_limit_bytes());
}
@@ -6276,19 +6522,44 @@ TEST_F(LayerTreeHostImplTest, MemoryPolicy) {
EXPECT_EQ(visible_cutoff_value, current_priority_cutoff_value_);
}
+class LayerTreeHostImplTestManageTiles : public LayerTreeHostImplTest {
+ public:
+ virtual void SetUp() OVERRIDE {
+ LayerTreeSettings settings;
+ settings.impl_side_painting = true;
+
+ fake_host_impl_ = new FakeLayerTreeHostImpl(settings, &proxy_);
+ host_impl_.reset(fake_host_impl_);
+ host_impl_->InitializeRenderer(CreateOutputSurface());
+ host_impl_->SetViewportSize(gfx::Size(10, 10));
+ }
+
+ FakeLayerTreeHostImpl* fake_host_impl_;
+};
+
+TEST_F(LayerTreeHostImplTestManageTiles, ManageTilesWhenInvisible) {
+ fake_host_impl_->DidModifyTilePriorities();
+ EXPECT_TRUE(fake_host_impl_->manage_tiles_needed());
+ fake_host_impl_->SetVisible(false);
+ EXPECT_FALSE(fake_host_impl_->manage_tiles_needed());
+}
+
TEST_F(LayerTreeHostImplTest, UIResourceManagement) {
scoped_ptr<TestWebGraphicsContext3D> context =
TestWebGraphicsContext3D::Create();
TestWebGraphicsContext3D* context3d = context.get();
- scoped_ptr<OutputSurface> output_surface = FakeOutputSurface::Create3d(
- context.PassAs<WebKit::WebGraphicsContext3D>()).PassAs<OutputSurface>();
+ scoped_ptr<OutputSurface> output_surface = CreateFakeOutputSurface();
host_impl_->InitializeRenderer(output_surface.Pass());
EXPECT_EQ(0u, context3d->NumTextures());
+ SkBitmap skbitmap;
+ skbitmap.setConfig(SkBitmap::kARGB_8888_Config, 1, 1);
+ skbitmap.allocPixels();
+ skbitmap.setImmutable();
+
UIResourceId ui_resource_id = 1;
- scoped_refptr<UIResourceBitmap> bitmap = UIResourceBitmap::Create(
- new uint8_t[1], UIResourceBitmap::RGBA8, gfx::Size(1, 1));
+ UIResourceBitmap bitmap(skbitmap);
host_impl_->CreateUIResource(ui_resource_id, bitmap);
EXPECT_EQ(1u, context3d->NumTextures());
ResourceProvider::ResourceId id1 =
@@ -6322,5 +6593,46 @@ TEST_F(LayerTreeHostImplTest, UIResourceManagement) {
EXPECT_EQ(0u, context3d->NumTextures());
}
+void ShutdownReleasesContext_Callback(scoped_ptr<CopyOutputResult> result) {
+}
+
+TEST_F(LayerTreeHostImplTest, ShutdownReleasesContext) {
+ scoped_refptr<TestContextProvider> context_provider =
+ TestContextProvider::Create();
+
+ host_impl_ = LayerTreeHostImpl::Create(LayerTreeSettings(),
+ this,
+ &proxy_,
+ &stats_instrumentation_);
+ host_impl_->InitializeRenderer(
+ FakeOutputSurface::Create3d(context_provider).PassAs<OutputSurface>());
+ host_impl_->SetViewportSize(gfx::Size(10, 10));
+
+ SetupRootLayerImpl(LayerImpl::Create(host_impl_->active_tree(), 1));
+
+ ScopedPtrVector<CopyOutputRequest> requests;
+ requests.push_back(CopyOutputRequest::CreateRequest(
+ base::Bind(&ShutdownReleasesContext_Callback)));
+
+ host_impl_->active_tree()->root_layer()->PassCopyRequests(&requests);
+
+ LayerTreeHostImpl::FrameData frame;
+ EXPECT_TRUE(host_impl_->PrepareToDraw(&frame, gfx::Rect()));
+ host_impl_->DrawLayers(&frame, base::TimeTicks::Now());
+ host_impl_->DidDrawAllLayers(frame);
+
+ // The CopyOutputResult's callback has a ref on the ContextProvider and a
+ // texture in a texture mailbox.
+ EXPECT_FALSE(context_provider->HasOneRef());
+ EXPECT_EQ(1u, context_provider->TestContext3d()->NumTextures());
+
+ host_impl_.reset();
+
+ // The CopyOutputResult's callback was cancelled, the CopyOutputResult
+ // released, and the texture deleted.
+ EXPECT_TRUE(context_provider->HasOneRef());
+ EXPECT_EQ(0u, context_provider->TestContext3d()->NumTextures());
+}
+
} // namespace
} // namespace cc
diff --git a/chromium/cc/trees/layer_tree_host_perftest.cc b/chromium/cc/trees/layer_tree_host_perftest.cc
index fc7673cd7ba..5ca78b64d49 100644
--- a/chromium/cc/trees/layer_tree_host_perftest.cc
+++ b/chromium/cc/trees/layer_tree_host_perftest.cc
@@ -8,14 +8,17 @@
#include "base/files/file_path.h"
#include "base/path_service.h"
#include "base/strings/string_piece.h"
+#include "base/time/time.h"
#include "cc/layers/content_layer.h"
#include "cc/layers/nine_patch_layer.h"
#include "cc/layers/solid_color_layer.h"
#include "cc/test/fake_content_layer_client.h"
+#include "cc/test/lap_timer.h"
#include "cc/test/layer_tree_json_parser.h"
#include "cc/test/layer_tree_test.h"
#include "cc/test/paths.h"
#include "cc/trees/layer_tree_impl.h"
+#include "testing/perf/perf_test.h"
namespace cc {
namespace {
@@ -27,8 +30,10 @@ static const int kTimeCheckInterval = 10;
class LayerTreeHostPerfTest : public LayerTreeTest {
public:
LayerTreeHostPerfTest()
- : num_draws_(0),
- num_commits_(0),
+ : draw_timer_(kWarmupRuns,
+ base::TimeDelta::FromMilliseconds(kTimeLimitMillis),
+ kTimeCheckInterval),
+ commit_timer_(0, base::TimeDelta(), 1),
full_damage_each_frame_(false),
animation_driven_drawing_(false),
measure_commit_cost_(false) {
@@ -47,28 +52,23 @@ class LayerTreeHostPerfTest : public LayerTreeTest {
virtual void BeginCommitOnThread(LayerTreeHostImpl* host_impl) OVERRIDE {
if (measure_commit_cost_)
- commit_start_time_ = base::TimeTicks::HighResNow();
+ commit_timer_.Start();
}
virtual void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) OVERRIDE {
- if (measure_commit_cost_ && num_draws_ >= kWarmupRuns) {
- total_commit_time_ += base::TimeTicks::HighResNow() - commit_start_time_;
- ++num_commits_;
+ if (measure_commit_cost_ && draw_timer_.IsWarmedUp()) {
+ commit_timer_.NextLap();
}
}
virtual void DrawLayersOnThread(LayerTreeHostImpl* impl) OVERRIDE {
- ++num_draws_;
- if (num_draws_ == kWarmupRuns)
- start_time_ = base::TimeTicks::HighResNow();
-
- if (!start_time_.is_null() && (num_draws_ % kTimeCheckInterval) == 0) {
- base::TimeDelta elapsed = base::TimeTicks::HighResNow() - start_time_;
- if (elapsed >= base::TimeDelta::FromMilliseconds(kTimeLimitMillis)) {
- elapsed_ = elapsed;
- EndTest();
- return;
- }
+ if (TestEnded()) {
+ return;
+ }
+ draw_timer_.NextLap();
+ if (draw_timer_.HasTimeLimitExpired()) {
+ EndTest();
+ return;
}
if (!animation_driven_drawing_)
impl->SetNeedsRedraw();
@@ -79,34 +79,29 @@ class LayerTreeHostPerfTest : public LayerTreeTest {
virtual void BuildTree() {}
virtual void AfterTest() OVERRIDE {
- num_draws_ -= kWarmupRuns;
-
- // Format matches chrome/test/perf/perf_test.h:PrintResult
- printf("*RESULT %s: frames: %d, %.2f ms/frame\n",
- test_name_.c_str(),
- num_draws_,
- elapsed_.InMillisecondsF() / num_draws_);
+ CHECK(!test_name_.empty()) << "Must SetTestName() before AfterTest().";
+ perf_test::PrintResult("layer_tree_host_frame_count", "", test_name_,
+ draw_timer_.NumLaps(), "frame_count", true);
+ perf_test::PrintResult("layer_tree_host_frame_time", "", test_name_,
+ 1000 * draw_timer_.MsPerLap(), "us", true);
if (measure_commit_cost_) {
- printf("*RESULT %s: commits: %d, %.2f ms/commit\n",
- test_name_.c_str(),
- num_commits_,
- total_commit_time_.InMillisecondsF() / num_commits_);
+ perf_test::PrintResult("layer_tree_host_commit_count", "", test_name_,
+ commit_timer_.NumLaps(), "commit_count", true);
+ perf_test::PrintResult("layer_tree_host_commit_time", "", test_name_,
+ 1000 * commit_timer_.MsPerLap(), "us", true);
}
}
protected:
- base::TimeTicks start_time_;
- int num_draws_;
- int num_commits_;
+ LapTimer draw_timer_;
+ LapTimer commit_timer_;
+
std::string test_name_;
- base::TimeDelta elapsed_;
FakeContentLayerClient fake_content_layer_client_;
bool full_damage_each_frame_;
bool animation_driven_drawing_;
bool measure_commit_cost_;
- base::TimeTicks commit_start_time_;
- base::TimeDelta total_commit_time_;
};
@@ -116,12 +111,15 @@ class LayerTreeHostPerfTestJsonReader : public LayerTreeHostPerfTest {
: LayerTreeHostPerfTest() {
}
- void ReadTestFile(std::string name) {
+ void SetTestName(const std::string& name) {
test_name_ = name;
+ }
+
+ void ReadTestFile(const std::string& name) {
base::FilePath test_data_dir;
ASSERT_TRUE(PathService::Get(cc::DIR_TEST_DATA, &test_data_dir));
base::FilePath json_file = test_data_dir.AppendASCII(name + ".json");
- ASSERT_TRUE(file_util::ReadFileToString(json_file, &json_));
+ ASSERT_TRUE(base::ReadFileToString(json_file, &json_));
}
virtual void BuildTree() OVERRIDE {
@@ -139,6 +137,7 @@ class LayerTreeHostPerfTestJsonReader : public LayerTreeHostPerfTest {
// Simulates a tab switcher scene with two stacks of 10 tabs each.
TEST_F(LayerTreeHostPerfTestJsonReader, TenTenSingleThread) {
+ SetTestName("10_10_single_thread");
ReadTestFile("10_10_layer_tree");
RunTest(false, false, false);
}
@@ -147,6 +146,7 @@ TEST_F(LayerTreeHostPerfTestJsonReader, TenTenSingleThread) {
TEST_F(LayerTreeHostPerfTestJsonReader,
TenTenSingleThread_FullDamageEachFrame) {
full_damage_each_frame_ = true;
+ SetTestName("10_10_single_thread_full_damage_each_frame");
ReadTestFile("10_10_layer_tree");
RunTest(false, false, false);
}
@@ -180,6 +180,7 @@ class LayerTreeHostPerfTestLeafInvalidates
// Simulates a tab switcher scene with two stacks of 10 tabs each. Invalidate a
// property on a leaf layer in the tree every commit.
TEST_F(LayerTreeHostPerfTestLeafInvalidates, TenTenSingleThread) {
+ SetTestName("10_10_single_thread_leaf_invalidates");
ReadTestFile("10_10_layer_tree");
RunTest(false, false, false);
}
@@ -207,6 +208,7 @@ class ScrollingLayerTreePerfTest : public LayerTreeHostPerfTestJsonReader {
};
TEST_F(ScrollingLayerTreePerfTest, LongScrollablePage) {
+ SetTestName("long_scrollable_page");
ReadTestFile("long_scrollable_page");
RunTest(false, false, false);
}
@@ -221,6 +223,7 @@ class ImplSidePaintingPerfTest : public LayerTreeHostPerfTestJsonReader {
TEST_F(ImplSidePaintingPerfTest, HeavyPage) {
animation_driven_drawing_ = true;
measure_commit_cost_ = true;
+ SetTestName("heavy_page");
ReadTestFile("heavy_layer_tree");
RunTestWithImplSidePainting();
}
@@ -284,6 +287,7 @@ class PageScaleImplSidePaintingPerfTest : public ImplSidePaintingPerfTest {
TEST_F(PageScaleImplSidePaintingPerfTest, HeavyPage) {
measure_commit_cost_ = true;
+ SetTestName("heavy_page_page_scale");
ReadTestFile("heavy_layer_tree");
RunTestWithImplSidePainting();
}
diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_filters.cc b/chromium/cc/trees/layer_tree_host_pixeltest_filters.cc
index 553e66e8dba..7068b4b1047 100644
--- a/chromium/cc/trees/layer_tree_host_pixeltest_filters.cc
+++ b/chromium/cc/trees/layer_tree_host_pixeltest_filters.cc
@@ -6,6 +6,8 @@
#include "cc/layers/solid_color_layer.h"
#include "cc/test/layer_tree_pixel_test.h"
#include "cc/test/pixel_comparator.h"
+#include "third_party/skia/include/effects/SkColorFilterImageFilter.h"
+#include "third_party/skia/include/effects/SkColorMatrixFilter.h"
#if !defined(OS_ANDROID)
@@ -52,7 +54,7 @@ TEST_F(LayerTreeHostFiltersPixelTest, BackgroundFilterBlur) {
base::FilePath(FILE_PATH_LITERAL("background_filter_blur.png")));
}
-TEST_F(LayerTreeHostFiltersPixelTest, DISABLED_BackgroundFilterBlurOutsets) {
+TEST_F(LayerTreeHostFiltersPixelTest, BackgroundFilterBlurOutsets) {
scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(
gfx::Rect(200, 200), SK_ColorWHITE);
@@ -73,8 +75,8 @@ TEST_F(LayerTreeHostFiltersPixelTest, DISABLED_BackgroundFilterBlurOutsets) {
blur->SetBackgroundFilters(filters);
#if defined(OS_WIN)
- // Windows has 2250 pixels off by at most 2: crbug.com/259922
- float percentage_pixels_large_error = 5.625f; // 2250px / (200*200)
+ // Windows has 2596 pixels off by at most 2: crbug.com/259922
+ float percentage_pixels_large_error = 6.5f; // 2596px / (200*200), rounded up
float percentage_pixels_small_error = 0.0f;
float average_error_allowed_in_bad_pixels = 1.f;
int large_error_allowed = 2;
@@ -148,6 +150,51 @@ TEST_F(LayerTreeHostFiltersPixelTest, BackgroundFilterBlurOffAxis) {
"background_filter_blur_off_axis.png")));
}
+TEST_F(LayerTreeHostFiltersPixelTest, ImageFilterClipped) {
+ scoped_refptr<SolidColorLayer> root = CreateSolidColorLayer(
+ gfx::Rect(200, 200), SK_ColorBLACK);
+
+ scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(
+ gfx::Rect(200, 200), SK_ColorYELLOW);
+ root->AddChild(background);
+
+ scoped_refptr<SolidColorLayer> foreground = CreateSolidColorLayer(
+ gfx::Rect(200, 200), SK_ColorRED);
+ background->AddChild(foreground);
+
+ SkScalar matrix[20];
+ memset(matrix, 0, 20 * sizeof(matrix[0]));
+ // This filter does a red-blue swap, so the foreground becomes blue.
+ matrix[2] = matrix[6] = matrix[10] = matrix[18] = SK_Scalar1;
+ skia::RefPtr<SkColorFilter> colorFilter(skia::AdoptRef(
+ new SkColorMatrixFilter(matrix)));
+ // We filter only the bottom 200x100 pixels of the foreground.
+ SkIRect cropRect = SkIRect::MakeXYWH(0, 100, 200, 100);
+ skia::RefPtr<SkImageFilter> filter =
+ skia::AdoptRef(SkColorFilterImageFilter::Create(
+ colorFilter.get(),
+ NULL,
+ &cropRect));
+
+ // Make the foreground layer's render surface be clipped by the background
+ // layer.
+ background->SetMasksToBounds(true);
+ foreground->SetFilter(filter);
+
+ // Then we translate the foreground up by 100 pixels in Y, so the cropped
+ // region is moved to to the top. This ensures that the crop rect is being
+ // correctly transformed in skia by the amount of clipping that the
+ // compositor performs.
+ gfx::Transform transform;
+ transform.Translate(0.0, -100.0);
+ foreground->SetTransform(transform);
+
+ RunPixelTest(GL_WITH_BITMAP,
+ background,
+ base::FilePath(FILE_PATH_LITERAL(
+ "blue_yellow.png")));
+}
+
} // namespace
} // namespace cc
diff --git a/chromium/cc/trees/layer_tree_host_pixeltest_readback.cc b/chromium/cc/trees/layer_tree_host_pixeltest_readback.cc
index a9bc6ceff1b..d8e286183f5 100644
--- a/chromium/cc/trees/layer_tree_host_pixeltest_readback.cc
+++ b/chromium/cc/trees/layer_tree_host_pixeltest_readback.cc
@@ -58,13 +58,15 @@ class LayerTreeHostReadbackPixelTest : public LayerTreePixelTest {
EXPECT_TRUE(proxy()->IsMainThread());
EXPECT_TRUE(result->HasTexture());
- scoped_ptr<TextureMailbox> texture_mailbox = result->TakeTexture().Pass();
- EXPECT_TRUE(texture_mailbox->IsValid());
- EXPECT_TRUE(texture_mailbox->IsTexture());
+ TextureMailbox texture_mailbox;
+ scoped_ptr<SingleReleaseCallback> release_callback;
+ result->TakeTexture(&texture_mailbox, &release_callback);
+ EXPECT_TRUE(texture_mailbox.IsValid());
+ EXPECT_TRUE(texture_mailbox.IsTexture());
scoped_ptr<SkBitmap> bitmap =
- CopyTextureMailboxToBitmap(result->size(), *texture_mailbox);
- texture_mailbox->RunReleaseCallback(0, false);
+ CopyTextureMailboxToBitmap(result->size(), texture_mailbox);
+ release_callback->Run(0, false);
ReadbackResultAsBitmap(CopyOutputResult::CreateBitmapResult(bitmap.Pass()));
}
@@ -333,6 +335,156 @@ TEST_F(LayerTreeHostReadbackPixelTest, ReadbackSmallNonRootLayerWithChild_GL) {
"green_small_with_blue_corner.png")));
}
+TEST_F(LayerTreeHostReadbackPixelTest,
+ ReadbackSubtreeSurroundsTargetLayer_Software) {
+ scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(
+ gfx::Rect(0, 0, 200, 200), SK_ColorWHITE);
+
+ scoped_refptr<SolidColorLayer> target = CreateSolidColorLayer(
+ gfx::Rect(100, 100, 100, 100), SK_ColorRED);
+ background->AddChild(target);
+
+ scoped_refptr<SolidColorLayer> green = CreateSolidColorLayer(
+ gfx::Rect(-100, -100, 300, 300), SK_ColorGREEN);
+ target->AddChild(green);
+
+ scoped_refptr<SolidColorLayer> blue = CreateSolidColorLayer(
+ gfx::Rect(50, 50, 50, 50), SK_ColorBLUE);
+ target->AddChild(blue);
+
+ copy_subrect_ = gfx::Rect(0, 0, 100, 100);
+ RunPixelTestWithReadbackTarget(SOFTWARE_WITH_DEFAULT,
+ background,
+ target.get(),
+ base::FilePath(FILE_PATH_LITERAL(
+ "green_small_with_blue_corner.png")));
+}
+
+TEST_F(LayerTreeHostReadbackPixelTest,
+ ReadbackSubtreeSurroundsLayer_GL_Bitmap) {
+ scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(
+ gfx::Rect(0, 0, 200, 200), SK_ColorWHITE);
+
+ scoped_refptr<SolidColorLayer> target = CreateSolidColorLayer(
+ gfx::Rect(100, 100, 100, 100), SK_ColorRED);
+ background->AddChild(target);
+
+ scoped_refptr<SolidColorLayer> green = CreateSolidColorLayer(
+ gfx::Rect(-100, -100, 300, 300), SK_ColorGREEN);
+ target->AddChild(green);
+
+ scoped_refptr<SolidColorLayer> blue = CreateSolidColorLayer(
+ gfx::Rect(50, 50, 50, 50), SK_ColorBLUE);
+ target->AddChild(blue);
+
+ copy_subrect_ = gfx::Rect(0, 0, 100, 100);
+ RunPixelTestWithReadbackTarget(GL_WITH_BITMAP,
+ background,
+ target.get(),
+ base::FilePath(FILE_PATH_LITERAL(
+ "green_small_with_blue_corner.png")));
+}
+
+TEST_F(LayerTreeHostReadbackPixelTest,
+ ReadbackSubtreeSurroundsTargetLayer_GL) {
+ scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(
+ gfx::Rect(0, 0, 200, 200), SK_ColorWHITE);
+
+ scoped_refptr<SolidColorLayer> target = CreateSolidColorLayer(
+ gfx::Rect(100, 100, 100, 100), SK_ColorRED);
+ background->AddChild(target);
+
+ scoped_refptr<SolidColorLayer> green = CreateSolidColorLayer(
+ gfx::Rect(-100, -100, 300, 300), SK_ColorGREEN);
+ target->AddChild(green);
+
+ scoped_refptr<SolidColorLayer> blue = CreateSolidColorLayer(
+ gfx::Rect(50, 50, 50, 50), SK_ColorBLUE);
+ target->AddChild(blue);
+
+ copy_subrect_ = gfx::Rect(0, 0, 100, 100);
+ RunPixelTestWithReadbackTarget(GL_WITH_DEFAULT,
+ background,
+ target.get(),
+ base::FilePath(FILE_PATH_LITERAL(
+ "green_small_with_blue_corner.png")));
+}
+
+TEST_F(LayerTreeHostReadbackPixelTest,
+ ReadbackSubtreeExtendsBeyondTargetLayer_Software) {
+ scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(
+ gfx::Rect(0, 0, 200, 200), SK_ColorWHITE);
+
+ scoped_refptr<SolidColorLayer> target = CreateSolidColorLayer(
+ gfx::Rect(50, 50, 150, 150), SK_ColorRED);
+ background->AddChild(target);
+
+ scoped_refptr<SolidColorLayer> green = CreateSolidColorLayer(
+ gfx::Rect(50, 50, 200, 200), SK_ColorGREEN);
+ target->AddChild(green);
+
+ scoped_refptr<SolidColorLayer> blue = CreateSolidColorLayer(
+ gfx::Rect(100, 100, 50, 50), SK_ColorBLUE);
+ target->AddChild(blue);
+
+ copy_subrect_ = gfx::Rect(50, 50, 100, 100);
+ RunPixelTestWithReadbackTarget(SOFTWARE_WITH_DEFAULT,
+ background,
+ target.get(),
+ base::FilePath(FILE_PATH_LITERAL(
+ "green_small_with_blue_corner.png")));
+}
+
+TEST_F(LayerTreeHostReadbackPixelTest,
+ ReadbackSubtreeExtendsBeyondTargetLayer_GL_Bitmap) {
+ scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(
+ gfx::Rect(0, 0, 200, 200), SK_ColorWHITE);
+
+ scoped_refptr<SolidColorLayer> target = CreateSolidColorLayer(
+ gfx::Rect(50, 50, 150, 150), SK_ColorRED);
+ background->AddChild(target);
+
+ scoped_refptr<SolidColorLayer> green = CreateSolidColorLayer(
+ gfx::Rect(50, 50, 200, 200), SK_ColorGREEN);
+ target->AddChild(green);
+
+ scoped_refptr<SolidColorLayer> blue = CreateSolidColorLayer(
+ gfx::Rect(100, 100, 50, 50), SK_ColorBLUE);
+ target->AddChild(blue);
+
+ copy_subrect_ = gfx::Rect(50, 50, 100, 100);
+ RunPixelTestWithReadbackTarget(GL_WITH_BITMAP,
+ background,
+ target.get(),
+ base::FilePath(FILE_PATH_LITERAL(
+ "green_small_with_blue_corner.png")));
+}
+
+TEST_F(LayerTreeHostReadbackPixelTest,
+ ReadbackSubtreeExtendsBeyondTargetLayer_GL) {
+ scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(
+ gfx::Rect(0, 0, 200, 200), SK_ColorWHITE);
+
+ scoped_refptr<SolidColorLayer> target = CreateSolidColorLayer(
+ gfx::Rect(50, 50, 150, 150), SK_ColorRED);
+ background->AddChild(target);
+
+ scoped_refptr<SolidColorLayer> green = CreateSolidColorLayer(
+ gfx::Rect(50, 50, 200, 200), SK_ColorGREEN);
+ target->AddChild(green);
+
+ scoped_refptr<SolidColorLayer> blue = CreateSolidColorLayer(
+ gfx::Rect(100, 100, 50, 50), SK_ColorBLUE);
+ target->AddChild(blue);
+
+ copy_subrect_ = gfx::Rect(50, 50, 100, 100);
+ RunPixelTestWithReadbackTarget(GL_WITH_DEFAULT,
+ background,
+ target.get(),
+ base::FilePath(FILE_PATH_LITERAL(
+ "green_small_with_blue_corner.png")));
+}
+
TEST_F(LayerTreeHostReadbackPixelTest, ReadbackSubrect_Software) {
scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer(
gfx::Rect(200, 200), SK_ColorWHITE);
@@ -852,7 +1004,7 @@ TEST_F(LayerTreeHostReadbackPixelTest,
bitmap.allocPixels();
bitmap.eraseColor(SK_ColorGREEN);
{
- SkDevice device(bitmap);
+ SkBitmapDevice device(bitmap);
skia::RefPtr<SkCanvas> canvas = skia::AdoptRef(new SkCanvas(&device));
SkPaint paint;
paint.setStyle(SkPaint::kFill_Style);
diff --git a/chromium/cc/trees/layer_tree_host_unittest.cc b/chromium/cc/trees/layer_tree_host_unittest.cc
index b18a88a7865..92cba325587 100644
--- a/chromium/cc/trees/layer_tree_host_unittest.cc
+++ b/chromium/cc/trees/layer_tree_host_unittest.cc
@@ -10,12 +10,13 @@
#include "base/synchronization/lock.h"
#include "cc/animation/timing_function.h"
#include "cc/debug/frame_rate_counter.h"
+#include "cc/debug/test_web_graphics_context_3d.h"
#include "cc/layers/content_layer.h"
#include "cc/layers/content_layer_client.h"
#include "cc/layers/io_surface_layer.h"
#include "cc/layers/layer_impl.h"
+#include "cc/layers/painted_scrollbar_layer.h"
#include "cc/layers/picture_layer.h"
-#include "cc/layers/scrollbar_layer.h"
#include "cc/layers/solid_color_layer.h"
#include "cc/layers/video_layer.h"
#include "cc/output/begin_frame_args.h"
@@ -30,11 +31,11 @@
#include "cc/test/fake_content_layer_client.h"
#include "cc/test/fake_layer_tree_host_client.h"
#include "cc/test/fake_output_surface.h"
+#include "cc/test/fake_painted_scrollbar_layer.h"
#include "cc/test/fake_picture_layer.h"
#include "cc/test/fake_picture_layer_impl.h"
#include "cc/test/fake_proxy.h"
#include "cc/test/fake_scoped_ui_resource.h"
-#include "cc/test/fake_scrollbar_layer.h"
#include "cc/test/fake_video_frame_provider.h"
#include "cc/test/geometry_test_utils.h"
#include "cc/test/layer_tree_test.h"
@@ -393,9 +394,8 @@ class LayerTreeHostTestNoExtraCommitFromScrollbarInvalidate
bool paint_scrollbar = true;
bool has_thumb = false;
- scrollbar_ = FakeScrollbarLayer::Create(paint_scrollbar,
- has_thumb,
- root_layer_->id());
+ scrollbar_ = FakePaintedScrollbarLayer::Create(
+ paint_scrollbar, has_thumb, root_layer_->id());
scrollbar_->SetPosition(gfx::Point(0, 10));
scrollbar_->SetBounds(gfx::Size(10, 10));
@@ -436,7 +436,7 @@ class LayerTreeHostTestNoExtraCommitFromScrollbarInvalidate
private:
FakeContentLayerClient client_;
scoped_refptr<Layer> root_layer_;
- scoped_refptr<FakeScrollbarLayer> scrollbar_;
+ scoped_refptr<FakePaintedScrollbarLayer> scrollbar_;
};
SINGLE_AND_MULTI_THREAD_TEST_F(
@@ -503,6 +503,139 @@ class LayerTreeHostTestCompositeAndReadbackBeforePreviousCommitDraws
MULTI_THREAD_TEST_F(
LayerTreeHostTestCompositeAndReadbackBeforePreviousCommitDraws);
+class LayerTreeHostTestCompositeAndReadbackDuringForcedDraw
+ : public LayerTreeHostTest {
+ protected:
+ static const int kFirstCommitSourceFrameNumber = 0;
+ static const int kReadbackSourceFrameNumber = 1;
+ static const int kReadbackReplacementAndForcedDrawSourceFrameNumber = 2;
+
+ LayerTreeHostTestCompositeAndReadbackDuringForcedDraw()
+ : did_post_readback_(false) {}
+
+ virtual void InitializeSettings(LayerTreeSettings* settings) OVERRIDE {
+ // This enables forced draws after a single prepare to draw failure.
+ settings->timeout_and_draw_when_animation_checkerboards = true;
+ settings->maximum_number_of_failed_draws_before_draw_is_forced_ = 1;
+ }
+
+ virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); }
+
+ virtual bool PrepareToDrawOnThread(LayerTreeHostImpl* host_impl,
+ LayerTreeHostImpl::FrameData* frame_data,
+ bool result) OVERRIDE {
+ int sfn = host_impl->active_tree()->source_frame_number();
+ EXPECT_TRUE(sfn == kFirstCommitSourceFrameNumber ||
+ sfn == kReadbackSourceFrameNumber ||
+ sfn == kReadbackReplacementAndForcedDrawSourceFrameNumber)
+ << sfn;
+
+ // Before we react to the failed draw by initiating the forced draw
+ // sequence, start a readback on the main thread.
+ if (sfn == kFirstCommitSourceFrameNumber && !did_post_readback_) {
+ did_post_readback_ = true;
+ PostReadbackToMainThread();
+ }
+
+ // Returning false will result in a forced draw.
+ return false;
+ }
+
+ virtual void DrawLayersOnThread(LayerTreeHostImpl* host_impl) OVERRIDE {
+ // We should only draw for the readback and the forced draw.
+ int sfn = host_impl->active_tree()->source_frame_number();
+ EXPECT_TRUE(sfn == kReadbackSourceFrameNumber ||
+ sfn == kReadbackReplacementAndForcedDrawSourceFrameNumber)
+ << sfn;
+ }
+
+ virtual void SwapBuffersOnThread(LayerTreeHostImpl* host_impl,
+ bool result) OVERRIDE {
+ // We should only swap for the forced draw.
+ int sfn = host_impl->active_tree()->source_frame_number();
+ EXPECT_TRUE(sfn == kReadbackReplacementAndForcedDrawSourceFrameNumber)
+ << sfn;
+ EndTest();
+ }
+
+ virtual void AfterTest() OVERRIDE {}
+
+ bool did_post_readback_;
+};
+
+MULTI_THREAD_TEST_F(LayerTreeHostTestCompositeAndReadbackDuringForcedDraw);
+
+class LayerTreeHostTestCompositeAndReadbackAfterForcedDraw
+ : public LayerTreeHostTest {
+ protected:
+ static const int kFirstCommitSourceFrameNumber = 0;
+ static const int kForcedDrawSourceFrameNumber = 1;
+ static const int kReadbackSourceFrameNumber = 2;
+ static const int kReadbackReplacementSourceFrameNumber = 3;
+
+ virtual void InitializeSettings(LayerTreeSettings* settings) OVERRIDE {
+ // This enables forced draws after a single prepare to draw failure.
+ settings->timeout_and_draw_when_animation_checkerboards = true;
+ settings->maximum_number_of_failed_draws_before_draw_is_forced_ = 1;
+ }
+
+ virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); }
+
+ virtual bool PrepareToDrawOnThread(LayerTreeHostImpl* host_impl,
+ LayerTreeHostImpl::FrameData* frame_data,
+ bool result) OVERRIDE {
+ int sfn = host_impl->active_tree()->source_frame_number();
+ EXPECT_TRUE(sfn == kFirstCommitSourceFrameNumber ||
+ sfn == kForcedDrawSourceFrameNumber ||
+ sfn == kReadbackSourceFrameNumber ||
+ sfn == kReadbackReplacementSourceFrameNumber)
+ << sfn;
+
+ // Returning false will result in a forced draw.
+ return false;
+ }
+
+ virtual void DidCommit() OVERRIDE {
+ if (layer_tree_host()->source_frame_number() ==
+ kForcedDrawSourceFrameNumber) {
+ // Avoid aborting the forced draw commit so source_frame_number
+ // increments.
+ layer_tree_host()->SetNeedsCommit();
+ } else if (layer_tree_host()->source_frame_number() ==
+ kReadbackSourceFrameNumber) {
+ // Perform a readback immediately after the forced draw's commit.
+ char pixels[4];
+ layer_tree_host()->CompositeAndReadback(&pixels, gfx::Rect(0, 0, 1, 1));
+ }
+ }
+
+ virtual void DrawLayersOnThread(LayerTreeHostImpl* host_impl) OVERRIDE {
+ // We should only draw for the the forced draw, readback, and
+ // replacement commit.
+ int sfn = host_impl->active_tree()->source_frame_number();
+ EXPECT_TRUE(sfn == kForcedDrawSourceFrameNumber ||
+ sfn == kReadbackSourceFrameNumber ||
+ sfn == kReadbackReplacementSourceFrameNumber)
+ << sfn;
+ }
+
+ virtual void SwapBuffersOnThread(LayerTreeHostImpl* host_impl,
+ bool result) OVERRIDE {
+ // We should only swap for the forced draw and replacement commit.
+ int sfn = host_impl->active_tree()->source_frame_number();
+ EXPECT_TRUE(sfn == kForcedDrawSourceFrameNumber ||
+ sfn == kReadbackReplacementSourceFrameNumber)
+ << sfn;
+
+ if (sfn == kReadbackReplacementSourceFrameNumber)
+ EndTest();
+ }
+
+ virtual void AfterTest() OVERRIDE {}
+};
+
+MULTI_THREAD_TEST_F(LayerTreeHostTestCompositeAndReadbackAfterForcedDraw);
+
// If the layerTreeHost says it can't draw, Then we should not try to draw.
class LayerTreeHostTestCanDrawBlocksDrawing : public LayerTreeHostTest {
public:
@@ -664,8 +797,6 @@ class LayerTreeHostTestAbortFrameWhenInvisible : public LayerTreeHostTest {
}
virtual void AfterTest() OVERRIDE {}
-
- private:
};
MULTI_THREAD_TEST_F(LayerTreeHostTestAbortFrameWhenInvisible);
@@ -684,7 +815,7 @@ class LayerTreeHostTestCommit : public LayerTreeHostTest {
}
virtual void DidActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE {
- EXPECT_EQ(gfx::Size(20, 20), impl->device_viewport_size());
+ EXPECT_EQ(gfx::Size(20, 20), impl->DrawViewportSize());
EXPECT_EQ(SK_ColorGRAY, impl->active_tree()->background_color());
EndTest();
@@ -701,7 +832,8 @@ MULTI_THREAD_TEST_F(LayerTreeHostTestCommit);
class LayerTreeHostTestFrameTimeUpdatesAfterActivationFails
: public LayerTreeHostTest {
public:
- LayerTreeHostTestFrameTimeUpdatesAfterActivationFails() : frame_(0) {}
+ LayerTreeHostTestFrameTimeUpdatesAfterActivationFails()
+ : frame_count_with_pending_tree_(0) {}
virtual void BeginTest() OVERRIDE {
layer_tree_host()->SetViewportSize(gfx::Size(20, 20));
@@ -710,9 +842,18 @@ class LayerTreeHostTestFrameTimeUpdatesAfterActivationFails
PostSetNeedsCommitToMainThread();
}
+ virtual void WillBeginImplFrameOnThread(LayerTreeHostImpl* host_impl,
+ const BeginFrameArgs& args) OVERRIDE {
+ if (host_impl->pending_tree())
+ frame_count_with_pending_tree_++;
+ host_impl->BlockNotifyReadyToActivateForTesting(
+ frame_count_with_pending_tree_ <= 1);
+ }
+
virtual void DrawLayersOnThread(LayerTreeHostImpl* impl) OVERRIDE {
- if (frame_ >= 1) {
- EXPECT_NE(first_frame_time_, impl->CurrentFrameTimeTicks());
+ if (frame_count_with_pending_tree_ > 1) {
+ EXPECT_NE(first_frame_time_.ToInternalValue(),
+ impl->CurrentFrameTimeTicks().ToInternalValue());
EndTest();
return;
}
@@ -720,35 +861,15 @@ class LayerTreeHostTestFrameTimeUpdatesAfterActivationFails
EXPECT_FALSE(impl->settings().impl_side_painting);
EndTest();
}
-
- virtual bool CanActivatePendingTree(LayerTreeHostImpl* impl) OVERRIDE {
- if (frame_ >= 1)
- return true;
-
- return false;
- }
-
- virtual bool CanActivatePendingTreeIfNeeded(LayerTreeHostImpl* impl)
- OVERRIDE {
- frame_++;
- if (frame_ == 1) {
- first_frame_time_ = impl->CurrentFrameTimeTicks();
-
- // Since base::TimeTicks::Now() uses a low-resolution clock on
- // Windows, we need to make sure that the clock has incremented past
- // first_frame_time_.
- while (first_frame_time_ == base::TimeTicks::Now()) {}
-
- return false;
- }
-
- return true;
+ virtual void DidActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE {
+ if (impl->settings().impl_side_painting)
+ EXPECT_NE(frame_count_with_pending_tree_, 1);
}
virtual void AfterTest() OVERRIDE {}
private:
- int frame_;
+ int frame_count_with_pending_tree_;
base::TimeTicks first_frame_time_;
};
@@ -806,7 +927,8 @@ SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestFrameTimeUpdatesAfterDraw);
// Verifies that StartPageScaleAnimation events propagate correctly
// from LayerTreeHost to LayerTreeHostImpl in the MT compositor.
-class LayerTreeHostTestStartPageScaleAnimation : public LayerTreeHostTest {
+class LayerTreeHostTestStartPageScaleAnimation
+ : public LayerTreeHostTest {
public:
LayerTreeHostTestStartPageScaleAnimation() {}
@@ -1044,7 +1166,7 @@ class LayerTreeHostTestDeviceScaleFactorScalesViewportAndLayers
ASSERT_EQ(1u, impl->active_tree()->root_layer()->children().size());
// Device viewport is scaled.
- EXPECT_EQ(gfx::Size(60, 60), impl->device_viewport_size());
+ EXPECT_EQ(gfx::Size(60, 60), impl->DrawViewportSize());
LayerImpl* root = impl->active_tree()->root_layer();
LayerImpl* child = impl->active_tree()->root_layer()->children()[0];
@@ -1111,13 +1233,13 @@ class LayerTreeHostTestDeviceScaleFactorScalesViewportAndLayers
MULTI_THREAD_TEST_F(LayerTreeHostTestDeviceScaleFactorScalesViewportAndLayers);
// Verify atomicity of commits and reuse of textures.
-class LayerTreeHostTestAtomicCommit : public LayerTreeHostTest {
+class LayerTreeHostTestDirectRendererAtomicCommit : public LayerTreeHostTest {
public:
virtual void InitializeSettings(LayerTreeSettings* settings) OVERRIDE {
// Make sure partial texture updates are turned off.
settings->max_partial_texture_updates = 0;
// Linear fade animator prevents scrollbars from drawing immediately.
- settings->use_linear_fade_scrollbar_animator = false;
+ settings->scrollbar_animator = LayerTreeSettings::NoAnimator;
}
virtual void SetupTree() OVERRIDE {
@@ -1126,8 +1248,8 @@ class LayerTreeHostTestAtomicCommit : public LayerTreeHostTest {
bool paint_scrollbar = true;
bool has_thumb = false;
- scrollbar_ =
- FakeScrollbarLayer::Create(paint_scrollbar, has_thumb, layer_->id());
+ scrollbar_ = FakePaintedScrollbarLayer::Create(
+ paint_scrollbar, has_thumb, layer_->id());
scrollbar_->SetPosition(gfx::Point(0, 10));
scrollbar_->SetBounds(gfx::Size(10, 10));
@@ -1146,7 +1268,7 @@ class LayerTreeHostTestAtomicCommit : public LayerTreeHostTest {
ASSERT_EQ(0u, layer_tree_host()->settings().max_partial_texture_updates);
TestWebGraphicsContext3D* context = static_cast<TestWebGraphicsContext3D*>(
- impl->output_surface()->context3d());
+ impl->output_surface()->context_provider()->Context3d());
switch (impl->active_tree()->source_frame_number()) {
case 0:
@@ -1162,19 +1284,18 @@ class LayerTreeHostTestAtomicCommit : public LayerTreeHostTest {
PostSetNeedsCommitToMainThread();
break;
case 1:
- // Number of textures should be doubled as the first textures
- // are used by impl thread and cannot by used for update.
- ASSERT_EQ(4u, context->NumTextures());
- // Number of textures used for commit should still be
- // one for each layer.
+ // Number of textures should be one for scrollbar layer since it was
+ // requested and deleted on the impl-thread, and double for the content
+ // layer since its first texture is used by impl thread and cannot by
+ // used for update.
+ ASSERT_EQ(3u, context->NumTextures());
+ // Number of textures used for commit should be one for each layer.
EXPECT_EQ(2u, context->NumUsedTextures());
// First textures should not have been used.
EXPECT_FALSE(context->UsedTexture(context->TextureAt(0)));
- EXPECT_FALSE(context->UsedTexture(context->TextureAt(1)));
+ EXPECT_TRUE(context->UsedTexture(context->TextureAt(1)));
// New textures should have been used.
EXPECT_TRUE(context->UsedTexture(context->TextureAt(2)));
- EXPECT_TRUE(context->UsedTexture(context->TextureAt(3)));
-
context->ResetUsedTextures();
PostSetNeedsCommitToMainThread();
break;
@@ -1189,7 +1310,7 @@ class LayerTreeHostTestAtomicCommit : public LayerTreeHostTest {
virtual void DrawLayersOnThread(LayerTreeHostImpl* impl) OVERRIDE {
TestWebGraphicsContext3D* context = static_cast<TestWebGraphicsContext3D*>(
- impl->output_surface()->context3d());
+ impl->output_surface()->context_provider()->Context3d());
if (drew_frame_ == impl->active_tree()->source_frame_number()) {
EXPECT_EQ(0u, context->NumUsedTextures()) << "For frame " << drew_frame_;
@@ -1209,14 +1330,68 @@ class LayerTreeHostTestAtomicCommit : public LayerTreeHostTest {
virtual void AfterTest() OVERRIDE {}
- private:
+ protected:
FakeContentLayerClient client_;
scoped_refptr<FakeContentLayer> layer_;
- scoped_refptr<FakeScrollbarLayer> scrollbar_;
+ scoped_refptr<FakePaintedScrollbarLayer> scrollbar_;
int drew_frame_;
};
-MULTI_THREAD_TEST_F(LayerTreeHostTestAtomicCommit);
+MULTI_THREAD_DIRECT_RENDERER_TEST_F(
+ LayerTreeHostTestDirectRendererAtomicCommit);
+
+class LayerTreeHostTestDelegatingRendererAtomicCommit
+ : public LayerTreeHostTestDirectRendererAtomicCommit {
+ public:
+ virtual void DidActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE {
+ ASSERT_EQ(0u, layer_tree_host()->settings().max_partial_texture_updates);
+
+ TestWebGraphicsContext3D* context = static_cast<TestWebGraphicsContext3D*>(
+ impl->output_surface()->context_provider()->Context3d());
+
+ switch (impl->active_tree()->source_frame_number()) {
+ case 0:
+ // Number of textures should be one for each layer
+ ASSERT_EQ(2u, context->NumTextures());
+ // Number of textures used for commit should be one for each layer.
+ EXPECT_EQ(2u, context->NumUsedTextures());
+ // Verify that used texture is correct.
+ EXPECT_TRUE(context->UsedTexture(context->TextureAt(0)));
+ EXPECT_TRUE(context->UsedTexture(context->TextureAt(1)));
+ context->ResetUsedTextures();
+ PostSetNeedsCommitToMainThread();
+ break;
+ case 1:
+ // Number of textures should be doubled as the first context layer
+ // texture is being used by the impl-thread and cannot be used for
+ // update. The scrollbar behavior is different direct renderer because
+ // UI resource deletion with delegating renderer occurs after tree
+ // activation.
+ ASSERT_EQ(4u, context->NumTextures());
+ // Number of textures used for commit should still be
+ // one for each layer.
+ EXPECT_EQ(2u, context->NumUsedTextures());
+ // First textures should not have been used.
+ EXPECT_FALSE(context->UsedTexture(context->TextureAt(0)));
+ EXPECT_FALSE(context->UsedTexture(context->TextureAt(1)));
+ // New textures should have been used.
+ EXPECT_TRUE(context->UsedTexture(context->TextureAt(2)));
+ EXPECT_TRUE(context->UsedTexture(context->TextureAt(3)));
+ context->ResetUsedTextures();
+ PostSetNeedsCommitToMainThread();
+ break;
+ case 2:
+ EndTest();
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+ }
+};
+
+MULTI_THREAD_DELEGATING_RENDERER_TEST_F(
+ LayerTreeHostTestDelegatingRendererAtomicCommit);
static void SetLayerPropertiesForTesting(Layer* layer,
Layer* parent,
@@ -1294,7 +1469,7 @@ class LayerTreeHostTestAtomicCommitWithPartialUpdate
ASSERT_EQ(1u, layer_tree_host()->settings().max_partial_texture_updates);
TestWebGraphicsContext3D* context = static_cast<TestWebGraphicsContext3D*>(
- impl->output_surface()->context3d());
+ impl->output_surface()->context_provider()->Context3d());
switch (impl->active_tree()->source_frame_number()) {
case 0:
@@ -1359,7 +1534,7 @@ class LayerTreeHostTestAtomicCommitWithPartialUpdate
EXPECT_LT(impl->active_tree()->source_frame_number(), 5);
TestWebGraphicsContext3D* context = static_cast<TestWebGraphicsContext3D*>(
- impl->output_surface()->context3d());
+ impl->output_surface()->context_provider()->Context3d());
// Number of textures used for drawing should one per layer except for
// frame 3 where the viewport only contains one layer.
@@ -1565,8 +1740,9 @@ class EvictionTestLayer : public Layer {
return;
texture_ = PrioritizedResource::Create(
layer_tree_host()->contents_texture_manager());
- texture_->SetDimensions(gfx::Size(10, 10), GL_RGBA);
+ texture_->SetDimensions(gfx::Size(10, 10), RGBA_8888);
bitmap_.setConfig(SkBitmap::kARGB_8888_Config, 10, 10);
+ bitmap_.allocPixels();
}
scoped_ptr<PrioritizedResource> texture_;
@@ -1654,7 +1830,6 @@ class LayerTreeHostTestEvictTextures : public LayerTreeHostTest {
}
void PostEvictTextures() {
- DCHECK(HasImplThread());
ImplThreadTaskRunner()->PostTask(
FROM_HERE,
base::Bind(&LayerTreeHostTestEvictTextures::EvictTexturesOnImplThread,
@@ -2228,6 +2403,51 @@ class LayerTreeHostTestBeginFrameNotificationShutdownWhileEnabled
MULTI_THREAD_TEST_F(
LayerTreeHostTestBeginFrameNotificationShutdownWhileEnabled);
+class LayerTreeHostTestAbortedCommitDoesntStallNextCommitWhenIdle
+ : public LayerTreeHostTest {
+ protected:
+ LayerTreeHostTestAbortedCommitDoesntStallNextCommitWhenIdle()
+ : commit_count_(0), commit_complete_count_(0) {}
+
+ virtual void InitializeSettings(LayerTreeSettings* settings) OVERRIDE {
+ settings->begin_frame_scheduling_enabled = true;
+ settings->using_synchronous_renderer_compositor = true;
+ }
+
+ virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); }
+
+ virtual void DidCommit() OVERRIDE {
+ commit_count_++;
+ if (commit_count_ == 2) {
+ // A commit was just aborted, request a real commit now to make sure a
+ // real commit following an aborted commit will still complete and
+ // end the test even when the Impl thread is idle.
+ layer_tree_host()->SetNeedsCommit();
+ }
+ }
+
+ virtual void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) OVERRIDE {
+ commit_complete_count_++;
+ if (commit_complete_count_ == 1) {
+ // Initiate an aborted commit after the first commit.
+ host_impl->SetNeedsCommit();
+ } else {
+ EndTest();
+ }
+ }
+
+ virtual void AfterTest() OVERRIDE {
+ EXPECT_EQ(commit_count_, 3);
+ EXPECT_EQ(commit_complete_count_, 2);
+ }
+
+ int commit_count_;
+ int commit_complete_count_;
+};
+
+MULTI_THREAD_TEST_F(
+ LayerTreeHostTestAbortedCommitDoesntStallNextCommitWhenIdle);
+
class LayerTreeHostTestUninvertibleTransformDoesNotBlockActivation
: public LayerTreeHostTest {
protected:
@@ -2317,23 +2537,17 @@ class LayerTreeHostTestChangeLayerPropertiesInPaintContents
SINGLE_THREAD_TEST_F(LayerTreeHostTestChangeLayerPropertiesInPaintContents);
-class MockIOSurfaceWebGraphicsContext3D : public FakeWebGraphicsContext3D {
+class MockIOSurfaceWebGraphicsContext3D : public TestWebGraphicsContext3D {
public:
- MockIOSurfaceWebGraphicsContext3D()
- : FakeWebGraphicsContext3D() {}
+ MockIOSurfaceWebGraphicsContext3D() {
+ test_capabilities_.iosurface = true;
+ test_capabilities_.texture_rectangle = true;
+ }
virtual WebKit::WebGLId createTexture() OVERRIDE {
return 1;
}
- virtual WebKit::WebString getString(WebKit::WGC3Denum name) OVERRIDE {
- if (name == GL_EXTENSIONS) {
- return WebKit::WebString(
- "GL_CHROMIUM_iosurface GL_ARB_texture_rectangle");
- }
- return WebKit::WebString();
- }
-
MOCK_METHOD1(activeTexture, void(WebKit::WGC3Denum texture));
MOCK_METHOD2(bindTexture, void(WebKit::WGC3Denum target,
WebKit::WebGLId texture_id));
@@ -2349,6 +2563,7 @@ class MockIOSurfaceWebGraphicsContext3D : public FakeWebGraphicsContext3D {
WebKit::WGC3Dsizei count,
WebKit::WGC3Denum type,
WebKit::WGC3Dintptr offset));
+ MOCK_METHOD1(deleteTexture, void(WebKit::WGC3Denum texture));
};
@@ -2356,11 +2571,12 @@ class LayerTreeHostTestIOSurfaceDrawing : public LayerTreeHostTest {
protected:
virtual scoped_ptr<OutputSurface> CreateOutputSurface(bool fallback)
OVERRIDE {
- scoped_ptr<MockIOSurfaceWebGraphicsContext3D> context(
+ scoped_ptr<MockIOSurfaceWebGraphicsContext3D> mock_context_owned(
new MockIOSurfaceWebGraphicsContext3D);
- mock_context_ = context.get();
- scoped_ptr<OutputSurface> output_surface = FakeOutputSurface::Create3d(
- context.PassAs<WebKit::WebGraphicsContext3D>()).PassAs<OutputSurface>();
+ mock_context_ = mock_context_owned.get();
+
+ scoped_ptr<OutputSurface> output_surface(FakeOutputSurface::Create3d(
+ mock_context_owned.PassAs<TestWebGraphicsContext3D>()));
return output_surface.Pass();
}
@@ -2439,6 +2655,8 @@ class LayerTreeHostTestIOSurfaceDrawing : public LayerTreeHostTest {
virtual void DrawLayersOnThread(LayerTreeHostImpl* host_impl) OVERRIDE {
Mock::VerifyAndClearExpectations(&mock_context_);
+
+ EXPECT_CALL(*mock_context_, deleteTexture(1)).Times(1);
EndTest();
}
@@ -2542,10 +2760,14 @@ class LayerTreeHostTestAsyncReadback : public LayerTreeHostTest {
virtual scoped_ptr<OutputSurface> CreateOutputSurface(bool fallback)
OVERRIDE {
- if (use_gl_renderer_)
- return FakeOutputSurface::Create3d().PassAs<OutputSurface>();
- return FakeOutputSurface::CreateSoftware(
- make_scoped_ptr(new SoftwareOutputDevice)).PassAs<OutputSurface>();
+ scoped_ptr<FakeOutputSurface> output_surface;
+ if (use_gl_renderer_) {
+ output_surface = FakeOutputSurface::Create3d().Pass();
+ } else {
+ output_surface = FakeOutputSurface::CreateSoftware(
+ make_scoped_ptr(new SoftwareOutputDevice)).Pass();
+ }
+ return output_surface.PassAs<OutputSurface>();
}
bool use_gl_renderer_;
@@ -2984,6 +3206,141 @@ class LayerTreeHostTestAsyncTwoReadbacksWithoutDraw : public LayerTreeHostTest {
SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F(
LayerTreeHostTestAsyncTwoReadbacksWithoutDraw);
+class LayerTreeHostTestAsyncReadbackLostOutputSurface
+ : public LayerTreeHostTest {
+ protected:
+ virtual scoped_ptr<OutputSurface> CreateOutputSurface(bool fallback)
+ OVERRIDE {
+ if (!first_context_provider_.get()) {
+ first_context_provider_ = TestContextProvider::Create();
+ return FakeOutputSurface::Create3d(first_context_provider_)
+ .PassAs<OutputSurface>();
+ }
+
+ EXPECT_FALSE(second_context_provider_.get());
+ second_context_provider_ = TestContextProvider::Create();
+ return FakeOutputSurface::Create3d(second_context_provider_)
+ .PassAs<OutputSurface>();
+ }
+
+ virtual void SetupTree() OVERRIDE {
+ root_ = FakeContentLayer::Create(&client_);
+ root_->SetBounds(gfx::Size(20, 20));
+
+ copy_layer_ = FakeContentLayer::Create(&client_);
+ copy_layer_->SetBounds(gfx::Size(10, 10));
+ root_->AddChild(copy_layer_);
+
+ layer_tree_host()->SetRootLayer(root_);
+ LayerTreeHostTest::SetupTree();
+ }
+
+ virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); }
+
+ void CopyOutputCallback(scoped_ptr<CopyOutputResult> result) {
+ EXPECT_TRUE(layer_tree_host()->proxy()->IsMainThread());
+ EXPECT_EQ(gfx::Size(10, 10).ToString(), result->size().ToString());
+ EXPECT_TRUE(result->HasTexture());
+
+ // Save the result for later.
+ EXPECT_FALSE(result_);
+ result_ = result.Pass();
+
+ // Post a commit to lose the output surface.
+ layer_tree_host()->SetNeedsCommit();
+ }
+
+ virtual void DidCommitAndDrawFrame() OVERRIDE {
+ switch (layer_tree_host()->source_frame_number()) {
+ case 1:
+ // The layers have been pushed to the impl side. The layer textures have
+ // been allocated.
+
+ // Request a copy of the layer. This will use another texture.
+ copy_layer_->RequestCopyOfOutput(
+ CopyOutputRequest::CreateRequest(base::Bind(
+ &LayerTreeHostTestAsyncReadbackLostOutputSurface::
+ CopyOutputCallback,
+ base::Unretained(this))));
+ break;
+ case 4:
+ // With SingleThreadProxy it takes two commits to finally swap after a
+ // context loss.
+ case 5:
+ // Now destroy the CopyOutputResult, releasing the texture inside back
+ // to the compositor.
+ EXPECT_TRUE(result_);
+ result_.reset();
+
+ // Check that it is released.
+ ImplThreadTaskRunner()->PostTask(
+ FROM_HERE,
+ base::Bind(&LayerTreeHostTestAsyncReadbackLostOutputSurface::
+ CheckNumTextures,
+ base::Unretained(this),
+ num_textures_after_loss_ - 1));
+ break;
+ }
+ }
+
+ virtual void SwapBuffersOnThread(LayerTreeHostImpl *impl, bool result)
+ OVERRIDE {
+ switch (impl->active_tree()->source_frame_number()) {
+ case 0:
+ // The layers have been drawn, so their textures have been allocated.
+ EXPECT_FALSE(result_);
+ num_textures_without_readback_ =
+ first_context_provider_->TestContext3d()->NumTextures();
+ break;
+ case 1:
+ // We did a readback, so there will be a readback texture around now.
+ EXPECT_LT(num_textures_without_readback_,
+ first_context_provider_->TestContext3d()->NumTextures());
+ break;
+ case 2:
+ // The readback texture is collected.
+ EXPECT_TRUE(result_);
+
+ // Lose the output surface.
+ first_context_provider_->TestContext3d()->loseContextCHROMIUM(
+ GL_GUILTY_CONTEXT_RESET_ARB,
+ GL_INNOCENT_CONTEXT_RESET_ARB);
+ break;
+ case 3:
+ // With SingleThreadProxy it takes two commits to finally swap after a
+ // context loss.
+ case 4:
+ // The output surface has been recreated.
+ EXPECT_TRUE(second_context_provider_.get());
+
+ num_textures_after_loss_ =
+ first_context_provider_->TestContext3d()->NumTextures();
+ break;
+ }
+ }
+
+ void CheckNumTextures(size_t expected_num_textures) {
+ EXPECT_EQ(expected_num_textures,
+ first_context_provider_->TestContext3d()->NumTextures());
+ EndTest();
+ }
+
+ virtual void AfterTest() OVERRIDE {}
+
+ scoped_refptr<TestContextProvider> first_context_provider_;
+ scoped_refptr<TestContextProvider> second_context_provider_;
+ size_t num_textures_without_readback_;
+ size_t num_textures_after_loss_;
+ FakeContentLayerClient client_;
+ scoped_refptr<FakeContentLayer> root_;
+ scoped_refptr<FakeContentLayer> copy_layer_;
+ scoped_ptr<CopyOutputResult> result_;
+};
+
+// No output to copy for delegated renderers.
+SINGLE_AND_MULTI_THREAD_DIRECT_RENDERER_TEST_F(
+ LayerTreeHostTestAsyncReadbackLostOutputSurface);
+
class LayerTreeHostTestNumFramesPending : public LayerTreeHostTest {
public:
virtual void BeginTest() OVERRIDE {
@@ -3130,10 +3487,12 @@ class LayerTreeHostTestDeferredInitialize : public LayerTreeHostTest {
void DeferredInitializeAndRedraw(LayerTreeHostImpl* host_impl) {
EXPECT_FALSE(did_initialize_gl_);
// SetAndInitializeContext3D calls SetNeedsCommit.
- EXPECT_TRUE(static_cast<FakeOutputSurface*>(host_impl->output_surface())
- ->SetAndInitializeContext3D(
- scoped_ptr<WebKit::WebGraphicsContext3D>(
- TestWebGraphicsContext3D::Create())));
+ FakeOutputSurface* fake_output_surface =
+ static_cast<FakeOutputSurface*>(host_impl->output_surface());
+ scoped_refptr<TestContextProvider> context_provider =
+ TestContextProvider::Create(); // Not bound to thread.
+ EXPECT_TRUE(fake_output_surface->InitializeAndSetContext3d(
+ context_provider, NULL));
did_initialize_gl_ = true;
}
@@ -3201,7 +3560,7 @@ class LayerTreeHostTestUIResource : public LayerTreeHostTest {
void PerformTest(LayerTreeHostImpl* impl) {
TestWebGraphicsContext3D* context = static_cast<TestWebGraphicsContext3D*>(
- impl->output_surface()->context3d());
+ impl->output_surface()->context_provider()->Context3d());
int frame = num_commits_;
switch (frame) {
@@ -3313,19 +3672,13 @@ class LayerTreeHostTestLayersPushProperties : public LayerTreeHostTest {
child_ = PushPropertiesCountingLayer::Create();
child2_ = PushPropertiesCountingLayer::Create();
grandchild_ = PushPropertiesCountingLayer::Create();
-
- if (layer_tree_host()->settings().impl_side_painting)
- leaf_picture_layer_ = FakePictureLayer::Create(&client_);
- else
- leaf_content_layer_ = FakeContentLayer::Create(&client_);
+ leaf_scrollbar_layer_ =
+ FakePaintedScrollbarLayer::Create(false, false, root_->id());
root_->AddChild(child_);
root_->AddChild(child2_);
child_->AddChild(grandchild_);
- if (leaf_picture_layer_)
- child2_->AddChild(leaf_picture_layer_);
- if (leaf_content_layer_)
- child2_->AddChild(leaf_content_layer_);
+ child2_->AddChild(leaf_scrollbar_layer_);
other_root_ = PushPropertiesCountingLayer::Create();
@@ -3344,16 +3697,10 @@ class LayerTreeHostTestLayersPushProperties : public LayerTreeHostTest {
child2_->push_properties_count());
EXPECT_EQ(expected_push_properties_other_root_,
other_root_->push_properties_count());
- if (leaf_content_layer_) {
- EXPECT_EQ(expected_push_properties_leaf_layer_,
- leaf_content_layer_->push_properties_count());
- }
- if (leaf_picture_layer_) {
- EXPECT_EQ(expected_push_properties_leaf_layer_,
- leaf_picture_layer_->push_properties_count());
- }
+ EXPECT_EQ(expected_push_properties_leaf_layer_,
+ leaf_scrollbar_layer_->push_properties_count());
- // The content/picture layer always needs to be pushed.
+ // The scrollbar layer always needs to be pushed.
if (root_->layer_tree_host()) {
EXPECT_TRUE(root_->descendant_needs_push_properties());
EXPECT_FALSE(root_->needs_push_properties());
@@ -3362,13 +3709,9 @@ class LayerTreeHostTestLayersPushProperties : public LayerTreeHostTest {
EXPECT_TRUE(child2_->descendant_needs_push_properties());
EXPECT_FALSE(child2_->needs_push_properties());
}
- if (leaf_content_layer_.get() && leaf_content_layer_->layer_tree_host()) {
- EXPECT_FALSE(leaf_content_layer_->descendant_needs_push_properties());
- EXPECT_TRUE(leaf_content_layer_->needs_push_properties());
- }
- if (leaf_picture_layer_.get() && leaf_picture_layer_->layer_tree_host()) {
- EXPECT_FALSE(leaf_picture_layer_->descendant_needs_push_properties());
- EXPECT_TRUE(leaf_picture_layer_->needs_push_properties());
+ if (leaf_scrollbar_layer_->layer_tree_host()) {
+ EXPECT_FALSE(leaf_scrollbar_layer_->descendant_needs_push_properties());
+ EXPECT_TRUE(leaf_scrollbar_layer_->needs_push_properties());
}
// child_ and grandchild_ don't persist their need to push properties.
@@ -3479,8 +3822,7 @@ class LayerTreeHostTestLayersPushProperties : public LayerTreeHostTest {
// Content/Picture layers require PushProperties every commit that they are
// in the tree.
- if ((leaf_content_layer_.get() && leaf_content_layer_->layer_tree_host()) ||
- (leaf_picture_layer_.get() && leaf_picture_layer_->layer_tree_host()))
+ if (leaf_scrollbar_layer_->layer_tree_host())
++expected_push_properties_leaf_layer_;
}
@@ -3493,8 +3835,7 @@ class LayerTreeHostTestLayersPushProperties : public LayerTreeHostTest {
scoped_refptr<PushPropertiesCountingLayer> child2_;
scoped_refptr<PushPropertiesCountingLayer> grandchild_;
scoped_refptr<PushPropertiesCountingLayer> other_root_;
- scoped_refptr<FakeContentLayer> leaf_content_layer_;
- scoped_refptr<FakePictureLayer> leaf_picture_layer_;
+ scoped_refptr<FakePaintedScrollbarLayer> leaf_scrollbar_layer_;
size_t expected_push_properties_root_;
size_t expected_push_properties_child_;
size_t expected_push_properties_child2_;
@@ -3518,8 +3859,8 @@ class LayerTreeHostTestPropertyChangesDuringUpdateArePushed
bool paint_scrollbar = true;
bool has_thumb = false;
- scrollbar_layer_ =
- FakeScrollbarLayer::Create(paint_scrollbar, has_thumb, root_->id());
+ scrollbar_layer_ = FakePaintedScrollbarLayer::Create(
+ paint_scrollbar, has_thumb, root_->id());
root_->AddChild(scrollbar_layer_);
@@ -3559,7 +3900,7 @@ class LayerTreeHostTestPropertyChangesDuringUpdateArePushed
virtual void AfterTest() OVERRIDE {}
scoped_refptr<Layer> root_;
- scoped_refptr<FakeScrollbarLayer> scrollbar_layer_;
+ scoped_refptr<FakePaintedScrollbarLayer> scrollbar_layer_;
};
MULTI_THREAD_TEST_F(LayerTreeHostTestPropertyChangesDuringUpdateArePushed);
@@ -4295,6 +4636,149 @@ class LayerTreeHostTestAbortEvictedTextures : public LayerTreeHostTest {
// Commits can only be aborted when using the thread proxy.
MULTI_THREAD_TEST_F(LayerTreeHostTestAbortEvictedTextures);
+class LayerTreeHostTestMaxTransferBufferUsageBytes : public LayerTreeHostTest {
+ protected:
+ virtual void InitializeSettings(LayerTreeSettings* settings) OVERRIDE {
+ settings->impl_side_painting = true;
+ }
+
+ virtual scoped_ptr<OutputSurface> CreateOutputSurface(bool fallback)
+ OVERRIDE {
+ scoped_refptr<TestContextProvider> context_provider =
+ TestContextProvider::Create();
+ context_provider->SetMaxTransferBufferUsageBytes(1024 * 1024);
+ return FakeOutputSurface::Create3d(context_provider)
+ .PassAs<OutputSurface>();
+ }
+
+ virtual void SetupTree() OVERRIDE {
+ scoped_refptr<FakePictureLayer> root_layer =
+ FakePictureLayer::Create(&client_);
+ root_layer->SetBounds(gfx::Size(6000, 6000));
+ root_layer->SetIsDrawable(true);
+
+ layer_tree_host()->SetRootLayer(root_layer);
+ LayerTreeHostTest::SetupTree();
+ }
+
+ virtual void BeginTest() OVERRIDE {
+ PostSetNeedsCommitToMainThread();
+ }
+
+ virtual void DidActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE {
+ TestWebGraphicsContext3D* context = static_cast<TestWebGraphicsContext3D*>(
+ impl->output_surface()->context_provider()->Context3d());
+
+ // Expect that the transfer buffer memory used is equal to the
+ // MaxTransferBufferUsageBytes value set in CreateOutputSurface.
+ EXPECT_EQ(1024 * 1024u,
+ context->GetTransferBufferMemoryUsedBytes());
+ EndTest();
+ }
+
+ virtual void AfterTest() OVERRIDE {}
+
+ private:
+ FakeContentLayerClient client_;
+};
+
+// Impl-side painting is a multi-threaded compositor feature.
+MULTI_THREAD_TEST_F(LayerTreeHostTestMaxTransferBufferUsageBytes);
+
+// Test ensuring that memory limits are sent to the prioritized resource
+// manager.
+class LayerTreeHostTestMemoryLimits : public LayerTreeHostTest {
+ public:
+ LayerTreeHostTestMemoryLimits() : num_commits_(0) {}
+
+ virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); }
+
+ virtual void DidCommit() OVERRIDE {
+ int frame = num_commits_;
+ switch (frame) {
+ case 0:
+ // Verify default values.
+ EXPECT_EQ(
+ PrioritizedResourceManager::DefaultMemoryAllocationLimit(),
+ layer_tree_host()->contents_texture_manager()->
+ MaxMemoryLimitBytes());
+ EXPECT_EQ(
+ PriorityCalculator::AllowEverythingCutoff(),
+ layer_tree_host()->contents_texture_manager()->
+ ExternalPriorityCutoff());
+ PostSetNeedsCommitToMainThread();
+ break;
+ case 1:
+ // The values should remain the same until the commit after the policy
+ // is changed.
+ EXPECT_EQ(
+ PrioritizedResourceManager::DefaultMemoryAllocationLimit(),
+ layer_tree_host()->contents_texture_manager()->
+ MaxMemoryLimitBytes());
+ EXPECT_EQ(
+ PriorityCalculator::AllowEverythingCutoff(),
+ layer_tree_host()->contents_texture_manager()->
+ ExternalPriorityCutoff());
+ break;
+ case 2:
+ // Verify values were correctly passed.
+ EXPECT_EQ(
+ 16u*1024u*1024u,
+ layer_tree_host()->contents_texture_manager()->
+ MaxMemoryLimitBytes());
+ EXPECT_EQ(
+ PriorityCalculator::AllowVisibleAndNearbyCutoff(),
+ layer_tree_host()->contents_texture_manager()->
+ ExternalPriorityCutoff());
+ EndTest();
+ break;
+ case 3:
+ // Make sure no extra commits happen.
+ NOTREACHED();
+ break;
+ }
+
+ ++num_commits_;
+ }
+
+ virtual void CommitCompleteOnThread(LayerTreeHostImpl* impl) OVERRIDE {
+ int frame = num_commits_;
+ switch (frame) {
+ case 0:
+ break;
+ case 1:
+ // This will trigger a commit because the priority cutoff has changed.
+ impl->SetMemoryPolicy(ManagedMemoryPolicy(
+ 16u*1024u*1024u,
+ ManagedMemoryPolicy::CUTOFF_ALLOW_NICE_TO_HAVE,
+ 0,
+ ManagedMemoryPolicy::CUTOFF_ALLOW_NOTHING,
+ 1000));
+ break;
+ case 2:
+ // This will not trigger a commit because the priority cutoff has not
+ // changed, and there is already enough memory for all allocations.
+ impl->SetMemoryPolicy(ManagedMemoryPolicy(
+ 32u*1024u*1024u,
+ ManagedMemoryPolicy::CUTOFF_ALLOW_NICE_TO_HAVE,
+ 0,
+ ManagedMemoryPolicy::CUTOFF_ALLOW_NOTHING,
+ 1000));
+ break;
+ case 3:
+ NOTREACHED();
+ break;
+ }
+ }
+
+ virtual void AfterTest() OVERRIDE {}
+
+ private:
+ int num_commits_;
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestMemoryLimits);
+
} // namespace
} // namespace cc
diff --git a/chromium/cc/trees/layer_tree_host_unittest_animation.cc b/chromium/cc/trees/layer_tree_host_unittest_animation.cc
index dbc427504d8..70e2a9ec6d8 100644
--- a/chromium/cc/trees/layer_tree_host_unittest_animation.cc
+++ b/chromium/cc/trees/layer_tree_host_unittest_animation.cc
@@ -293,6 +293,136 @@ class LayerTreeHostAnimationTestTickAnimationWhileBackgrounded
SINGLE_AND_MULTI_THREAD_TEST_F(
LayerTreeHostAnimationTestTickAnimationWhileBackgrounded);
+// Ensures that animations do not tick when we are backgrounded and
+// and we have an empty active tree.
+class LayerTreeHostAnimationTestNoBackgroundTickingWithoutActiveTree
+ : public LayerTreeHostAnimationTest {
+ protected:
+ LayerTreeHostAnimationTestNoBackgroundTickingWithoutActiveTree()
+ : active_tree_was_animated_(false) {}
+
+ virtual base::TimeDelta LowFrequencyAnimationInterval() const OVERRIDE {
+ return base::TimeDelta::FromMilliseconds(4);
+ }
+
+ virtual void BeginTest() OVERRIDE {
+ PostAddAnimationToMainThread(layer_tree_host()->root_layer());
+ }
+
+ virtual void NotifyAnimationFinished(double time) OVERRIDE {
+ // Replace animated commits with an empty tree.
+ layer_tree_host()->SetRootLayer(make_scoped_refptr<Layer>(NULL));
+ }
+
+ virtual void DidCommit() OVERRIDE {
+ // This alternates setting an empty tree and a non-empty tree with an
+ // animation.
+ switch (layer_tree_host()->source_frame_number()) {
+ case 1:
+ // Wait for NotifyAnimationFinished to commit an empty tree.
+ break;
+ case 2:
+ SetupTree();
+ AddOpacityTransitionToLayer(
+ layer_tree_host()->root_layer(), 0.000001, 0, 0.5, true);
+ break;
+ case 3:
+ // Wait for NotifyAnimationFinished to commit an empty tree.
+ break;
+ case 4:
+ EndTest();
+ break;
+ }
+ }
+
+ virtual void BeginCommitOnThread(LayerTreeHostImpl* host_impl) OVERRIDE {
+ // At the start of every commit, block activations and make sure
+ // we are backgrounded.
+ host_impl->BlockNotifyReadyToActivateForTesting(true);
+ PostSetVisibleToMainThread(false);
+ }
+
+ virtual void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) OVERRIDE {
+ if (!host_impl->settings().impl_side_painting) {
+ // There are no activations to block if we're not impl-side-painting,
+ // so just advance the test immediately.
+ if (host_impl->active_tree()->source_frame_number() < 3)
+ UnblockActivations(host_impl);
+ return;
+ }
+
+ // We block activation for several ticks to make sure that, even though
+ // there is a pending tree with animations, we still do not background
+ // tick if the active tree is empty.
+ if (host_impl->pending_tree()->source_frame_number() < 3) {
+ base::MessageLoopProxy::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(
+ &LayerTreeHostAnimationTestNoBackgroundTickingWithoutActiveTree::
+ UnblockActivations,
+ base::Unretained(this),
+ host_impl),
+ 4 * LowFrequencyAnimationInterval());
+ }
+ }
+
+ virtual void UnblockActivations(LayerTreeHostImpl* host_impl) {
+ host_impl->BlockNotifyReadyToActivateForTesting(false);
+ }
+
+ virtual void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) OVERRIDE {
+ active_tree_was_animated_ = false;
+
+ // Verify that commits are actually alternating with empty / non-empty
+ // trees.
+ switch (host_impl->active_tree()->source_frame_number()) {
+ case 0:
+ case 2:
+ EXPECT_TRUE(host_impl->active_tree()->root_layer());
+ break;
+ case 1:
+ case 3:
+ EXPECT_FALSE(host_impl->active_tree()->root_layer());
+ break;
+ }
+
+ if (host_impl->active_tree()->source_frame_number() < 3) {
+ // Initiate the next commit after a delay to give us a chance to
+ // background tick if the active tree isn't empty.
+ base::MessageLoopProxy::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(
+ &LayerTreeHostAnimationTestNoBackgroundTickingWithoutActiveTree::
+ InitiateNextCommit,
+ base::Unretained(this),
+ host_impl),
+ 4 * LowFrequencyAnimationInterval());
+ }
+ }
+
+ virtual void WillAnimateLayers(LayerTreeHostImpl* host_impl,
+ base::TimeTicks monotonic_time) OVERRIDE {
+ EXPECT_TRUE(host_impl->active_tree()->root_layer());
+ active_tree_was_animated_ = true;
+ }
+
+ void InitiateNextCommit(LayerTreeHostImpl* host_impl) {
+ // Verify that we actually animated when we should have.
+ bool has_active_tree = host_impl->active_tree()->root_layer();
+ EXPECT_EQ(has_active_tree, active_tree_was_animated_);
+
+ // The next commit is blocked until we become visible again.
+ PostSetVisibleToMainThread(true);
+ }
+
+ virtual void AfterTest() OVERRIDE {}
+
+ bool active_tree_was_animated_;
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(
+ LayerTreeHostAnimationTestNoBackgroundTickingWithoutActiveTree);
+
// Ensure that an animation's timing function is respected.
class LayerTreeHostAnimationTestAddAnimationWithTimingFunction
: public LayerTreeHostAnimationTest {
diff --git a/chromium/cc/trees/layer_tree_host_unittest_context.cc b/chromium/cc/trees/layer_tree_host_unittest_context.cc
index 1787d31ddf3..18683e826b6 100644
--- a/chromium/cc/trees/layer_tree_host_unittest_context.cc
+++ b/chromium/cc/trees/layer_tree_host_unittest_context.cc
@@ -5,12 +5,14 @@
#include "cc/trees/layer_tree_host.h"
#include "base/basictypes.h"
+#include "cc/debug/test_context_provider.h"
+#include "cc/debug/test_web_graphics_context_3d.h"
#include "cc/layers/content_layer.h"
#include "cc/layers/heads_up_display_layer.h"
#include "cc/layers/io_surface_layer.h"
#include "cc/layers/layer_impl.h"
+#include "cc/layers/painted_scrollbar_layer.h"
#include "cc/layers/picture_layer.h"
-#include "cc/layers/scrollbar_layer.h"
#include "cc/layers/texture_layer.h"
#include "cc/layers/texture_layer_impl.h"
#include "cc/layers/video_layer.h"
@@ -19,18 +21,16 @@
#include "cc/test/fake_content_layer.h"
#include "cc/test/fake_content_layer_client.h"
#include "cc/test/fake_content_layer_impl.h"
-#include "cc/test/fake_context_provider.h"
#include "cc/test/fake_delegated_renderer_layer.h"
#include "cc/test/fake_delegated_renderer_layer_impl.h"
#include "cc/test/fake_layer_tree_host_client.h"
#include "cc/test/fake_output_surface.h"
+#include "cc/test/fake_painted_scrollbar_layer.h"
#include "cc/test/fake_scoped_ui_resource.h"
#include "cc/test/fake_scrollbar.h"
-#include "cc/test/fake_scrollbar_layer.h"
#include "cc/test/fake_video_frame_provider.h"
#include "cc/test/layer_tree_test.h"
#include "cc/test/render_pass_test_common.h"
-#include "cc/test/test_web_graphics_context_3d.h"
#include "cc/trees/layer_tree_host_impl.h"
#include "cc/trees/layer_tree_impl.h"
#include "cc/trees/single_thread_proxy.h"
@@ -108,11 +108,11 @@ class LayerTreeHostContextTest : public LayerTreeTest {
}
if (delegating_renderer()) {
- return FakeOutputSurface::CreateDelegating3d(
- context3d.PassAs<WebGraphicsContext3D>()).PassAs<OutputSurface>();
+ return FakeOutputSurface::CreateDelegating3d(context3d.Pass())
+ .PassAs<OutputSurface>();
}
- return FakeOutputSurface::Create3d(
- context3d.PassAs<WebGraphicsContext3D>()).PassAs<OutputSurface>();
+ return FakeOutputSurface::Create3d(context3d.Pass())
+ .PassAs<OutputSurface>();
}
scoped_ptr<TestWebGraphicsContext3D> CreateOffscreenContext3d() {
@@ -141,10 +141,11 @@ class LayerTreeHostContextTest : public LayerTreeTest {
if (!offscreen_contexts_main_thread_.get() ||
offscreen_contexts_main_thread_->DestroyedOnMainThread()) {
- offscreen_contexts_main_thread_ = FakeContextProvider::Create(
- base::Bind(&LayerTreeHostContextTest::CreateOffscreenContext3d,
- base::Unretained(this)));
- if (offscreen_contexts_main_thread_.get() &&
+ offscreen_contexts_main_thread_ =
+ TestContextProvider::Create(
+ base::Bind(&LayerTreeHostContextTest::CreateOffscreenContext3d,
+ base::Unretained(this)));
+ if (offscreen_contexts_main_thread_ &&
!offscreen_contexts_main_thread_->BindToCurrentThread())
offscreen_contexts_main_thread_ = NULL;
}
@@ -157,9 +158,10 @@ class LayerTreeHostContextTest : public LayerTreeTest {
if (!offscreen_contexts_compositor_thread_.get() ||
offscreen_contexts_compositor_thread_->DestroyedOnMainThread()) {
- offscreen_contexts_compositor_thread_ = FakeContextProvider::Create(
- base::Bind(&LayerTreeHostContextTest::CreateOffscreenContext3d,
- base::Unretained(this)));
+ offscreen_contexts_compositor_thread_ =
+ TestContextProvider::Create(
+ base::Bind(&LayerTreeHostContextTest::CreateOffscreenContext3d,
+ base::Unretained(this)));
}
return offscreen_contexts_compositor_thread_;
}
@@ -236,8 +238,8 @@ class LayerTreeHostContextTest : public LayerTreeTest {
bool context_should_support_io_surface_;
bool fallback_context_works_;
- scoped_refptr<FakeContextProvider> offscreen_contexts_main_thread_;
- scoped_refptr<FakeContextProvider> offscreen_contexts_compositor_thread_;
+ scoped_refptr<TestContextProvider> offscreen_contexts_main_thread_;
+ scoped_refptr<TestContextProvider> offscreen_contexts_compositor_thread_;
};
class LayerTreeHostContextTestLostContextSucceeds
@@ -474,9 +476,9 @@ class LayerTreeHostContextTestLostContextSucceedsWithContent
// the active context.
EXPECT_TRUE(content_impl->HaveResourceForTileAt(0, 0));
- cc::ContextProvider* contexts =
- host_impl->resource_provider()->offscreen_context_provider();
+ cc::ContextProvider* contexts = host_impl->offscreen_context_provider();
if (use_surface_) {
+ ASSERT_TRUE(contexts);
EXPECT_TRUE(contexts->Context3d());
// TODO(danakj): Make a fake GrContext.
// EXPECT_TRUE(contexts->GrContext());
@@ -595,8 +597,7 @@ class LayerTreeHostContextTestOffscreenContextFails
}
virtual void DrawLayersOnThread(LayerTreeHostImpl* host_impl) OVERRIDE {
- cc::ContextProvider* contexts =
- host_impl->resource_provider()->offscreen_context_provider();
+ cc::ContextProvider* contexts = host_impl->offscreen_context_provider();
EXPECT_FALSE(contexts);
// This did not lead to create failure.
@@ -1198,9 +1199,9 @@ class LayerTreeHostContextTestDontUseLostResources
debug_state.show_property_changed_rects = true;
layer_tree_host()->SetDebugState(debug_state);
- scoped_refptr<ScrollbarLayer> scrollbar_ = ScrollbarLayer::Create(
- scoped_ptr<Scrollbar>(new FakeScrollbar).Pass(),
- content_->id());
+ scoped_refptr<PaintedScrollbarLayer> scrollbar_ =
+ PaintedScrollbarLayer::Create(
+ scoped_ptr<Scrollbar>(new FakeScrollbar).Pass(), content_->id());
scrollbar_->SetBounds(gfx::Size(10, 10));
scrollbar_->SetAnchorPoint(gfx::PointF());
scrollbar_->SetIsDrawable(true);
@@ -1219,6 +1220,10 @@ class LayerTreeHostContextTestDontUseLostResources
LayerTreeHostContextTest::CommitCompleteOnThread(host_impl);
ResourceProvider* resource_provider = host_impl->resource_provider();
+ ContextProvider* context_provider =
+ host_impl->output_surface()->context_provider();
+
+ DCHECK(context_provider);
if (host_impl->active_tree()->source_frame_number() == 0) {
// Set up impl resources on the first commit.
@@ -1255,19 +1260,18 @@ class LayerTreeHostContextTestDontUseLostResources
static_cast<TextureLayerImpl*>(
host_impl->active_tree()->root_layer()->children()[2]);
texture_impl->set_texture_id(
- resource_provider->GraphicsContext3D()->createTexture());
+ context_provider->Context3d()->createTexture());
- DCHECK(resource_provider->GraphicsContext3D());
ResourceProvider::ResourceId texture = resource_provider->CreateResource(
gfx::Size(4, 4),
- resource_provider->default_resource_type(),
- ResourceProvider::TextureUsageAny);
+ GL_CLAMP_TO_EDGE,
+ ResourceProvider::TextureUsageAny,
+ RGBA_8888);
ResourceProvider::ScopedWriteLockGL lock(resource_provider, texture);
gpu::Mailbox mailbox;
- resource_provider->GraphicsContext3D()->genMailboxCHROMIUM(mailbox.name);
- unsigned sync_point =
- resource_provider->GraphicsContext3D()->insertSyncPoint();
+ context_provider->Context3d()->genMailboxCHROMIUM(mailbox.name);
+ unsigned sync_point = context_provider->Context3d()->insertSyncPoint();
color_video_frame_ = VideoFrame::CreateColorFrame(
gfx::Size(4, 4), 0x80, 0x80, 0x80, base::TimeDelta());
@@ -1343,7 +1347,7 @@ class LayerTreeHostContextTestDontUseLostResources
scoped_refptr<VideoLayer> video_hw_;
scoped_refptr<VideoLayer> video_scaled_hw_;
scoped_refptr<IOSurfaceLayer> io_surface_;
- scoped_refptr<ScrollbarLayer> scrollbar_;
+ scoped_refptr<PaintedScrollbarLayer> scrollbar_;
scoped_refptr<VideoFrame> color_video_frame_;
scoped_refptr<VideoFrame> hw_video_frame_;
@@ -1457,6 +1461,29 @@ class LayerTreeHostContextTestCompositeAndReadbackBeforeOutputSurfaceInit
EXPECT_EQ(1, times_output_surface_created_);
}
+ virtual bool PrepareToDrawOnThread(LayerTreeHostImpl* host_impl,
+ LayerTreeHostImpl::FrameData* frame_data,
+ bool result) OVERRIDE {
+ EXPECT_GE(host_impl->active_tree()->source_frame_number(), 0);
+ EXPECT_LE(host_impl->active_tree()->source_frame_number(), 1);
+ return true;
+ }
+
+ virtual void DrawLayersOnThread(LayerTreeHostImpl* host_impl) OVERRIDE {
+ // We should only draw for the readback and the replacement commit.
+ // The replacement commit will also be the first commit after output
+ // surface initialization.
+ EXPECT_GE(host_impl->active_tree()->source_frame_number(), 0);
+ EXPECT_LE(host_impl->active_tree()->source_frame_number(), 1);
+ }
+
+ virtual void SwapBuffersOnThread(LayerTreeHostImpl* host_impl,
+ bool result) OVERRIDE {
+ // We should only swap for the replacement commit.
+ EXPECT_EQ(host_impl->active_tree()->source_frame_number(), 1);
+ EndTest();
+ }
+
private:
int times_output_surface_created_;
};
@@ -1464,6 +1491,172 @@ class LayerTreeHostContextTestCompositeAndReadbackBeforeOutputSurfaceInit
SINGLE_AND_MULTI_THREAD_TEST_F(
LayerTreeHostContextTestCompositeAndReadbackBeforeOutputSurfaceInit);
+// This test verifies that losing an output surface during a
+// simultaneous readback and forced redraw works and does not deadlock.
+class LayerTreeHostContextTestLoseOutputSurfaceDuringReadbackAndForcedDraw
+ : public LayerTreeHostContextTest {
+ protected:
+ static const int kFirstOutputSurfaceInitSourceFrameNumber = 0;
+ static const int kReadbackSourceFrameNumber = 1;
+ static const int kReadbackReplacementSourceFrameNumber = 2;
+ static const int kSecondOutputSurfaceInitSourceFrameNumber = 3;
+
+ LayerTreeHostContextTestLoseOutputSurfaceDuringReadbackAndForcedDraw()
+ : did_react_to_first_commit_(false) {}
+
+ virtual void InitializeSettings(LayerTreeSettings* settings) OVERRIDE {
+ // This enables forced draws after a single prepare to draw failure.
+ settings->timeout_and_draw_when_animation_checkerboards = true;
+ settings->maximum_number_of_failed_draws_before_draw_is_forced_ = 1;
+ }
+
+ virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); }
+
+ virtual bool PrepareToDrawOnThread(LayerTreeHostImpl* host_impl,
+ LayerTreeHostImpl::FrameData* frame_data,
+ bool result) OVERRIDE {
+ int sfn = host_impl->active_tree()->source_frame_number();
+ EXPECT_TRUE(sfn == kFirstOutputSurfaceInitSourceFrameNumber ||
+ sfn == kSecondOutputSurfaceInitSourceFrameNumber ||
+ sfn == kReadbackSourceFrameNumber)
+ << sfn;
+
+ // Before we react to the failed draw by initiating the forced draw
+ // sequence, start a readback on the main thread and then lose the context
+ // to start output surface initialization all at the same time.
+ if (sfn == kFirstOutputSurfaceInitSourceFrameNumber &&
+ !did_react_to_first_commit_) {
+ did_react_to_first_commit_ = true;
+ PostReadbackToMainThread();
+ LoseContext();
+ }
+
+ return false;
+ }
+
+ virtual void InitializedRendererOnThread(LayerTreeHostImpl* host_impl,
+ bool success) OVERRIDE {
+ // -1 is for the first output surface initialization.
+ int sfn = host_impl->active_tree()->source_frame_number();
+ EXPECT_TRUE(sfn == -1 || sfn == kReadbackReplacementSourceFrameNumber)
+ << sfn;
+ }
+
+ virtual void DrawLayersOnThread(LayerTreeHostImpl* host_impl) OVERRIDE {
+ // We should only draw the first commit after output surface initialization
+ // and attempt to draw the readback commit (which will fail).
+ // All others should abort because the output surface is lost.
+ int sfn = host_impl->active_tree()->source_frame_number();
+ EXPECT_TRUE(sfn == kSecondOutputSurfaceInitSourceFrameNumber ||
+ sfn == kReadbackSourceFrameNumber)
+ << sfn;
+ }
+
+ virtual void SwapBuffersOnThread(LayerTreeHostImpl* host_impl,
+ bool result) OVERRIDE {
+ // We should only swap the first commit after the second output surface
+ // initialization.
+ int sfn = host_impl->active_tree()->source_frame_number();
+ EXPECT_TRUE(sfn == kSecondOutputSurfaceInitSourceFrameNumber) << sfn;
+ EndTest();
+ }
+
+ virtual void AfterTest() OVERRIDE {}
+
+ int did_react_to_first_commit_;
+};
+
+MULTI_THREAD_TEST_F(
+ LayerTreeHostContextTestLoseOutputSurfaceDuringReadbackAndForcedDraw);
+
+// This test verifies that losing an output surface right before a
+// simultaneous readback and forced redraw works and does not deadlock.
+class LayerTreeHostContextTestReadbackWithForcedDrawAndOutputSurfaceInit
+ : public LayerTreeHostContextTest {
+ protected:
+ static const int kFirstOutputSurfaceInitSourceFrameNumber = 0;
+ static const int kReadbackSourceFrameNumber = 1;
+ static const int kReadbackReplacementSourceFrameNumber = 2;
+ static const int kForcedDrawCommitSourceFrameNumber = 2;
+ static const int kSecondOutputSurfaceInitSourceFrameNumber = 2;
+
+ LayerTreeHostContextTestReadbackWithForcedDrawAndOutputSurfaceInit()
+ : did_lose_context_(false) {}
+
+ virtual void InitializeSettings(LayerTreeSettings* settings) OVERRIDE {
+ // This enables forced draws after a single prepare to draw failure.
+ settings->timeout_and_draw_when_animation_checkerboards = true;
+ settings->maximum_number_of_failed_draws_before_draw_is_forced_ = 1;
+ }
+
+ virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); }
+
+ virtual bool PrepareToDrawOnThread(LayerTreeHostImpl* host_impl,
+ LayerTreeHostImpl::FrameData* frame_data,
+ bool result) OVERRIDE {
+ int sfn = host_impl->active_tree()->source_frame_number();
+ EXPECT_TRUE(sfn == kFirstOutputSurfaceInitSourceFrameNumber ||
+ sfn == kSecondOutputSurfaceInitSourceFrameNumber ||
+ sfn == kReadbackSourceFrameNumber)
+ << sfn;
+
+ // Before we react to the failed draw by initiating the forced draw
+ // sequence, start a readback on the main thread and then lose the context
+ // to start output surface initialization all at the same time.
+ if (sfn == kFirstOutputSurfaceInitSourceFrameNumber && !did_lose_context_) {
+ did_lose_context_ = true;
+ LoseContext();
+ }
+
+ // Returning false will result in a forced draw.
+ return false;
+ }
+
+ virtual void DidInitializeOutputSurface(bool succeeded) OVERRIDE {
+ EXPECT_TRUE(succeeded);
+ if (layer_tree_host()->source_frame_number() > 0) {
+ // Perform a readback right after the second output surface
+ // initialization.
+ char pixels[4];
+ layer_tree_host()->CompositeAndReadback(&pixels, gfx::Rect(0, 0, 1, 1));
+ }
+ }
+
+ virtual void InitializedRendererOnThread(LayerTreeHostImpl* host_impl,
+ bool success) OVERRIDE {
+ // -1 is for the first output surface initialization.
+ int sfn = host_impl->active_tree()->source_frame_number();
+ EXPECT_TRUE(sfn == -1 || sfn == kFirstOutputSurfaceInitSourceFrameNumber)
+ << sfn;
+ }
+
+ virtual void DrawLayersOnThread(LayerTreeHostImpl* host_impl) OVERRIDE {
+ // We should only draw the first commit after output surface initialization
+ // and attempt to draw the readback commit (which will fail).
+ // All others should abort because the output surface is lost.
+ int sfn = host_impl->active_tree()->source_frame_number();
+ EXPECT_TRUE(sfn == kForcedDrawCommitSourceFrameNumber ||
+ sfn == kReadbackSourceFrameNumber)
+ << sfn;
+ }
+
+ virtual void SwapBuffersOnThread(LayerTreeHostImpl* host_impl,
+ bool result) OVERRIDE {
+ // We should only swap the first commit after the second output surface
+ // initialization.
+ int sfn = host_impl->active_tree()->source_frame_number();
+ EXPECT_TRUE(sfn == kForcedDrawCommitSourceFrameNumber) << sfn;
+ EndTest();
+ }
+
+ virtual void AfterTest() OVERRIDE {}
+
+ int did_lose_context_;
+};
+
+MULTI_THREAD_TEST_F(
+ LayerTreeHostContextTestReadbackWithForcedDrawAndOutputSurfaceInit);
+
class ImplSidePaintingLayerTreeHostContextTest
: public LayerTreeHostContextTest {
public:
@@ -1515,7 +1708,7 @@ class ScrollbarLayerLostContext : public LayerTreeHostContextTest {
virtual void BeginTest() OVERRIDE {
scoped_refptr<Layer> scroll_layer = Layer::Create();
- scrollbar_layer_ = FakeScrollbarLayer::Create(
+ scrollbar_layer_ = FakePaintedScrollbarLayer::Create(
false, true, scroll_layer->id());
scrollbar_layer_->SetBounds(gfx::Size(10, 100));
layer_tree_host()->root_layer()->AddChild(scrollbar_layer_);
@@ -1542,6 +1735,12 @@ class ScrollbarLayerLostContext : public LayerTreeHostContextTest {
EXPECT_EQ(2, scrollbar_layer_->update_count());
EndTest();
break;
+ case 3:
+ // Single thread proxy issues extra commits after context lost.
+ // http://crbug.com/287250
+ if (HasImplThread())
+ NOTREACHED();
+ break;
default:
NOTREACHED();
}
@@ -1549,7 +1748,7 @@ class ScrollbarLayerLostContext : public LayerTreeHostContextTest {
private:
int commits_;
- scoped_refptr<FakeScrollbarLayer> scrollbar_layer_;
+ scoped_refptr<FakePaintedScrollbarLayer> scrollbar_layer_;
};
SINGLE_AND_MULTI_THREAD_TEST_F(ScrollbarLayerLostContext);
@@ -1633,27 +1832,107 @@ class UIResourceLostTest : public LayerTreeHostContextTest {
virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); }
virtual void AfterTest() OVERRIDE {}
+ // This is called on the main thread after each commit and
+ // DidActivateTreeOnThread, with the value of time_step_ at the time
+ // of the call to DidActivateTreeOnThread. Similar tests will do
+ // work on the main thread in DidCommit but that is unsuitable because
+ // the main thread work for these tests must happen after
+ // DidActivateTreeOnThread, which happens after DidCommit with impl-side
+ // painting.
+ virtual void StepCompleteOnMainThread(int time_step) = 0;
+
+ // Called after DidActivateTreeOnThread. If this is done during the commit,
+ // the call to StepCompleteOnMainThread will not occur until after
+ // the commit completes, because the main thread is blocked.
+ void PostStepCompleteToMainThread() {
+ proxy()->MainThreadTaskRunner()->PostTask(
+ FROM_HERE,
+ base::Bind(
+ &UIResourceLostTest::StepCompleteOnMainThreadInternal,
+ base::Unretained(this),
+ time_step_));
+ }
+
+ void PostLoseContextToImplThread() {
+ EXPECT_TRUE(layer_tree_host()->proxy()->IsMainThread());
+ base::SingleThreadTaskRunner* task_runner =
+ HasImplThread() ? ImplThreadTaskRunner()
+ : base::MessageLoopProxy::current();
+ task_runner->PostTask(
+ FROM_HERE,
+ base::Bind(
+ &LayerTreeHostContextTest::LoseContext,
+ base::Unretained(this)));
+ }
+
protected:
int time_step_;
scoped_ptr<FakeScopedUIResource> ui_resource_;
+
+ private:
+ void StepCompleteOnMainThreadInternal(int step) {
+ EXPECT_TRUE(layer_tree_host()->proxy()->IsMainThread());
+ StepCompleteOnMainThread(step);
+ }
};
-// Losing context after an UI resource has been created.
-class UIResourceLostAfterCommit : public UIResourceLostTest {
+class UIResourceLostTestSimple : public UIResourceLostTest {
public:
+ // This is called when the commit is complete and the new layer tree has been
+ // activated.
+ virtual void StepCompleteOnImplThread(LayerTreeHostImpl* impl) = 0;
+
virtual void CommitCompleteOnThread(LayerTreeHostImpl* impl) OVERRIDE {
- LayerTreeHostContextTest::CommitCompleteOnThread(impl);
- switch (time_step_) {
+ if (!layer_tree_host()->settings().impl_side_painting) {
+ StepCompleteOnImplThread(impl);
+ PostStepCompleteToMainThread();
+ ++time_step_;
+ }
+ }
+
+ virtual void DidActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE {
+ if (layer_tree_host()->settings().impl_side_painting) {
+ StepCompleteOnImplThread(impl);
+ PostStepCompleteToMainThread();
+ ++time_step_;
+ }
+ }
+};
+
+// Losing context after an UI resource has been created.
+class UIResourceLostAfterCommit : public UIResourceLostTestSimple {
+ public:
+ virtual void StepCompleteOnMainThread(int step) OVERRIDE {
+ EXPECT_TRUE(layer_tree_host()->proxy()->IsMainThread());
+ switch (step) {
case 0:
ui_resource_ = FakeScopedUIResource::Create(layer_tree_host());
// Expects a valid UIResourceId.
EXPECT_NE(0, ui_resource_->id());
PostSetNeedsCommitToMainThread();
break;
+ case 4:
+ // Release resource before ending the test.
+ ui_resource_.reset();
+ EndTest();
+ break;
+ case 5:
+ // Single thread proxy issues extra commits after context lost.
+ // http://crbug.com/287250
+ if (HasImplThread())
+ NOTREACHED();
+ break;
+ case 6:
+ NOTREACHED();
+ }
+ }
+
+ virtual void StepCompleteOnImplThread(LayerTreeHostImpl* impl) OVERRIDE {
+ LayerTreeHostContextTest::CommitCompleteOnThread(impl);
+ switch (time_step_) {
case 1:
// The resource should have been created on LTHI after the commit.
- if (!layer_tree_host()->settings().impl_side_painting)
- EXPECT_NE(0u, impl->ResourceIdForUIResource(ui_resource_->id()));
+ EXPECT_NE(0u, impl->ResourceIdForUIResource(ui_resource_->id()));
PostSetNeedsCommitToMainThread();
break;
case 2:
@@ -1665,32 +1944,11 @@ class UIResourceLostAfterCommit : public UIResourceLostTest {
EXPECT_EQ(1, ui_resource_->lost_resource_count);
// Resource Id on the impl-side have been recreated as well. Note
// that the same UIResourceId persists after the context lost.
- if (!layer_tree_host()->settings().impl_side_painting)
- EXPECT_NE(0u, impl->ResourceIdForUIResource(ui_resource_->id()));
+ EXPECT_NE(0u, impl->ResourceIdForUIResource(ui_resource_->id()));
PostSetNeedsCommitToMainThread();
break;
- case 4:
- // Release resource before ending test.
- ui_resource_.reset();
- EndTest();
- break;
}
}
-
- virtual void DidActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE {
- LayerTreeHostContextTest::DidActivateTreeOnThread(impl);
- switch (time_step_) {
- case 1:
- if (layer_tree_host()->settings().impl_side_painting)
- EXPECT_NE(0u, impl->ResourceIdForUIResource(ui_resource_->id()));
- break;
- case 3:
- if (layer_tree_host()->settings().impl_side_painting)
- EXPECT_NE(0u, impl->ResourceIdForUIResource(ui_resource_->id()));
- break;
- }
- ++time_step_;
- }
};
SINGLE_AND_MULTI_THREAD_TEST_F(UIResourceLostAfterCommit);
@@ -1704,34 +1962,18 @@ SINGLE_AND_MULTI_THREAD_TEST_F(UIResourceLostAfterCommit);
// test_id1_ to have been created.
// 3. Create one resource -> Delete that same resource -> Context Lost => Expect
// the resource to not exist in the manager.
-class UIResourceLostBeforeCommit : public UIResourceLostTest {
+class UIResourceLostBeforeCommit : public UIResourceLostTestSimple {
public:
UIResourceLostBeforeCommit()
: test_id0_(0),
test_id1_(0) {}
- virtual void CommitCompleteOnThread(LayerTreeHostImpl* impl) OVERRIDE {
- LayerTreeHostContextTest::CommitCompleteOnThread(impl);
- switch (time_step_) {
+ virtual void StepCompleteOnMainThread(int step) OVERRIDE {
+ switch (step) {
case 0:
- // Sequence 1:
ui_resource_ = FakeScopedUIResource::Create(layer_tree_host());
- LoseContext();
- // Resource Id on the impl-side should no longer be valid after
- // context is lost.
- EXPECT_EQ(0u, impl->ResourceIdForUIResource(ui_resource_->id()));
- break;
- case 1:
- // The resources should have been recreated.
- EXPECT_EQ(2, ui_resource_->resource_create_count);
- // "resource lost" callback was called once for the resource in the
- // resource map.
- EXPECT_EQ(1, ui_resource_->lost_resource_count);
- // Resource Id on the impl-side have been recreated as well. Note
- // that the same UIResourceId persists after the context lost.
- if (!layer_tree_host()->settings().impl_side_painting)
- EXPECT_NE(0u, impl->ResourceIdForUIResource(ui_resource_->id()));
- PostSetNeedsCommitToMainThread();
+ // Lose the context on the impl thread before the commit.
+ PostLoseContextToImplThread();
break;
case 2:
// Sequence 2:
@@ -1744,21 +1986,10 @@ class UIResourceLostBeforeCommit : public UIResourceLostTest {
test_id1_ = ui_resource_->id();
// Sanity check that two resource creations return different ids.
EXPECT_NE(test_id0_, test_id1_);
- // Lose the context before commit.
- LoseContext();
+ // Lose the context on the impl thread before the commit.
+ PostLoseContextToImplThread();
break;
case 3:
- if (!layer_tree_host()->settings().impl_side_painting) {
- // The previous resource should have been deleted.
- EXPECT_EQ(0u, impl->ResourceIdForUIResource(test_id0_));
- // The second resource should have been created.
- EXPECT_NE(0u, impl->ResourceIdForUIResource(test_id1_));
- }
-
- // The second resource called the resource callback once and since the
- // context is lost, a "resource lost" callback was also issued.
- EXPECT_EQ(2, ui_resource_->resource_create_count);
- EXPECT_EQ(1, ui_resource_->lost_resource_count);
// Clear the manager of resources.
ui_resource_.reset();
PostSetNeedsCommitToMainThread();
@@ -1773,45 +2004,66 @@ class UIResourceLostBeforeCommit : public UIResourceLostTest {
// destructor (so usually ui_resource_.reset()). But here we need
// ui_resource_ for the next step, so call DeleteUIResource directly.
layer_tree_host()->DeleteUIResource(test_id0_);
- LoseContext();
+ // Delete the resouce and then lose the context.
+ PostLoseContextToImplThread();
break;
case 5:
- // Expect the resource callback to have been called once.
- EXPECT_EQ(1, ui_resource_->resource_create_count);
- // No "resource lost" callbacks.
- EXPECT_EQ(0, ui_resource_->lost_resource_count);
- if (!layer_tree_host()->settings().impl_side_painting) {
- // The UI resource id should not be valid
- EXPECT_EQ(0u, impl->ResourceIdForUIResource(test_id0_));
- }
- PostSetNeedsCommitToMainThread();
- break;
- case 6:
+ // Release resource before ending the test.
ui_resource_.reset();
EndTest();
break;
+ case 6:
+ // Single thread proxy issues extra commits after context lost.
+ // http://crbug.com/287250
+ if (HasImplThread())
+ NOTREACHED();
+ break;
+ case 8:
+ NOTREACHED();
}
}
- virtual void DidActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE {
- LayerTreeHostContextTest::DidActivateTreeOnThread(impl);
+ virtual void StepCompleteOnImplThread(LayerTreeHostImpl* impl) OVERRIDE {
+ LayerTreeHostContextTest::CommitCompleteOnThread(impl);
switch (time_step_) {
case 1:
- if (layer_tree_host()->settings().impl_side_painting)
- EXPECT_NE(0u, impl->ResourceIdForUIResource(ui_resource_->id()));
+ // Sequence 1 (continued):
+ // The first context lost happens before the resources were created,
+ // and because it resulted in no resources being destroyed, it does not
+ // trigger resource re-creation.
+ EXPECT_EQ(1, ui_resource_->resource_create_count);
+ EXPECT_EQ(0, ui_resource_->lost_resource_count);
+ // Resource Id on the impl-side has been created.
+ PostSetNeedsCommitToMainThread();
break;
case 3:
- if (layer_tree_host()->settings().impl_side_painting) {
- EXPECT_EQ(0u, impl->ResourceIdForUIResource(test_id0_));
+ // Sequence 2 (continued):
+ // The previous resource should have been deleted.
+ EXPECT_EQ(0u, impl->ResourceIdForUIResource(test_id0_));
+ if (HasImplThread()) {
+ // The second resource should have been created.
EXPECT_NE(0u, impl->ResourceIdForUIResource(test_id1_));
+ } else {
+ // The extra commit that happens at context lost in the single thread
+ // proxy changes the timing so that the resource has been destroyed.
+ // http://crbug.com/287250
+ EXPECT_EQ(0u, impl->ResourceIdForUIResource(test_id1_));
}
+ // The second resource called the resource callback once and since the
+ // context is lost, a "resource lost" callback was also issued.
+ EXPECT_EQ(2, ui_resource_->resource_create_count);
+ EXPECT_EQ(1, ui_resource_->lost_resource_count);
break;
case 5:
- if (layer_tree_host()->settings().impl_side_painting)
- EXPECT_EQ(0u, impl->ResourceIdForUIResource(test_id0_));
+ // Sequence 3 (continued):
+ // Expect the resource callback to have been called once.
+ EXPECT_EQ(1, ui_resource_->resource_create_count);
+ // No "resource lost" callbacks.
+ EXPECT_EQ(0, ui_resource_->lost_resource_count);
+ // The UI resource id should not be valid
+ EXPECT_EQ(0u, impl->ResourceIdForUIResource(test_id0_));
break;
}
- ++time_step_;
}
private:
@@ -1824,34 +2076,43 @@ SINGLE_AND_MULTI_THREAD_TEST_F(UIResourceLostBeforeCommit);
// Losing UI resource before the pending trees is activated but after the
// commit. Impl-side-painting only.
class UIResourceLostBeforeActivateTree : public UIResourceLostTest {
- virtual void CommitCompleteOnThread(LayerTreeHostImpl* impl) OVERRIDE {
- LayerTreeHostContextTest::CommitCompleteOnThread(impl);
- switch (time_step_) {
+ virtual void StepCompleteOnMainThread(int step) OVERRIDE {
+ EXPECT_TRUE(layer_tree_host()->proxy()->IsMainThread());
+ switch (step) {
case 0:
ui_resource_ = FakeScopedUIResource::Create(layer_tree_host());
PostSetNeedsCommitToMainThread();
break;
- case 2:
- PostSetNeedsCommitToMainThread();
- break;
case 3:
test_id_ = ui_resource_->id();
ui_resource_.reset();
PostSetNeedsCommitToMainThread();
break;
- case 4:
- PostSetNeedsCommitToMainThread();
- break;
case 5:
+ // Release resource before ending the test.
+ ui_resource_.reset();
EndTest();
break;
+ case 6:
+ // Make sure no extra commits happened.
+ NOTREACHED();
}
}
- virtual void WillActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE {
+ virtual void CommitCompleteOnThread(LayerTreeHostImpl* impl) OVERRIDE {
+ LayerTreeHostContextTest::CommitCompleteOnThread(impl);
switch (time_step_) {
- case 0:
+ case 2:
+ PostSetNeedsCommitToMainThread();
break;
+ case 4:
+ PostSetNeedsCommitToMainThread();
+ break;
+ }
+ }
+
+ virtual void WillActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE {
+ switch (time_step_) {
case 1:
// The resource creation callback has been called.
EXPECT_EQ(1, ui_resource_->resource_create_count);
@@ -1884,6 +2145,8 @@ class UIResourceLostBeforeActivateTree : public UIResourceLostTest {
EXPECT_EQ(0u, impl->ResourceIdForUIResource(test_id_));
break;
}
+
+ PostStepCompleteToMainThread();
++time_step_;
}
@@ -1901,5 +2164,91 @@ TEST_F(UIResourceLostBeforeActivateTree,
RunTest(true, true, true);
}
+// Resources evicted explicitly and by visibility changes.
+class UIResourceLostEviction : public UIResourceLostTestSimple {
+ public:
+ virtual void StepCompleteOnMainThread(int step) OVERRIDE {
+ EXPECT_TRUE(layer_tree_host()->proxy()->IsMainThread());
+ switch (step) {
+ case 0:
+ ui_resource_ = FakeScopedUIResource::Create(layer_tree_host());
+ EXPECT_NE(0, ui_resource_->id());
+ PostSetNeedsCommitToMainThread();
+ break;
+ case 2:
+ // Make the tree not visible.
+ PostSetVisibleToMainThread(false);
+ break;
+ case 3:
+ // Release resource before ending the test.
+ ui_resource_.reset();
+ EndTest();
+ break;
+ case 4:
+ NOTREACHED();
+ }
+ }
+
+ virtual void DidSetVisibleOnImplTree(LayerTreeHostImpl* impl,
+ bool visible) OVERRIDE {
+ TestWebGraphicsContext3D* context = static_cast<TestWebGraphicsContext3D*>(
+ impl->output_surface()->context_provider()->Context3d());
+ if (!visible) {
+ // All resources should have been evicted.
+ ASSERT_EQ(0u, context->NumTextures());
+ EXPECT_EQ(0u, impl->ResourceIdForUIResource(ui_resource_->id()));
+ EXPECT_EQ(2, ui_resource_->resource_create_count);
+ EXPECT_EQ(1, ui_resource_->lost_resource_count);
+ // Drawing is disabled both because of the evicted resources and
+ // because the renderer is not visible.
+ EXPECT_FALSE(impl->CanDraw());
+ // Make the renderer visible again.
+ PostSetVisibleToMainThread(true);
+ }
+ }
+
+ virtual void StepCompleteOnImplThread(LayerTreeHostImpl* impl) OVERRIDE {
+ TestWebGraphicsContext3D* context = static_cast<TestWebGraphicsContext3D*>(
+ impl->output_surface()->context_provider()->Context3d());
+ LayerTreeHostContextTest::CommitCompleteOnThread(impl);
+ switch (time_step_) {
+ case 1:
+ // The resource should have been created on LTHI after the commit.
+ ASSERT_EQ(1u, context->NumTextures());
+ EXPECT_NE(0u, impl->ResourceIdForUIResource(ui_resource_->id()));
+ EXPECT_EQ(1, ui_resource_->resource_create_count);
+ EXPECT_EQ(0, ui_resource_->lost_resource_count);
+ EXPECT_TRUE(impl->CanDraw());
+ // Evict all UI resources. This will trigger a commit.
+ impl->EvictAllUIResources();
+ ASSERT_EQ(0u, context->NumTextures());
+ EXPECT_EQ(0u, impl->ResourceIdForUIResource(ui_resource_->id()));
+ EXPECT_EQ(1, ui_resource_->resource_create_count);
+ EXPECT_EQ(0, ui_resource_->lost_resource_count);
+ EXPECT_FALSE(impl->CanDraw());
+ break;
+ case 2:
+ // The resource should have been recreated.
+ ASSERT_EQ(1u, context->NumTextures());
+ EXPECT_NE(0u, impl->ResourceIdForUIResource(ui_resource_->id()));
+ EXPECT_EQ(2, ui_resource_->resource_create_count);
+ EXPECT_EQ(1, ui_resource_->lost_resource_count);
+ EXPECT_TRUE(impl->CanDraw());
+ break;
+ case 3:
+ // The resource should have been recreated after visibility was
+ // restored.
+ ASSERT_EQ(1u, context->NumTextures());
+ EXPECT_NE(0u, impl->ResourceIdForUIResource(ui_resource_->id()));
+ EXPECT_EQ(3, ui_resource_->resource_create_count);
+ EXPECT_EQ(2, ui_resource_->lost_resource_count);
+ EXPECT_TRUE(impl->CanDraw());
+ break;
+ }
+ }
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(UIResourceLostEviction);
+
} // namespace
} // namespace cc
diff --git a/chromium/cc/trees/layer_tree_host_unittest_damage.cc b/chromium/cc/trees/layer_tree_host_unittest_damage.cc
index 805c9275c14..64c7d4b7c30 100644
--- a/chromium/cc/trees/layer_tree_host_unittest_damage.cc
+++ b/chromium/cc/trees/layer_tree_host_unittest_damage.cc
@@ -4,9 +4,15 @@
#include "cc/trees/layer_tree_host.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/location.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/time/time.h"
#include "cc/test/fake_content_layer.h"
#include "cc/test/fake_content_layer_client.h"
-#include "cc/test/fake_scrollbar_layer.h"
+#include "cc/test/fake_painted_scrollbar_layer.h"
+#include "cc/test/fake_picture_layer.h"
#include "cc/test/layer_tree_test.h"
#include "cc/trees/damage_tracker.h"
#include "cc/trees/layer_tree_impl.h"
@@ -17,6 +23,225 @@ namespace {
// These tests deal with damage tracking.
class LayerTreeHostDamageTest : public LayerTreeTest {};
+// Changing visibility alone does not cause drawing.
+class LayerTreeHostDamageTestSetVisibleDoesNotDraw
+ : public LayerTreeHostDamageTest {
+ virtual void BeginTest() OVERRIDE {
+ step_ = 0;
+ PostSetNeedsCommitToMainThread();
+ }
+
+ virtual void SetupTree() OVERRIDE {
+ // Viewport is 10x10.
+ scoped_refptr<FakeContentLayer> root = FakeContentLayer::Create(&client_);
+ root->SetBounds(gfx::Size(10, 10));
+
+ layer_tree_host()->SetRootLayer(root);
+ LayerTreeHostDamageTest::SetupTree();
+ }
+
+ virtual bool PrepareToDrawOnThread(LayerTreeHostImpl* impl,
+ LayerTreeHostImpl::FrameData* frame_data,
+ bool result) OVERRIDE {
+ EXPECT_TRUE(result);
+
+ RenderSurfaceImpl* root_surface =
+ impl->active_tree()->root_layer()->render_surface();
+ gfx::RectF root_damage =
+ root_surface->damage_tracker()->current_damage_rect();
+
+ switch (step_) {
+ case 0:
+ // The first frame has full damage.
+ EXPECT_EQ(gfx::RectF(10.f, 10.f).ToString(), root_damage.ToString());
+
+ // No evictions when we become not-visible.
+ impl->SetMemoryPolicy(ManagedMemoryPolicy(
+ 1000 * 1000 * 1000,
+ ManagedMemoryPolicy::CUTOFF_ALLOW_EVERYTHING,
+ 1000 * 1000 * 1000,
+ ManagedMemoryPolicy::CUTOFF_ALLOW_EVERYTHING,
+ ManagedMemoryPolicy::kDefaultNumResourcesLimit));
+
+ PostSetVisibleToMainThread(false);
+ break;
+ case 1:
+ // The compositor has been set not-visible.
+ EXPECT_FALSE(impl->visible());
+ // This frame not visible, so not drawn.
+ NOTREACHED();
+ break;
+ case 2:
+ // The compositor has been set visible again.
+ EXPECT_TRUE(impl->visible());
+ // But it still does not draw.
+ NOTREACHED();
+ break;
+ case 3:
+ // Finally we force a draw, but it will have no damage.
+ EXPECT_EQ(gfx::RectF().ToString(), root_damage.ToString());
+ EndTest();
+ break;
+ case 4:
+ NOTREACHED();
+ }
+ return result;
+ }
+
+ virtual void DidSetVisibleOnImplTree(LayerTreeHostImpl* impl,
+ bool visible) OVERRIDE {
+ if (!visible) {
+ EXPECT_EQ(0, step_);
+ PostSetVisibleToMainThread(true);
+ } else {
+ EXPECT_EQ(1, step_);
+
+ base::MessageLoopProxy::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&LayerTreeHostDamageTestSetVisibleDoesNotDraw::Redraw,
+ base::Unretained(this),
+ impl),
+ base::TimeDelta::FromMilliseconds(10));
+ }
+ ++step_;
+ }
+
+ void Redraw(LayerTreeHostImpl* impl) {
+ EXPECT_EQ(2, step_);
+ impl->SetNeedsRedraw();
+ ++step_;
+ }
+
+ virtual void AfterTest() OVERRIDE {}
+
+ int step_;
+ FakeContentLayerClient client_;
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostDamageTestSetVisibleDoesNotDraw);
+
+// LayerTreeHost::SetNeedsRedraw should damage the whole viewport.
+class LayerTreeHostDamageTestSetNeedsRedraw
+ : public LayerTreeHostDamageTest {
+ virtual void SetupTree() OVERRIDE {
+ // Viewport is 10x10.
+ scoped_refptr<FakeContentLayer> root = FakeContentLayer::Create(&client_);
+ root->SetBounds(gfx::Size(10, 10));
+
+ layer_tree_host()->SetRootLayer(root);
+ LayerTreeHostDamageTest::SetupTree();
+ }
+
+ virtual void BeginTest() OVERRIDE {
+ draw_count_ = 0;
+ PostSetNeedsCommitToMainThread();
+ }
+
+ virtual void DidCommitAndDrawFrame() OVERRIDE {
+ switch (layer_tree_host()->source_frame_number()) {
+ case 1:
+ layer_tree_host()->SetNeedsRedraw();
+ break;
+ }
+ }
+
+ virtual bool PrepareToDrawOnThread(LayerTreeHostImpl* impl,
+ LayerTreeHostImpl::FrameData* frame_data,
+ bool result) OVERRIDE {
+ EXPECT_TRUE(result);
+
+ RenderSurfaceImpl* root_surface =
+ impl->active_tree()->root_layer()->render_surface();
+ gfx::RectF root_damage =
+ root_surface->damage_tracker()->current_damage_rect();
+
+ switch (draw_count_) {
+ case 0:
+ // The first frame has full damage.
+ EXPECT_EQ(gfx::RectF(10.f, 10.f).ToString(), root_damage.ToString());
+ break;
+ case 1:
+ // The second frame has full damage.
+ EXPECT_EQ(gfx::RectF(10.f, 10.f).ToString(), root_damage.ToString());
+ EndTest();
+ break;
+ case 2:
+ NOTREACHED();
+ }
+
+ ++draw_count_;
+ return result;
+ }
+
+ virtual void AfterTest() OVERRIDE {}
+
+ int draw_count_;
+ FakeContentLayerClient client_;
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostDamageTestSetNeedsRedraw);
+
+// LayerTreeHost::SetViewportSize should damage the whole viewport.
+class LayerTreeHostDamageTestSetViewportSize
+ : public LayerTreeHostDamageTest {
+ virtual void SetupTree() OVERRIDE {
+ // Viewport is 10x10.
+ scoped_refptr<FakeContentLayer> root = FakeContentLayer::Create(&client_);
+ root->SetBounds(gfx::Size(10, 10));
+
+ layer_tree_host()->SetRootLayer(root);
+ LayerTreeHostDamageTest::SetupTree();
+ }
+
+ virtual void BeginTest() OVERRIDE {
+ draw_count_ = 0;
+ PostSetNeedsCommitToMainThread();
+ }
+
+ virtual void DidCommitAndDrawFrame() OVERRIDE {
+ switch (layer_tree_host()->source_frame_number()) {
+ case 1:
+ layer_tree_host()->SetViewportSize(gfx::Size(15, 15));
+ break;
+ }
+ }
+
+ virtual bool PrepareToDrawOnThread(LayerTreeHostImpl* impl,
+ LayerTreeHostImpl::FrameData* frame_data,
+ bool result) OVERRIDE {
+ EXPECT_TRUE(result);
+
+ RenderSurfaceImpl* root_surface =
+ impl->active_tree()->root_layer()->render_surface();
+ gfx::RectF root_damage =
+ root_surface->damage_tracker()->current_damage_rect();
+
+ switch (draw_count_) {
+ case 0:
+ // The first frame has full damage.
+ EXPECT_EQ(gfx::RectF(10.f, 10.f).ToString(), root_damage.ToString());
+ break;
+ case 1:
+ // The second frame has full damage.
+ EXPECT_EQ(gfx::RectF(15.f, 15.f).ToString(), root_damage.ToString());
+ EndTest();
+ break;
+ case 2:
+ NOTREACHED();
+ }
+
+ ++draw_count_;
+ return result;
+ }
+
+ virtual void AfterTest() OVERRIDE {}
+
+ int draw_count_;
+ FakeContentLayerClient client_;
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostDamageTestSetViewportSize);
+
class LayerTreeHostDamageTestNoDamageDoesNotSwap
: public LayerTreeHostDamageTest {
virtual void BeginTest() OVERRIDE {
@@ -286,16 +511,10 @@ class LayerTreeHostDamageTestForcedFullDamage : public LayerTreeHostDamageTest {
SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostDamageTestForcedFullDamage);
-class LayerTreeHostDamageTestScrollbarDoesDamage
- : public LayerTreeHostDamageTest {
- virtual void BeginTest() OVERRIDE {
- did_swaps_ = 0;
- PostSetNeedsCommitToMainThread();
- }
-
+class LayerTreeHostScrollbarDamageTest : public LayerTreeHostDamageTest {
virtual void SetupTree() OVERRIDE {
scoped_refptr<Layer> root_layer = Layer::Create();
- root_layer->SetBounds(gfx::Size(400,400));
+ root_layer->SetBounds(gfx::Size(400, 400));
layer_tree_host()->SetRootLayer(root_layer);
scoped_refptr<Layer> content_layer = FakeContentLayer::Create(&client_);
@@ -306,7 +525,7 @@ class LayerTreeHostDamageTestScrollbarDoesDamage
root_layer->AddChild(content_layer);
scoped_refptr<Layer> scrollbar_layer =
- FakeScrollbarLayer::Create(false, true, content_layer->id());
+ FakePaintedScrollbarLayer::Create(false, true, content_layer->id());
scrollbar_layer->SetPosition(gfx::Point(300, 300));
scrollbar_layer->SetBounds(gfx::Size(10, 100));
root_layer->AddChild(scrollbar_layer);
@@ -320,6 +539,17 @@ class LayerTreeHostDamageTestScrollbarDoesDamage
LayerTreeHostDamageTest::SetupTree();
}
+ private:
+ FakeContentLayerClient client_;
+};
+
+class LayerTreeHostDamageTestScrollbarDoesDamage
+ : public LayerTreeHostScrollbarDamageTest {
+ virtual void BeginTest() OVERRIDE {
+ did_swaps_ = 0;
+ PostSetNeedsCommitToMainThread();
+ }
+
virtual bool PrepareToDrawOnThread(LayerTreeHostImpl* host_impl,
LayerTreeHostImpl::FrameData* frame_data,
bool result) OVERRIDE {
@@ -334,16 +564,16 @@ class LayerTreeHostDamageTestScrollbarDoesDamage
// The first frame has damage, so we should draw and swap.
break;
case 1:
- // The second frame should damage the scrollbars.
- EXPECT_TRUE(root_damage.Contains(gfx::Rect(300, 300, 10, 100)));
+ // The second frame should not damage the scrollbars.
+ EXPECT_FALSE(root_damage.Intersects(gfx::Rect(300, 300, 10, 100)));
break;
case 2:
// The third frame should damage the scrollbars.
EXPECT_TRUE(root_damage.Contains(gfx::Rect(300, 300, 10, 100)));
break;
case 3:
- // The fourth frame should not damage the scrollbars.
- EXPECT_FALSE(root_damage.Intersects(gfx::Rect(300, 300, 10, 100)));
+ // The fourth frame should damage the scrollbars.
+ EXPECT_TRUE(root_damage.Contains(gfx::Rect(300, 300, 10, 100)));
EndTest();
break;
}
@@ -358,34 +588,204 @@ class LayerTreeHostDamageTestScrollbarDoesDamage
LayerImpl* scroll_layer = root->children()[0];
switch (did_swaps_) {
case 1:
- host_impl->ScrollBegin(gfx::Point(1,1), InputHandler::Wheel);
- EXPECT_TRUE(host_impl->ScrollBy(gfx::Point(),
- gfx::Vector2dF(10.0, 10.0)));
+ // Test that modifying the position of the content layer (not
+ // scrolling) won't damage the scrollbar.
+ scroll_layer->SetPosition(gfx::Point(1, 1));
+ scroll_layer->SetScrollOffset(scroll_layer->scroll_offset());
+ host_impl->SetNeedsRedraw();
break;
case 2:
- scroll_layer->SetMaxScrollOffset(gfx::Vector2d(60, 100));
+ scroll_layer->ScrollBy(gfx::Vector2dF(10.f, 10.f));
host_impl->SetNeedsRedraw();
break;
case 3:
- // Test that modifying the position of the content layer (not
- // scrolling) won't damage the scrollbar.
- scroll_layer->SetPosition(gfx::Point(1,1));
- scroll_layer->SetScrollOffset(scroll_layer->scroll_offset());
+ scroll_layer->SetMaxScrollOffset(gfx::Vector2d(60, 100));
host_impl->SetNeedsRedraw();
break;
}
-
}
virtual void AfterTest() OVERRIDE {
EXPECT_EQ(4, did_swaps_);
}
- FakeContentLayerClient client_;
int did_swaps_;
};
MULTI_THREAD_TEST_F(LayerTreeHostDamageTestScrollbarDoesDamage);
+class DISABLED_LayerTreeHostDamageTestScrollbarCommitDoesNoDamage
+ : public LayerTreeHostScrollbarDamageTest {
+ virtual void BeginTest() OVERRIDE {
+ did_swaps_ = 0;
+ PostSetNeedsCommitToMainThread();
+ }
+
+ virtual bool PrepareToDrawOnThread(LayerTreeHostImpl* host_impl,
+ LayerTreeHostImpl::FrameData* frame_data,
+ bool result) OVERRIDE {
+ EXPECT_TRUE(result);
+ RenderSurfaceImpl* root_surface =
+ host_impl->active_tree()->root_layer()->render_surface();
+ gfx::RectF root_damage =
+ root_surface->damage_tracker()->current_damage_rect();
+ root_damage.Intersect(root_surface->content_rect());
+ int frame = host_impl->active_tree()->source_frame_number();
+ switch (did_swaps_) {
+ case 0:
+ // The first frame has damage, so we should draw and swap.
+ EXPECT_EQ(0, frame);
+ break;
+ case 1:
+ // The second frame has scrolled, so the scrollbar should be damaged.
+ EXPECT_EQ(0, frame);
+ EXPECT_TRUE(root_damage.Contains(gfx::Rect(300, 300, 10, 100)));
+ break;
+ case 2:
+ // The third frame (after the commit) has no changes, so it shouldn't.
+ EXPECT_EQ(1, frame);
+ EXPECT_FALSE(root_damage.Intersects(gfx::Rect(300, 300, 10, 100)));
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+ return result;
+ }
+
+ virtual void SwapBuffersOnThread(LayerTreeHostImpl* host_impl,
+ bool result) OVERRIDE {
+ ++did_swaps_;
+ EXPECT_TRUE(result);
+ LayerImpl* root = host_impl->active_tree()->root_layer();
+ LayerImpl* scroll_layer = root->children()[0];
+ switch (did_swaps_) {
+ case 1:
+ // Scroll on the thread. This should damage the scrollbar for the
+ // next draw on the thread.
+ scroll_layer->ScrollBy(gfx::Vector2dF(10.f, 10.f));
+ host_impl->SetNeedsRedraw();
+ break;
+ case 2:
+ // Forcibly send the scroll to the main thread.
+ PostSetNeedsCommitToMainThread();
+ break;
+ case 3:
+ // First swap after second commit.
+ EndTest();
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+ }
+
+ virtual void AfterTest() OVERRIDE {
+ EXPECT_EQ(3, did_swaps_);
+ }
+
+ int did_swaps_;
+};
+
+MULTI_THREAD_TEST_F(
+ DISABLED_LayerTreeHostDamageTestScrollbarCommitDoesNoDamage);
+
+class LayerTreeHostDamageTestVisibleTilesStillTriggerDraws
+ : public LayerTreeHostDamageTest {
+
+ virtual void InitializeSettings(LayerTreeSettings* settings) OVERRIDE {
+ settings->impl_side_painting = true;
+ }
+
+ virtual void BeginTest() OVERRIDE {
+ PostSetNeedsCommitToMainThread();
+ }
+
+ virtual void SetupTree() OVERRIDE {
+ scoped_refptr<FakePictureLayer> root = FakePictureLayer::Create(&client_);
+ root->SetBounds(gfx::Size(500, 500));
+ layer_tree_host()->SetRootLayer(root);
+ LayerTreeHostDamageTest::SetupTree();
+
+ swap_count_ = 0;
+ prepare_to_draw_count_ = 0;
+ update_visible_tile_count_ = 0;
+ }
+
+ virtual bool PrepareToDrawOnThread(LayerTreeHostImpl* host_impl,
+ LayerTreeHostImpl::FrameData* frame_data,
+ bool result) OVERRIDE {
+ EXPECT_TRUE(result);
+ prepare_to_draw_count_++;
+ switch (prepare_to_draw_count_) {
+ case 1:
+ // Detect that we have an incomplete tile, during the first frame.
+ // The first frame should have damage.
+ frame_data->contains_incomplete_tile = true;
+ DCHECK(!frame_data->has_no_damage);
+ break;
+ case 2:
+ // Make a no-damage frame. We early out and can't detect
+ // incomplete tiles, even if they still exist.
+ frame_data->contains_incomplete_tile = false;
+ frame_data->has_no_damage = true;
+ break;
+ case 3:
+ // Trigger the last swap for the completed tile.
+ frame_data->contains_incomplete_tile = false;
+ frame_data->has_no_damage = false;
+ EndTest();
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+
+ return result;
+ }
+
+ virtual void UpdateVisibleTilesOnThread(
+ LayerTreeHostImpl* host_impl) OVERRIDE {
+ // Simulate creating some visible tiles (that trigger prepare-to-draws).
+ // The first we make into a no-damage-frame during prepare-to-draw (see
+ // above). This is to ensure we still get UpdateVisibleTiles calls after
+ // a no-damage or aborted frame.
+ update_visible_tile_count_++;
+ switch (update_visible_tile_count_) {
+ case 3:
+ case 6:
+ host_impl->DidInitializeVisibleTileForTesting();
+ break;
+ case 7:
+ NOTREACHED();
+ break;
+ }
+ }
+
+ virtual void SwapBuffersOnThread(LayerTreeHostImpl* host_impl,
+ bool didSwap) OVERRIDE {
+ if (!didSwap)
+ return;
+ ++swap_count_;
+ }
+
+ virtual void AfterTest() OVERRIDE {
+ // We should keep getting update-visible-tiles calls
+ // until we report there are no more incomplete-tiles.
+ EXPECT_EQ(update_visible_tile_count_, 6);
+ // First frame, plus two triggered by DidInitializeVisibleTile()
+ EXPECT_EQ(prepare_to_draw_count_, 3);
+ // First swap, plus final swap (contained damage).
+ EXPECT_EQ(swap_count_, 2);
+ }
+
+ FakeContentLayerClient client_;
+ int swap_count_;
+ int prepare_to_draw_count_;
+ int update_visible_tile_count_;
+};
+
+MULTI_THREAD_TEST_F(LayerTreeHostDamageTestVisibleTilesStillTriggerDraws);
+
} // namespace
} // namespace cc
diff --git a/chromium/cc/trees/layer_tree_host_unittest_delegated.cc b/chromium/cc/trees/layer_tree_host_unittest_delegated.cc
index d48931328af..012f6f82fdf 100644
--- a/chromium/cc/trees/layer_tree_host_unittest_delegated.cc
+++ b/chromium/cc/trees/layer_tree_host_unittest_delegated.cc
@@ -4,25 +4,73 @@
#include "cc/trees/layer_tree_host.h"
+#include <algorithm>
+
#include "base/bind.h"
+#include "base/location.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "base/time/time.h"
#include "cc/layers/delegated_renderer_layer.h"
#include "cc/layers/delegated_renderer_layer_client.h"
#include "cc/layers/delegated_renderer_layer_impl.h"
#include "cc/output/compositor_frame.h"
#include "cc/output/compositor_frame_ack.h"
#include "cc/output/delegated_frame_data.h"
+#include "cc/quads/render_pass_draw_quad.h"
#include "cc/quads/shared_quad_state.h"
#include "cc/quads/texture_draw_quad.h"
+#include "cc/resources/returned_resource.h"
#include "cc/test/fake_delegated_renderer_layer.h"
#include "cc/test/fake_delegated_renderer_layer_impl.h"
#include "cc/test/fake_output_surface.h"
#include "cc/test/layer_tree_test.h"
#include "cc/trees/layer_tree_impl.h"
#include "gpu/GLES2/gl2extchromium.h"
+#include "third_party/WebKit/public/platform/WebGraphicsContext3D.h"
namespace cc {
namespace {
+bool ReturnedResourceLower(const ReturnedResource& a,
+ const ReturnedResource& b) {
+ return a.id < b.id;
+}
+
+// Tests if the list of resources matches an expectation, modulo the order.
+bool ResourcesMatch(ReturnedResourceArray actual,
+ unsigned* expected,
+ size_t expected_count) {
+ std::sort(actual.begin(), actual.end(), ReturnedResourceLower);
+ std::sort(expected, expected + expected_count);
+ size_t actual_index = 0;
+
+ // for each element of the expected array, count off one of the actual array
+ // (after checking it matches).
+ for (size_t expected_index = 0; expected_index < expected_count;
+ ++expected_index) {
+ EXPECT_LT(actual_index, actual.size());
+ if (actual_index >= actual.size())
+ return false;
+ EXPECT_EQ(actual[actual_index].id, expected[expected_index]);
+ if (actual[actual_index].id != expected[expected_index])
+ return false;
+ EXPECT_GT(actual[actual_index].count, 0);
+ if (actual[actual_index].count <= 0) {
+ return false;
+ } else {
+ --actual[actual_index].count;
+ if (actual[actual_index].count == 0)
+ ++actual_index;
+ }
+ }
+ EXPECT_EQ(actual_index, actual.size());
+ return actual_index == actual.size();
+}
+
+#define EXPECT_RESOURCES(expected, actual) \
+ EXPECT_TRUE(ResourcesMatch(actual, expected, arraysize(expected)));
+
// These tests deal with delegated renderer layers.
class LayerTreeHostDelegatedTest : public LayerTreeTest {
protected:
@@ -108,6 +156,39 @@ class LayerTreeHostDelegatedTest : public LayerTreeTest {
frame->render_pass_list[0]->quad_list.push_back(quad.PassAs<DrawQuad>());
}
+ void AddRenderPass(DelegatedFrameData* frame,
+ RenderPass::Id id,
+ gfx::Rect output_rect,
+ gfx::Rect damage_rect,
+ const FilterOperations& filters,
+ const FilterOperations& background_filters) {
+ for (size_t i = 0; i < frame->render_pass_list.size(); ++i)
+ DCHECK(id != frame->render_pass_list[i]->id);
+
+ scoped_ptr<RenderPass> pass(RenderPass::Create());
+ pass->SetNew(id,
+ output_rect,
+ damage_rect,
+ gfx::Transform());
+ frame->render_pass_list.push_back(pass.Pass());
+
+ scoped_ptr<SharedQuadState> sqs = SharedQuadState::Create();
+ scoped_ptr<RenderPassDrawQuad> quad = RenderPassDrawQuad::Create();
+
+ quad->SetNew(sqs.get(),
+ output_rect,
+ id,
+ false, // is_replica
+ 0, // mask_resource_id
+ damage_rect,
+ gfx::Rect(0, 0, 1, 1), // mask_uv_rect
+ filters,
+ skia::RefPtr<SkImageFilter>(),
+ background_filters);
+ frame->render_pass_list[0]->shared_quad_state_list.push_back(sqs.Pass());
+ frame->render_pass_list[0]->quad_list.push_back(quad.PassAs<DrawQuad>());
+ }
+
scoped_ptr<DelegatedFrameData> CreateEmptyFrameData() {
scoped_ptr<DelegatedFrameData> frame(new DelegatedFrameData);
return frame.Pass();
@@ -158,7 +239,8 @@ class LayerTreeHostDelegatedTest : public LayerTreeTest {
CompositorFrameAck ack;
for (size_t i = 0; i < resources_to_return.size(); ++i)
output_surface()->ReturnResource(resources_to_return[i], &ack);
- host_impl->OnSwapBuffersComplete(&ack);
+ host_impl->ReclaimResources(&ack);
+ host_impl->OnSwapBuffersComplete();
}
};
@@ -246,8 +328,8 @@ class LayerTreeHostDelegatedTestCreateChildId
FakeDelegatedRendererLayerImpl* delegated_impl =
static_cast<FakeDelegatedRendererLayerImpl*>(root_impl->children()[0]);
- WebKit::WebGraphicsContext3D* context =
- host_impl->resource_provider()->GraphicsContext3D();
+ ContextProvider* context_provider =
+ host_impl->output_surface()->context_provider();
++num_activates_;
switch (num_activates_) {
@@ -255,8 +337,9 @@ class LayerTreeHostDelegatedTestCreateChildId
EXPECT_TRUE(delegated_impl->ChildId());
EXPECT_FALSE(did_reset_child_id_);
- context->loseContextCHROMIUM(GL_GUILTY_CONTEXT_RESET_ARB,
- GL_INNOCENT_CONTEXT_RESET_ARB);
+ context_provider->Context3d()->loseContextCHROMIUM(
+ GL_GUILTY_CONTEXT_RESET_ARB,
+ GL_INNOCENT_CONTEXT_RESET_ARB);
break;
case 3:
EXPECT_TRUE(delegated_impl->ChildId());
@@ -291,6 +374,131 @@ class LayerTreeHostDelegatedTestCreateChildId
SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostDelegatedTestCreateChildId);
+class LayerTreeHostDelegatedTestOffscreenContext_NoFilters
+ : public LayerTreeHostDelegatedTestCaseSingleDelegatedLayer {
+ protected:
+ virtual void BeginTest() OVERRIDE {
+ scoped_ptr<DelegatedFrameData> frame =
+ CreateFrameData(gfx::Rect(0, 0, 1, 1),
+ gfx::Rect(0, 0, 1, 1));
+ delegated_->SetFrameData(frame.Pass());
+
+ PostSetNeedsCommitToMainThread();
+ }
+
+ virtual void DrawLayersOnThread(LayerTreeHostImpl* host_impl) OVERRIDE {
+ EXPECT_FALSE(host_impl->offscreen_context_provider());
+ EndTest();
+ }
+
+ virtual void AfterTest() OVERRIDE {}
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(
+ LayerTreeHostDelegatedTestOffscreenContext_NoFilters);
+
+class LayerTreeHostDelegatedTestOffscreenContext_Filters
+ : public LayerTreeHostDelegatedTestCaseSingleDelegatedLayer {
+ protected:
+ virtual void BeginTest() OVERRIDE {
+ scoped_ptr<DelegatedFrameData> frame =
+ CreateFrameData(gfx::Rect(0, 0, 1, 1),
+ gfx::Rect(0, 0, 1, 1));
+
+ FilterOperations filters;
+ filters.Append(FilterOperation::CreateGrayscaleFilter(0.5f));
+ AddRenderPass(frame.get(),
+ RenderPass::Id(2, 1),
+ gfx::Rect(0, 0, 1, 1),
+ gfx::Rect(0, 0, 1, 1),
+ filters,
+ FilterOperations());
+ delegated_->SetFrameData(frame.Pass());
+
+ PostSetNeedsCommitToMainThread();
+ }
+
+ virtual void DrawLayersOnThread(LayerTreeHostImpl* host_impl) OVERRIDE {
+ bool expect_context = !delegating_renderer();
+ EXPECT_EQ(expect_context, !!host_impl->offscreen_context_provider());
+ EndTest();
+ }
+
+ virtual void AfterTest() OVERRIDE {}
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(
+ LayerTreeHostDelegatedTestOffscreenContext_Filters);
+
+class LayerTreeHostDelegatedTestOffscreenContext_BackgroundFilters
+ : public LayerTreeHostDelegatedTestCaseSingleDelegatedLayer {
+ protected:
+ virtual void BeginTest() OVERRIDE {
+ scoped_ptr<DelegatedFrameData> frame =
+ CreateFrameData(gfx::Rect(0, 0, 1, 1),
+ gfx::Rect(0, 0, 1, 1));
+
+ FilterOperations filters;
+ filters.Append(FilterOperation::CreateGrayscaleFilter(0.5f));
+ AddRenderPass(frame.get(),
+ RenderPass::Id(2, 1),
+ gfx::Rect(0, 0, 1, 1),
+ gfx::Rect(0, 0, 1, 1),
+ FilterOperations(),
+ filters);
+ delegated_->SetFrameData(frame.Pass());
+
+ PostSetNeedsCommitToMainThread();
+ }
+
+ virtual void DrawLayersOnThread(LayerTreeHostImpl* host_impl) OVERRIDE {
+ bool expect_context = !delegating_renderer();
+ EXPECT_EQ(expect_context, !!host_impl->offscreen_context_provider());
+ EndTest();
+ }
+
+ virtual void AfterTest() OVERRIDE {}
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(
+ LayerTreeHostDelegatedTestOffscreenContext_BackgroundFilters);
+
+class LayerTreeHostDelegatedTestOffscreenContext_Filters_AddedToTree
+ : public LayerTreeHostDelegatedTestCaseSingleDelegatedLayer {
+ protected:
+ virtual void BeginTest() OVERRIDE {
+ scoped_ptr<DelegatedFrameData> frame =
+ CreateFrameData(gfx::Rect(0, 0, 1, 1),
+ gfx::Rect(0, 0, 1, 1));
+
+ FilterOperations filters;
+ filters.Append(FilterOperation::CreateGrayscaleFilter(0.5f));
+ AddRenderPass(frame.get(),
+ RenderPass::Id(2, 1),
+ gfx::Rect(0, 0, 1, 1),
+ gfx::Rect(0, 0, 1, 1),
+ filters,
+ FilterOperations());
+
+ delegated_->RemoveFromParent();
+ delegated_->SetFrameData(frame.Pass());
+ layer_tree_host()->root_layer()->AddChild(delegated_);
+
+ PostSetNeedsCommitToMainThread();
+ }
+
+ virtual void DrawLayersOnThread(LayerTreeHostImpl* host_impl) OVERRIDE {
+ bool expect_context = !delegating_renderer();
+ EXPECT_EQ(expect_context, !!host_impl->offscreen_context_provider());
+ EndTest();
+ }
+
+ virtual void AfterTest() OVERRIDE {}
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(
+ LayerTreeHostDelegatedTestOffscreenContext_Filters_AddedToTree);
+
class LayerTreeHostDelegatedTestLayerUsesFrameDamage
: public LayerTreeHostDelegatedTestCaseSingleDelegatedLayer {
public:
@@ -377,6 +585,28 @@ class LayerTreeHostDelegatedTestLayerUsesFrameDamage
// Should create zero damage.
layer_tree_host()->SetNeedsCommit();
break;
+ case 16:
+ // Moving the layer out of the tree and back in will damage the whole
+ // impl layer.
+ delegated_->RemoveFromParent();
+ layer_tree_host()->root_layer()->AddChild(delegated_);
+ break;
+ case 17:
+ // Make a larger frame with lots of damage. Then a frame smaller than
+ // the first frame's damage. The entire layer should be damaged, but
+ // nothing more.
+ delegated_->SetFrameData(CreateFrameData(gfx::Rect(0, 0, 10, 10),
+ gfx::Rect(0, 0, 10, 10)));
+ delegated_->SetFrameData(CreateFrameData(gfx::Rect(0, 0, 5, 5),
+ gfx::Rect(1, 1, 2, 2)));
+ break;
+ case 18:
+ // Make a frame with lots of damage. Then replace it with an empty
+ // frame. The entire layer should be damaged, but nothing more.
+ delegated_->SetFrameData(CreateFrameData(gfx::Rect(0, 0, 10, 10),
+ gfx::Rect(0, 0, 10, 10)));
+ delegated_->SetFrameData(CreateEmptyFrameData());
+ break;
}
first_draw_for_source_frame_ = true;
}
@@ -462,6 +692,18 @@ class LayerTreeHostDelegatedTestLayerUsesFrameDamage
case 15:
EXPECT_EQ(gfx::RectF(0.f, 0.f, 0.f, 0.f).ToString(),
damage_rect.ToString());
+ break;
+ case 16:
+ EXPECT_EQ(gfx::RectF(0.f, 0.f, 6.f, 6.f).ToString(),
+ damage_rect.ToString());
+ break;
+ case 17:
+ EXPECT_EQ(gfx::RectF(0.f, 0.f, 6.f, 6.f).ToString(),
+ damage_rect.ToString());
+ break;
+ case 18:
+ EXPECT_EQ(gfx::RectF(0.f, 0.f, 6.f, 6.f).ToString(),
+ damage_rect.ToString());
EndTest();
break;
}
@@ -492,10 +734,20 @@ class LayerTreeHostDelegatedTestMergeResources
scoped_ptr<DelegatedFrameData> frame2 =
CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1));
AddTextureQuad(frame2.get(), 999);
+ AddTransferableResource(frame2.get(), 999);
AddTextureQuad(frame2.get(), 555);
AddTransferableResource(frame2.get(), 555);
delegated_->SetFrameData(frame2.Pass());
+ // The resource 999 from frame1 is returned since it is still on the main
+ // thread.
+ ReturnedResourceArray returned_resources;
+ delegated_->TakeUnusedResourcesForChildCompositor(&returned_resources);
+ {
+ unsigned expected[] = {999};
+ EXPECT_RESOURCES(expected, returned_resources);
+ }
+
PostSetNeedsCommitToMainThread();
}
@@ -514,8 +766,8 @@ class LayerTreeHostDelegatedTestMergeResources
EXPECT_EQ(1u, map.count(555));
EXPECT_EQ(2u, delegated_impl->Resources().size());
- EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(999)->second));
- EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(555)->second));
+ EXPECT_EQ(1u, delegated_impl->Resources().count(999));
+ EXPECT_EQ(1u, delegated_impl->Resources().count(555));
EndTest();
}
@@ -585,7 +837,7 @@ class LayerTreeHostDelegatedTestReturnUnusedResources
virtual void DidCommitAndDrawFrame() OVERRIDE {
scoped_ptr<DelegatedFrameData> frame;
- TransferableResourceArray resources;
+ ReturnedResourceArray resources;
int next_source_frame_number = layer_tree_host()->source_frame_number();
switch (next_source_frame_number) {
@@ -599,11 +851,6 @@ class LayerTreeHostDelegatedTestReturnUnusedResources
delegated_->SetFrameData(frame.Pass());
break;
case 2:
- // Retrieve unused resources to the main thread.
- // TODO(danakj): Shouldn't need to commit to get resources.
- layer_tree_host()->SetNeedsCommit();
- return;
- case 3:
// All of the resources are in use.
delegated_->TakeUnusedResourcesForChildCompositor(&resources);
EXPECT_EQ(0u, resources.size());
@@ -611,47 +858,42 @@ class LayerTreeHostDelegatedTestReturnUnusedResources
// Keep using 999 but stop using 555.
frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1));
AddTextureQuad(frame.get(), 999);
+ AddTransferableResource(frame.get(), 999);
AddTextureQuad(frame.get(), 444);
AddTransferableResource(frame.get(), 444);
delegated_->SetFrameData(frame.Pass());
break;
- case 4:
- // Retrieve unused resources to the main thread.
- // TODO(danakj): Shouldn't need to commit to get resources.
- layer_tree_host()->SetNeedsCommit();
- return;
- case 5:
+ case 3:
// 555 is no longer in use.
delegated_->TakeUnusedResourcesForChildCompositor(&resources);
- EXPECT_EQ(1u, resources.size());
- EXPECT_EQ(555u, resources[0].id);
+ {
+ unsigned expected[] = {555};
+ EXPECT_RESOURCES(expected, resources);
+ }
// Stop using any resources.
frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1));
delegated_->SetFrameData(frame.Pass());
break;
- case 6:
+ case 4:
// Postpone collecting resources for a frame. They should still be there
// the next frame.
layer_tree_host()->SetNeedsCommit();
return;
- case 7:
- // 444 and 999 are no longer in use.
+ case 5:
+ // 444 and 999 are no longer in use. We sent two refs to 999, so we
+ // should get two back.
delegated_->TakeUnusedResourcesForChildCompositor(&resources);
- EXPECT_EQ(2u, resources.size());
- if (resources[0].id == 999) {
- EXPECT_EQ(999u, resources[0].id);
- EXPECT_EQ(444u, resources[1].id);
- } else {
- EXPECT_EQ(444u, resources[0].id);
- EXPECT_EQ(999u, resources[1].id);
+ {
+ unsigned expected[] = {444, 999, 999};
+ EXPECT_RESOURCES(expected, resources);
}
EndTest();
break;
}
// Resource are never immediately released.
- TransferableResourceArray empty_resources;
+ ReturnedResourceArray empty_resources;
delegated_->TakeUnusedResourcesForChildCompositor(&empty_resources);
EXPECT_TRUE(empty_resources.empty());
}
@@ -676,7 +918,7 @@ class LayerTreeHostDelegatedTestReusedResources
virtual void DidCommitAndDrawFrame() OVERRIDE {
scoped_ptr<DelegatedFrameData> frame;
- TransferableResourceArray resources;
+ ReturnedResourceArray resources;
int next_source_frame_number = layer_tree_host()->source_frame_number();
switch (next_source_frame_number) {
@@ -692,11 +934,6 @@ class LayerTreeHostDelegatedTestReusedResources
delegated_->SetFrameData(frame.Pass());
break;
case 2:
- // Retrieve unused resources to the main thread.
- // TODO(danakj): Shouldn't need to commit to get resources.
- layer_tree_host()->SetNeedsCommit();
- return;
- case 3:
// All of the resources are in use.
delegated_->TakeUnusedResourcesForChildCompositor(&resources);
EXPECT_EQ(0u, resources.size());
@@ -704,6 +941,7 @@ class LayerTreeHostDelegatedTestReusedResources
// Keep using 999 but stop using 555 and 444.
frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1));
AddTextureQuad(frame.get(), 999);
+ AddTransferableResource(frame.get(), 999);
delegated_->SetFrameData(frame.Pass());
// Resource are not immediately released.
@@ -713,19 +951,19 @@ class LayerTreeHostDelegatedTestReusedResources
// Now using 555 and 444 again, but not 999.
frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1));
AddTextureQuad(frame.get(), 555);
+ AddTransferableResource(frame.get(), 555);
AddTextureQuad(frame.get(), 444);
+ AddTransferableResource(frame.get(), 444);
delegated_->SetFrameData(frame.Pass());
break;
- case 4:
- // Retrieve unused resources to the main thread.
- // TODO(danakj): Shouldn't need to commit to get resources.
- layer_tree_host()->SetNeedsCommit();
- return;
- case 5:
- // The 999 resource is the only unused one.
+ case 3:
+ // The 999 resource is the only unused one. Two references were sent, so
+ // two should be returned.
delegated_->TakeUnusedResourcesForChildCompositor(&resources);
- EXPECT_EQ(1u, resources.size());
- EXPECT_EQ(999u, resources[0].id);
+ {
+ unsigned expected[] = {999, 999};
+ EXPECT_RESOURCES(expected, resources);
+ }
EndTest();
break;
}
@@ -750,7 +988,7 @@ class LayerTreeHostDelegatedTestFrameBeforeAck
virtual void DidCommitAndDrawFrame() OVERRIDE {
scoped_ptr<DelegatedFrameData> frame;
- TransferableResourceArray resources;
+ ReturnedResourceArray resources;
int next_source_frame_number = layer_tree_host()->source_frame_number();
switch (next_source_frame_number) {
@@ -766,11 +1004,6 @@ class LayerTreeHostDelegatedTestFrameBeforeAck
delegated_->SetFrameData(frame.Pass());
break;
case 2:
- // Retrieve unused resources to the main thread.
- // TODO(danakj): Shouldn't need to commit to get resources.
- layer_tree_host()->SetNeedsCommit();
- return;
- case 3:
// All of the resources are in use.
delegated_->TakeUnusedResourcesForChildCompositor(&resources);
EXPECT_EQ(0u, resources.size());
@@ -778,6 +1011,7 @@ class LayerTreeHostDelegatedTestFrameBeforeAck
// Keep using 999 but stop using 555 and 444.
frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1));
AddTextureQuad(frame.get(), 999);
+ AddTransferableResource(frame.get(), 999);
delegated_->SetFrameData(frame.Pass());
// Resource are not immediately released.
@@ -786,24 +1020,15 @@ class LayerTreeHostDelegatedTestFrameBeforeAck
// The parent compositor (this one) does a commit.
break;
- case 4:
- // Retrieve unused resources to the main thread.
- // TODO(danakj): Shouldn't need to commit to get resources.
- layer_tree_host()->SetNeedsCommit();
- return;
- case 5:
+ case 3:
delegated_->TakeUnusedResourcesForChildCompositor(&resources);
- EXPECT_EQ(2u, resources.size());
- if (resources[0].id == 555) {
- EXPECT_EQ(555u, resources[0].id);
- EXPECT_EQ(444u, resources[1].id);
- } else {
- EXPECT_EQ(444u, resources[0].id);
- EXPECT_EQ(555u, resources[1].id);
+ {
+ unsigned expected[] = {444, 555};
+ EXPECT_RESOURCES(expected, resources);
}
- // The child compositor sends a frame before receiving an for the
- // second frame. It uses 999, 444, and 555 again.
+ // The child compositor sends a frame referring to resources not in the
+ // frame.
frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1));
AddTextureQuad(frame.get(), 999);
AddTextureQuad(frame.get(), 555);
@@ -814,7 +1039,7 @@ class LayerTreeHostDelegatedTestFrameBeforeAck
}
virtual void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) OVERRIDE {
- if (host_impl->active_tree()->source_frame_number() != 5)
+ if (host_impl->active_tree()->source_frame_number() != 3)
return;
LayerImpl* root_impl = host_impl->active_tree()->root_layer();
@@ -832,7 +1057,7 @@ class LayerTreeHostDelegatedTestFrameBeforeAck
EXPECT_EQ(1u, map.count(999));
EXPECT_EQ(1u, delegated_impl->Resources().size());
- EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(999)->second));
+ EXPECT_EQ(1u, delegated_impl->Resources().count(999));
const RenderPass* pass = delegated_impl->RenderPassesInDrawOrder()[0];
EXPECT_EQ(1u, pass->quad_list.size());
@@ -862,7 +1087,7 @@ class LayerTreeHostDelegatedTestFrameBeforeTakeResources
virtual void DidCommitAndDrawFrame() OVERRIDE {
scoped_ptr<DelegatedFrameData> frame;
- TransferableResourceArray resources;
+ ReturnedResourceArray resources;
int next_source_frame_number = layer_tree_host()->source_frame_number();
switch (next_source_frame_number) {
@@ -878,11 +1103,6 @@ class LayerTreeHostDelegatedTestFrameBeforeTakeResources
delegated_->SetFrameData(frame.Pass());
break;
case 2:
- // Retrieve unused resources to the main thread.
- // TODO(danakj): Shouldn't need to commit to get resources.
- layer_tree_host()->SetNeedsCommit();
- return;
- case 3:
// All of the resources are in use.
delegated_->TakeUnusedResourcesForChildCompositor(&resources);
EXPECT_EQ(0u, resources.size());
@@ -890,6 +1110,7 @@ class LayerTreeHostDelegatedTestFrameBeforeTakeResources
// Keep using 999 but stop using 555 and 444.
frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1));
AddTextureQuad(frame.get(), 999);
+ AddTransferableResource(frame.get(), 999);
delegated_->SetFrameData(frame.Pass());
// Resource are not immediately released.
@@ -898,31 +1119,28 @@ class LayerTreeHostDelegatedTestFrameBeforeTakeResources
// The parent compositor (this one) does a commit.
break;
- case 4:
- // Retrieve unused resources to the main thread.
- // TODO(danakj): Shouldn't need to commit to get resources.
- layer_tree_host()->SetNeedsCommit();
- return;
- case 5:
+ case 3:
// The child compositor sends a frame before taking resources back
// from the previous commit. This frame makes use of the resources 555
// and 444, which were just released during commit.
frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1));
AddTextureQuad(frame.get(), 999);
+ AddTransferableResource(frame.get(), 999);
AddTextureQuad(frame.get(), 555);
+ AddTransferableResource(frame.get(), 555);
AddTextureQuad(frame.get(), 444);
+ AddTransferableResource(frame.get(), 444);
delegated_->SetFrameData(frame.Pass());
- // The resources are used by the new frame so are not returned.
+ // The resources are used by the new frame but are returned anyway since
+ // we passed them again.
delegated_->TakeUnusedResourcesForChildCompositor(&resources);
- EXPECT_EQ(0u, resources.size());
+ {
+ unsigned expected[] = {444, 555};
+ EXPECT_RESOURCES(expected, resources);
+ }
break;
- case 6:
- // Retrieve unused resources to the main thread.
- // TODO(danakj): Shouldn't need to commit to get resources.
- layer_tree_host()->SetNeedsCommit();
- return;
- case 7:
+ case 4:
delegated_->TakeUnusedResourcesForChildCompositor(&resources);
EXPECT_EQ(0u, resources.size());
EndTest();
@@ -931,7 +1149,7 @@ class LayerTreeHostDelegatedTestFrameBeforeTakeResources
}
virtual void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) OVERRIDE {
- if (host_impl->active_tree()->source_frame_number() != 5)
+ if (host_impl->active_tree()->source_frame_number() != 3)
return;
LayerImpl* root_impl = host_impl->active_tree()->root_layer();
@@ -950,9 +1168,9 @@ class LayerTreeHostDelegatedTestFrameBeforeTakeResources
EXPECT_EQ(1u, map.count(444));
EXPECT_EQ(3u, delegated_impl->Resources().size());
- EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(999)->second));
- EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(555)->second));
- EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(444)->second));
+ EXPECT_EQ(1u, delegated_impl->Resources().count(999));
+ EXPECT_EQ(1u, delegated_impl->Resources().count(555));
+ EXPECT_EQ(1u, delegated_impl->Resources().count(444));
const RenderPass* pass = delegated_impl->RenderPassesInDrawOrder()[0];
EXPECT_EQ(3u, pass->quad_list.size());
@@ -987,7 +1205,7 @@ class LayerTreeHostDelegatedTestBadFrame
virtual void DidCommitAndDrawFrame() OVERRIDE {
scoped_ptr<DelegatedFrameData> frame;
- TransferableResourceArray resources;
+ ReturnedResourceArray resources;
int next_source_frame_number = layer_tree_host()->source_frame_number();
switch (next_source_frame_number) {
@@ -1001,11 +1219,6 @@ class LayerTreeHostDelegatedTestBadFrame
delegated_->SetFrameData(frame.Pass());
break;
case 2:
- // Retrieve unused resources to the main thread.
- // TODO(danakj): Shouldn't need to commit to get resources.
- layer_tree_host()->SetNeedsCommit();
- return;
- case 3:
// All of the resources are in use.
delegated_->TakeUnusedResourcesForChildCompositor(&resources);
EXPECT_EQ(0u, resources.size());
@@ -1024,32 +1237,26 @@ class LayerTreeHostDelegatedTestBadFrame
// The parent compositor (this one) does a commit.
break;
- case 4:
- // Retrieve unused resources to the main thread.
- // TODO(danakj): Shouldn't need to commit to get resources.
- layer_tree_host()->SetNeedsCommit();
- return;
- case 5:
+ case 3:
// The bad frame's resource is given back to the child compositor.
delegated_->TakeUnusedResourcesForChildCompositor(&resources);
- EXPECT_EQ(1u, resources.size());
- EXPECT_EQ(444u, resources[0].id);
+ {
+ unsigned expected[] = {444};
+ EXPECT_RESOURCES(expected, resources);
+ }
// Now send a good frame with 999 again.
frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1));
AddTextureQuad(frame.get(), 999);
delegated_->SetFrameData(frame.Pass());
break;
- case 6:
- // Retrieve unused resources to the main thread.
- // TODO(danakj): Shouldn't need to commit to get resources.
- layer_tree_host()->SetNeedsCommit();
- return;
- case 7:
+ case 4:
// The unused 555 from the last good frame is now released.
delegated_->TakeUnusedResourcesForChildCompositor(&resources);
- EXPECT_EQ(1u, resources.size());
- EXPECT_EQ(555u, resources[0].id);
+ {
+ unsigned expected[] = {555};
+ EXPECT_RESOURCES(expected, resources);
+ }
EndTest();
break;
@@ -1080,8 +1287,8 @@ class LayerTreeHostDelegatedTestBadFrame
EXPECT_EQ(1u, map.count(555));
EXPECT_EQ(2u, delegated_impl->Resources().size());
- EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(999)->second));
- EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(555)->second));
+ EXPECT_EQ(1u, delegated_impl->Resources().count(999));
+ EXPECT_EQ(1u, delegated_impl->Resources().count(555));
const RenderPass* pass = delegated_impl->RenderPassesInDrawOrder()[0];
EXPECT_EQ(2u, pass->quad_list.size());
@@ -1093,15 +1300,15 @@ class LayerTreeHostDelegatedTestBadFrame
EXPECT_EQ(map.find(555)->second, quad2->resource_id);
break;
}
- case 3: {
+ case 2: {
// We only keep resources from the last valid frame.
EXPECT_EQ(2u, map.size());
EXPECT_EQ(1u, map.count(999));
EXPECT_EQ(1u, map.count(555));
EXPECT_EQ(2u, delegated_impl->Resources().size());
- EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(999)->second));
- EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(555)->second));
+ EXPECT_EQ(1u, delegated_impl->Resources().count(999));
+ EXPECT_EQ(1u, delegated_impl->Resources().count(555));
// The bad frame is dropped though, we still have the frame with 999 and
// 555 in it.
@@ -1115,20 +1322,13 @@ class LayerTreeHostDelegatedTestBadFrame
EXPECT_EQ(map.find(555)->second, quad2->resource_id);
break;
}
- case 5:
- // Resources given to our parent compositor will be returned now, but
- // the DelegatedRendererLayerImpl doesn't know about it until the next
- // commit.
- // TODO(danakj): Shouldn't need a commit to return resources to the
- // DelegatedRendererLayerImpl or to the main thread.
- break;
- case 6: {
+ case 3: {
// We have the new good frame with just 999 in it.
EXPECT_EQ(1u, map.size());
EXPECT_EQ(1u, map.count(999));
EXPECT_EQ(1u, delegated_impl->Resources().size());
- EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(999)->second));
+ EXPECT_EQ(1u, delegated_impl->Resources().count(999));
const RenderPass* pass = delegated_impl->RenderPassesInDrawOrder()[0];
EXPECT_EQ(1u, pass->quad_list.size());
@@ -1154,7 +1354,7 @@ class LayerTreeHostDelegatedTestUnnamedResource
virtual void DidCommit() OVERRIDE {
scoped_ptr<DelegatedFrameData> frame;
- TransferableResourceArray resources;
+ ReturnedResourceArray resources;
int next_source_frame_number = layer_tree_host()->source_frame_number();
switch (next_source_frame_number) {
@@ -1169,8 +1369,10 @@ class LayerTreeHostDelegatedTestUnnamedResource
case 2:
// The unused resource should be returned.
delegated_->TakeUnusedResourcesForChildCompositor(&resources);
- EXPECT_EQ(1u, resources.size());
- EXPECT_EQ(999u, resources[0].id);
+ {
+ unsigned expected[] = {999};
+ EXPECT_RESOURCES(expected, resources);
+ }
EndTest();
break;
@@ -1194,7 +1396,7 @@ class LayerTreeHostDelegatedTestUnnamedResource
EXPECT_EQ(1u, map.count(555));
EXPECT_EQ(1u, delegated_impl->Resources().size());
- EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(555)->second));
+ EXPECT_EQ(1u, delegated_impl->Resources().count(555));
}
virtual void AfterTest() OVERRIDE {}
@@ -1209,9 +1411,9 @@ class LayerTreeHostDelegatedTestDontLeakResource
PostSetNeedsCommitToMainThread();
}
- virtual void DidCommit() OVERRIDE {
+ virtual void DidCommitAndDrawFrame() OVERRIDE {
scoped_ptr<DelegatedFrameData> frame;
- TransferableResourceArray resources;
+ ReturnedResourceArray resources;
int next_source_frame_number = layer_tree_host()->source_frame_number();
switch (next_source_frame_number) {
@@ -1227,14 +1429,29 @@ class LayerTreeHostDelegatedTestDontLeakResource
// But then we immediately stop using 999.
frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1));
AddTextureQuad(frame.get(), 555);
+ AddTransferableResource(frame.get(), 555);
delegated_->SetFrameData(frame.Pass());
break;
case 2:
- // The unused resource should be returned.
+ // The unused resources should be returned. 555 is still used, but it's
+ // returned once to account for the first frame.
delegated_->TakeUnusedResourcesForChildCompositor(&resources);
- EXPECT_EQ(1u, resources.size());
- EXPECT_EQ(999u, resources[0].id);
-
+ {
+ unsigned expected[] = {555, 999};
+ EXPECT_RESOURCES(expected, resources);
+ }
+ // Send a frame with no resources in it.
+ frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1));
+ delegated_->SetFrameData(frame.Pass());
+ break;
+ case 3:
+ // The now unused resource 555 should be returned.
+ resources.clear();
+ delegated_->TakeUnusedResourcesForChildCompositor(&resources);
+ {
+ unsigned expected[] = {555};
+ EXPECT_RESOURCES(expected, resources);
+ }
EndTest();
break;
}
@@ -1257,7 +1474,12 @@ class LayerTreeHostDelegatedTestDontLeakResource
EXPECT_EQ(1u, map.count(555));
EXPECT_EQ(1u, delegated_impl->Resources().size());
- EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(555)->second));
+ EXPECT_EQ(1u, delegated_impl->Resources().count(555));
+ }
+
+ virtual void SwapBuffersOnThread(LayerTreeHostImpl* host_impl,
+ bool result) OVERRIDE {
+ ReturnUnusedResourcesFromParent(host_impl);
}
virtual void AfterTest() OVERRIDE {}
@@ -1270,7 +1492,7 @@ class LayerTreeHostDelegatedTestResourceSentToParent
public:
virtual void DidCommitAndDrawFrame() OVERRIDE {
scoped_ptr<DelegatedFrameData> frame;
- TransferableResourceArray resources;
+ ReturnedResourceArray resources;
int next_source_frame_number = layer_tree_host()->source_frame_number();
switch (next_source_frame_number) {
@@ -1291,6 +1513,7 @@ class LayerTreeHostDelegatedTestResourceSentToParent
// it present.
frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1));
AddTextureQuad(frame.get(), 555);
+ AddTransferableResource(frame.get(), 555);
delegated_->SetFrameData(frame.Pass());
break;
case 3:
@@ -1299,18 +1522,46 @@ class LayerTreeHostDelegatedTestResourceSentToParent
EXPECT_EQ(0u, resources.size());
// The impl side will get back the resource at some point.
- // TODO(danakj): The test should work without this.
- layer_tree_host()->SetNeedsCommit();
+ ImplThreadTaskRunner()->PostTask(FROM_HERE,
+ receive_resource_on_thread_);
break;
- case 4:
- // 999 was returned from the grandparent and could be released.
- delegated_->TakeUnusedResourcesForChildCompositor(&resources);
- EXPECT_EQ(1u, resources.size());
- EXPECT_EQ(999u, resources[0].id);
+ }
+ }
- EndTest();
- break;
+ void ReceiveResourceOnThread(LayerTreeHostImpl* host_impl) {
+ LayerImpl* root_impl = host_impl->active_tree()->root_layer();
+ FakeDelegatedRendererLayerImpl* delegated_impl =
+ static_cast<FakeDelegatedRendererLayerImpl*>(root_impl->children()[0]);
+
+ const ResourceProvider::ResourceIdMap& map =
+ host_impl->resource_provider()->GetChildToParentMap(
+ delegated_impl->ChildId());
+
+ // Receive 999 back from the grandparent.
+ CompositorFrameAck ack;
+ output_surface()->ReturnResource(map.find(999)->second, &ack);
+ host_impl->ReclaimResources(&ack);
+ host_impl->OnSwapBuffersComplete();
+
+ // And then it should be released by the DelegatedRendererLayer.
+ MainThreadTaskRunner()->PostTask(
+ FROM_HERE,
+ base::Bind(&LayerTreeHostDelegatedTestResourceSentToParent::
+ DidReceiveResourceOnMainThread,
+ base::Unretained(this)));
+ }
+
+ void DidReceiveResourceOnMainThread() {
+ ReturnedResourceArray resources;
+
+ // 999 was returned from the grandparent and could be released.
+ delegated_->TakeUnusedResourcesForChildCompositor(&resources);
+ {
+ unsigned expected[] = {999};
+ EXPECT_RESOURCES(expected, resources);
}
+
+ EndTest();
}
virtual void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) OVERRIDE {
@@ -1332,8 +1583,8 @@ class LayerTreeHostDelegatedTestResourceSentToParent
EXPECT_EQ(1u, map.count(555));
EXPECT_EQ(2u, delegated_impl->Resources().size());
- EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(999)->second));
- EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(555)->second));
+ EXPECT_EQ(1u, delegated_impl->Resources().count(999));
+ EXPECT_EQ(1u, delegated_impl->Resources().count(555));
// The 999 resource will be sent to a grandparent compositor.
break;
@@ -1345,12 +1596,13 @@ class LayerTreeHostDelegatedTestResourceSentToParent
// 999 is in the parent, so not held by delegated renderer layer.
EXPECT_EQ(1u, delegated_impl->Resources().size());
- EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(555)->second));
+ EXPECT_EQ(1u, delegated_impl->Resources().count(555));
- // Receive 999 back from the grandparent.
- CompositorFrameAck ack;
- output_surface()->ReturnResource(map.find(999)->second, &ack);
- host_impl->OnSwapBuffersComplete(&ack);
+ receive_resource_on_thread_ =
+ base::Bind(&LayerTreeHostDelegatedTestResourceSentToParent::
+ ReceiveResourceOnThread,
+ base::Unretained(this),
+ host_impl);
break;
}
case 3:
@@ -1366,7 +1618,7 @@ class LayerTreeHostDelegatedTestResourceSentToParent
virtual void AfterTest() OVERRIDE {}
- TransferableResource resource_in_grandparent;
+ base::Closure receive_resource_on_thread_;
};
SINGLE_AND_MULTI_THREAD_DELEGATING_RENDERER_TEST_F(
@@ -1383,7 +1635,7 @@ class LayerTreeHostDelegatedTestCommitWithoutTake
virtual void DidCommit() OVERRIDE {
scoped_ptr<DelegatedFrameData> frame;
- TransferableResourceArray resources;
+ ReturnedResourceArray resources;
int next_source_frame_number = layer_tree_host()->source_frame_number();
switch (next_source_frame_number) {
@@ -1404,20 +1656,40 @@ class LayerTreeHostDelegatedTestCommitWithoutTake
// Stop using 999 and 444 in this frame and commit.
frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1));
AddTextureQuad(frame.get(), 555);
+ AddTransferableResource(frame.get(), 555);
delegated_->SetFrameData(frame.Pass());
+ // 999 and 444 will be returned for frame 1, but not 555 since it's in
+ // the current frame.
break;
case 3:
// Don't take resources here, but set a new frame that uses 999 again.
frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1));
AddTextureQuad(frame.get(), 999);
+ AddTransferableResource(frame.get(), 999);
AddTextureQuad(frame.get(), 555);
+ AddTransferableResource(frame.get(), 555);
delegated_->SetFrameData(frame.Pass());
break;
case 4:
- // 999 and 555 are in use, but 444 should be returned now.
+ // 555 from frame 1 and 2 isn't returned since it's still in use. 999
+ // from frame 1 is returned though.
delegated_->TakeUnusedResourcesForChildCompositor(&resources);
- EXPECT_EQ(1u, resources.size());
- EXPECT_EQ(444u, resources[0].id);
+ {
+ unsigned expected[] = {444, 999};
+ EXPECT_RESOURCES(expected, resources);
+ }
+
+ frame = CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1));
+ delegated_->SetFrameData(frame.Pass());
+ // 555 will be returned 3 times for frames 1 2 and 3, and 999 will be
+ // returned once for frame 3.
+ break;
+ case 5:
+ delegated_->TakeUnusedResourcesForChildCompositor(&resources);
+ {
+ unsigned expected[] = {555, 555, 555, 999};
+ EXPECT_RESOURCES(expected, resources);
+ }
EndTest();
break;
@@ -1444,16 +1716,16 @@ class LayerTreeHostDelegatedTestCommitWithoutTake
EXPECT_EQ(1u, map.count(444));
EXPECT_EQ(3u, delegated_impl->Resources().size());
- EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(999)->second));
- EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(555)->second));
- EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(444)->second));
+ EXPECT_EQ(1u, delegated_impl->Resources().count(999));
+ EXPECT_EQ(1u, delegated_impl->Resources().count(555));
+ EXPECT_EQ(1u, delegated_impl->Resources().count(444));
break;
case 2:
EXPECT_EQ(1u, map.size());
EXPECT_EQ(1u, map.count(555));
EXPECT_EQ(1u, delegated_impl->Resources().size());
- EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(555)->second));
+ EXPECT_EQ(1u, delegated_impl->Resources().count(555));
break;
case 3:
EXPECT_EQ(2u, map.size());
@@ -1461,8 +1733,8 @@ class LayerTreeHostDelegatedTestCommitWithoutTake
EXPECT_EQ(1u, map.count(555));
EXPECT_EQ(2u, delegated_impl->Resources().size());
- EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(999)->second));
- EXPECT_EQ(1u, delegated_impl->Resources().count(map.find(555)->second));
+ EXPECT_EQ(1u, delegated_impl->Resources().count(999));
+ EXPECT_EQ(1u, delegated_impl->Resources().count(555));
}
}
@@ -1471,5 +1743,107 @@ class LayerTreeHostDelegatedTestCommitWithoutTake
SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostDelegatedTestCommitWithoutTake);
+class DelegatedFrameIsActivatedDuringCommit
+ : public LayerTreeHostDelegatedTestCaseSingleDelegatedLayer {
+ protected:
+ DelegatedFrameIsActivatedDuringCommit()
+ : wait_thread_("WAIT"),
+ wait_event_(false, false) {
+ wait_thread_.Start();
+ }
+
+ virtual void BeginTest() OVERRIDE {
+ activate_count_ = 0;
+
+ scoped_ptr<DelegatedFrameData> frame =
+ CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1));
+ AddTextureQuad(frame.get(), 999);
+ AddTransferableResource(frame.get(), 999);
+ delegated_->SetFrameData(frame.Pass());
+
+ PostSetNeedsCommitToMainThread();
+ }
+
+ virtual void WillActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE {
+ // Slow down activation so the main thread DidCommit() will run if
+ // not blocked.
+ wait_thread_.message_loop()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&base::WaitableEvent::Signal,
+ base::Unretained(&wait_event_)),
+ base::TimeDelta::FromMilliseconds(10));
+ wait_event_.Wait();
+
+ base::AutoLock lock(activate_lock_);
+ ++activate_count_;
+ }
+
+ virtual void DidActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE {
+ // The main thread is awake now, and will run DidCommit() immediately.
+ // Run DidActivate() afterwards by posting it now.
+ proxy()->MainThreadTaskRunner()->PostTask(
+ FROM_HERE,
+ base::Bind(&DelegatedFrameIsActivatedDuringCommit::DidActivate,
+ base::Unretained(this)));
+ }
+
+ void DidActivate() {
+ base::AutoLock lock(activate_lock_);
+ switch (activate_count_) {
+ case 1: {
+ // The first frame has been activated. Set a new frame, and
+ // expect the next commit to finish *after* it is activated.
+ scoped_ptr<DelegatedFrameData> frame =
+ CreateFrameData(gfx::Rect(0, 0, 1, 1), gfx::Rect(0, 0, 1, 1));
+ AddTextureQuad(frame.get(), 555);
+ AddTransferableResource(frame.get(), 555);
+ delegated_->SetFrameData(frame.Pass());
+ // So this commit number should complete after the second activate.
+ EXPECT_EQ(1, layer_tree_host()->source_frame_number());
+ break;
+ }
+ case 2:
+ // The second frame has been activated. Remove the layer from
+ // the tree to cause another commit/activation. The commit should
+ // finish *after* the layer is removed from the active tree.
+ delegated_->RemoveFromParent();
+ // So this commit number should complete after the third activate.
+ EXPECT_EQ(2, layer_tree_host()->source_frame_number());
+ break;
+ case 3:
+ EndTest();
+ break;
+ }
+ }
+
+ virtual void DidCommit() OVERRIDE {
+ switch (layer_tree_host()->source_frame_number()) {
+ case 2: {
+ // The activate for the 2nd frame should have happened before now.
+ base::AutoLock lock(activate_lock_);
+ EXPECT_EQ(2, activate_count_);
+ break;
+ }
+ case 3: {
+ // The activate to remove the layer should have happened before now.
+ base::AutoLock lock(activate_lock_);
+ EXPECT_EQ(3, activate_count_);
+ break;
+ }
+ }
+ }
+
+
+ virtual void AfterTest() OVERRIDE {}
+
+ base::Thread wait_thread_;
+ base::WaitableEvent wait_event_;
+ base::Lock activate_lock_;
+ int activate_count_;
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(
+ DelegatedFrameIsActivatedDuringCommit);
+
} // namespace
} // namespace cc
diff --git a/chromium/cc/trees/layer_tree_host_unittest_scroll.cc b/chromium/cc/trees/layer_tree_host_unittest_scroll.cc
index 303e92496b4..ab87be1b5c5 100644
--- a/chromium/cc/trees/layer_tree_host_unittest_scroll.cc
+++ b/chromium/cc/trees/layer_tree_host_unittest_scroll.cc
@@ -722,8 +722,7 @@ class ImplSidePaintingScrollTestSimple : public ImplSidePaintingScrollTest {
main_thread_scroll_(40, 5),
impl_thread_scroll1_(2, -1),
impl_thread_scroll2_(-3, 10),
- num_scrolls_(0),
- can_activate_(true) {}
+ num_scrolls_(0) {}
virtual void BeginTest() OVERRIDE {
layer_tree_host()->root_layer()->SetScrollable(true);
@@ -748,15 +747,6 @@ class ImplSidePaintingScrollTestSimple : public ImplSidePaintingScrollTest {
}
}
- virtual bool CanActivatePendingTree(LayerTreeHostImpl* impl) OVERRIDE {
- return can_activate_;
- }
-
- virtual bool CanActivatePendingTreeIfNeeded(LayerTreeHostImpl* impl)
- OVERRIDE {
- return can_activate_;
- }
-
virtual void CommitCompleteOnThread(LayerTreeHostImpl* impl) OVERRIDE {
// We force a second draw here of the first commit before activating
// the second commit.
@@ -774,7 +764,7 @@ class ImplSidePaintingScrollTestSimple : public ImplSidePaintingScrollTest {
switch (impl->active_tree()->source_frame_number()) {
case 0:
if (!impl->pending_tree()) {
- can_activate_ = false;
+ impl->BlockNotifyReadyToActivateForTesting(true);
EXPECT_VECTOR_EQ(root->ScrollDelta(), gfx::Vector2d());
root->ScrollBy(impl_thread_scroll1_);
@@ -786,7 +776,7 @@ class ImplSidePaintingScrollTestSimple : public ImplSidePaintingScrollTest {
// CommitCompleteOnThread will trigger this function again
// and cause us to take the else clause.
} else {
- can_activate_ = true;
+ impl->BlockNotifyReadyToActivateForTesting(false);
ASSERT_TRUE(pending_root);
EXPECT_EQ(impl->pending_tree()->source_frame_number(), 1);
@@ -828,11 +818,118 @@ class ImplSidePaintingScrollTestSimple : public ImplSidePaintingScrollTest {
gfx::Vector2d impl_thread_scroll1_;
gfx::Vector2d impl_thread_scroll2_;
int num_scrolls_;
- bool can_activate_;
};
MULTI_THREAD_TEST_F(ImplSidePaintingScrollTestSimple);
+// This test makes sure that layers pick up scrolls that occur between
+// beginning a commit and finishing a commit (aka scroll deltas not
+// included in sent scroll delta) still apply to layers that don't
+// push properties.
+class ImplSidePaintingScrollTestImplOnlyScroll
+ : public ImplSidePaintingScrollTest {
+ public:
+ ImplSidePaintingScrollTestImplOnlyScroll()
+ : initial_scroll_(20, 10), impl_thread_scroll_(-2, 3) {}
+
+ virtual void BeginTest() OVERRIDE {
+ layer_tree_host()->root_layer()->SetScrollable(true);
+ layer_tree_host()->root_layer()->SetMaxScrollOffset(
+ gfx::Vector2d(100, 100));
+ layer_tree_host()->root_layer()->SetScrollOffset(initial_scroll_);
+ PostSetNeedsCommitToMainThread();
+ }
+
+ virtual void WillCommit() OVERRIDE {
+ Layer* root = layer_tree_host()->root_layer();
+ switch (layer_tree_host()->source_frame_number()) {
+ case 0:
+ EXPECT_TRUE(root->needs_push_properties());
+ break;
+ case 1:
+ // Even if this layer doesn't need push properties, it should
+ // still pick up scrolls that happen on the active layer during
+ // commit.
+ EXPECT_FALSE(root->needs_push_properties());
+ break;
+ }
+ }
+
+ virtual void BeginCommitOnThread(LayerTreeHostImpl* impl) OVERRIDE {
+ // Scroll after the 2nd commit has started.
+ if (impl->active_tree()->source_frame_number() == 0) {
+ LayerImpl* active_root = impl->active_tree()->root_layer();
+ ASSERT_TRUE(active_root);
+ active_root->ScrollBy(impl_thread_scroll_);
+ }
+ }
+
+ virtual void CommitCompleteOnThread(LayerTreeHostImpl* impl) OVERRIDE {
+ // We force a second draw here of the first commit before activating
+ // the second commit.
+ LayerImpl* active_root = impl->active_tree()->root_layer();
+ LayerImpl* pending_root = impl->pending_tree()->root_layer();
+
+ ASSERT_TRUE(pending_root);
+ switch (impl->pending_tree()->source_frame_number()) {
+ case 0:
+ EXPECT_VECTOR_EQ(pending_root->scroll_offset(), initial_scroll_);
+ EXPECT_VECTOR_EQ(pending_root->ScrollDelta(), gfx::Vector2d());
+ EXPECT_VECTOR_EQ(pending_root->sent_scroll_delta(), gfx::Vector2d());
+ EXPECT_FALSE(active_root);
+ break;
+ case 1:
+ // Even though the scroll happened during the commit, both layers
+ // should have the appropriate scroll delta.
+ EXPECT_VECTOR_EQ(pending_root->scroll_offset(), initial_scroll_);
+ EXPECT_VECTOR_EQ(pending_root->ScrollDelta(), impl_thread_scroll_);
+ EXPECT_VECTOR_EQ(pending_root->sent_scroll_delta(), gfx::Vector2d());
+ ASSERT_TRUE(active_root);
+ EXPECT_VECTOR_EQ(active_root->scroll_offset(), initial_scroll_);
+ EXPECT_VECTOR_EQ(active_root->ScrollDelta(), impl_thread_scroll_);
+ EXPECT_VECTOR_EQ(active_root->sent_scroll_delta(), gfx::Vector2d());
+ break;
+ case 2:
+ // On the next commit, this delta should have been sent and applied.
+ EXPECT_VECTOR_EQ(pending_root->scroll_offset(),
+ initial_scroll_ + impl_thread_scroll_);
+ EXPECT_VECTOR_EQ(pending_root->ScrollDelta(), gfx::Vector2d());
+ EXPECT_VECTOR_EQ(pending_root->sent_scroll_delta(), gfx::Vector2d());
+ EndTest();
+ break;
+ }
+ }
+
+ virtual void DrawLayersOnThread(LayerTreeHostImpl* impl) OVERRIDE {
+ ImplSidePaintingScrollTest::DrawLayersOnThread(impl);
+
+ LayerImpl* root = impl->active_tree()->root_layer();
+
+ switch (impl->active_tree()->source_frame_number()) {
+ case 0:
+ EXPECT_VECTOR_EQ(root->scroll_offset(), initial_scroll_);
+ EXPECT_VECTOR_EQ(root->ScrollDelta(), gfx::Vector2d());
+ EXPECT_VECTOR_EQ(root->sent_scroll_delta(), gfx::Vector2d());
+ PostSetNeedsCommitToMainThread();
+ break;
+ case 1:
+ EXPECT_VECTOR_EQ(root->scroll_offset(), initial_scroll_);
+ EXPECT_VECTOR_EQ(root->ScrollDelta(), impl_thread_scroll_);
+ EXPECT_VECTOR_EQ(root->sent_scroll_delta(), gfx::Vector2d());
+ PostSetNeedsCommitToMainThread();
+ break;
+ }
+ }
+
+ virtual void AfterTest() OVERRIDE {}
+
+ private:
+ gfx::Vector2d initial_scroll_;
+ gfx::Vector2d impl_thread_scroll_;
+};
+
+MULTI_THREAD_TEST_F(ImplSidePaintingScrollTestImplOnlyScroll);
+
class LayerTreeHostScrollTestScrollZeroMaxScrollOffset
: public LayerTreeHostScrollTest {
public:
diff --git a/chromium/cc/trees/layer_tree_impl.cc b/chromium/cc/trees/layer_tree_impl.cc
index 70ad6de88fa..280bdad8ee1 100644
--- a/chromium/cc/trees/layer_tree_impl.cc
+++ b/chromium/cc/trees/layer_tree_impl.cc
@@ -9,8 +9,9 @@
#include "cc/animation/scrollbar_animation_controller.h"
#include "cc/debug/traced_value.h"
#include "cc/layers/heads_up_display_layer_impl.h"
+#include "cc/layers/layer.h"
#include "cc/layers/render_surface_impl.h"
-#include "cc/layers/scrollbar_layer_impl.h"
+#include "cc/layers/scrollbar_layer_impl_base.h"
#include "cc/trees/layer_tree_host_common.h"
#include "cc/trees/layer_tree_host_impl.h"
#include "ui/gfx/size_conversions.h"
@@ -27,6 +28,9 @@ LayerTreeImpl::LayerTreeImpl(LayerTreeHostImpl* layer_tree_host_impl)
root_layer_scroll_offset_delegate_(NULL),
background_color_(0),
has_transparent_background_(false),
+ page_scale_layer_(NULL),
+ inner_viewport_scroll_layer_(NULL),
+ outer_viewport_scroll_layer_(NULL),
page_scale_factor_(1),
page_scale_delta_(1),
sent_page_scale_delta_(1),
@@ -115,6 +119,13 @@ void LayerTreeImpl::PushPropertiesTo(LayerTreeImpl* target_tree) {
target_tree->page_scale_delta() / target_tree->sent_page_scale_delta());
target_tree->set_sent_page_scale_delta(1);
+ if (settings().use_pinch_virtual_viewport) {
+ target_tree->SetViewportLayersFromIds(
+ page_scale_layer_->id(),
+ inner_viewport_scroll_layer_->id(),
+ outer_viewport_scroll_layer_ ? outer_viewport_scroll_layer_->id()
+ : Layer::INVALID_ID);
+ }
// This should match the property synchronization in
// LayerTreeHost::finishCommitOnImplThread().
target_tree->set_source_frame_number(source_frame_number());
@@ -207,7 +218,7 @@ void LayerTreeImpl::SetPageScaleDelta(float delta) {
}
gfx::SizeF LayerTreeImpl::ScrollableViewportSize() const {
- return gfx::ScaleSize(layer_tree_host_impl_->VisibleViewportSize(),
+ return gfx::ScaleSize(layer_tree_host_impl_->UnscaledScrollableViewportSize(),
1.0f / total_page_scale_factor());
}
@@ -226,11 +237,11 @@ void LayerTreeImpl::UpdateMaxScrollOffset() {
root_scroll_layer_->SetMaxScrollOffset(gfx::ToFlooredVector2d(max_scroll));
}
-static void ApplySentScrollDeltasOn(LayerImpl* layer) {
- layer->ApplySentScrollDeltas();
+static void ApplySentScrollDeltasFromAbortedCommitTo(LayerImpl* layer) {
+ layer->ApplySentScrollDeltasFromAbortedCommit();
}
-void LayerTreeImpl::ApplySentScrollAndScaleDeltas() {
+void LayerTreeImpl::ApplySentScrollAndScaleDeltasFromAbortedCommit() {
DCHECK(IsActiveTree());
page_scale_factor_ *= sent_page_scale_delta_;
@@ -241,9 +252,48 @@ void LayerTreeImpl::ApplySentScrollAndScaleDeltas() {
return;
LayerTreeHostCommon::CallFunctionForSubtree(
- root_layer(), base::Bind(&ApplySentScrollDeltasOn));
+ root_layer(), base::Bind(&ApplySentScrollDeltasFromAbortedCommitTo));
+}
+
+static void ApplyScrollDeltasSinceBeginFrameTo(LayerImpl* layer) {
+ layer->ApplyScrollDeltasSinceBeginFrame();
+}
+
+void LayerTreeImpl::ApplyScrollDeltasSinceBeginFrame() {
+ DCHECK(IsPendingTree());
+ if (!root_layer())
+ return;
+
+ LayerTreeHostCommon::CallFunctionForSubtree(
+ root_layer(), base::Bind(&ApplyScrollDeltasSinceBeginFrameTo));
+}
+
+void LayerTreeImpl::SetViewportLayersFromIds(
+ int page_scale_layer_id,
+ int inner_viewport_scroll_layer_id,
+ int outer_viewport_scroll_layer_id) {
+ page_scale_layer_ = LayerById(page_scale_layer_id);
+ DCHECK(page_scale_layer_);
+
+ inner_viewport_scroll_layer_ =
+ LayerById(inner_viewport_scroll_layer_id);
+ DCHECK(inner_viewport_scroll_layer_);
+
+ outer_viewport_scroll_layer_ =
+ LayerById(outer_viewport_scroll_layer_id);
+ DCHECK(outer_viewport_scroll_layer_ ||
+ outer_viewport_scroll_layer_id == Layer::INVALID_ID);
}
+void LayerTreeImpl::ClearViewportLayers() {
+ page_scale_layer_ = NULL;
+ inner_viewport_scroll_layer_ = NULL;
+ outer_viewport_scroll_layer_ = NULL;
+}
+
+// TODO(wjmaclean) This needs to go away, and be replaced with a single core
+// of login that works for both scrollbar layer types. This is already planned
+// as part of the larger pinch-zoom re-factoring viewport.
void LayerTreeImpl::UpdateSolidColorScrollbars() {
DCHECK(settings().solid_color_scrollbars);
@@ -256,14 +306,17 @@ void LayerTreeImpl::UpdateSolidColorScrollbars() {
ScrollableViewportSize());
float vertical_adjust = 0.0f;
if (RootContainerLayer())
- vertical_adjust = layer_tree_host_impl_->VisibleViewportSize().height() -
- RootContainerLayer()->bounds().height();
- if (ScrollbarLayerImpl* horiz = root_scroll->horizontal_scrollbar_layer()) {
+ vertical_adjust =
+ layer_tree_host_impl_->UnscaledScrollableViewportSize().height() -
+ RootContainerLayer()->bounds().height();
+ if (ScrollbarLayerImplBase* horiz =
+ root_scroll->horizontal_scrollbar_layer()) {
horiz->SetVerticalAdjust(vertical_adjust);
horiz->SetVisibleToTotalLengthRatio(
scrollable_viewport.width() / ScrollableSize().width());
}
- if (ScrollbarLayerImpl* vertical = root_scroll->vertical_scrollbar_layer()) {
+ if (ScrollbarLayerImplBase* vertical =
+ root_scroll->vertical_scrollbar_layer()) {
vertical->SetVerticalAdjust(vertical_adjust);
vertical->SetVisibleToTotalLengthRatio(
scrollable_viewport.height() / ScrollableSize().height());
@@ -297,13 +350,15 @@ void LayerTreeImpl::UpdateDrawProperties() {
IsActiveTree(),
"SourceFrameNumber",
source_frame_number_);
+ LayerImpl* page_scale_layer =
+ page_scale_layer_ ? page_scale_layer_ : RootContainerLayer();
LayerTreeHostCommon::CalcDrawPropsImplInputs inputs(
root_layer(),
- layer_tree_host_impl_->DeviceViewport().size(),
- layer_tree_host_impl_->DeviceTransform(),
+ DrawViewportSize(),
+ layer_tree_host_impl_->DrawTransform(),
device_scale_factor(),
total_page_scale_factor(),
- root_scroll_layer_ ? root_scroll_layer_->parent() : NULL,
+ page_scale_layer,
MaxTextureSize(),
settings().can_use_lcd_text,
settings().layer_transforms_should_scale_layer_contents,
@@ -408,6 +463,10 @@ const RendererCapabilities& LayerTreeImpl::GetRendererCapabilities() const {
return layer_tree_host_impl_->GetRendererCapabilities();
}
+ContextProvider* LayerTreeImpl::context_provider() const {
+ return output_surface()->context_provider();
+}
+
OutputSurface* LayerTreeImpl::output_surface() const {
return layer_tree_host_impl_->output_surface();
}
@@ -432,6 +491,10 @@ MemoryHistory* LayerTreeImpl::memory_history() const {
return layer_tree_host_impl_->memory_history();
}
+bool LayerTreeImpl::device_viewport_valid_for_tile_management() const {
+ return layer_tree_host_impl_->device_viewport_valid_for_tile_management();
+}
+
bool LayerTreeImpl::IsActiveTree() const {
return layer_tree_host_impl_->active_tree() == this;
}
@@ -482,6 +545,14 @@ void LayerTreeImpl::SetNeedsCommit() {
layer_tree_host_impl_->SetNeedsCommit();
}
+gfx::Size LayerTreeImpl::DrawViewportSize() const {
+ return layer_tree_host_impl_->DrawViewportSize();
+}
+
+void LayerTreeImpl::StartScrollbarAnimation() {
+ layer_tree_host_impl_->StartScrollbarAnimation();
+}
+
void LayerTreeImpl::SetNeedsRedraw() {
layer_tree_host_impl_->SetNeedsRedraw();
}
@@ -494,10 +565,6 @@ float LayerTreeImpl::device_scale_factor() const {
return layer_tree_host_impl_->device_scale_factor();
}
-gfx::Size LayerTreeImpl::device_viewport_size() const {
- return layer_tree_host_impl_->device_viewport_size();
-}
-
DebugRectHistory* LayerTreeImpl::debug_rect_history() const {
return layer_tree_host_impl_->debug_rect_history();
}
@@ -571,8 +638,8 @@ void LayerTreeImpl::ClearLatencyInfo() {
latency_info_.Clear();
}
-void LayerTreeImpl::WillModifyTilePriorities() {
- layer_tree_host_impl_->SetNeedsManageTiles();
+void LayerTreeImpl::DidModifyTilePriorities() {
+ layer_tree_host_impl_->DidModifyTilePriorities();
}
void LayerTreeImpl::set_ui_resource_request_queue(
@@ -590,18 +657,23 @@ void LayerTreeImpl::ProcessUIResourceRequestQueue() {
UIResourceRequest req = ui_resource_request_queue_.front();
ui_resource_request_queue_.pop_front();
- switch (req.type) {
+ switch (req.GetType()) {
case UIResourceRequest::UIResourceCreate:
- layer_tree_host_impl_->CreateUIResource(req.id, req.bitmap);
+ layer_tree_host_impl_->CreateUIResource(req.GetId(), req.GetBitmap());
break;
case UIResourceRequest::UIResourceDelete:
- layer_tree_host_impl_->DeleteUIResource(req.id);
+ layer_tree_host_impl_->DeleteUIResource(req.GetId());
break;
- default:
+ case UIResourceRequest::UIResourceInvalidRequest:
NOTREACHED();
break;
}
}
+
+ // If all UI resource evictions were not recreated by processing this queue,
+ // then another commit is required.
+ if (layer_tree_host_impl_->EvictedUIResourcesExist())
+ layer_tree_host_impl_->SetNeedsCommit();
}
void LayerTreeImpl::AddLayerWithCopyOutputRequest(LayerImpl* layer) {
diff --git a/chromium/cc/trees/layer_tree_impl.h b/chromium/cc/trees/layer_tree_impl.h
index 4f1afbe3cc7..2ae33df2ba2 100644
--- a/chromium/cc/trees/layer_tree_impl.h
+++ b/chromium/cc/trees/layer_tree_impl.h
@@ -14,7 +14,7 @@
#include "cc/layers/layer_impl.h"
#include "cc/trees/layer_tree_host.h"
#include "cc/resources/ui_resource_client.h"
-#include "ui/base/latency_info.h"
+#include "ui/events/latency_info.h"
#if defined(COMPILER_GCC)
namespace BASE_HASH_NAMESPACE {
@@ -29,6 +29,7 @@ struct hash<cc::LayerImpl*> {
namespace cc {
+class ContextProvider;
class DebugRectHistory;
class FrameRateCounter;
class HeadsUpDisplayLayerImpl;
@@ -42,6 +43,7 @@ class PaintTimeCounter;
class Proxy;
class ResourceProvider;
class TileManager;
+class UIResourceRequest;
struct RendererCapabilities;
typedef std::list<UIResourceRequest> UIResourceRequestQueue;
@@ -58,12 +60,14 @@ class CC_EXPORT LayerTreeImpl {
// ---------------------------------------------------------------------------
const LayerTreeSettings& settings() const;
const RendererCapabilities& GetRendererCapabilities() const;
+ ContextProvider* context_provider() const;
OutputSurface* output_surface() const;
ResourceProvider* resource_provider() const;
TileManager* tile_manager() const;
FrameRateCounter* frame_rate_counter() const;
PaintTimeCounter* paint_time_counter() const;
MemoryHistory* memory_history() const;
+ bool device_viewport_valid_for_tile_management() const;
bool IsActiveTree() const;
bool IsPendingTree() const;
bool IsRecycleTree() const;
@@ -75,6 +79,8 @@ class CC_EXPORT LayerTreeImpl {
base::Time CurrentFrameTime() const;
base::TimeTicks CurrentPhysicalTimeTicks() const;
void SetNeedsCommit();
+ gfx::Size DrawViewportSize() const;
+ void StartScrollbarAnimation();
// Tree specific methods exposed to layer-impl tree.
// ---------------------------------------------------------------------------
@@ -84,7 +90,6 @@ class CC_EXPORT LayerTreeImpl {
// trivial accessors in a followup patch.
const LayerTreeDebugState& debug_state() const;
float device_scale_factor() const;
- gfx::Size device_viewport_size() const;
DebugRectHistory* debug_rect_history() const;
scoped_ptr<base::Value> AsValue() const;
@@ -114,7 +119,12 @@ class CC_EXPORT LayerTreeImpl {
void FindRootScrollLayer();
void UpdateMaxScrollOffset();
- void ApplySentScrollAndScaleDeltas();
+ void SetViewportLayersFromIds(int page_scale_layer_id,
+ int inner_viewport_scroll_layer_id,
+ int outer_viewport_scroll_layer_id);
+ void ClearViewportLayers();
+ void ApplySentScrollAndScaleDeltasFromAbortedCommit();
+ void ApplyScrollDeltasSinceBeginFrame();
SkColor background_color() const { return background_color_; }
void set_background_color(SkColor color) { background_color_ = color; }
@@ -196,7 +206,7 @@ class CC_EXPORT LayerTreeImpl {
const ui::LatencyInfo& GetLatencyInfo();
void ClearLatencyInfo();
- void WillModifyTilePriorities();
+ void DidModifyTilePriorities();
ResourceProvider::ResourceId ResourceIdForUIResource(UIResourceId uid) const;
void ProcessUIResourceRequestQueue();
@@ -222,6 +232,10 @@ class CC_EXPORT LayerTreeImpl {
SkColor background_color_;
bool has_transparent_background_;
+ LayerImpl* page_scale_layer_;
+ LayerImpl* inner_viewport_scroll_layer_;
+ LayerImpl* outer_viewport_scroll_layer_;
+
float page_scale_factor_;
float page_scale_delta_;
float sent_page_scale_delta_;
diff --git a/chromium/cc/trees/layer_tree_settings.cc b/chromium/cc/trees/layer_tree_settings.cc
index 62fd261ef82..8e8e35d5faa 100644
--- a/chromium/cc/trees/layer_tree_settings.cc
+++ b/chromium/cc/trees/layer_tree_settings.cc
@@ -17,6 +17,7 @@ LayerTreeSettings::LayerTreeSettings()
allow_antialiasing(true),
throttle_frame_production(true),
begin_frame_scheduling_enabled(false),
+ deadline_scheduling_enabled(false),
using_synchronous_renderer_compositor(false),
per_tile_painting_enabled(false),
partial_swap_enabled(false),
@@ -26,15 +27,15 @@ LayerTreeSettings::LayerTreeSettings()
show_overdraw_in_tracing(false),
can_use_lcd_text(true),
should_clear_root_render_pass(true),
- use_linear_fade_scrollbar_animator(false),
+ scrollbar_animator(NoAnimator),
scrollbar_linear_fade_delay_ms(300),
scrollbar_linear_fade_length_ms(300),
solid_color_scrollbars(false),
solid_color_scrollbar_color(SK_ColorWHITE),
- solid_color_scrollbar_thickness_dip(-1),
calculate_top_controls_position(false),
use_memory_management(true),
timeout_and_draw_when_animation_checkerboards(true),
+ maximum_number_of_failed_draws_before_draw_is_forced_(3),
layer_transforms_should_scale_layer_contents(false),
minimum_contents_scale(0.0625f),
low_res_contents_scale_factor(0.125f),
@@ -56,8 +57,8 @@ LayerTreeSettings::LayerTreeSettings()
force_direct_layer_drawing(false),
strict_layer_property_change_checking(false),
use_map_image(false),
- compositor_name("ChromiumCompositor"),
- ignore_root_layer_flings(false) {
+ ignore_root_layer_flings(false),
+ use_rgba_4444_textures(false) {
// TODO(danakj): Renable surface caching when we can do it more realiably.
// crbug.com/170713
cache_render_pass_contents = false;
diff --git a/chromium/cc/trees/layer_tree_settings.h b/chromium/cc/trees/layer_tree_settings.h
index d3a71cf8ed7..aa8cdd9860f 100644
--- a/chromium/cc/trees/layer_tree_settings.h
+++ b/chromium/cc/trees/layer_tree_settings.h
@@ -5,8 +5,6 @@
#ifndef CC_TREES_LAYER_TREE_SETTINGS_H_
#define CC_TREES_LAYER_TREE_SETTINGS_H_
-#include <string>
-
#include "base/basictypes.h"
#include "cc/base/cc_export.h"
#include "cc/debug/layer_tree_debug_state.h"
@@ -24,6 +22,7 @@ class CC_EXPORT LayerTreeSettings {
bool allow_antialiasing;
bool throttle_frame_production;
bool begin_frame_scheduling_enabled;
+ bool deadline_scheduling_enabled;
bool using_synchronous_renderer_compositor;
bool per_tile_painting_enabled;
bool partial_swap_enabled;
@@ -33,15 +32,21 @@ class CC_EXPORT LayerTreeSettings {
bool show_overdraw_in_tracing;
bool can_use_lcd_text;
bool should_clear_root_render_pass;
- bool use_linear_fade_scrollbar_animator;
+
+ enum ScrollbarAnimator {
+ NoAnimator,
+ LinearFade,
+ Thinning,
+ };
+ ScrollbarAnimator scrollbar_animator;
int scrollbar_linear_fade_delay_ms;
int scrollbar_linear_fade_length_ms;
bool solid_color_scrollbars;
SkColor solid_color_scrollbar_color;
- int solid_color_scrollbar_thickness_dip;
bool calculate_top_controls_position;
bool use_memory_management;
bool timeout_and_draw_when_animation_checkerboards;
+ int maximum_number_of_failed_draws_before_draw_is_forced_;
bool layer_transforms_should_scale_layer_contents;
float minimum_contents_scale;
float low_res_contents_scale_factor;
@@ -62,8 +67,8 @@ class CC_EXPORT LayerTreeSettings {
bool force_direct_layer_drawing; // With Skia GPU backend.
bool strict_layer_property_change_checking;
bool use_map_image;
- std::string compositor_name;
bool ignore_root_layer_flings;
+ bool use_rgba_4444_textures;
LayerTreeDebugState initial_debug_state;
};
diff --git a/chromium/cc/trees/proxy.cc b/chromium/cc/trees/proxy.cc
index c8dacafb234..9315f31f56a 100644
--- a/chromium/cc/trees/proxy.cc
+++ b/chromium/cc/trees/proxy.cc
@@ -77,8 +77,8 @@ Proxy::~Proxy() {
DCHECK(IsMainThread());
}
-std::string Proxy::SchedulerStateAsStringForTesting() {
- return "";
+scoped_ptr<base::Value> Proxy::SchedulerStateAsValueForTesting() {
+ return make_scoped_ptr(base::Value::CreateNullValue());
}
} // namespace cc
diff --git a/chromium/cc/trees/proxy.h b/chromium/cc/trees/proxy.h
index c5f9d555711..84844c89497 100644
--- a/chromium/cc/trees/proxy.h
+++ b/chromium/cc/trees/proxy.h
@@ -69,6 +69,7 @@ class CC_EXPORT Proxy {
virtual void SetNeedsUpdateLayers() = 0;
virtual void SetNeedsCommit() = 0;
virtual void SetNeedsRedraw(gfx::Rect damage_rect) = 0;
+ virtual void SetNextCommitWaitsForActivation() = 0;
virtual void NotifyInputThrottledUntilCommit() = 0;
@@ -98,7 +99,7 @@ class CC_EXPORT Proxy {
// Testing hooks
virtual bool CommitPendingForTesting() = 0;
- virtual std::string SchedulerStateAsStringForTesting();
+ virtual scoped_ptr<base::Value> SchedulerStateAsValueForTesting();
protected:
explicit Proxy(
diff --git a/chromium/cc/trees/single_thread_proxy.cc b/chromium/cc/trees/single_thread_proxy.cc
index 3a26a079ec6..85db94c6902 100644
--- a/chromium/cc/trees/single_thread_proxy.cc
+++ b/chromium/cc/trees/single_thread_proxy.cc
@@ -11,6 +11,7 @@
#include "cc/quads/draw_quad.h"
#include "cc/resources/prioritized_resource_manager.h"
#include "cc/resources/resource_update_controller.h"
+#include "cc/trees/blocking_task_runner.h"
#include "cc/trees/layer_tree_host.h"
#include "cc/trees/layer_tree_impl.h"
@@ -69,10 +70,7 @@ bool SingleThreadProxy::CompositeAndReadback(void* pixels, gfx::Rect rect) {
if (layer_tree_host_impl_->IsContextLost())
return false;
-
- layer_tree_host_impl_->SwapBuffers(frame);
}
- DidSwapFrame();
return true;
}
@@ -100,7 +98,7 @@ void SingleThreadProxy::SetVisible(bool visible) {
layer_tree_host_impl_->SetVisible(visible);
// Changing visibility could change ShouldComposite().
- layer_tree_host_impl_->UpdateBackgroundAnimateTicking(!ShouldComposite());
+ UpdateBackgroundAnimateTicking();
}
void SingleThreadProxy::CreateAndInitializeOutputSurface() {
@@ -127,7 +125,7 @@ void SingleThreadProxy::CreateAndInitializeOutputSurface() {
}
{
- DebugScopedSetMainThreadBlocked mainThreadBlocked(this);
+ DebugScopedSetMainThreadBlocked main_thread_blocked(this);
DebugScopedSetImplThread impl(this);
layer_tree_host_->DeleteContentsTexturesOnImplThread(
layer_tree_host_impl_->resource_provider());
@@ -143,12 +141,13 @@ void SingleThreadProxy::CreateAndInitializeOutputSurface() {
if (initialized) {
renderer_capabilities_for_main_thread_ =
layer_tree_host_impl_->GetRendererCapabilities();
-
- layer_tree_host_impl_->resource_provider()->
- set_offscreen_context_provider(offscreen_context_provider);
} else if (offscreen_context_provider.get()) {
offscreen_context_provider->VerifyContexts();
+ offscreen_context_provider = NULL;
}
+
+ layer_tree_host_impl_->SetOffscreenContextProvider(
+ offscreen_context_provider);
}
OnOutputSurfaceInitializeAttempted(initialized);
@@ -183,9 +182,14 @@ void SingleThreadProxy::DoCommit(scoped_ptr<ResourceUpdateQueue> queue) {
DCHECK(Proxy::IsMainThread());
// Commit immediately.
{
- DebugScopedSetMainThreadBlocked mainThreadBlocked(this);
+ DebugScopedSetMainThreadBlocked main_thread_blocked(this);
DebugScopedSetImplThread impl(this);
+ // This CapturePostTasks should be destroyed before CommitComplete() is
+ // called since that goes out to the embedder, and we want the embedder
+ // to receive its callbacks before that.
+ BlockingTaskRunner::CapturePostTasks blocked;
+
RenderingStatsInstrumentation* stats_instrumentation =
layer_tree_host_->rendering_stats_instrumentation();
base::TimeTicks start_time = stats_instrumentation->StartRecording();
@@ -206,6 +210,9 @@ void SingleThreadProxy::DoCommit(scoped_ptr<ResourceUpdateQueue> queue) {
layer_tree_host_impl_->resource_provider());
update_controller->Finalize();
+ if (layer_tree_host_impl_->EvictedUIResourcesExist())
+ layer_tree_host_->RecreateUIResources();
+
layer_tree_host_->FinishCommitOnImplThread(layer_tree_host_impl_.get());
layer_tree_host_impl_->CommitComplete();
@@ -221,6 +228,8 @@ void SingleThreadProxy::DoCommit(scoped_ptr<ResourceUpdateQueue> queue) {
base::TimeDelta duration = stats_instrumentation->EndRecording(start_time);
stats_instrumentation->AddCommit(duration);
+ stats_instrumentation->IssueTraceEventForMainThreadStats();
+ stats_instrumentation->AccumulateAndClearMainThreadStats();
}
layer_tree_host_->CommitComplete();
next_frame_is_newly_committed_frame_ = true;
@@ -235,9 +244,8 @@ void SingleThreadProxy::SetNeedsRedraw(gfx::Rect damage_rect) {
SetNeedsRedrawRectOnImplThread(damage_rect);
}
-void SingleThreadProxy::OnHasPendingTreeStateChanged(bool have_pending_tree) {
- // Thread-only feature.
- NOTREACHED();
+void SingleThreadProxy::SetNextCommitWaitsForActivation() {
+ // There is no activation here other than commit. So do nothing.
}
void SingleThreadProxy::SetDeferCommits(bool defer_commits) {
@@ -255,7 +263,7 @@ void SingleThreadProxy::Stop() {
TRACE_EVENT0("cc", "SingleThreadProxy::stop");
DCHECK(Proxy::IsMainThread());
{
- DebugScopedSetMainThreadBlocked mainThreadBlocked(this);
+ DebugScopedSetMainThreadBlocked main_thread_blocked(this);
DebugScopedSetImplThread impl(this);
layer_tree_host_->DeleteContentsTexturesOnImplThread(
@@ -267,13 +275,23 @@ void SingleThreadProxy::Stop() {
void SingleThreadProxy::OnCanDrawStateChanged(bool can_draw) {
DCHECK(Proxy::IsImplThread());
- layer_tree_host_impl_->UpdateBackgroundAnimateTicking(!ShouldComposite());
+ UpdateBackgroundAnimateTicking();
+}
+
+void SingleThreadProxy::NotifyReadyToActivate() {
+ // Thread-only feature.
+ NOTREACHED();
}
void SingleThreadProxy::SetNeedsRedrawOnImplThread() {
layer_tree_host_->ScheduleComposite();
}
+void SingleThreadProxy::SetNeedsManageTilesOnImplThread() {
+ // Thread-only/Impl-side-painting-only feature.
+ NOTREACHED();
+}
+
void SingleThreadProxy::SetNeedsRedrawRectOnImplThread(gfx::Rect damage_rect) {
// TODO(brianderson): Once we move render_widget scheduling into this class,
// we can treat redraw requests more efficiently than CommitAndRedraw
@@ -332,13 +350,6 @@ void SingleThreadProxy::SendManagedMemoryStats() {
bool SingleThreadProxy::IsInsideDraw() { return inside_draw_; }
-void SingleThreadProxy::DidTryInitializeRendererOnImplThread(
- bool success,
- scoped_refptr<ContextProvider> offscreen_context_provider) {
- NOTREACHED()
- << "This is only used on threaded compositing with impl-side painting";
-}
-
void SingleThreadProxy::DidLoseOutputSurfaceOnImplThread() {
// Cause a commit so we can notice the lost context.
SetNeedsCommitOnImplThread();
@@ -354,7 +365,21 @@ void SingleThreadProxy::CompositeImmediately(base::TimeTicks frame_begin_time) {
device_viewport_damage_rect,
false, // for_readback
&frame)) {
- layer_tree_host_impl_->SwapBuffers(frame);
+ {
+ DebugScopedSetMainThreadBlocked main_thread_blocked(this);
+ DebugScopedSetImplThread impl(this);
+
+ // This CapturePostTasks should be destroyed before
+ // DidCommitAndDrawFrame() is called since that goes out to the embedder,
+ // and we want the embedder to receive its callbacks before that.
+ // NOTE: This maintains consistent ordering with the ThreadProxy since
+ // the DidCommitAndDrawFrame() must be post-tasked from the impl thread
+ // there as the main thread is not blocked, so any posted tasks inside
+ // the swap buffers will execute first.
+ BlockingTaskRunner::CapturePostTasks blocked;
+
+ layer_tree_host_impl_->SwapBuffers(frame);
+ }
DidSwapFrame();
}
}
@@ -407,12 +432,15 @@ bool SingleThreadProxy::CommitAndComposite(
if (layer_tree_host_->contents_texture_manager()) {
layer_tree_host_->contents_texture_manager()
->UnlinkAndClearEvictedBackings();
+ layer_tree_host_->contents_texture_manager()->SetMaxMemoryLimitBytes(
+ layer_tree_host_impl_->memory_allocation_limit_bytes());
+ layer_tree_host_->contents_texture_manager()->SetExternalPriorityCutoff(
+ layer_tree_host_impl_->memory_allocation_priority_cutoff());
}
scoped_ptr<ResourceUpdateQueue> queue =
make_scoped_ptr(new ResourceUpdateQueue);
- layer_tree_host_->UpdateLayers(
- queue.get(), layer_tree_host_impl_->memory_allocation_limit_bytes());
+ layer_tree_host_->UpdateLayers(queue.get());
layer_tree_host_->WillCommit();
DoCommit(queue.Pass());
@@ -431,6 +459,12 @@ bool SingleThreadProxy::ShouldComposite() const {
layer_tree_host_impl_->CanDraw();
}
+void SingleThreadProxy::UpdateBackgroundAnimateTicking() {
+ DCHECK(Proxy::IsImplThread());
+ layer_tree_host_impl_->UpdateBackgroundAnimateTicking(
+ !ShouldComposite() && layer_tree_host_impl_->active_tree()->root_layer());
+}
+
bool SingleThreadProxy::DoComposite(
scoped_refptr<cc::ContextProvider> offscreen_context_provider,
base::TimeTicks frame_begin_time,
@@ -444,8 +478,8 @@ bool SingleThreadProxy::DoComposite(
DebugScopedSetImplThread impl(this);
base::AutoReset<bool> mark_inside(&inside_draw_, true);
- layer_tree_host_impl_->resource_provider()->
- set_offscreen_context_provider(offscreen_context_provider);
+ layer_tree_host_impl_->SetOffscreenContextProvider(
+ offscreen_context_provider);
bool can_do_readback = layer_tree_host_impl_->renderer()->CanReadPixels();
@@ -454,14 +488,14 @@ bool SingleThreadProxy::DoComposite(
// DrawLayers() depends on the result of PrepareToDraw(), it is guarded on
// CanDraw() as well.
if (!ShouldComposite() || (for_readback && !can_do_readback)) {
- layer_tree_host_impl_->UpdateBackgroundAnimateTicking(true);
+ UpdateBackgroundAnimateTicking();
return false;
}
layer_tree_host_impl_->Animate(
layer_tree_host_impl_->CurrentFrameTimeTicks(),
layer_tree_host_impl_->CurrentFrameTime());
- layer_tree_host_impl_->UpdateBackgroundAnimateTicking(false);
+ UpdateBackgroundAnimateTicking();
layer_tree_host_impl_->PrepareToDraw(frame, device_viewport_damage_rect);
layer_tree_host_impl_->DrawLayers(frame, frame_begin_time);
@@ -475,8 +509,8 @@ bool SingleThreadProxy::DoComposite(
}
if (lost_output_surface) {
- cc::ContextProvider* offscreen_contexts = layer_tree_host_impl_->
- resource_provider()->offscreen_context_provider();
+ cc::ContextProvider* offscreen_contexts =
+ layer_tree_host_impl_->offscreen_context_provider();
if (offscreen_contexts)
offscreen_contexts->VerifyContexts();
layer_tree_host_->DidLoseOutputSurface();
diff --git a/chromium/cc/trees/single_thread_proxy.h b/chromium/cc/trees/single_thread_proxy.h
index 8e9438ccbef..474fb1f1101 100644
--- a/chromium/cc/trees/single_thread_proxy.h
+++ b/chromium/cc/trees/single_thread_proxy.h
@@ -35,6 +35,7 @@ class SingleThreadProxy : public Proxy, LayerTreeHostImplClient {
virtual void SetNeedsUpdateLayers() OVERRIDE;
virtual void SetNeedsCommit() OVERRIDE;
virtual void SetNeedsRedraw(gfx::Rect damage_rect) OVERRIDE;
+ virtual void SetNextCommitWaitsForActivation() OVERRIDE;
virtual void NotifyInputThrottledUntilCommit() OVERRIDE {}
virtual void SetDeferCommits(bool defer_commits) OVERRIDE;
virtual bool CommitRequested() const OVERRIDE;
@@ -48,17 +49,15 @@ class SingleThreadProxy : public Proxy, LayerTreeHostImplClient {
virtual bool CommitPendingForTesting() OVERRIDE;
// LayerTreeHostImplClient implementation
- virtual void DidTryInitializeRendererOnImplThread(
- bool success,
- scoped_refptr<ContextProvider> offscreen_context_provider) OVERRIDE;
virtual void DidLoseOutputSurfaceOnImplThread() OVERRIDE;
virtual void OnSwapBuffersCompleteOnImplThread() OVERRIDE {}
virtual void BeginFrameOnImplThread(const BeginFrameArgs& args)
OVERRIDE {}
virtual void OnCanDrawStateChanged(bool can_draw) OVERRIDE;
- virtual void OnHasPendingTreeStateChanged(bool have_pending_tree) OVERRIDE;
+ virtual void NotifyReadyToActivate() OVERRIDE;
virtual void SetNeedsRedrawOnImplThread() OVERRIDE;
virtual void SetNeedsRedrawRectOnImplThread(gfx::Rect dirty_rect) OVERRIDE;
+ virtual void SetNeedsManageTilesOnImplThread() OVERRIDE;
virtual void DidInitializeVisibleTileOnImplThread() OVERRIDE;
virtual void SetNeedsCommitOnImplThread() OVERRIDE;
virtual void PostAnimationEventsToMainThreadOnImplThread(
@@ -96,6 +95,7 @@ class SingleThreadProxy : public Proxy, LayerTreeHostImplClient {
void DidSwapFrame();
bool ShouldComposite() const;
+ void UpdateBackgroundAnimateTicking();
// Accessed on main thread only.
LayerTreeHost* layer_tree_host_;
diff --git a/chromium/cc/trees/thread_proxy.cc b/chromium/cc/trees/thread_proxy.cc
index a0bd4bf5a7e..b5dff0e2bcb 100644
--- a/chromium/cc/trees/thread_proxy.cc
+++ b/chromium/cc/trees/thread_proxy.cc
@@ -18,6 +18,7 @@
#include "cc/scheduler/delay_based_time_source.h"
#include "cc/scheduler/frame_rate_controller.h"
#include "cc/scheduler/scheduler.h"
+#include "cc/trees/blocking_task_runner.h"
#include "cc/trees/layer_tree_host.h"
#include "cc/trees/layer_tree_impl.h"
@@ -52,7 +53,7 @@ struct ThreadProxy::CommitPendingRequest {
struct ThreadProxy::SchedulerStateRequest {
CompletionEvent completion;
- std::string state;
+ scoped_ptr<base::Value> state;
};
scoped_ptr<Proxy> ThreadProxy::Create(
@@ -75,6 +76,8 @@ ThreadProxy::ThreadProxy(
textures_acquired_(true),
in_composite_and_readback_(false),
manage_tiles_pending_(false),
+ commit_waits_for_activation_(false),
+ inside_commit_(false),
weak_factory_on_impl_thread_(this),
weak_factory_(this),
begin_frame_sent_to_main_thread_completion_event_on_impl_thread_(NULL),
@@ -123,19 +126,27 @@ bool ThreadProxy::CompositeAndReadback(void* pixels, gfx::Rect rect) {
return false;
}
- // Perform a synchronous commit.
+ // Perform a synchronous commit with an associated readback.
+ ReadbackRequest request;
+ request.rect = rect;
+ request.pixels = pixels;
{
DebugScopedSetMainThreadBlocked main_thread_blocked(this);
CompletionEvent begin_frame_sent_to_main_thread_completion;
- Proxy::ImplThreadTaskRunner()->PostTask(
- FROM_HERE,
- base::Bind(&ThreadProxy::ForceCommitOnImplThread,
- impl_thread_weak_ptr_,
- &begin_frame_sent_to_main_thread_completion));
+ Proxy::ImplThreadTaskRunner()
+ ->PostTask(FROM_HERE,
+ base::Bind(&ThreadProxy::ForceCommitForReadbackOnImplThread,
+ impl_thread_weak_ptr_,
+ &begin_frame_sent_to_main_thread_completion,
+ &request));
begin_frame_sent_to_main_thread_completion.Wait();
}
in_composite_and_readback_ = true;
+ // This is the forced commit.
+ // Note: The Impl thread also queues a separate BeginFrameOnMainThread on the
+ // main thread, which will be called after this CompositeAndReadback
+ // completes, to replace the forced commit.
BeginFrameOnMainThread(scoped_ptr<BeginFrameAndCommitState>());
in_composite_and_readback_ = false;
@@ -143,48 +154,35 @@ bool ThreadProxy::CompositeAndReadback(void* pixels, gfx::Rect rect) {
// that it made.
can_cancel_commit_ = false;
- // Perform a synchronous readback.
- ReadbackRequest request;
- request.rect = rect;
- request.pixels = pixels;
- {
- DebugScopedSetMainThreadBlocked main_thread_blocked(this);
- Proxy::ImplThreadTaskRunner()->PostTask(
- FROM_HERE,
- base::Bind(&ThreadProxy::RequestReadbackOnImplThread,
- impl_thread_weak_ptr_,
- &request));
- request.completion.Wait();
- }
+ request.completion.Wait();
return request.success;
}
-void ThreadProxy::ForceCommitOnImplThread(CompletionEvent* completion) {
- TRACE_EVENT0("cc", "ThreadProxy::ForceCommitOnImplThread");
+void ThreadProxy::ForceCommitForReadbackOnImplThread(
+ CompletionEvent* begin_frame_sent_completion,
+ ReadbackRequest* request) {
+ TRACE_EVENT0("cc", "ThreadProxy::ForceCommitForReadbackOnImplThread");
DCHECK(IsImplThread());
DCHECK(!begin_frame_sent_to_main_thread_completion_event_on_impl_thread_);
-
- scheduler_on_impl_thread_->SetNeedsForcedCommit();
- if (scheduler_on_impl_thread_->CommitPending()) {
- completion->Signal();
- return;
- }
-
- begin_frame_sent_to_main_thread_completion_event_on_impl_thread_ = completion;
-}
-
-void ThreadProxy::RequestReadbackOnImplThread(ReadbackRequest* request) {
- DCHECK(Proxy::IsImplThread());
DCHECK(!readback_request_on_impl_thread_);
+
if (!layer_tree_host_impl_) {
+ begin_frame_sent_completion->Signal();
request->success = false;
request->completion.Signal();
return;
}
readback_request_on_impl_thread_ = request;
- scheduler_on_impl_thread_->SetNeedsRedraw();
- scheduler_on_impl_thread_->SetNeedsForcedRedraw();
+
+ scheduler_on_impl_thread_->SetNeedsForcedCommitForReadback();
+ if (scheduler_on_impl_thread_->CommitPending()) {
+ begin_frame_sent_completion->Signal();
+ return;
+ }
+
+ begin_frame_sent_to_main_thread_completion_event_on_impl_thread_ =
+ begin_frame_sent_completion;
}
void ThreadProxy::FinishAllRendering() {
@@ -223,6 +221,7 @@ void ThreadProxy::SetLayerTreeHostClientReadyOnImplThread() {
void ThreadProxy::SetVisible(bool visible) {
TRACE_EVENT0("cc", "ThreadProxy::SetVisible");
DebugScopedSetMainThreadBlocked main_thread_blocked(this);
+
CompletionEvent completion;
Proxy::ImplThreadTaskRunner()->PostTask(
FROM_HERE,
@@ -238,11 +237,16 @@ void ThreadProxy::SetVisibleOnImplThread(CompletionEvent* completion,
TRACE_EVENT0("cc", "ThreadProxy::SetVisibleOnImplThread");
layer_tree_host_impl_->SetVisible(visible);
scheduler_on_impl_thread_->SetVisible(visible);
- layer_tree_host_impl_->UpdateBackgroundAnimateTicking(
- !scheduler_on_impl_thread_->WillDrawIfNeeded());
+ UpdateBackgroundAnimateTicking();
completion->Signal();
}
+void ThreadProxy::UpdateBackgroundAnimateTicking() {
+ layer_tree_host_impl_->UpdateBackgroundAnimateTicking(
+ !scheduler_on_impl_thread_->WillDrawIfNeeded() &&
+ layer_tree_host_impl_->active_tree()->root_layer());
+}
+
void ThreadProxy::DoCreateAndInitializeOutputSurface() {
TRACE_EVENT0("cc", "ThreadProxy::DoCreateAndInitializeOutputSurface");
DCHECK(IsMainThread());
@@ -363,10 +367,7 @@ void ThreadProxy::SetNeedsCommit() {
void ThreadProxy::DidLoseOutputSurfaceOnImplThread() {
DCHECK(IsImplThread());
TRACE_EVENT0("cc", "ThreadProxy::DidLoseOutputSurfaceOnImplThread");
- Proxy::ImplThreadTaskRunner()->PostTask(
- FROM_HERE,
- base::Bind(&ThreadProxy::CheckOutputSurfaceStatusOnImplThread,
- impl_thread_weak_ptr_));
+ CheckOutputSurfaceStatusOnImplThread();
}
void ThreadProxy::CheckOutputSurfaceStatusOnImplThread() {
@@ -374,8 +375,8 @@ void ThreadProxy::CheckOutputSurfaceStatusOnImplThread() {
TRACE_EVENT0("cc", "ThreadProxy::CheckOutputSurfaceStatusOnImplThread");
if (!layer_tree_host_impl_->IsContextLost())
return;
- if (cc::ContextProvider* offscreen_contexts = layer_tree_host_impl_
- ->resource_provider()->offscreen_context_provider())
+ if (cc::ContextProvider* offscreen_contexts =
+ layer_tree_host_impl_->offscreen_context_provider())
offscreen_contexts->VerifyContexts();
scheduler_on_impl_thread_->DidLoseOutputSurface();
}
@@ -393,28 +394,35 @@ void ThreadProxy::SetNeedsBeginFrameOnImplThread(bool enable) {
TRACE_EVENT1("cc", "ThreadProxy::SetNeedsBeginFrameOnImplThread",
"enable", enable);
layer_tree_host_impl_->SetNeedsBeginFrame(enable);
+ UpdateBackgroundAnimateTicking();
}
void ThreadProxy::BeginFrameOnImplThread(const BeginFrameArgs& args) {
DCHECK(IsImplThread());
TRACE_EVENT0("cc", "ThreadProxy::BeginFrameOnImplThread");
+
+ // Sample the frame time now. This time will be used for updating animations
+ // when we draw.
+ layer_tree_host_impl_->CurrentFrameTimeTicks();
+
scheduler_on_impl_thread_->BeginFrame(args);
}
+void ThreadProxy::DidBeginFrameDeadlineOnImplThread() {
+ layer_tree_host_impl_->ResetCurrentFrameTimeForNextFrame();
+}
+
void ThreadProxy::OnCanDrawStateChanged(bool can_draw) {
DCHECK(IsImplThread());
TRACE_EVENT1(
"cc", "ThreadProxy::OnCanDrawStateChanged", "can_draw", can_draw);
scheduler_on_impl_thread_->SetCanDraw(can_draw);
- layer_tree_host_impl_->UpdateBackgroundAnimateTicking(
- !scheduler_on_impl_thread_->WillDrawIfNeeded());
+ UpdateBackgroundAnimateTicking();
}
-void ThreadProxy::OnHasPendingTreeStateChanged(bool has_pending_tree) {
- DCHECK(IsImplThread());
- TRACE_EVENT1("cc", "ThreadProxy::OnHasPendingTreeStateChanged",
- "has_pending_tree", has_pending_tree);
- scheduler_on_impl_thread_->SetHasPendingTree(has_pending_tree);
+void ThreadProxy::NotifyReadyToActivate() {
+ TRACE_EVENT0("cc", "ThreadProxy::NotifyReadyToActivate");
+ scheduler_on_impl_thread_->NotifyReadyToActivate();
}
void ThreadProxy::SetNeedsCommitOnImplThread() {
@@ -502,6 +510,12 @@ void ThreadProxy::SetNeedsRedraw(gfx::Rect damage_rect) {
damage_rect));
}
+void ThreadProxy::SetNextCommitWaitsForActivation() {
+ DCHECK(IsMainThread());
+ DCHECK(!inside_commit_);
+ commit_waits_for_activation_ = true;
+}
+
void ThreadProxy::SetDeferCommits(bool defer_commits) {
DCHECK(IsMainThread());
DCHECK_NE(defer_commits_, defer_commits);
@@ -531,16 +545,29 @@ void ThreadProxy::SetNeedsRedrawOnImplThread() {
scheduler_on_impl_thread_->SetNeedsRedraw();
}
+void ThreadProxy::SetNeedsManageTilesOnImplThread() {
+ DCHECK(IsImplThread());
+ TRACE_EVENT0("cc", "ThreadProxy::SetNeedsManageTilesOnImplThread");
+ scheduler_on_impl_thread_->SetNeedsManageTiles();
+}
+
void ThreadProxy::SetNeedsRedrawRectOnImplThread(gfx::Rect damage_rect) {
DCHECK(IsImplThread());
layer_tree_host_impl_->SetViewportDamage(damage_rect);
SetNeedsRedrawOnImplThread();
}
-void ThreadProxy::DidSwapUseIncompleteTileOnImplThread() {
+void ThreadProxy::SetSwapUsedIncompleteTileOnImplThread(
+ bool used_incomplete_tile) {
DCHECK(IsImplThread());
- TRACE_EVENT0("cc", "ThreadProxy::DidSwapUseIncompleteTileOnImplThread");
- scheduler_on_impl_thread_->DidSwapUseIncompleteTile();
+ if (used_incomplete_tile) {
+ TRACE_EVENT_INSTANT0(
+ "cc",
+ "ThreadProxy::SetSwapUsedIncompleteTileOnImplThread",
+ TRACE_EVENT_SCOPE_THREAD);
+ }
+ scheduler_on_impl_thread_->SetSwapUsedIncompleteTile(
+ used_incomplete_tile);
}
void ThreadProxy::DidInitializeVisibleTileOnImplThread() {
@@ -584,6 +611,7 @@ void ThreadProxy::Start(scoped_ptr<OutputSurface> first_output_surface) {
DCHECK(IsMainThread());
DCHECK(Proxy::HasImplThread());
DCHECK(first_output_surface);
+
// Create LayerTreeHostImpl.
DebugScopedSetMainThreadBlocked main_thread_blocked(this);
CompletionEvent completion;
@@ -678,6 +706,10 @@ void ThreadProxy::ScheduledActionSendBeginFrameToMainThread() {
}
begin_frame_state->memory_allocation_limit_bytes =
layer_tree_host_impl_->memory_allocation_limit_bytes();
+ begin_frame_state->memory_allocation_priority_cutoff =
+ layer_tree_host_impl_->memory_allocation_priority_cutoff();
+ begin_frame_state->evicted_ui_resources =
+ layer_tree_host_impl_->EvictedUIResourcesExist();
Proxy::MainThreadTaskRunner()->PostTask(
FROM_HERE,
base::Bind(&ThreadProxy::BeginFrameOnMainThread,
@@ -695,6 +727,7 @@ void ThreadProxy::BeginFrameOnMainThread(
scoped_ptr<BeginFrameAndCommitState> begin_frame_state) {
TRACE_EVENT0("cc", "ThreadProxy::BeginFrameOnMainThread");
DCHECK(IsMainThread());
+
if (!layer_tree_host_)
return;
@@ -749,8 +782,22 @@ void ThreadProxy::BeginFrameOnMainThread(
if (layer_tree_host_->contents_texture_manager()) {
layer_tree_host_->contents_texture_manager()->
UnlinkAndClearEvictedBackings();
+
+ if (begin_frame_state) {
+ layer_tree_host_->contents_texture_manager()->SetMaxMemoryLimitBytes(
+ begin_frame_state->memory_allocation_limit_bytes);
+ layer_tree_host_->contents_texture_manager()->SetExternalPriorityCutoff(
+ begin_frame_state->memory_allocation_priority_cutoff);
+ }
}
+ // Recreate all UI resources if there were evicted UI resources when the impl
+ // thread initiated the commit.
+ bool evicted_ui_resources =
+ begin_frame_state ? begin_frame_state->evicted_ui_resources : false;
+ if (evicted_ui_resources)
+ layer_tree_host_->RecreateUIResources();
+
layer_tree_host_->Layout();
// Clear the commit flag after updating animations and layout here --- objects
@@ -759,15 +806,15 @@ void ThreadProxy::BeginFrameOnMainThread(
commit_requested_ = false;
commit_request_sent_to_impl_thread_ = false;
bool can_cancel_this_commit =
- can_cancel_commit_ && !in_composite_and_readback_;
+ can_cancel_commit_ &&
+ !in_composite_and_readback_ &&
+ !evicted_ui_resources;
can_cancel_commit_ = true;
scoped_ptr<ResourceUpdateQueue> queue =
make_scoped_ptr(new ResourceUpdateQueue);
- bool updated = layer_tree_host_->UpdateLayers(
- queue.get(),
- begin_frame_state ? begin_frame_state->memory_allocation_limit_bytes
- : 0u);
+
+ bool updated = layer_tree_host_->UpdateLayers(queue.get());
// Once single buffered layers are committed, they cannot be modified until
// they are drawn by the impl thread.
@@ -820,6 +867,11 @@ void ThreadProxy::BeginFrameOnMainThread(
DebugScopedSetMainThreadBlocked main_thread_blocked(this);
+ // This CapturePostTasks should be destroyed before CommitComplete() is
+ // called since that goes out to the embedder, and we want the embedder
+ // to receive its callbacks before that.
+ BlockingTaskRunner::CapturePostTasks blocked;
+
RenderingStatsInstrumentation* stats_instrumentation =
layer_tree_host_->rendering_stats_instrumentation();
base::TimeTicks start_time = stats_instrumentation->StartRecording();
@@ -836,6 +888,8 @@ void ThreadProxy::BeginFrameOnMainThread(
base::TimeDelta duration = stats_instrumentation->EndRecording(start_time);
stats_instrumentation->AddCommit(duration);
+ stats_instrumentation->IssueTraceEventForMainThreadStats();
+ stats_instrumentation->AccumulateAndClearMainThreadStats();
}
layer_tree_host_->CommitComplete();
@@ -862,8 +916,8 @@ void ThreadProxy::StartCommitOnImplThread(
if (offscreen_context_provider.get())
offscreen_context_provider->BindToCurrentThread();
- layer_tree_host_impl_->resource_provider()->
- set_offscreen_context_provider(offscreen_context_provider);
+ layer_tree_host_impl_->SetOffscreenContextProvider(
+ offscreen_context_provider);
if (layer_tree_host_->contents_texture_manager()) {
if (layer_tree_host_->contents_texture_manager()->
@@ -902,7 +956,8 @@ void ThreadProxy::BeginFrameAbortedByMainThreadOnImplThread(bool did_handle) {
// by the main thread, so the active tree needs to be updated as if these sent
// values were applied and committed.
if (did_handle) {
- layer_tree_host_impl_->active_tree()->ApplySentScrollAndScaleDeltas();
+ layer_tree_host_impl_->active_tree()
+ ->ApplySentScrollAndScaleDeltasFromAbortedCommit();
layer_tree_host_impl_->active_tree()->ResetContentsTexturesPurged();
SetInputThrottledUntilCommitOnImplThread(false);
}
@@ -919,21 +974,21 @@ void ThreadProxy::ScheduledActionCommit() {
current_resource_update_controller_on_impl_thread_->Finalize();
current_resource_update_controller_on_impl_thread_.reset();
+ inside_commit_ = true;
layer_tree_host_impl_->BeginCommit();
layer_tree_host_->BeginCommitOnImplThread(layer_tree_host_impl_.get());
layer_tree_host_->FinishCommitOnImplThread(layer_tree_host_impl_.get());
layer_tree_host_impl_->CommitComplete();
+ inside_commit_ = false;
SetInputThrottledUntilCommitOnImplThread(false);
- layer_tree_host_impl_->UpdateBackgroundAnimateTicking(
- !scheduler_on_impl_thread_->WillDrawIfNeeded());
+ UpdateBackgroundAnimateTicking();
next_frame_is_newly_committed_frame_on_impl_thread_ = true;
if (layer_tree_host_->settings().impl_side_painting &&
- layer_tree_host_->BlocksPendingCommit() &&
- layer_tree_host_impl_->pending_tree()) {
+ commit_waits_for_activation_) {
// For some layer types in impl-side painting, the commit is held until
// the pending tree is activated. It's also possible that the
// pending tree has already activated if there was no work to be done.
@@ -946,6 +1001,8 @@ void ThreadProxy::ScheduledActionCommit() {
commit_completion_event_on_impl_thread_ = NULL;
}
+ commit_waits_for_activation_ = false;
+
commit_complete_time_ = base::TimeTicks::HighResNow();
begin_frame_to_commit_duration_history_.InsertSample(
commit_complete_time_ - begin_frame_sent_to_main_thread_time_);
@@ -960,28 +1017,29 @@ void ThreadProxy::ScheduledActionUpdateVisibleTiles() {
layer_tree_host_impl_->UpdateVisibleTiles();
}
-void ThreadProxy::ScheduledActionActivatePendingTreeIfNeeded() {
+void ThreadProxy::ScheduledActionActivatePendingTree() {
DCHECK(IsImplThread());
- TRACE_EVENT0("cc", "ThreadProxy::ScheduledActionActivatePendingTreeIfNeeded");
- layer_tree_host_impl_->ActivatePendingTreeIfNeeded();
+ TRACE_EVENT0("cc", "ThreadProxy::ScheduledActionActivatePendingTree");
+ layer_tree_host_impl_->ActivatePendingTree();
}
void ThreadProxy::ScheduledActionBeginOutputSurfaceCreation() {
DCHECK(IsImplThread());
+ TRACE_EVENT0("cc", "ThreadProxy::ScheduledActionBeginOutputSurfaceCreation");
Proxy::MainThreadTaskRunner()->PostTask(
FROM_HERE,
base::Bind(&ThreadProxy::CreateAndInitializeOutputSurface,
main_thread_weak_ptr_));
}
-ScheduledActionDrawAndSwapResult
-ThreadProxy::ScheduledActionDrawAndSwapInternal(bool forced_draw) {
- TRACE_EVENT1(
- "cc", "ThreadProxy::ScheduledActionDrawAndSwap", "forced", forced_draw);
-
- ScheduledActionDrawAndSwapResult result;
+DrawSwapReadbackResult ThreadProxy::DrawSwapReadbackInternal(
+ bool forced_draw,
+ bool swap_requested,
+ bool readback_requested) {
+ DrawSwapReadbackResult result;
result.did_draw = false;
result.did_swap = false;
+ result.did_readback = false;
DCHECK(IsImplThread());
DCHECK(layer_tree_host_impl_.get());
if (!layer_tree_host_impl_)
@@ -991,22 +1049,19 @@ ThreadProxy::ScheduledActionDrawAndSwapInternal(bool forced_draw) {
if (!layer_tree_host_impl_->renderer())
return result;
+ base::TimeTicks start_time = base::TimeTicks::HighResNow();
+ base::TimeDelta draw_duration_estimate = DrawDurationEstimate();
+ base::AutoReset<bool> mark_inside(&inside_draw_, true);
+
+ // Advance our animations.
base::TimeTicks monotonic_time =
layer_tree_host_impl_->CurrentFrameTimeTicks();
base::Time wall_clock_time = layer_tree_host_impl_->CurrentFrameTime();
// TODO(enne): This should probably happen post-animate.
- if (layer_tree_host_impl_->pending_tree()) {
- layer_tree_host_impl_->ActivatePendingTreeIfNeeded();
- if (layer_tree_host_impl_->pending_tree())
- layer_tree_host_impl_->pending_tree()->UpdateDrawProperties();
- }
+ if (layer_tree_host_impl_->pending_tree())
+ layer_tree_host_impl_->pending_tree()->UpdateDrawProperties();
layer_tree_host_impl_->Animate(monotonic_time, wall_clock_time);
- layer_tree_host_impl_->UpdateBackgroundAnimateTicking(false);
-
- base::TimeTicks start_time = base::TimeTicks::HighResNow();
- base::TimeDelta draw_duration_estimate = DrawDurationEstimate();
- base::AutoReset<bool> mark_inside(&inside_draw_, true);
// This method is called on a forced draw, regardless of whether we are able
// to produce a frame, as the calling site on main thread is blocked until its
@@ -1019,12 +1074,12 @@ ThreadProxy::ScheduledActionDrawAndSwapInternal(bool forced_draw) {
// DrawLayers() depends on the result of PrepareToDraw(), it is guarded on
// CanDraw() as well.
- bool drawing_for_readback = !!readback_request_on_impl_thread_;
+ bool drawing_for_readback =
+ readback_requested && !!readback_request_on_impl_thread_;
bool can_do_readback = layer_tree_host_impl_->renderer()->CanReadPixels();
LayerTreeHostImpl::FrameData frame;
bool draw_frame = false;
- bool start_ready_animations = true;
if (layer_tree_host_impl_->CanDraw() &&
(!drawing_for_readback || can_do_readback)) {
@@ -1033,13 +1088,9 @@ ThreadProxy::ScheduledActionDrawAndSwapInternal(bool forced_draw) {
if (drawing_for_readback)
readback_rect = readback_request_on_impl_thread_->rect;
- // Do not start animations if we skip drawing the frame to avoid
- // checkerboarding.
if (layer_tree_host_impl_->PrepareToDraw(&frame, readback_rect) ||
forced_draw)
draw_frame = true;
- else
- start_ready_animations = false;
}
if (draw_frame) {
@@ -1050,24 +1101,30 @@ ThreadProxy::ScheduledActionDrawAndSwapInternal(bool forced_draw) {
}
layer_tree_host_impl_->DidDrawAllLayers(frame);
+ bool start_ready_animations = draw_frame;
layer_tree_host_impl_->UpdateAnimationState(start_ready_animations);
// Check for a pending CompositeAndReadback.
- if (readback_request_on_impl_thread_) {
- readback_request_on_impl_thread_->success = false;
- if (draw_frame) {
+ if (drawing_for_readback) {
+ DCHECK(!swap_requested);
+ result.did_readback = false;
+ if (draw_frame && !layer_tree_host_impl_->IsContextLost()) {
layer_tree_host_impl_->Readback(readback_request_on_impl_thread_->pixels,
readback_request_on_impl_thread_->rect);
- readback_request_on_impl_thread_->success =
- !layer_tree_host_impl_->IsContextLost();
+ result.did_readback = true;
}
+ readback_request_on_impl_thread_->success = result.did_readback;
readback_request_on_impl_thread_->completion.Signal();
readback_request_on_impl_thread_ = NULL;
} else if (draw_frame) {
+ DCHECK(swap_requested);
result.did_swap = layer_tree_host_impl_->SwapBuffers(frame);
- if (frame.contains_incomplete_tile)
- DidSwapUseIncompleteTileOnImplThread();
+ // We don't know if we have incomplete tiles if we didn't actually swap.
+ if (result.did_swap) {
+ DCHECK(!frame.has_no_damage);
+ SetSwapUsedIncompleteTileOnImplThread(frame.contains_incomplete_tile);
+ }
}
// Tell the main thread that the the newly-commited frame was drawn.
@@ -1106,12 +1163,6 @@ ThreadProxy::ScheduledActionDrawAndSwapInternal(bool forced_draw) {
50);
}
- // Update the tile state after drawing. This prevents manage tiles from
- // being in the critical path for getting things on screen, but still
- // makes sure that tile state is updated on a semi-regular basis.
- if (layer_tree_host_impl_->settings().impl_side_painting)
- layer_tree_host_impl_->ManageTiles();
-
return result;
}
@@ -1156,21 +1207,43 @@ void ThreadProxy::ScheduledActionAcquireLayerTexturesForMainThread() {
texture_acquisition_completion_event_on_impl_thread_ = NULL;
}
-ScheduledActionDrawAndSwapResult
-ThreadProxy::ScheduledActionDrawAndSwapIfPossible() {
- return ScheduledActionDrawAndSwapInternal(false);
+void ThreadProxy::ScheduledActionManageTiles() {
+ TRACE_EVENT0("cc", "ThreadProxy::ScheduledActionManageTiles");
+ DCHECK(layer_tree_host_impl_->settings().impl_side_painting);
+ layer_tree_host_impl_->ManageTiles();
+}
+
+DrawSwapReadbackResult ThreadProxy::ScheduledActionDrawAndSwapIfPossible() {
+ TRACE_EVENT0("cc", "ThreadProxy::ScheduledActionDrawAndSwap");
+ bool forced_draw = false;
+ bool swap_requested = true;
+ bool readback_requested = false;
+ return DrawSwapReadbackInternal(
+ forced_draw, swap_requested, readback_requested);
+}
+
+DrawSwapReadbackResult ThreadProxy::ScheduledActionDrawAndSwapForced() {
+ TRACE_EVENT0("cc", "ThreadProxy::ScheduledActionDrawAndSwapForced");
+ bool forced_draw = true;
+ bool swap_requested = true;
+ bool readback_requested = false;
+ return DrawSwapReadbackInternal(
+ forced_draw, swap_requested, readback_requested);
}
-ScheduledActionDrawAndSwapResult
-ThreadProxy::ScheduledActionDrawAndSwapForced() {
- return ScheduledActionDrawAndSwapInternal(true);
+DrawSwapReadbackResult ThreadProxy::ScheduledActionDrawAndReadback() {
+ TRACE_EVENT0("cc", "ThreadProxy::ScheduledActionDrawAndReadback");
+ bool forced_draw = true;
+ bool swap_requested = false;
+ bool readback_requested = true;
+ return DrawSwapReadbackInternal(
+ forced_draw, swap_requested, readback_requested);
}
void ThreadProxy::DidAnticipatedDrawTimeChange(base::TimeTicks time) {
if (current_resource_update_controller_on_impl_thread_)
- current_resource_update_controller_on_impl_thread_
- ->PerformMoreUpdates(time);
- layer_tree_host_impl_->ResetCurrentFrameTimeForNextFrame();
+ current_resource_update_controller_on_impl_thread_->PerformMoreUpdates(
+ time);
}
base::TimeDelta ThreadProxy::DrawDurationEstimate() {
@@ -1191,6 +1264,14 @@ base::TimeDelta ThreadProxy::CommitToActivateDurationEstimate() {
kCommitAndActivationDurationEstimationPercentile);
}
+void ThreadProxy::PostBeginFrameDeadline(const base::Closure& closure,
+ base::TimeTicks deadline) {
+ base::TimeDelta delta = deadline - base::TimeTicks::Now();
+ if (delta <= base::TimeDelta())
+ delta = base::TimeDelta();
+ Proxy::ImplThreadTaskRunner()->PostDelayedTask(FROM_HERE, closure, delta);
+}
+
void ThreadProxy::ReadyToFinalizeTextureUpdates() {
DCHECK(IsImplThread());
scheduler_on_impl_thread_->FinishCommit();
@@ -1261,9 +1342,13 @@ void ThreadProxy::InitializeImplOnImplThread(CompletionEvent* completion) {
layer_tree_host_impl_ = layer_tree_host_->CreateLayerTreeHostImpl(this);
const LayerTreeSettings& settings = layer_tree_host_->settings();
SchedulerSettings scheduler_settings;
+ scheduler_settings.deadline_scheduling_enabled =
+ settings.deadline_scheduling_enabled;
scheduler_settings.impl_side_painting = settings.impl_side_painting;
scheduler_settings.timeout_and_draw_when_animation_checkerboards =
settings.timeout_and_draw_when_animation_checkerboards;
+ scheduler_settings.maximum_number_of_failed_draws_before_draw_is_forced_ =
+ settings.maximum_number_of_failed_draws_before_draw_is_forced_;
scheduler_settings.using_synchronous_renderer_compositor =
settings.using_synchronous_renderer_compositor;
scheduler_settings.throttle_frame_production =
@@ -1295,30 +1380,18 @@ void ThreadProxy::InitializeOutputSurfaceOnImplThread(
if (*success) {
*capabilities = layer_tree_host_impl_->GetRendererCapabilities();
scheduler_on_impl_thread_->DidCreateAndInitializeOutputSurface();
+ } else if (offscreen_context_provider.get()) {
+ if (offscreen_context_provider->BindToCurrentThread())
+ offscreen_context_provider->VerifyContexts();
+ offscreen_context_provider = NULL;
}
- DidTryInitializeRendererOnImplThread(*success, offscreen_context_provider);
+ layer_tree_host_impl_->SetOffscreenContextProvider(
+ offscreen_context_provider);
completion->Signal();
}
-void ThreadProxy::DidTryInitializeRendererOnImplThread(
- bool success,
- scoped_refptr<ContextProvider> offscreen_context_provider) {
- DCHECK(IsImplThread());
- DCHECK(!inside_draw_);
-
- if (offscreen_context_provider.get())
- offscreen_context_provider->BindToCurrentThread();
-
- if (success) {
- layer_tree_host_impl_->resource_provider()->
- set_offscreen_context_provider(offscreen_context_provider);
- } else if (offscreen_context_provider.get()) {
- offscreen_context_provider->VerifyContexts();
- }
-}
-
void ThreadProxy::FinishGLOnImplThread(CompletionEvent* completion) {
TRACE_EVENT0("cc", "ThreadProxy::FinishGLOnImplThread");
DCHECK(IsImplThread());
@@ -1332,6 +1405,7 @@ void ThreadProxy::LayerTreeHostClosedOnImplThread(CompletionEvent* completion) {
DCHECK(IsImplThread());
layer_tree_host_->DeleteContentsTexturesOnImplThread(
layer_tree_host_impl_->resource_provider());
+ current_resource_update_controller_on_impl_thread_.reset();
layer_tree_host_impl_->SetNeedsBeginFrame(false);
scheduler_on_impl_thread_.reset();
layer_tree_host_impl_.reset();
@@ -1344,7 +1418,9 @@ size_t ThreadProxy::MaxPartialTextureUpdates() const {
}
ThreadProxy::BeginFrameAndCommitState::BeginFrameAndCommitState()
- : memory_allocation_limit_bytes(0) {}
+ : memory_allocation_limit_bytes(0),
+ memory_allocation_priority_cutoff(0),
+ evicted_ui_resources(false) {}
ThreadProxy::BeginFrameAndCommitState::~BeginFrameAndCommitState() {}
@@ -1398,27 +1474,27 @@ void ThreadProxy::CommitPendingOnImplThreadForTesting(
request->completion.Signal();
}
-std::string ThreadProxy::SchedulerStateAsStringForTesting() {
+scoped_ptr<base::Value> ThreadProxy::SchedulerStateAsValueForTesting() {
if (IsImplThread())
- return scheduler_on_impl_thread_->StateAsStringForTesting();
+ return scheduler_on_impl_thread_->StateAsValue().Pass();
SchedulerStateRequest scheduler_state_request;
{
DebugScopedSetMainThreadBlocked main_thread_blocked(this);
Proxy::ImplThreadTaskRunner()->PostTask(
FROM_HERE,
- base::Bind(&ThreadProxy::SchedulerStateAsStringOnImplThreadForTesting,
+ base::Bind(&ThreadProxy::SchedulerStateAsValueOnImplThreadForTesting,
impl_thread_weak_ptr_,
&scheduler_state_request));
scheduler_state_request.completion.Wait();
}
- return scheduler_state_request.state;
+ return scheduler_state_request.state.Pass();
}
-void ThreadProxy::SchedulerStateAsStringOnImplThreadForTesting(
+void ThreadProxy::SchedulerStateAsValueOnImplThreadForTesting(
SchedulerStateRequest* request) {
DCHECK(IsImplThread());
- request->state = scheduler_on_impl_thread_->StateAsStringForTesting();
+ request->state = scheduler_on_impl_thread_->StateAsValue();
request->completion.Signal();
}
@@ -1449,10 +1525,13 @@ void ThreadProxy::RenewTreePriority() {
// evicted resources or there is an invalid viewport size.
if (layer_tree_host_impl_->active_tree()->ContentsTexturesPurged() ||
layer_tree_host_impl_->active_tree()->ViewportSizeInvalid() ||
+ layer_tree_host_impl_->EvictedUIResourcesExist() ||
input_throttled_until_commit_)
priority = NEW_CONTENT_TAKES_PRIORITY;
layer_tree_host_impl_->SetTreePriority(priority);
+ scheduler_on_impl_thread_->SetSmoothnessTakesPriority(
+ priority == SMOOTHNESS_TAKES_PRIORITY);
// Notify the the client of this compositor via the output surface.
// TODO(epenner): Route this to compositor-thread instead of output-surface
@@ -1512,6 +1591,8 @@ void ThreadProxy::DidActivatePendingTree() {
completion_event_for_commit_held_on_tree_activation_ = NULL;
}
+ UpdateBackgroundAnimateTicking();
+
commit_to_activate_duration_history_.InsertSample(
base::TimeTicks::HighResNow() - commit_complete_time_);
}
diff --git a/chromium/cc/trees/thread_proxy.h b/chromium/cc/trees/thread_proxy.h
index 1f5f29eec6c..78a59613d39 100644
--- a/chromium/cc/trees/thread_proxy.h
+++ b/chromium/cc/trees/thread_proxy.h
@@ -52,6 +52,7 @@ class ThreadProxy : public Proxy,
virtual void SetNeedsUpdateLayers() OVERRIDE;
virtual void SetNeedsCommit() OVERRIDE;
virtual void SetNeedsRedraw(gfx::Rect damage_rect) OVERRIDE;
+ virtual void SetNextCommitWaitsForActivation() OVERRIDE;
virtual void NotifyInputThrottledUntilCommit() OVERRIDE;
virtual void SetDeferCommits(bool defer_commits) OVERRIDE;
virtual bool CommitRequested() const OVERRIDE;
@@ -63,19 +64,18 @@ class ThreadProxy : public Proxy,
virtual void ForceSerializeOnSwapBuffers() OVERRIDE;
virtual scoped_ptr<base::Value> AsValue() const OVERRIDE;
virtual bool CommitPendingForTesting() OVERRIDE;
- virtual std::string SchedulerStateAsStringForTesting() OVERRIDE;
+ virtual scoped_ptr<base::Value> SchedulerStateAsValueForTesting() OVERRIDE;
// LayerTreeHostImplClient implementation
- virtual void DidTryInitializeRendererOnImplThread(
- bool success,
- scoped_refptr<ContextProvider> offscreen_context_provider) OVERRIDE;
virtual void DidLoseOutputSurfaceOnImplThread() OVERRIDE;
virtual void OnSwapBuffersCompleteOnImplThread() OVERRIDE;
virtual void BeginFrameOnImplThread(const BeginFrameArgs& args) OVERRIDE;
+ virtual void DidBeginFrameDeadlineOnImplThread() OVERRIDE;
virtual void OnCanDrawStateChanged(bool can_draw) OVERRIDE;
- virtual void OnHasPendingTreeStateChanged(bool has_pending_tree) OVERRIDE;
+ virtual void NotifyReadyToActivate() OVERRIDE;
virtual void SetNeedsRedrawOnImplThread() OVERRIDE;
virtual void SetNeedsRedrawRectOnImplThread(gfx::Rect dirty_rect) OVERRIDE;
+ virtual void SetNeedsManageTilesOnImplThread() OVERRIDE;
virtual void DidInitializeVisibleTileOnImplThread() OVERRIDE;
virtual void SetNeedsCommitOnImplThread() OVERRIDE;
virtual void PostAnimationEventsToMainThreadOnImplThread(
@@ -95,19 +95,22 @@ class ThreadProxy : public Proxy,
// SchedulerClient implementation
virtual void SetNeedsBeginFrameOnImplThread(bool enable) OVERRIDE;
virtual void ScheduledActionSendBeginFrameToMainThread() OVERRIDE;
- virtual ScheduledActionDrawAndSwapResult
- ScheduledActionDrawAndSwapIfPossible() OVERRIDE;
- virtual ScheduledActionDrawAndSwapResult ScheduledActionDrawAndSwapForced()
+ virtual DrawSwapReadbackResult ScheduledActionDrawAndSwapIfPossible()
OVERRIDE;
+ virtual DrawSwapReadbackResult ScheduledActionDrawAndSwapForced() OVERRIDE;
+ virtual DrawSwapReadbackResult ScheduledActionDrawAndReadback() OVERRIDE;
virtual void ScheduledActionCommit() OVERRIDE;
virtual void ScheduledActionUpdateVisibleTiles() OVERRIDE;
- virtual void ScheduledActionActivatePendingTreeIfNeeded() OVERRIDE;
+ virtual void ScheduledActionActivatePendingTree() OVERRIDE;
virtual void ScheduledActionBeginOutputSurfaceCreation() OVERRIDE;
virtual void ScheduledActionAcquireLayerTexturesForMainThread() OVERRIDE;
+ virtual void ScheduledActionManageTiles() OVERRIDE;
virtual void DidAnticipatedDrawTimeChange(base::TimeTicks time) OVERRIDE;
virtual base::TimeDelta DrawDurationEstimate() OVERRIDE;
virtual base::TimeDelta BeginFrameToCommitDurationEstimate() OVERRIDE;
virtual base::TimeDelta CommitToActivateDurationEstimate() OVERRIDE;
+ virtual void PostBeginFrameDeadline(const base::Closure& closure,
+ base::TimeTicks deadline) OVERRIDE;
// ResourceUpdateControllerClient implementation
virtual void ReadyToFinalizeTextureUpdates() OVERRIDE;
@@ -123,6 +126,8 @@ class ThreadProxy : public Proxy,
base::TimeTicks monotonic_frame_begin_time;
scoped_ptr<ScrollAndScaleSet> scroll_info;
size_t memory_allocation_limit_bytes;
+ int memory_allocation_priority_cutoff;
+ bool evicted_ui_resources;
};
// Called on main thread.
@@ -144,7 +149,9 @@ class ThreadProxy : public Proxy,
struct CommitPendingRequest;
struct SchedulerStateRequest;
- void ForceCommitOnImplThread(CompletionEvent* completion);
+ void ForceCommitForReadbackOnImplThread(
+ CompletionEvent* begin_frame_sent_completion,
+ ReadbackRequest* request);
void StartCommitOnImplThread(
CompletionEvent* completion,
ResourceUpdateQueue* queue,
@@ -155,6 +162,7 @@ class ThreadProxy : public Proxy,
void InitializeImplOnImplThread(CompletionEvent* completion);
void SetLayerTreeHostClientReadyOnImplThread();
void SetVisibleOnImplThread(CompletionEvent* completion, bool visible);
+ void UpdateBackgroundAnimateTicking();
void HasInitializedOutputSurfaceOnImplThread(
CompletionEvent* completion,
bool* has_initialized_output_surface);
@@ -168,17 +176,18 @@ class ThreadProxy : public Proxy,
void LayerTreeHostClosedOnImplThread(CompletionEvent* completion);
void AcquireLayerTexturesForMainThreadOnImplThread(
CompletionEvent* completion);
- ScheduledActionDrawAndSwapResult ScheduledActionDrawAndSwapInternal(
- bool forced_draw);
+ DrawSwapReadbackResult DrawSwapReadbackInternal(bool forced_draw,
+ bool swap_requested,
+ bool readback_requested);
void ForceSerializeOnSwapBuffersOnImplThread(CompletionEvent* completion);
void CheckOutputSurfaceStatusOnImplThread();
void CommitPendingOnImplThreadForTesting(CommitPendingRequest* request);
- void SchedulerStateAsStringOnImplThreadForTesting(
+ void SchedulerStateAsValueOnImplThreadForTesting(
SchedulerStateRequest* request);
void AsValueOnImplThread(CompletionEvent* completion,
base::DictionaryValue* state) const;
void RenewTreePriorityOnImplThread();
- void DidSwapUseIncompleteTileOnImplThread();
+ void SetSwapUsedIncompleteTileOnImplThread(bool used_incomplete_tile);
void StartScrollbarAnimationOnImplThread();
void MainThreadHasStoppedFlingingOnImplThread();
void SetInputThrottledUntilCommitOnImplThread(bool is_throttled);
@@ -206,6 +215,10 @@ class ThreadProxy : public Proxy,
// anything else.
scoped_ptr<OutputSurface> first_output_surface_;
+ // Accessed on the main thread, or when main thread is blocked.
+ bool commit_waits_for_activation_;
+ bool inside_commit_;
+
base::WeakPtrFactory<ThreadProxy> weak_factory_on_impl_thread_;
base::WeakPtr<ThreadProxy> main_thread_weak_ptr_;
diff --git a/chromium/cc/trees/tree_synchronizer.cc b/chromium/cc/trees/tree_synchronizer.cc
index f307257ff05..48a9d5bd966 100644
--- a/chromium/cc/trees/tree_synchronizer.cc
+++ b/chromium/cc/trees/tree_synchronizer.cc
@@ -5,18 +5,19 @@
#include "cc/trees/tree_synchronizer.h"
#include "base/containers/hash_tables.h"
+#include "base/containers/scoped_ptr_hash_map.h"
#include "base/debug/trace_event.h"
#include "base/logging.h"
#include "cc/animation/scrollbar_animation_controller.h"
#include "cc/input/scrollbar.h"
#include "cc/layers/layer.h"
#include "cc/layers/layer_impl.h"
-#include "cc/layers/scrollbar_layer.h"
-#include "cc/layers/scrollbar_layer_impl.h"
+#include "cc/layers/scrollbar_layer_impl_base.h"
+#include "cc/layers/scrollbar_layer_interface.h"
namespace cc {
-typedef ScopedPtrHashMap<int, LayerImpl> ScopedPtrLayerImplMap;
+typedef base::ScopedPtrHashMap<int, LayerImpl> ScopedPtrLayerImplMap;
typedef base::hash_map<int, LayerImpl*> RawPtrLayerImplMap;
void CollectExistingLayerImplRecursive(ScopedPtrLayerImplMap* old_layers,
@@ -154,18 +155,19 @@ void UpdateScrollbarLayerPointersRecursiveInternal(
return;
RawPtrLayerImplMap::const_iterator iter =
- new_layers->find(scrollbar_layer->id());
- ScrollbarLayerImpl* scrollbar_layer_impl =
- iter != new_layers->end() ? static_cast<ScrollbarLayerImpl*>(iter->second)
- : NULL;
- iter = new_layers->find(scrollbar_layer->scroll_layer_id());
+ new_layers->find(layer->id());
+ ScrollbarLayerImplBase* scrollbar_layer_impl =
+ iter != new_layers->end()
+ ? static_cast<ScrollbarLayerImplBase*>(iter->second)
+ : NULL;
+ iter = new_layers->find(scrollbar_layer->ScrollLayerId());
LayerImpl* scroll_layer_impl =
iter != new_layers->end() ? iter->second : NULL;
DCHECK(scrollbar_layer_impl);
DCHECK(scroll_layer_impl);
- if (scrollbar_layer->Orientation() == HORIZONTAL)
+ if (scrollbar_layer->orientation() == HORIZONTAL)
scroll_layer_impl->SetHorizontalScrollbarLayer(scrollbar_layer_impl);
else
scroll_layer_impl->SetVerticalScrollbarLayer(scrollbar_layer_impl);
@@ -173,14 +175,15 @@ void UpdateScrollbarLayerPointersRecursiveInternal(
void UpdateScrollbarLayerPointersRecursive(const RawPtrLayerImplMap* new_layers,
Layer* layer) {
- UpdateScrollbarLayerPointersRecursiveInternal<Layer, ScrollbarLayer>(
+ UpdateScrollbarLayerPointersRecursiveInternal<Layer, ScrollbarLayerInterface>(
new_layers, layer);
}
void UpdateScrollbarLayerPointersRecursive(const RawPtrLayerImplMap* new_layers,
LayerImpl* layer) {
- UpdateScrollbarLayerPointersRecursiveInternal<LayerImpl, ScrollbarLayerImpl>(
- new_layers, layer);
+ UpdateScrollbarLayerPointersRecursiveInternal<
+ LayerImpl,
+ ScrollbarLayerImplBase>(new_layers, layer);
}
// static
diff --git a/chromium/cc/trees/tree_synchronizer.h b/chromium/cc/trees/tree_synchronizer.h
index 40fa69d2b57..de48cee26f0 100644
--- a/chromium/cc/trees/tree_synchronizer.h
+++ b/chromium/cc/trees/tree_synchronizer.h
@@ -8,7 +8,6 @@
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "cc/base/cc_export.h"
-#include "cc/base/scoped_ptr_hash_map.h"
namespace cc {
diff --git a/chromium/cc/trees/tree_synchronizer_unittest.cc b/chromium/cc/trees/tree_synchronizer_unittest.cc
index f1b6d32df27..4e0c89fca5c 100644
--- a/chromium/cc/trees/tree_synchronizer_unittest.cc
+++ b/chromium/cc/trees/tree_synchronizer_unittest.cc
@@ -5,6 +5,7 @@
#include "cc/trees/tree_synchronizer.h"
#include <algorithm>
+#include <set>
#include <vector>
#include "cc/animation/layer_animation_controller.h"
@@ -124,6 +125,56 @@ void ExpectTreesAreIdentical(Layer* layer,
ASSERT_EQ(layer_children.size(), layer_impl_children.size());
+ const std::set<Layer*>* layer_scroll_children = layer->scroll_children();
+ const std::set<LayerImpl*>* layer_impl_scroll_children =
+ layer_impl->scroll_children();
+
+ ASSERT_EQ(!!layer_scroll_children, !!layer_impl_scroll_children);
+
+ if (layer_scroll_children) {
+ ASSERT_EQ(
+ layer_scroll_children->size(),
+ layer_impl_scroll_children->size());
+ }
+
+ const Layer* layer_scroll_parent = layer->scroll_parent();
+ const LayerImpl* layer_impl_scroll_parent = layer_impl->scroll_parent();
+
+ ASSERT_EQ(!!layer_scroll_parent, !!layer_impl_scroll_parent);
+
+ if (layer_scroll_parent) {
+ ASSERT_EQ(layer_scroll_parent->id(), layer_impl_scroll_parent->id());
+ ASSERT_TRUE(layer_scroll_parent->scroll_children()->find(layer) !=
+ layer_scroll_parent->scroll_children()->end());
+ ASSERT_TRUE(layer_impl_scroll_parent->scroll_children()->find(layer_impl) !=
+ layer_impl_scroll_parent->scroll_children()->end());
+ }
+
+ const std::set<Layer*>* layer_clip_children = layer->clip_children();
+ const std::set<LayerImpl*>* layer_impl_clip_children =
+ layer_impl->clip_children();
+
+ ASSERT_EQ(!!layer_clip_children, !!layer_impl_clip_children);
+
+ if (layer_clip_children)
+ ASSERT_EQ(layer_clip_children->size(), layer_impl_clip_children->size());
+
+ const Layer* layer_clip_parent = layer->clip_parent();
+ const LayerImpl* layer_impl_clip_parent = layer_impl->clip_parent();
+
+ ASSERT_EQ(!!layer_clip_parent, !!layer_impl_clip_parent);
+
+ if (layer_clip_parent) {
+ const std::set<LayerImpl*>* clip_children_impl =
+ layer_impl_clip_parent->clip_children();
+ const std::set<Layer*>* clip_children =
+ layer_clip_parent->clip_children();
+ ASSERT_EQ(layer_clip_parent->id(), layer_impl_clip_parent->id());
+ ASSERT_TRUE(clip_children->find(layer) != clip_children->end());
+ ASSERT_TRUE(clip_children_impl->find(layer_impl) !=
+ clip_children_impl->end());
+ }
+
for (size_t i = 0; i < layer_children.size(); ++i) {
ExpectTreesAreIdentical(
layer_children[i].get(), layer_impl_children[i], tree_impl);
@@ -531,5 +582,167 @@ TEST_F(TreeSynchronizerTest, SynchronizeAnimations) {
layer_tree_root->layer_animation_controller())->SynchronizedAnimations());
}
+TEST_F(TreeSynchronizerTest, SynchronizeScrollParent) {
+ LayerTreeSettings settings;
+ FakeProxy proxy;
+ DebugScopedSetImplThread impl(&proxy);
+ FakeRenderingStatsInstrumentation stats_instrumentation;
+ scoped_ptr<LayerTreeHostImpl> host_impl =
+ LayerTreeHostImpl::Create(settings,
+ NULL,
+ &proxy,
+ &stats_instrumentation);
+
+ scoped_refptr<Layer> layer_tree_root = Layer::Create();
+ scoped_refptr<Layer> scroll_parent = Layer::Create();
+ layer_tree_root->AddChild(scroll_parent);
+ layer_tree_root->AddChild(Layer::Create());
+ layer_tree_root->AddChild(Layer::Create());
+
+ host_->SetRootLayer(layer_tree_root);
+
+ // First child is the second and third child's scroll parent.
+ layer_tree_root->children()[1]->SetScrollParent(scroll_parent);
+ layer_tree_root->children()[2]->SetScrollParent(scroll_parent);
+
+ scoped_ptr<LayerImpl> layer_impl_tree_root =
+ TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(),
+ scoped_ptr<LayerImpl>(),
+ host_impl->active_tree());
+ TreeSynchronizer::PushProperties(layer_tree_root.get(),
+ layer_impl_tree_root.get());
+ ExpectTreesAreIdentical(layer_tree_root.get(),
+ layer_impl_tree_root.get(),
+ host_impl->active_tree());
+
+ // Remove the first scroll child.
+ layer_tree_root->children()[1]->RemoveFromParent();
+ layer_impl_tree_root =
+ TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(),
+ layer_impl_tree_root.Pass(),
+ host_impl->active_tree());
+ TreeSynchronizer::PushProperties(layer_tree_root.get(),
+ layer_impl_tree_root.get());
+ ExpectTreesAreIdentical(layer_tree_root.get(),
+ layer_impl_tree_root.get(),
+ host_impl->active_tree());
+
+ // Add an additional scroll layer.
+ scoped_refptr<Layer> additional_scroll_child = Layer::Create();
+ layer_tree_root->AddChild(additional_scroll_child);
+ additional_scroll_child->SetScrollParent(scroll_parent);
+ layer_impl_tree_root =
+ TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(),
+ layer_impl_tree_root.Pass(),
+ host_impl->active_tree());
+ TreeSynchronizer::PushProperties(layer_tree_root.get(),
+ layer_impl_tree_root.get());
+ ExpectTreesAreIdentical(layer_tree_root.get(),
+ layer_impl_tree_root.get(),
+ host_impl->active_tree());
+
+ // Remove the scroll parent.
+ scroll_parent->RemoveFromParent();
+ scroll_parent = NULL;
+ layer_impl_tree_root =
+ TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(),
+ layer_impl_tree_root.Pass(),
+ host_impl->active_tree());
+ TreeSynchronizer::PushProperties(layer_tree_root.get(),
+ layer_impl_tree_root.get());
+ ExpectTreesAreIdentical(layer_tree_root.get(),
+ layer_impl_tree_root.get(),
+ host_impl->active_tree());
+
+ // The scroll children should have been unhooked.
+ EXPECT_EQ(2u, layer_tree_root->children().size());
+ EXPECT_FALSE(layer_tree_root->children()[0]->scroll_parent());
+ EXPECT_FALSE(layer_tree_root->children()[1]->scroll_parent());
+}
+
+TEST_F(TreeSynchronizerTest, SynchronizeClipParent) {
+ LayerTreeSettings settings;
+ FakeProxy proxy;
+ DebugScopedSetImplThread impl(&proxy);
+ FakeRenderingStatsInstrumentation stats_instrumentation;
+ scoped_ptr<LayerTreeHostImpl> host_impl =
+ LayerTreeHostImpl::Create(settings,
+ NULL,
+ &proxy,
+ &stats_instrumentation);
+
+ scoped_refptr<Layer> layer_tree_root = Layer::Create();
+ scoped_refptr<Layer> clip_parent = Layer::Create();
+ scoped_refptr<Layer> intervening = Layer::Create();
+ scoped_refptr<Layer> clip_child1 = Layer::Create();
+ scoped_refptr<Layer> clip_child2 = Layer::Create();
+ layer_tree_root->AddChild(clip_parent);
+ clip_parent->AddChild(intervening);
+ intervening->AddChild(clip_child1);
+ intervening->AddChild(clip_child2);
+
+ host_->SetRootLayer(layer_tree_root);
+
+ // First child is the second and third child's scroll parent.
+ clip_child1->SetClipParent(clip_parent);
+ clip_child2->SetClipParent(clip_parent);
+
+ scoped_ptr<LayerImpl> layer_impl_tree_root =
+ TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(),
+ scoped_ptr<LayerImpl>(),
+ host_impl->active_tree());
+ TreeSynchronizer::PushProperties(layer_tree_root.get(),
+ layer_impl_tree_root.get());
+ ExpectTreesAreIdentical(layer_tree_root.get(),
+ layer_impl_tree_root.get(),
+ host_impl->active_tree());
+
+ // Remove the first clip child.
+ clip_child1->RemoveFromParent();
+ clip_child1 = NULL;
+
+ layer_impl_tree_root =
+ TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(),
+ layer_impl_tree_root.Pass(),
+ host_impl->active_tree());
+ TreeSynchronizer::PushProperties(layer_tree_root.get(),
+ layer_impl_tree_root.get());
+ ExpectTreesAreIdentical(layer_tree_root.get(),
+ layer_impl_tree_root.get(),
+ host_impl->active_tree());
+
+ // Add an additional clip child.
+ scoped_refptr<Layer> additional_clip_child = Layer::Create();
+ intervening->AddChild(additional_clip_child);
+ additional_clip_child->SetClipParent(clip_parent);
+ layer_impl_tree_root =
+ TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(),
+ layer_impl_tree_root.Pass(),
+ host_impl->active_tree());
+ TreeSynchronizer::PushProperties(layer_tree_root.get(),
+ layer_impl_tree_root.get());
+ ExpectTreesAreIdentical(layer_tree_root.get(),
+ layer_impl_tree_root.get(),
+ host_impl->active_tree());
+
+ // Remove the nearest clipping ancestor.
+ clip_parent->RemoveFromParent();
+ clip_parent = NULL;
+ layer_impl_tree_root =
+ TreeSynchronizer::SynchronizeTrees(layer_tree_root.get(),
+ layer_impl_tree_root.Pass(),
+ host_impl->active_tree());
+ TreeSynchronizer::PushProperties(layer_tree_root.get(),
+ layer_impl_tree_root.get());
+ ExpectTreesAreIdentical(layer_tree_root.get(),
+ layer_impl_tree_root.get(),
+ host_impl->active_tree());
+
+ // The clip children should have been unhooked.
+ EXPECT_EQ(2u, intervening->children().size());
+ EXPECT_FALSE(clip_child2->clip_parent());
+ EXPECT_FALSE(additional_clip_child->clip_parent());
+}
+
} // namespace
} // namespace cc