diff options
26 files changed, 466 insertions, 67 deletions
diff --git a/cc/test/layer_tree_test.cc b/cc/test/layer_tree_test.cc index 87fcb54..6a7cf50 100644 --- a/cc/test/layer_tree_test.cc +++ b/cc/test/layer_tree_test.cc @@ -414,6 +414,13 @@ void LayerTreeTest::PostSetVisibleToMainThread(bool visible) { visible)); } +void LayerTreeTest::PostSetNextCommitForcesRedrawToMainThread() { + proxy()->MainThreadTaskRunner()->PostTask( + FROM_HERE, + base::Bind(&LayerTreeTest::DispatchSetNextCommitForcesRedraw, + main_thread_weak_ptr_)); +} + void LayerTreeTest::DoBeginTest() { client_ = LayerTreeHostClientForTesting::Create(this); @@ -558,6 +565,13 @@ void LayerTreeTest::DispatchSetVisible(bool visible) { ScheduleComposite(); } +void LayerTreeTest::DispatchSetNextCommitForcesRedraw() { + DCHECK(!proxy() || proxy()->IsMainThread()); + + if (layer_tree_host_) + layer_tree_host_->SetNextCommitForcesRedraw(); +} + void LayerTreeTest::DispatchComposite() { scheduled_ = false; diff --git a/cc/test/layer_tree_test.h b/cc/test/layer_tree_test.h index c9e14d6..d7607fb 100644 --- a/cc/test/layer_tree_test.h +++ b/cc/test/layer_tree_test.h @@ -117,6 +117,7 @@ class LayerTreeTest : public testing::Test, public TestHooks { void PostSetNeedsRedrawToMainThread(); void PostSetNeedsRedrawRectToMainThread(gfx::Rect damage_rect); void PostSetVisibleToMainThread(bool visible); + void PostSetNextCommitForcesRedrawToMainThread(); void DoBeginTest(); void Timeout(); @@ -138,6 +139,7 @@ class LayerTreeTest : public testing::Test, public TestHooks { void DispatchSetNeedsRedraw(); void DispatchSetNeedsRedrawRect(gfx::Rect damage_rect); void DispatchSetVisible(bool visible); + void DispatchSetNextCommitForcesRedraw(); void DispatchComposite(); void DispatchDidAddAnimation(); diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc index 8bbf742..6e153d1 100644 --- a/cc/trees/layer_tree_host.cc +++ b/cc/trees/layer_tree_host.cc @@ -133,7 +133,8 @@ LayerTreeHost::LayerTreeHost(LayerTreeHostClient* client, partial_texture_update_requests_(0), in_paint_layer_contents_(false), total_frames_used_for_lcd_text_metrics_(0), - tree_id_(s_next_tree_id++) { + tree_id_(s_next_tree_id++), + next_commit_forces_redraw_(false) { if (settings_.accelerated_animation_enabled) animation_registrar_ = AnimationRegistrar::Create(); s_num_layer_tree_instances++; @@ -323,11 +324,17 @@ void LayerTreeHost::FinishCommitOnImplThread(LayerTreeHostImpl* host_impl) { DCHECK(!host_impl->pending_tree()); host_impl->CreatePendingTree(); sync_tree = host_impl->pending_tree(); + if (next_commit_forces_redraw_) + sync_tree->ForceRedrawNextActivation(); } else { + if (next_commit_forces_redraw_) + host_impl->SetFullRootLayerDamage(); contents_texture_manager_->ReduceMemory(host_impl->resource_provider()); sync_tree = host_impl->active_tree(); } + next_commit_forces_redraw_ = false; + sync_tree->set_source_frame_number(source_frame_number()); if (needs_full_tree_sync_) @@ -565,6 +572,10 @@ void LayerTreeHost::SetNextCommitWaitsForActivation() { proxy_->SetNextCommitWaitsForActivation(); } +void LayerTreeHost::SetNextCommitForcesRedraw() { + next_commit_forces_redraw_ = true; +} + void LayerTreeHost::SetAnimationEvents(scoped_ptr<AnimationEventsVector> events, base::Time wall_clock_time) { DCHECK(proxy_->IsMainThread()); diff --git a/cc/trees/layer_tree_host.h b/cc/trees/layer_tree_host.h index e84f292..20c2660 100644 --- a/cc/trees/layer_tree_host.h +++ b/cc/trees/layer_tree_host.h @@ -215,6 +215,8 @@ class CC_EXPORT LayerTreeHost : NON_EXPORTED_BASE(public RateLimiterClient) { void SetNextCommitWaitsForActivation(); + void SetNextCommitForcesRedraw(); + void SetAnimationEvents(scoped_ptr<AnimationEventsVector> events, base::Time wall_clock_time); @@ -456,6 +458,7 @@ class CC_EXPORT LayerTreeHost : NON_EXPORTED_BASE(public RateLimiterClient) { }; LCDTextMetrics lcd_text_metrics_; int tree_id_; + bool next_commit_forces_redraw_; scoped_refptr<Layer> page_scale_layer_; scoped_refptr<Layer> inner_viewport_scroll_layer_; diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc index 535f271..8afd94b 100644 --- a/cc/trees/layer_tree_host_unittest.cc +++ b/cc/trees/layer_tree_host_unittest.cc @@ -637,6 +637,99 @@ class LayerTreeHostTestCompositeAndReadbackAfterForcedDraw MULTI_THREAD_TEST_F(LayerTreeHostTestCompositeAndReadbackAfterForcedDraw); +class LayerTreeHostTestSetNextCommitForcesRedraw : public LayerTreeHostTest { + public: + LayerTreeHostTestSetNextCommitForcesRedraw() + : num_draws_(0), + bounds_(50, 50), + invalid_rect_(10, 10, 20, 20), + root_layer_(ContentLayer::Create(&client_)) { + } + + virtual void BeginTest() OVERRIDE { + root_layer_->SetIsDrawable(true); + root_layer_->SetBounds(bounds_); + layer_tree_host()->SetRootLayer(root_layer_); + layer_tree_host()->SetViewportSize(bounds_); + PostSetNeedsCommitToMainThread(); + } + + virtual void CommitCompleteOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { + if (num_draws_ == 3 && host_impl->settings().impl_side_painting) + host_impl->SetNeedsRedrawRect(invalid_rect_); + } + + virtual bool PrepareToDrawOnThread(LayerTreeHostImpl* host_impl, + LayerTreeHostImpl::FrameData* frame_data, + bool result) OVERRIDE { + EXPECT_TRUE(result); + + gfx::RectF root_damage_rect; + if (!frame_data->render_passes.empty()) + root_damage_rect = frame_data->render_passes.back()->damage_rect; + + switch (num_draws_) { + case 0: + EXPECT_RECT_EQ(gfx::Rect(bounds_), root_damage_rect); + break; + case 1: + case 2: + EXPECT_RECT_EQ(gfx::Rect(0, 0, 0, 0), root_damage_rect); + break; + case 3: + EXPECT_RECT_EQ(invalid_rect_, root_damage_rect); + break; + case 4: + EXPECT_RECT_EQ(gfx::Rect(bounds_), root_damage_rect); + break; + default: + NOTREACHED(); + } + + return result; + } + + virtual void DrawLayersOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { + switch (num_draws_) { + case 0: + case 1: + // Cycle through a couple of empty commits to ensure we're observing the + // right behavior + PostSetNeedsCommitToMainThread(); + break; + case 2: + // Should force full frame damage on the next commit + PostSetNextCommitForcesRedrawToMainThread(); + PostSetNeedsCommitToMainThread(); + if (host_impl->settings().impl_side_painting) + host_impl->BlockNotifyReadyToActivateForTesting(true); + else + num_draws_++; + break; + case 3: + host_impl->BlockNotifyReadyToActivateForTesting(false); + break; + default: + EndTest(); + break; + } + num_draws_++; + } + + virtual void AfterTest() OVERRIDE { + EXPECT_EQ(5, num_draws_); + } + + private: + int num_draws_; + const gfx::Size bounds_; + const gfx::Rect invalid_rect_; + FakeContentLayerClient client_; + scoped_refptr<ContentLayer> root_layer_; +}; + +SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostTestSetNextCommitForcesRedraw); + // If the layerTreeHost says it can't draw, Then we should not try to draw. class LayerTreeHostTestCanDrawBlocksDrawing : public LayerTreeHostTest { public: diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc index 8c87b75..471a133 100644 --- a/cc/trees/layer_tree_impl.cc +++ b/cc/trees/layer_tree_impl.cc @@ -40,7 +40,8 @@ LayerTreeImpl::LayerTreeImpl(LayerTreeHostImpl* layer_tree_host_impl) contents_textures_purged_(false), viewport_size_invalid_(false), needs_update_draw_properties_(true), - needs_full_tree_sync_(true) { + needs_full_tree_sync_(true), + next_activation_forces_redraw_(false) { } LayerTreeImpl::~LayerTreeImpl() { @@ -111,6 +112,11 @@ void LayerTreeImpl::PushPropertiesTo(LayerTreeImpl* target_tree) { // The request queue should have been processed and does not require a push. DCHECK_EQ(ui_resource_request_queue_.size(), 0u); + if (next_activation_forces_redraw_) { + layer_tree_host_impl_->SetFullRootLayerDamage(); + next_activation_forces_redraw_ = false; + } + target_tree->SetLatencyInfo(latency_info_); latency_info_.Clear(); target_tree->SetPageScaleFactorAndLimits( diff --git a/cc/trees/layer_tree_impl.h b/cc/trees/layer_tree_impl.h index 6714503..faf360f 100644 --- a/cc/trees/layer_tree_impl.h +++ b/cc/trees/layer_tree_impl.h @@ -164,6 +164,8 @@ class CC_EXPORT LayerTreeImpl { void set_needs_full_tree_sync(bool needs) { needs_full_tree_sync_ = needs; } bool needs_full_tree_sync() const { return needs_full_tree_sync_; } + void ForceRedrawNextActivation() { next_activation_forces_redraw_ = true; } + void set_ui_resource_request_queue(const UIResourceRequestQueue& queue); const LayerImplList& RenderSurfaceLayerList() const; @@ -261,6 +263,8 @@ class CC_EXPORT LayerTreeImpl { // structural differences relative to the active tree. bool needs_full_tree_sync_; + bool next_activation_forces_redraw_; + ui::LatencyInfo latency_info_; UIResourceRequestQueue ui_resource_request_queue_; diff --git a/content/browser/aura/software_browser_compositor_output_surface.cc b/content/browser/aura/software_browser_compositor_output_surface.cc index f196c63..773e7df 100644 --- a/content/browser/aura/software_browser_compositor_output_surface.cc +++ b/content/browser/aura/software_browser_compositor_output_surface.cc @@ -4,6 +4,7 @@ #include "content/browser/aura/software_browser_compositor_output_surface.h" +#include "base/message_loop/message_loop.h" #include "base/time/time.h" #include "cc/output/compositor_frame.h" #include "cc/output/software_output_device.h" @@ -21,7 +22,12 @@ void SoftwareBrowserCompositorOutputSurface::SwapBuffers( ui::LatencyInfo latency_info = frame->metadata.latency_info; latency_info.AddLatencyNumber( ui::INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT, 0, 0); - RenderWidgetHostImpl::CompositorFrameDrawn(latency_info); + + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind( + &RenderWidgetHostImpl::CompositorFrameDrawn, + latency_info)); } } // namespace content diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc index 442f126..26fd958 100644 --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc @@ -1289,6 +1289,8 @@ void RenderProcessHostImpl::OnChannelConnected(int32 peer_pid) { tracked_objects::ThreadData::Status status = tracked_objects::ThreadData::status(); Send(new ChildProcessMsg_SetProfilerStatus(status)); + + Send(new ViewMsg_SetRendererProcessID(GetID())); } void RenderProcessHostImpl::OnChannelError() { diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc index ed07949..26daa67 100644 --- a/content/browser/renderer_host/render_view_host_impl.cc +++ b/content/browser/renderer_host/render_view_host_impl.cc @@ -68,7 +68,6 @@ #include "ui/gfx/image/image_skia.h" #include "ui/gfx/native_widget_types.h" #include "ui/shell_dialogs/selected_file_info.h" -#include "ui/snapshot/snapshot.h" #include "webkit/browser/fileapi/isolated_context.h" #if defined(OS_MACOSX) @@ -993,7 +992,6 @@ bool RenderViewHostImpl::OnMessageReceived(const IPC::Message& msg) { OnSelectionBoundsChanged) IPC_MESSAGE_HANDLER(ViewHostMsg_ScriptEvalResponse, OnScriptEvalResponse) IPC_MESSAGE_HANDLER(ViewHostMsg_DidZoomURL, OnDidZoomURL) - IPC_MESSAGE_HANDLER(ViewHostMsg_GetWindowSnapshot, OnGetWindowSnapshot) IPC_MESSAGE_HANDLER(DesktopNotificationHostMsg_RequestPermission, OnRequestDesktopNotificationPermission) IPC_MESSAGE_HANDLER(DesktopNotificationHostMsg_Show, @@ -1994,29 +1992,6 @@ void RenderViewHostImpl::OnDomOperationResponse( Details<DomOperationNotificationDetails>(&details)); } -void RenderViewHostImpl::OnGetWindowSnapshot(const int snapshot_id) { - std::vector<unsigned char> png; - - // This feature is behind the kEnableGpuBenchmarking command line switch - // because it poses security concerns and should only be used for testing. - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); - if (command_line.HasSwitch(switches::kEnableGpuBenchmarking)) { - gfx::Rect view_bounds = GetView()->GetViewBounds(); - gfx::Rect snapshot_bounds(view_bounds.size()); - gfx::Size snapshot_size = snapshot_bounds.size(); - - if (ui::GrabViewSnapshot(GetView()->GetNativeView(), - &png, snapshot_bounds)) { - Send(new ViewMsg_WindowSnapshotCompleted( - GetRoutingID(), snapshot_id, snapshot_size, png)); - return; - } - } - - Send(new ViewMsg_WindowSnapshotCompleted( - GetRoutingID(), snapshot_id, gfx::Size(), png)); -} - #if defined(OS_MACOSX) || defined(OS_ANDROID) void RenderViewHostImpl::OnShowPopup( const ViewHostMsg_ShowPopup_Params& params) { diff --git a/content/browser/renderer_host/render_view_host_impl.h b/content/browser/renderer_host/render_view_host_impl.h index 0364894..54f6f6a 100644 --- a/content/browser/renderer_host/render_view_host_impl.h +++ b/content/browser/renderer_host/render_view_host_impl.h @@ -583,7 +583,6 @@ class CONTENT_EXPORT RenderViewHostImpl void OnDidAccessInitialDocument(); void OnDomOperationResponse(const std::string& json_string, int automation_id); - void OnGetWindowSnapshot(const int snapshot_id); #if defined(OS_MACOSX) || defined(OS_ANDROID) void OnShowPopup(const ViewHostMsg_ShowPopup_Params& params); diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc index 74fc640..5de04bb 100644 --- a/content/browser/renderer_host/render_widget_host_impl.cc +++ b/content/browser/renderer_host/render_widget_host_impl.cc @@ -5,6 +5,7 @@ #include "content/browser/renderer_host/render_widget_host_impl.h" #include <math.h> +#include <set> #include <utility> #include "base/auto_reset.h" @@ -57,6 +58,7 @@ #include "ui/gfx/size_conversions.h" #include "ui/gfx/skbitmap_operations.h" #include "ui/gfx/vector2d_conversions.h" +#include "ui/snapshot/snapshot.h" #include "webkit/common/cursors/webcursor.h" #include "webkit/common/webpreferences.h" @@ -210,6 +212,7 @@ RenderWidgetHostImpl::RenderWidgetHostImpl(RenderWidgetHostDelegate* delegate, is_threaded_compositing_enabled_ = IsThreadedCompositingEnabled(); + g_routing_id_widget_map.Get().insert(std::make_pair( RenderWidgetHostID(process->GetID(), routing_id_), this)); process_->AddRoute(routing_id_, this); @@ -2381,6 +2384,14 @@ void RenderWidgetHostImpl::ComputeTouchLatency( } void RenderWidgetHostImpl::FrameSwapped(const ui::LatencyInfo& latency_info) { + ui::LatencyInfo::LatencyComponent window_snapshot_component; + if (latency_info.FindLatency(ui::WINDOW_SNAPSHOT_FRAME_NUMBER_COMPONENT, + GetLatencyComponentId(), + &window_snapshot_component)) { + WindowSnapshotReachedScreen( + static_cast<int>(window_snapshot_component.sequence_number)); + } + ui::LatencyInfo::LatencyComponent rwh_component; ui::LatencyInfo::LatencyComponent swap_component; if (!latency_info.FindLatency(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, @@ -2430,23 +2441,54 @@ void RenderWidgetHostImpl::DidReceiveRendererFrame() { view_->DidReceiveRendererFrame(); } +void RenderWidgetHostImpl::WindowSnapshotReachedScreen(int snapshot_id) { + DCHECK(base::MessageLoop::current()->IsType(base::MessageLoop::TYPE_UI)); + + std::vector<unsigned char> png; + + // This feature is behind the kEnableGpuBenchmarking command line switch + // because it poses security concerns and should only be used for testing. + const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + if (command_line.HasSwitch(switches::kEnableGpuBenchmarking)) { + gfx::Rect view_bounds = GetView()->GetViewBounds(); + gfx::Rect snapshot_bounds(view_bounds.size()); + gfx::Size snapshot_size = snapshot_bounds.size(); + + if (ui::GrabViewSnapshot(GetView()->GetNativeView(), + &png, snapshot_bounds)) { + Send(new ViewMsg_WindowSnapshotCompleted( + GetRoutingID(), snapshot_id, snapshot_size, png)); + return; + } + } + + Send(new ViewMsg_WindowSnapshotCompleted( + GetRoutingID(), snapshot_id, gfx::Size(), png)); +} + // static void RenderWidgetHostImpl::CompositorFrameDrawn( const ui::LatencyInfo& latency_info) { + std::set<RenderWidgetHostImpl*> rwhi_set; + for (ui::LatencyInfo::LatencyMap::const_iterator b = latency_info.latency_components.begin(); b != latency_info.latency_components.end(); ++b) { - if (b->first.first != ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT) - continue; - // Matches with GetLatencyComponentId - int routing_id = b->first.second & 0xffffffff; - int process_id = (b->first.second >> 32) & 0xffffffff; - RenderWidgetHost* rwh = - RenderWidgetHost::FromID(process_id, routing_id); - if (!rwh) - continue; - RenderWidgetHostImpl::From(rwh)->FrameSwapped(latency_info); + if (b->first.first == ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT || + b->first.first == ui::WINDOW_SNAPSHOT_FRAME_NUMBER_COMPONENT) { + // Matches with GetLatencyComponentId + int routing_id = b->first.second & 0xffffffff; + int process_id = (b->first.second >> 32) & 0xffffffff; + RenderWidgetHost* rwh = + RenderWidgetHost::FromID(process_id, routing_id); + if (!rwh) { + continue; + } + RenderWidgetHostImpl* rwhi = RenderWidgetHostImpl::From(rwh); + if (rwhi_set.insert(rwhi).second) + rwhi->FrameSwapped(latency_info); + } } } diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h index 6882c34..b4ba0d6 100644 --- a/content/browser/renderer_host/render_widget_host_impl.h +++ b/content/browser/renderer_host/render_widget_host_impl.h @@ -745,6 +745,8 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost, // which may get in recursive loops). void DelayedAutoResized(); + void WindowSnapshotReachedScreen(int snapshot_id); + // Our delegate, which wants to know mainly about keyboard events. // It will remain non-NULL until DetachDelegate() is called. RenderWidgetHostDelegate* delegate_; diff --git a/content/browser/renderer_host/render_widget_host_view_gtk.cc b/content/browser/renderer_host/render_widget_host_view_gtk.cc index 4c51767..bacdde3 100644 --- a/content/browser/renderer_host/render_widget_host_view_gtk.cc +++ b/content/browser/renderer_host/render_widget_host_view_gtk.cc @@ -1069,7 +1069,7 @@ void RenderWidgetHostViewGtk::AcceleratedSurfaceBuffersSwapped( ack_params.sync_point = 0; RenderWidgetHostImpl::AcknowledgeBufferPresent( params.route_id, gpu_host_id, ack_params); - host_->FrameSwapped(params.latency_info); + RenderWidgetHostImpl::CompositorFrameDrawn(params.latency_info); } void RenderWidgetHostViewGtk::AcceleratedSurfacePostSubBuffer( @@ -1079,7 +1079,7 @@ void RenderWidgetHostViewGtk::AcceleratedSurfacePostSubBuffer( ack_params.sync_point = 0; RenderWidgetHostImpl::AcknowledgeBufferPresent( params.route_id, gpu_host_id, ack_params); - host_->FrameSwapped(params.latency_info); + RenderWidgetHostImpl::CompositorFrameDrawn(params.latency_info); } void RenderWidgetHostViewGtk::AcceleratedSurfaceSuspend() { diff --git a/content/common/view_messages.h b/content/common/view_messages.h index eea3e79..355489b 100644 --- a/content/common/view_messages.h +++ b/content/common/view_messages.h @@ -806,6 +806,11 @@ IPC_MESSAGE_ROUTED2(ViewMsg_SetHistoryLengthAndPrune, int, /* merge_history_length */ int32 /* minimum_page_id */) +// Tells the renderer the browser's notion of its process ID. +// Some subsystems, like LatencyInfo, require this to be known to the renderer. +IPC_MESSAGE_CONTROL1(ViewMsg_SetRendererProcessID, + base::ProcessId /* process_id */) + // Tells the renderer to create a new view. // This message is slightly different, the view it takes (via // ViewMsg_New_Params) is the view to create, the message itself is sent as a @@ -1860,10 +1865,6 @@ IPC_MESSAGE_ROUTED3(ViewHostMsg_WebUISend, std::string /* message */, base::ListValue /* args */) -// Requests a snapshot of the given window. -IPC_MESSAGE_ROUTED1(ViewHostMsg_GetWindowSnapshot, - int /* snapshot_id */) - // A renderer sends this to the browser process when it wants to create a ppapi // plugin. The browser will create the plugin process if necessary, and will // return a handle to the channel on success. diff --git a/content/renderer/gpu/render_widget_compositor.cc b/content/renderer/gpu/render_widget_compositor.cc index 78622ee..858f226f 100644 --- a/content/renderer/gpu/render_widget_compositor.cc +++ b/content/renderer/gpu/render_widget_compositor.cc @@ -382,6 +382,11 @@ void RenderWidgetCompositor::SetNeedsRedrawRect(gfx::Rect damage_rect) { layer_tree_host_->SetNeedsRedrawRect(damage_rect); } +void RenderWidgetCompositor::SetNeedsForcedRedraw() { + layer_tree_host_->SetNextCommitForcesRedraw(); + setNeedsRedraw(); +} + void RenderWidgetCompositor::SetLatencyInfo( const ui::LatencyInfo& latency_info) { layer_tree_host_->SetLatencyInfo(latency_info); diff --git a/content/renderer/gpu/render_widget_compositor.h b/content/renderer/gpu/render_widget_compositor.h index 910d99c..8de7478 100644 --- a/content/renderer/gpu/render_widget_compositor.h +++ b/content/renderer/gpu/render_widget_compositor.h @@ -51,6 +51,9 @@ class RenderWidgetCompositor : public WebKit::WebLayerTreeView, bool animate); void SetOverdrawBottomHeight(float overdraw_bottom_height); void SetNeedsRedrawRect(gfx::Rect damage_rect); + // Like setNeedsRedraw but forces the frame to be drawn, without early-outs. + // Redraw will be forced after the next commit + void SetNeedsForcedRedraw(); void SetLatencyInfo(const ui::LatencyInfo& latency_info); int GetLayerTreeId() const; void NotifyInputThrottledUntilCommit(); diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc index bc5f679..1eb2c21 100644 --- a/content/renderer/render_thread_impl.cc +++ b/content/renderer/render_thread_impl.cc @@ -398,6 +398,8 @@ void RenderThreadImpl::Init() { memory_pressure_listener_.reset(new base::MemoryPressureListener( base::Bind(&RenderThreadImpl::OnMemoryPressure, base::Unretained(this)))); + renderer_process_id_ = base::kNullProcessId; + TRACE_EVENT_END_ETW("RenderThreadImpl::Init", 0, ""); } @@ -1094,6 +1096,7 @@ bool RenderThreadImpl::OnControlMessageReceived(const IPC::Message& msg) { IPC_MESSAGE_HANDLER(ViewMsg_PurgePluginListCache, OnPurgePluginListCache) IPC_MESSAGE_HANDLER(ViewMsg_NetworkStateChanged, OnNetworkStateChanged) IPC_MESSAGE_HANDLER(ViewMsg_TempCrashWithData, OnTempCrashWithData) + IPC_MESSAGE_HANDLER(ViewMsg_SetRendererProcessID, OnSetRendererProcessID) IPC_MESSAGE_HANDLER(ViewMsg_SetWebKitSharedTimersSuspended, OnSetWebKitSharedTimersSuspended) IPC_MESSAGE_UNHANDLED(handled = false) @@ -1231,6 +1234,10 @@ void RenderThreadImpl::OnTempCrashWithData(const GURL& data) { CHECK(false); } +void RenderThreadImpl::OnSetRendererProcessID(base::ProcessId process_id) { + renderer_process_id_ = process_id; +} + void RenderThreadImpl::OnSetWebKitSharedTimersSuspended(bool suspend) { ToggleWebKitSharedTimer(suspend); } @@ -1295,4 +1302,8 @@ void RenderThreadImpl::SampleGamepads(WebKit::WebGamepads* data) { gamepad_shared_memory_reader_->SampleGamepads(*data); } +base::ProcessId RenderThreadImpl::renderer_process_id() const { + return renderer_process_id_; +} + } // namespace content diff --git a/content/renderer/render_thread_impl.h b/content/renderer/render_thread_impl.h index 59b7c1d..aea1836 100644 --- a/content/renderer/render_thread_impl.h +++ b/content/renderer/render_thread_impl.h @@ -11,6 +11,7 @@ #include "base/memory/memory_pressure_listener.h" #include "base/observer_list.h" +#include "base/process/process_handle.h" #include "base/strings/string16.h" #include "base/timer/timer.h" #include "build/build_config.h" @@ -343,6 +344,11 @@ class CONTENT_EXPORT RenderThreadImpl : public RenderThread, // Retrieve current gamepad data. void SampleGamepads(WebKit::WebGamepads* data); + // Get the browser process's notion of the renderer process's ID. + // This is the first argument to RenderWidgetHost::FromID. Ideally + // this would be available on all platforms via base::Process. + base::ProcessId renderer_process_id() const; + private: // ChildThread virtual bool OnControlMessageReceived(const IPC::Message& msg) OVERRIDE; @@ -374,6 +380,7 @@ class CONTENT_EXPORT RenderThreadImpl : public RenderThread, void OnNetworkStateChanged(bool online); void OnGetAccessibilityTree(); void OnTempCrashWithData(const GURL& data); + void OnSetRendererProcessID(base::ProcessId process_id); void OnSetWebKitSharedTimersSuspended(bool suspend); void OnMemoryPressure( base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level); @@ -486,6 +493,8 @@ class CONTENT_EXPORT RenderThreadImpl : public RenderThread, scoped_ptr<GamepadSharedMemoryReader> gamepad_shared_memory_reader_; + base::ProcessId renderer_process_id_; + DISALLOW_COPY_AND_ASSIGN(RenderThreadImpl); }; diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc index dff71f2..557090d 100644 --- a/content/renderer/render_view_impl.cc +++ b/content/renderer/render_view_impl.cc @@ -23,6 +23,7 @@ #include "base/metrics/histogram.h" #include "base/path_service.h" #include "base/process/kill.h" +#include "base/process/process.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_piece.h" #include "base/strings/string_split.h" @@ -193,6 +194,7 @@ #include "third_party/WebKit/public/web/WebWindowFeatures.h" #include "third_party/WebKit/public/web/default/WebRenderTheme.h" #include "ui/base/ui_base_switches_util.h" +#include "ui/events/latency_info.h" #include "ui/gfx/native_widget_types.h" #include "ui/gfx/point.h" #include "ui/gfx/rect.h" @@ -2253,15 +2255,32 @@ bool RenderViewImpl::SendAndRunNestedMessageLoop(IPC::SyncMessage* message) { void RenderViewImpl::GetWindowSnapshot(const WindowSnapshotCallback& callback) { int id = next_snapshot_id_++; pending_snapshots_.insert(std::make_pair(id, callback)); - Send(new ViewHostMsg_GetWindowSnapshot(routing_id_, id)); + ui::LatencyInfo latency_info; + latency_info.AddLatencyNumber(ui::WINDOW_SNAPSHOT_FRAME_NUMBER_COMPONENT, + GetLatencyComponentId(), + id); + if (RenderWidgetCompositor* rwc = compositor()) { + rwc->SetLatencyInfo(latency_info); + } else { + latency_info_.MergeWith(latency_info); + } + ScheduleCompositeWithForcedRedraw(); } void RenderViewImpl::OnWindowSnapshotCompleted(const int snapshot_id, const gfx::Size& size, const std::vector<unsigned char>& png) { - PendingSnapshotMap::iterator it = pending_snapshots_.find(snapshot_id); - DCHECK(it != pending_snapshots_.end()); - it->second.Run(size, png); - pending_snapshots_.erase(it); + + // Any pending snapshots with a lower ID than the one received are considered + // to be implicitly complete, and returned the same snapshot data. + PendingSnapshotMap::iterator it = pending_snapshots_.begin(); + while(it != pending_snapshots_.end()) { + if (it->first <= snapshot_id) { + it->second.Run(size, png); + pending_snapshots_.erase(it++); + } else { + ++it; + } + } } // WebKit::WebViewClient ------------------------------------------------------ @@ -2746,6 +2765,12 @@ gfx::RectF RenderViewImpl::ClientRectToPhysicalWindowRect( return window_rect; } +int64 RenderViewImpl::GetLatencyComponentId() { + // Note: this must match the logic in RenderWidgetHostImpl. + return GetRoutingID() | (static_cast<int64>( + RenderThreadImpl::current()->renderer_process_id()) << 32); +} + void RenderViewImpl::StartNavStateSyncTimerIfNecessary() { // No need to update state if no page has committed yet. if (page_id_ == -1) diff --git a/content/renderer/render_view_impl.h b/content/renderer/render_view_impl.h index 6b0f1c4a..13d69f7 100644 --- a/content/renderer/render_view_impl.h +++ b/content/renderer/render_view_impl.h @@ -1166,6 +1166,9 @@ class CONTENT_EXPORT RenderViewImpl gfx::RectF ClientRectToPhysicalWindowRect(const gfx::RectF& rect) const; + // Helper for LatencyInfo construction. + int64 GetLatencyComponentId(); + // --------------------------------------------------------------------------- // ADDING NEW FUNCTIONS? Please keep private functions alphabetized and put // it in the same order in the .cc file as it was in the header. diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc index e1b7353..1cb162a 100644 --- a/content/renderer/render_widget.cc +++ b/content/renderer/render_widget.cc @@ -582,6 +582,34 @@ void RenderWidget::OnShowHostContextMenu(ContextMenuParams* params) { screen_metrics_emulator_->OnShowContextMenu(params); } +void RenderWidget::ScheduleCompositeWithForcedRedraw() { + if (compositor_) { + // Regardless of whether threaded compositing is enabled, always + // use this mechanism to force the compositor to redraw. However, + // the invalidation code path below is still needed for the + // non-threaded case. + compositor_->SetNeedsForcedRedraw(); + } + ScheduleCompositeImpl(true); +} + +void RenderWidget::ScheduleCompositeImpl(bool force_redraw) { + if (RenderThreadImpl::current()->compositor_message_loop_proxy().get() && + compositor_) { + if (!force_redraw) { + compositor_->setNeedsRedraw(); + } + } else { + // TODO(nduca): replace with something a little less hacky. The reason this + // hack is still used is because the Invalidate-DoDeferredUpdate loop + // contains a lot of host-renderer synchronization logic that is still + // important for the accelerated compositing case. The option of simply + // duplicating all that code is less desirable than "faking out" the + // invalidation path using a magical damage rect. + didInvalidateRect(WebRect(0, 0, 1, 1)); + } +} + bool RenderWidget::OnMessageReceived(const IPC::Message& message) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(RenderWidget, message) @@ -1883,18 +1911,7 @@ void RenderWidget::didCompleteSwapBuffers() { } void RenderWidget::scheduleComposite() { - if (RenderThreadImpl::current()->compositor_message_loop_proxy().get() && - compositor_) { - compositor_->setNeedsRedraw(); - } else { - // TODO(nduca): replace with something a little less hacky. The reason this - // hack is still used is because the Invalidate-DoDeferredUpdate loop - // contains a lot of host-renderer synchronization logic that is still - // important for the accelerated compositing case. The option of simply - // duplicating all that code is less desirable than "faking out" the - // invalidation path using a magical damage rect. - didInvalidateRect(WebRect(0, 0, 1, 1)); - } + ScheduleCompositeImpl(false); } void RenderWidget::scheduleAnimation() { diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h index 8341b0a..3987064 100644 --- a/content/renderer/render_widget.h +++ b/content/renderer/render_widget.h @@ -241,6 +241,8 @@ class CONTENT_EXPORT RenderWidget void DisableScreenMetricsEmulation(); void SetPopupOriginAdjustmentsForEmulation(ScreenMetricsEmulator* emulator); + void ScheduleCompositeWithForcedRedraw(); + protected: // Friend RefCounted so that the dtor can be non-public. Using this class // without ref-counting is an error. @@ -547,6 +549,8 @@ class CONTENT_EXPORT RenderWidget bool OnSnapshotHelper(const gfx::Rect& src_subrect, SkBitmap* bitmap); + void ScheduleCompositeImpl(bool force_redraw); + // Routing ID that allows us to communicate to the parent browser process // RenderWidgetHost. When MSG_ROUTING_NONE, no messages may be sent. int32 routing_id_; diff --git a/tools/telemetry/telemetry/core/tab_unittest.py b/tools/telemetry/telemetry/core/tab_unittest.py index 3bc3089..b47cd44 100644 --- a/tools/telemetry/telemetry/core/tab_unittest.py +++ b/tools/telemetry/telemetry/core/tab_unittest.py @@ -3,12 +3,10 @@ # found in the LICENSE file. import logging -import time from telemetry.core import util from telemetry.core import exceptions from telemetry.unittest import tab_test_case -from telemetry.unittest import DisabledTest def _IsDocumentVisible(tab): @@ -57,7 +55,6 @@ class GpuTabTest(tab_test_case.TabTestCase): self._extra_browser_args = ['--enable-gpu-benchmarking'] super(GpuTabTest, self).setUp() - @DisabledTest def testScreenshot(self): if not self._tab.screenshot_supported: logging.warning('Browser does not support screenshots, skipping test.') @@ -69,8 +66,6 @@ class GpuTabTest(tab_test_case.TabTestCase): self._tab.WaitForDocumentReadyStateToBeComplete() pixel_ratio = self._tab.EvaluateJavaScript('window.devicePixelRatio || 1') - # TODO(bajones): Sleep for a bit to counter BUG 260878. - time.sleep(0.5) screenshot = self._tab.Screenshot(5) assert screenshot screenshot.GetPixelColor(0 * pixel_ratio, 0 * pixel_ratio).AssertIsRGB( @@ -79,3 +74,22 @@ class GpuTabTest(tab_test_case.TabTestCase): 0, 255, 0, tolerance=2) screenshot.GetPixelColor(32 * pixel_ratio, 32 * pixel_ratio).AssertIsRGB( 255, 255, 255, tolerance=2) + + def testScreenshotSync(self): + if not self._tab.screenshot_supported: + logging.warning('Browser does not support screenshots, skipping test.') + return + + self._browser.SetHTTPServerDirectories(util.GetUnittestDataDir()) + self._tab.Navigate( + self._browser.http_server.UrlOf('screenshot_sync.html')) + self._tab.WaitForDocumentReadyStateToBeComplete() + + def IsTestComplete(): + return self._tab.EvaluateJavaScript('window.__testComplete') + util.WaitFor(IsTestComplete, 120) + + message = self._tab.EvaluateJavaScript('window.__testMessage') + if message: + logging.error(message) + assert self._tab.EvaluateJavaScript('window.__testSuccess') diff --git a/tools/telemetry/unittest_data/screenshot_sync.html b/tools/telemetry/unittest_data/screenshot_sync.html new file mode 100644 index 0000000..2c4bd10 --- /dev/null +++ b/tools/telemetry/unittest_data/screenshot_sync.html @@ -0,0 +1,145 @@ +<!DOCTYPE html> +<html> +<head> +<title>Synchronized screenshot test</title> +<style> + html, body { margin: 0; } + #log { height: 150px; overflow: auto; width: 512px; } + #log.failed { background-color: #FFAAAA; } +</style> +</head> +<body> + <canvas id="src-canvas" width="256" height="256"></canvas> + <canvas id="dest-canvas" width="256" height="256"></canvas><br/> + <div id="log"></div> + + <script> + "use strict"; + + (function () { + window.__testComplete = false; + window.__testMessage = ''; + window.__testSuccess = true; + var log = document.getElementById("log"); + var canvas1 = document.getElementById("src-canvas"); + var canvas2 = document.getElementById("dest-canvas"); + + if (!window.chrome || !window.chrome.gpuBenchmarking || + !window.chrome.gpuBenchmarking.beginWindowSnapshotPNG) { + canvas1.style.display = "none"; + canvas2.style.display = "none"; + log.innerHTML = "chrome.gpuBenchmarking.beginWindowSnapshotPNG not available.<br/>" + + "Please make sure Chrome was launched with --enable-gpu-benchmarking." + window.__testComplete = true; + window.__testMessage = 'chrome.gpuBenchmarking.beginWindowSnapshotPNG not available.'; + window.__testSuccess = false; + return; + } + + var totalTests = 100; + var testInterval = 75; + var testStartDelay = 1000; + var testCount = 0; + + var ctx1 = canvas1.getContext("2d"); + var ctx2 = canvas2.getContext("2d"); + + var clearColor = [0, 0, 0]; + var screenshotClearColor = [0, 0, 0]; + var validatedColor = [0, 0, 0]; + var capturing = false; + function draw() { + if (testCount == totalTests || !window.__testSuccess) + return; + + if (!capturing) { + clearColor[0] = Math.floor(Math.random() * 256.0); + clearColor[1] = Math.floor(Math.random() * 256.0); + clearColor[2] = Math.floor(Math.random() * 256.0); + + ctx1.fillStyle="RGB(" + + clearColor[0] + "," + + clearColor[1] + "," + + clearColor[2] + ")"; + ctx1.fillRect(0,0,canvas1.width,canvas1.height); + } + + window.requestAnimationFrame(draw); + }; + draw(); + + var snapshotImg = new Image(); + var snapshotBtn = document.getElementById("snapshot"); + var snapshotColorSpan = document.getElementById("snapshotColorSpan"); + var validatedColorSpan = document.getElementById("validatedColorSpan"); + + + function colorsMatch(a, b) { + return (Math.abs(a[0] - b[0]) < 2 && + Math.abs(a[1] - b[1]) < 2 && + Math.abs(a[2] - b[2]) < 2); + } + + function logString(str) { + var entry = document.createElement("div"); + entry.innerHTML = str + log.insertBefore(entry, log.firstChild); + } + + function colorToString(a) { + return "[" + a[0] +", " + a[1] +", " + a[2] +"]"; + } + + function logTest(id, a, b) { + var match = colorsMatch(a, b); + logString("Test " + id + ": " + + colorToString(a) + " " + + "~= " + + colorToString(b) + " " + + ": " + + (match ? "<b style='color: green'>Pass</b>" : "<b style='color: red'>Fail</b>")); + return match; + } + + // Take snapshots at an arbitrary interval and ensure that the resulting + // image matches the color we last cleared the webgl canvas with + function testSnapshot() { + capturing = true; + ++testCount; + + screenshotClearColor[0] = clearColor[0]; + screenshotClearColor[1] = clearColor[1]; + screenshotClearColor[2] = clearColor[2]; + + window.chrome.gpuBenchmarking.beginWindowSnapshotPNG( + function(s) { + snapshotImg.src = "data:image/png;base64," + s.data; + ctx2.drawImage(snapshotImg,0,0); + + var img_data = ctx2.getImageData(0, 0, 1, 1); + validatedColor[0] = img_data.data[0]; + validatedColor[1] = img_data.data[1]; + validatedColor[2] = img_data.data[2]; + + window.__testSuccess = window.__testSuccess && logTest(testCount, screenshotClearColor, validatedColor); + if (!window.__testSuccess) { + log.classList.add("failed"); + window.__testMessage = 'Color mismatch after ' + testCount + ' iterations.'; + } + + capturing = false; + + if (testCount < totalTests /*&& window.__testSuccess*/) { + if (window.__testSuccess) + setTimeout(testSnapshot, testInterval); + } else { + window.__testComplete = true; + } + } + ); + } + setTimeout(testSnapshot, testStartDelay); + })(); + </script> + </body> +</html> diff --git a/ui/events/latency_info.h b/ui/events/latency_info.h index d1e6f75..fb58176 100644 --- a/ui/events/latency_info.h +++ b/ui/events/latency_info.h @@ -52,6 +52,9 @@ enum LatencyComponentType { // Timestamp when the frame is swapped (i.e. when the rendering caused by // input event actually takes effect). INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT, + // Frame number when a window snapshot was requested. The snapshot + // is taken when the rendering results actually reach the screen. + WINDOW_SNAPSHOT_FRAME_NUMBER_COMPONENT }; struct EVENTS_EXPORT LatencyInfo { |