Index: content/browser/renderer_host/render_widget_host_view_mac.mm |
diff --git a/content/browser/renderer_host/render_widget_host_view_mac.mm b/content/browser/renderer_host/render_widget_host_view_mac.mm |
index 9697ea63797bf6aeabbfdc8d2856e8829a2f2b57..a94bfce8ed8e637c38a68bd98667e129c9539003 100644 |
--- a/content/browser/renderer_host/render_widget_host_view_mac.mm |
+++ b/content/browser/renderer_host/render_widget_host_view_mac.mm |
@@ -397,6 +397,13 @@ |
return results; |
} |
+void RemoveLayerFromSuperlayer( |
+ base::scoped_nsobject<CompositingIOSurfaceLayer> layer) { |
+ // Disable the fade-out animation as the layer is removed. |
+ ScopedCAActionDisabler disabler; |
+ [layer removeFromSuperlayer]; |
+} |
+ |
} // namespace |
namespace content { |
@@ -499,10 +506,14 @@ |
can_compose_inline_(true), |
browser_compositor_view_placeholder_( |
new BrowserCompositorViewPlaceholderMac), |
+ backing_store_scale_factor_(1), |
is_loading_(false), |
allow_pause_for_resize_or_repaint_(true), |
weak_factory_(this), |
- fullscreen_parent_host_view_(NULL) { |
+ fullscreen_parent_host_view_(NULL), |
+ software_frame_weak_ptr_factory_(this) { |
+ software_frame_manager_.reset(new SoftwareFrameManager( |
+ software_frame_weak_ptr_factory_.GetWeakPtr())); |
// |cocoa_view_| owns us and we will be deleted when |cocoa_view_| |
// goes away. Since we autorelease it, our caller must put |
// |GetNativeView()| into the view hierarchy right after calling us. |
@@ -517,8 +528,21 @@ |
[cocoa_view_ setLayer:background_layer_]; |
[cocoa_view_ setWantsLayer:YES]; |
- root_layer_.reset(new ui::Layer(ui::LAYER_TEXTURED)); |
- delegated_frame_host_.reset(new DelegatedFrameHost(this)); |
+ if (!IsDelegatedRendererEnabled()) { |
+ // Add a flipped transparent layer as a child, so that we don't need to |
+ // fiddle with the position of sub-layers -- they will always be at the |
+ // origin. |
+ flipped_layer_.reset([[CALayer alloc] init]); |
+ [flipped_layer_ setGeometryFlipped:YES]; |
+ [flipped_layer_ |
+ setAutoresizingMask:kCALayerWidthSizable|kCALayerHeightSizable]; |
+ [background_layer_ addSublayer:flipped_layer_]; |
+ } |
+ |
+ if (IsDelegatedRendererEnabled()) { |
+ root_layer_.reset(new ui::Layer(ui::LAYER_TEXTURED)); |
+ delegated_frame_host_.reset(new DelegatedFrameHost(this)); |
+ } |
gfx::Screen::GetScreenFor(cocoa_view_)->AddObserver(this); |
@@ -536,6 +560,10 @@ |
// Ensure that the browser compositor is destroyed in a safe order. |
ShutdownBrowserCompositor(); |
+ |
+ // Make sure that the layer doesn't reach into the now-invalid object. |
+ DestroyCompositedIOSurfaceAndLayer(); |
+ DestroySoftwareLayer(); |
// We are owned by RenderWidgetHostViewCocoa, so if we go away before the |
// RenderWidgetHost does we need to tell it not to hold a stale pointer to |
@@ -556,7 +584,64 @@ |
/////////////////////////////////////////////////////////////////////////////// |
// RenderWidgetHostViewMac, RenderWidgetHostView implementation: |
+bool RenderWidgetHostViewMac::EnsureCompositedIOSurface() { |
+ // If the context or the IOSurface's context has had an error, re-build |
+ // everything from scratch. |
+ if (compositing_iosurface_context_ && |
+ compositing_iosurface_context_->HasBeenPoisoned()) { |
+ LOG(ERROR) << "Failing EnsureCompositedIOSurface because " |
+ << "context was poisoned"; |
+ return false; |
+ } |
+ if (compositing_iosurface_ && |
+ compositing_iosurface_->HasBeenPoisoned()) { |
+ LOG(ERROR) << "Failing EnsureCompositedIOSurface because " |
+ << "surface was poisoned"; |
+ return false; |
+ } |
+ |
+ int current_window_number = |
+ CompositingIOSurfaceContext::kOffscreenContextWindowNumber; |
+ bool new_surface_needed = !compositing_iosurface_; |
+ bool new_context_needed = |
+ !compositing_iosurface_context_ || |
+ (compositing_iosurface_context_ && |
+ compositing_iosurface_context_->window_number() != |
+ current_window_number); |
+ |
+ if (!new_surface_needed && !new_context_needed) |
+ return true; |
+ |
+ // Create the GL context and shaders. |
+ if (new_context_needed) { |
+ scoped_refptr<CompositingIOSurfaceContext> new_context = |
+ CompositingIOSurfaceContext::Get(current_window_number); |
+ // Un-bind the GL context from this view before binding the new GL |
+ // context. Having two GL contexts bound to a view will result in |
+ // crashes and corruption. |
+ // http://crbug.com/230883 |
+ if (!new_context) { |
+ LOG(ERROR) << "Failed to create CompositingIOSurfaceContext"; |
+ return false; |
+ } |
+ compositing_iosurface_context_ = new_context; |
+ } |
+ |
+ // Create the IOSurface texture. |
+ if (new_surface_needed) { |
+ compositing_iosurface_ = CompositingIOSurfaceMac::Create(); |
+ if (!compositing_iosurface_) { |
+ LOG(ERROR) << "Failed to create CompositingIOSurface"; |
+ return false; |
+ } |
+ } |
+ |
+ return true; |
+} |
+ |
void RenderWidgetHostViewMac::EnsureBrowserCompositorView() { |
+ if (!delegated_frame_host_) |
+ return; |
if (browser_compositor_view_) |
return; |
@@ -579,6 +664,70 @@ |
delegated_frame_host_->WasHidden(); |
delegated_frame_host_->RemovingFromWindow(); |
browser_compositor_view_.reset(); |
+} |
+ |
+void RenderWidgetHostViewMac::EnsureSoftwareLayer() { |
+ TRACE_EVENT0("browser", "RenderWidgetHostViewMac::EnsureSoftwareLayer"); |
+ if (software_layer_) |
+ return; |
+ |
+ software_layer_.reset([[SoftwareLayer alloc] init]); |
+ DCHECK(software_layer_); |
+ |
+ // Disable the fade-in animation as the layer is added. |
+ ScopedCAActionDisabler disabler; |
+ [flipped_layer_ addSublayer:software_layer_]; |
+} |
+ |
+void RenderWidgetHostViewMac::DestroySoftwareLayer() { |
+ if (!software_layer_) |
+ return; |
+ |
+ // Disable the fade-out animation as the layer is removed. |
+ ScopedCAActionDisabler disabler; |
+ [software_layer_ removeFromSuperlayer]; |
+ software_layer_.reset(); |
+} |
+ |
+void RenderWidgetHostViewMac::EnsureCompositedIOSurfaceLayer() { |
+ TRACE_EVENT0("browser", |
+ "RenderWidgetHostViewMac::EnsureCompositedIOSurfaceLayer"); |
+ DCHECK(compositing_iosurface_context_); |
+ if (compositing_iosurface_layer_) |
+ return; |
+ |
+ compositing_iosurface_layer_.reset([[CompositingIOSurfaceLayer alloc] |
+ initWithIOSurface:compositing_iosurface_ |
+ withScaleFactor:compositing_iosurface_->scale_factor() |
+ withClient:this]); |
+ DCHECK(compositing_iosurface_layer_); |
+ |
+ // Disable the fade-in animation as the layer is added. |
+ ScopedCAActionDisabler disabler; |
+ [flipped_layer_ addSublayer:compositing_iosurface_layer_]; |
+} |
+ |
+void RenderWidgetHostViewMac::DestroyCompositedIOSurfaceLayer( |
+ DestroyCompositedIOSurfaceLayerBehavior destroy_layer_behavior) { |
+ if (!compositing_iosurface_layer_) |
+ return; |
+ |
+ if (destroy_layer_behavior == kRemoveLayerFromHierarchy) { |
+ // Disable the fade-out animation as the layer is removed. |
+ ScopedCAActionDisabler disabler; |
+ [compositing_iosurface_layer_ removeFromSuperlayer]; |
+ } |
+ [compositing_iosurface_layer_ resetClient]; |
+ compositing_iosurface_layer_.reset(); |
+} |
+ |
+void RenderWidgetHostViewMac::DestroyCompositedIOSurfaceAndLayer() { |
+ // Any pending frames will not be displayed, so ack them now. |
+ SendPendingSwapAck(); |
+ |
+ DestroyCompositedIOSurfaceLayer(kRemoveLayerFromHierarchy); |
+ compositing_iosurface_ = NULL; |
+ compositing_iosurface_context_ = NULL; |
} |
bool RenderWidgetHostViewMac::OnMessageReceived(const IPC::Message& message) { |
@@ -736,6 +885,12 @@ |
void RenderWidgetHostViewMac::UpdateBackingStoreScaleFactor() { |
if (!render_widget_host_) |
return; |
+ |
+ float new_scale_factor = ui::GetScaleFactorForNativeView(cocoa_view_); |
+ if (new_scale_factor == backing_store_scale_factor_) |
+ return; |
+ backing_store_scale_factor_ = new_scale_factor; |
+ |
render_widget_host_->NotifyScreenInfoChanged(); |
} |
@@ -748,27 +903,50 @@ |
return; |
ui::LatencyInfo renderer_latency_info; |
- renderer_latency_info.AddLatencyNumber( |
- ui::TAB_SHOW_COMPONENT, |
- render_widget_host_->GetLatencyComponentId(), |
- 0); |
+ if ((compositing_iosurface_ && compositing_iosurface_->HasIOSurface()) || |
+ software_frame_manager_->HasCurrentFrame() || |
+ (delegated_frame_host_ && delegated_frame_host_->HasSavedFrame())) { |
+ ui::LatencyInfo browser_latency_info; |
+ browser_latency_info.AddLatencyNumber( |
+ ui::TAB_SHOW_COMPONENT, |
+ render_widget_host_->GetLatencyComponentId(), |
+ 0); |
+ pending_latency_info_.push_back(browser_latency_info); |
+ } else { |
+ renderer_latency_info.AddLatencyNumber( |
+ ui::TAB_SHOW_COMPONENT, |
+ render_widget_host_->GetLatencyComponentId(), |
+ 0); |
+ } |
+ |
render_widget_host_->WasShown(renderer_latency_info); |
+ software_frame_manager_->SetVisibility(true); |
// If there is not a frame being currently drawn, kick one, so that the below |
// pause will have a frame to wait on. |
- render_widget_host_->ScheduleComposite(); |
+ if (IsDelegatedRendererEnabled()) |
+ render_widget_host_->ScheduleComposite(); |
+ |
+ // Call setNeedsDisplay before pausing for new frames to come in -- if any |
+ // do, and are drawn, then the needsDisplay bit will be cleared. |
+ [compositing_iosurface_layer_ setNeedsDisplay]; |
PauseForPendingResizeOrRepaintsAndDraw(); |
} |
void RenderWidgetHostViewMac::WasHidden() { |
if (render_widget_host_->is_hidden()) |
return; |
+ |
+ // Any pending frames will not be displayed until this is shown again. Ack |
+ // them now. |
+ SendPendingSwapAck(); |
DestroyBrowserCompositorView(); |
// If we have a renderer, then inform it that we are being hidden so it can |
// reduce its resource utilization. |
render_widget_host_->WasHidden(); |
+ software_frame_manager_->SetVisibility(false); |
} |
void RenderWidgetHostViewMac::SetSize(const gfx::Size& size) { |
@@ -862,7 +1040,9 @@ |
bool RenderWidgetHostViewMac::IsSurfaceAvailableForCopy() const { |
if (delegated_frame_host_) |
return delegated_frame_host_->CanCopyToBitmap(); |
- return false; |
+ |
+ return software_frame_manager_->HasCurrentFrame() || |
+ (compositing_iosurface_ && compositing_iosurface_->HasIOSurface()); |
} |
void RenderWidgetHostViewMac::Show() { |
@@ -1113,6 +1293,57 @@ |
if (delegated_frame_host_) { |
delegated_frame_host_->CopyFromCompositingSurface( |
src_subrect, dst_size, callback, color_type); |
+ return; |
+ } |
+ |
+ if (color_type != kN32_SkColorType) { |
+ NOTIMPLEMENTED(); |
+ callback.Run(false, SkBitmap()); |
+ } |
+ base::ScopedClosureRunner scoped_callback_runner( |
+ base::Bind(callback, false, SkBitmap())); |
+ float scale = ui::GetScaleFactorForNativeView(cocoa_view_); |
+ gfx::Size dst_pixel_size = gfx::ToFlooredSize( |
+ gfx::ScaleSize(dst_size, scale)); |
+ if (compositing_iosurface_ && compositing_iosurface_->HasIOSurface()) { |
+ ignore_result(scoped_callback_runner.Release()); |
+ compositing_iosurface_->CopyTo(GetScaledOpenGLPixelRect(src_subrect), |
+ dst_pixel_size, |
+ callback); |
+ } else if (software_frame_manager_->HasCurrentFrame()) { |
+ gfx::Rect src_pixel_rect = gfx::ToEnclosingRect(gfx::ScaleRect( |
+ src_subrect, |
+ software_frame_manager_->GetCurrentFrameDeviceScaleFactor())); |
+ SkBitmap source_bitmap; |
+ SkImageInfo source_info = SkImageInfo::MakeN32( |
+ software_frame_manager_->GetCurrentFrameSizeInPixels().width(), |
+ software_frame_manager_->GetCurrentFrameSizeInPixels().height(), |
+ kOpaque_SkAlphaType); |
+ source_bitmap.installPixels( |
+ source_info, |
+ software_frame_manager_->GetCurrentFramePixels(), |
+ source_info.minRowBytes()); |
+ |
+ SkBitmap target_bitmap; |
+ if (!target_bitmap.allocN32Pixels( |
+ dst_pixel_size.width(), dst_pixel_size.height(), true)) |
+ return; |
+ |
+ SkCanvas target_canvas(target_bitmap); |
+ SkRect src_pixel_skrect = SkRect::MakeXYWH( |
+ src_pixel_rect.x(), src_pixel_rect.y(), |
+ src_pixel_rect.width(), src_pixel_rect.height()); |
+ target_canvas.drawBitmapRectToRect( |
+ source_bitmap, |
+ &src_pixel_skrect, |
+ SkRect::MakeXYWH(0, 0, dst_pixel_size.width(), dst_pixel_size.height()), |
+ NULL, |
+ SkCanvas::kNone_DrawBitmapRectFlag); |
+ |
+ ignore_result(scoped_callback_runner.Release()); |
+ callback.Run(true, target_bitmap); |
+ } else { |
+ callback.Run(false, SkBitmap()); |
} |
} |
@@ -1123,30 +1354,66 @@ |
if (delegated_frame_host_) { |
delegated_frame_host_->CopyFromCompositingSurfaceToVideoFrame( |
src_subrect, target, callback); |
- } |
+ return; |
+ } |
+ |
+ base::ScopedClosureRunner scoped_callback_runner(base::Bind(callback, false)); |
+ if (!compositing_iosurface_ || !compositing_iosurface_->HasIOSurface()) |
+ return; |
+ |
+ if (!target.get()) { |
+ NOTREACHED(); |
+ return; |
+ } |
+ |
+ if (target->format() != media::VideoFrame::YV12 && |
+ target->format() != media::VideoFrame::I420) { |
+ NOTREACHED(); |
+ return; |
+ } |
+ |
+ if (src_subrect.IsEmpty()) |
+ return; |
+ |
+ ignore_result(scoped_callback_runner.Release()); |
+ compositing_iosurface_->CopyToVideoFrame( |
+ GetScaledOpenGLPixelRect(src_subrect), |
+ target, |
+ callback); |
} |
bool RenderWidgetHostViewMac::CanCopyToVideoFrame() const { |
if (delegated_frame_host_) |
return delegated_frame_host_->CanCopyToVideoFrame(); |
- return false; |
+ |
+ return (!software_frame_manager_->HasCurrentFrame() && |
+ compositing_iosurface_ && |
+ compositing_iosurface_->HasIOSurface()); |
} |
bool RenderWidgetHostViewMac::CanSubscribeFrame() const { |
if (delegated_frame_host_) |
return delegated_frame_host_->CanSubscribeFrame(); |
- return false; |
+ |
+ return !software_frame_manager_->HasCurrentFrame(); |
} |
void RenderWidgetHostViewMac::BeginFrameSubscription( |
scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber) { |
- if (delegated_frame_host_) |
+ if (delegated_frame_host_) { |
delegated_frame_host_->BeginFrameSubscription(subscriber.Pass()); |
+ return; |
+ } |
+ frame_subscriber_ = subscriber.Pass(); |
} |
void RenderWidgetHostViewMac::EndFrameSubscription() { |
- if (delegated_frame_host_) |
+ if (delegated_frame_host_) { |
delegated_frame_host_->EndFrameSubscription(); |
+ return; |
+ } |
+ |
+ frame_subscriber_.reset(); |
} |
// Sets whether or not to accept first responder status. |
@@ -1192,6 +1459,185 @@ |
render_widget_host_->Send(new ViewMsg_PluginImeCompositionCompleted( |
render_widget_host_->GetRoutingID(), text, plugin_id)); |
} |
+} |
+ |
+void RenderWidgetHostViewMac::CompositorSwapBuffers( |
+ IOSurfaceID surface_handle, |
+ const gfx::Rect& damage_rect, |
+ const gfx::Size& size, |
+ float surface_scale_factor, |
+ const std::vector<ui::LatencyInfo>& latency_info) { |
+ // Ensure that the frame be acked unless it is explicitly passed to a |
+ // display function. |
+ base::ScopedClosureRunner scoped_ack( |
+ base::Bind(&RenderWidgetHostViewMac::SendPendingSwapAck, |
+ weak_factory_.GetWeakPtr())); |
+ |
+ if (render_widget_host_->is_hidden()) |
+ return; |
+ |
+ // Ensure that if this function exits before the frame is set up (but not |
+ // necessarily drawn) then it is treated as an error. |
+ base::ScopedClosureRunner scoped_error( |
+ base::Bind(&RenderWidgetHostViewMac::GotAcceleratedCompositingError, |
+ weak_factory_.GetWeakPtr())); |
+ |
+ AddPendingLatencyInfo(latency_info); |
+ |
+ // If compositing_iosurface_ exists and has been poisoned, destroy it |
+ // and allow EnsureCompositedIOSurface to recreate it below. Keep a |
+ // reference to the destroyed layer around until after the below call |
+ // to LayoutLayers, to avoid flickers. |
+ base::ScopedClosureRunner scoped_layer_remover; |
+ if (compositing_iosurface_context_ && |
+ compositing_iosurface_context_->HasBeenPoisoned()) { |
+ scoped_layer_remover.Reset( |
+ base::Bind(RemoveLayerFromSuperlayer, compositing_iosurface_layer_)); |
+ DestroyCompositedIOSurfaceLayer(kLeaveLayerInHierarchy); |
+ DestroyCompositedIOSurfaceAndLayer(); |
+ } |
+ |
+ // Ensure compositing_iosurface_ and compositing_iosurface_context_ be |
+ // allocated. |
+ if (!EnsureCompositedIOSurface()) { |
+ LOG(ERROR) << "Failed EnsureCompositingIOSurface"; |
+ return; |
+ } |
+ |
+ // Make the context current and update the IOSurface with the handle |
+ // passed in by the swap command. |
+ { |
+ gfx::ScopedCGLSetCurrentContext scoped_set_current_context( |
+ compositing_iosurface_context_->cgl_context()); |
+ if (!compositing_iosurface_->SetIOSurfaceWithContextCurrent( |
+ compositing_iosurface_context_, surface_handle, size, |
+ surface_scale_factor)) { |
+ LOG(ERROR) << "Failed SetIOSurface on CompositingIOSurfaceMac"; |
+ return; |
+ } |
+ } |
+ |
+ // Grab video frames now that the IOSurface has been set up. Note that this |
+ // will be done in an offscreen context, so it is necessary to re-set the |
+ // current context afterward. |
+ bool frame_was_captured = false; |
+ if (frame_subscriber_) { |
+ const base::TimeTicks now = gfx::FrameTime::Now(); |
+ base::TimeTicks present_time; |
+ if (vsync_timebase_.is_null() || vsync_interval_ <= base::TimeDelta()) { |
+ present_time = now; |
+ } else { |
+ const int64 intervals_elapsed = (now - vsync_timebase_) / vsync_interval_; |
+ present_time = vsync_timebase_ + |
+ (intervals_elapsed + 1) * vsync_interval_; |
+ } |
+ |
+ scoped_refptr<media::VideoFrame> frame; |
+ RenderWidgetHostViewFrameSubscriber::DeliverFrameCallback callback; |
+ if (frame_subscriber_->ShouldCaptureFrame( |
+ damage_rect, present_time, &frame, &callback)) { |
+ // Flush the context that updated the IOSurface, to ensure that the |
+ // context that does the copy picks up the correct version. |
+ { |
+ gfx::ScopedCGLSetCurrentContext scoped_set_current_context( |
+ compositing_iosurface_context_->cgl_context()); |
+ glFlush(); |
+ } |
+ compositing_iosurface_->CopyToVideoFrame( |
+ gfx::Rect(size), frame, |
+ base::Bind(callback, present_time)); |
+ frame_was_captured = true; |
+ } |
+ } |
+ |
+ // At this point the surface, its context, and its layer have been set up, so |
+ // don't generate an error (one may be generated when drawing). |
+ ignore_result(scoped_error.Release()); |
+ |
+ GotAcceleratedFrame(); |
+ |
+ gfx::Size window_size(NSSizeToCGSize([cocoa_view_ frame].size)); |
+ if (window_size.IsEmpty()) { |
+ // setNeedsDisplay will never display and we'll never ack if the window is |
+ // empty, so ack now and don't bother calling setNeedsDisplay below. |
+ return; |
+ } |
+ if (window_number() <= 0) { |
+ // It's normal for a backgrounded tab that is being captured to have no |
+ // window but not be hidden. Immediately ack the frame, and don't try to |
+ // draw it. |
+ if (frame_was_captured) |
+ return; |
+ |
+ // If this frame was not captured, there is likely some sort of bug. Ack |
+ // the frame and hope for the best. Because the IOSurface and layer are |
+ // populated, it will likely be displayed when the view is added to a |
+ // window's hierarchy. |
+ |
+ // TODO(shess) If the view does not have a window, or the window |
+ // does not have backing, the IOSurface will log "invalid drawable" |
+ // in -setView:. It is not clear how this code is reached with such |
+ // a case, so record some info into breakpad (some subset of |
+ // browsers are likely to crash later for unrelated reasons). |
+ // http://crbug.com/148882 |
+ const char* const kCrashKey = "rwhvm_window"; |
+ NSWindow* window = [cocoa_view_ window]; |
+ if (!window) { |
+ base::debug::SetCrashKeyValue(kCrashKey, "Missing window"); |
+ } else { |
+ std::string value = |
+ base::StringPrintf("window %s delegate %s controller %s", |
+ object_getClassName(window), |
+ object_getClassName([window delegate]), |
+ object_getClassName([window windowController])); |
+ base::debug::SetCrashKeyValue(kCrashKey, value); |
+ } |
+ return; |
+ } |
+ |
+ // If we reach here, then the frame will be displayed by a future draw |
+ // call, so don't make the callback. |
+ ignore_result(scoped_ack.Release()); |
+ DCHECK(compositing_iosurface_layer_); |
+ [compositing_iosurface_layer_ gotNewFrame]; |
+ |
+ // Try to finish previous copy requests after draw to get better pipelining. |
+ if (compositing_iosurface_) |
+ compositing_iosurface_->CheckIfAllCopiesAreFinished(false); |
+ |
+ // The IOSurface's size may have changed, so re-layout the layers to take |
+ // this into account. This may force an immediate draw. |
+ LayoutLayers(); |
+} |
+ |
+void RenderWidgetHostViewMac::GotAcceleratedCompositingError() { |
+ LOG(ERROR) << "Encountered accelerated compositing error"; |
+ base::MessageLoop::current()->PostTask( |
+ FROM_HERE, |
+ base::Bind(&RenderWidgetHostViewMac::DestroyCompositingStateOnError, |
+ weak_factory_.GetWeakPtr())); |
+} |
+ |
+void RenderWidgetHostViewMac::DestroyCompositingStateOnError() { |
+ // This should be called with a clean stack. Make sure that no context is |
+ // current. |
+ DCHECK(!CGLGetCurrentContext()); |
+ |
+ // The existing GL contexts may be in a bad state, so don't re-use any of the |
+ // existing ones anymore, rather, allocate new ones. |
+ if (compositing_iosurface_context_) |
+ compositing_iosurface_context_->PoisonContextAndSharegroup(); |
+ |
+ DestroyCompositedIOSurfaceAndLayer(); |
+ |
+ // Request that a new frame be generated and dirty the view. |
+ if (render_widget_host_) |
+ render_widget_host_->ScheduleComposite(); |
+ [cocoa_view_ setNeedsDisplay:YES]; |
+ |
+ // TODO(ccameron): It may be a good idea to request that the renderer recreate |
+ // its GL context as well, and fall back to software if this happens |
+ // repeatedly. |
} |
bool RenderWidgetHostViewMac::GetLineBreakIndex( |
@@ -1330,23 +1776,97 @@ |
void RenderWidgetHostViewMac::AcceleratedSurfaceBuffersSwapped( |
const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params, |
int gpu_host_id) { |
+ TRACE_EVENT0("browser", |
+ "RenderWidgetHostViewMac::AcceleratedSurfaceBuffersSwapped"); |
+ DCHECK_CURRENTLY_ON(BrowserThread::UI); |
+ |
+ AddPendingSwapAck(params.route_id, |
+ gpu_host_id, |
+ compositing_iosurface_ ? |
+ compositing_iosurface_->GetRendererID() : 0); |
+ |
+ switch (GetSurfaceHandleType(params.surface_handle)) { |
+ case kSurfaceHandleTypeIOSurface: { |
+ IOSurfaceID io_surface_id = IOSurfaceIDFromSurfaceHandle( |
+ params.surface_handle); |
+ |
+ CompositorSwapBuffers(io_surface_id, |
+ gfx::Rect(), |
+ params.size, |
+ params.scale_factor, |
+ params.latency_info); |
+ } break; |
+ case kSurfaceHandleTypeCAContext: { |
+ // Disable the fade-out animation as the layer is added. |
+ ScopedCAActionDisabler disabler; |
+ |
+ CAContextID context_id = CAContextIDFromSurfaceHandle( |
+ params.surface_handle); |
+ |
+ // If if the layer has changed put the new layer in the hierarchy and |
+ // take the old one out. |
+ if ([remote_layer_host_ contextId] != context_id) { |
+ [remote_layer_host_ removeFromSuperlayer]; |
+ |
+ remote_layer_host_.reset([[CALayerHost alloc] init]); |
+ [remote_layer_host_ setContextId:context_id]; |
+ [remote_layer_host_ |
+ setAutoresizingMask:kCALayerMaxXMargin|kCALayerMaxYMargin]; |
+ [flipped_layer_ addSublayer:remote_layer_host_]; |
+ } |
+ |
+ // Ack the frame immediately. Any GPU back pressure will be applied by |
+ // the remote layer from within the GPU process. |
+ SendPendingSwapAck(); |
+ } break; |
+ default: |
+ LOG(ERROR) << "Invalid surface handle type."; |
+ break; |
+ } |
} |
void RenderWidgetHostViewMac::AcceleratedSurfacePostSubBuffer( |
const GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params& params, |
int gpu_host_id) { |
+ TRACE_EVENT0("browser", |
+ "RenderWidgetHostViewMac::AcceleratedSurfacePostSubBuffer"); |
+ DCHECK_CURRENTLY_ON(BrowserThread::UI); |
+ |
+ AddPendingSwapAck(params.route_id, |
+ gpu_host_id, |
+ compositing_iosurface_ ? |
+ compositing_iosurface_->GetRendererID() : 0); |
+ CompositorSwapBuffers( |
+ IOSurfaceIDFromSurfaceHandle(params.surface_handle), |
+ gfx::Rect(params.x, params.y, params.width, params.height), |
+ params.surface_size, |
+ params.surface_scale_factor, |
+ params.latency_info); |
} |
void RenderWidgetHostViewMac::AcceleratedSurfaceSuspend() { |
+ if (render_widget_host_->is_hidden()) |
+ DestroyCompositedIOSurfaceAndLayer(); |
} |
void RenderWidgetHostViewMac::AcceleratedSurfaceRelease() { |
+ DestroyCompositedIOSurfaceAndLayer(); |
} |
bool RenderWidgetHostViewMac::HasAcceleratedSurface( |
const gfx::Size& desired_size) { |
if (browser_compositor_view_) |
return browser_compositor_view_->HasFrameOfSize(desired_size); |
+ if (compositing_iosurface_) { |
+ return compositing_iosurface_->HasIOSurface() && |
+ (desired_size.IsEmpty() || |
+ compositing_iosurface_->dip_io_surface_size() == desired_size); |
+ } |
+ if (software_frame_manager_->HasCurrentFrame()) { |
+ return (desired_size.IsEmpty() || |
+ software_frame_manager_->GetCurrentFrameSizeInDIP() == |
+ desired_size); |
+ } |
return false; |
} |
@@ -1378,6 +1898,50 @@ |
frame->delegated_frame_data.Pass(), |
frame->metadata.device_scale_factor, |
frame->metadata.latency_info); |
+ } else if (frame->software_frame_data) { |
+ if (!software_frame_manager_->SwapToNewFrame( |
+ output_surface_id, |
+ frame->software_frame_data.get(), |
+ frame->metadata.device_scale_factor, |
+ render_widget_host_->GetProcess()->GetHandle())) { |
+ render_widget_host_->GetProcess()->ReceivedBadMessage(); |
+ return; |
+ } |
+ |
+ // Add latency info to report when the frame finishes drawing. |
+ AddPendingLatencyInfo(frame->metadata.latency_info); |
+ |
+ const void* pixels = software_frame_manager_->GetCurrentFramePixels(); |
+ gfx::Size size_in_pixels = |
+ software_frame_manager_->GetCurrentFrameSizeInPixels(); |
+ |
+ EnsureSoftwareLayer(); |
+ [software_layer_ setContentsToData:pixels |
+ withRowBytes:4 * size_in_pixels.width() |
+ withPixelSize:size_in_pixels |
+ withScaleFactor:frame->metadata.device_scale_factor]; |
+ |
+ // Send latency information to the host immediately, as there will be no |
+ // subsequent draw call in which to do so. |
+ SendPendingLatencyInfoToHost(); |
+ |
+ GotSoftwareFrame(); |
+ |
+ cc::CompositorFrameAck ack; |
+ RenderWidgetHostImpl::SendSwapCompositorFrameAck( |
+ render_widget_host_->GetRoutingID(), |
+ software_frame_manager_->GetCurrentFrameOutputSurfaceId(), |
+ render_widget_host_->GetProcess()->GetID(), |
+ ack); |
+ software_frame_manager_->SwapToNewFrameComplete( |
+ !render_widget_host_->is_hidden()); |
+ |
+ // Notify observers, tab capture observers in particular, that a new |
+ // software frame has come in. |
+ NotificationService::current()->Notify( |
+ NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE, |
+ Source<RenderWidgetHost>(render_widget_host_), |
+ NotificationService::NoDetails()); |
} else { |
DLOG(ERROR) << "Received unexpected frame type."; |
RecordAction( |
@@ -1458,6 +2022,23 @@ |
return false; |
} |
+void RenderWidgetHostViewMac::SoftwareFrameWasFreed( |
+ uint32 output_surface_id, unsigned frame_id) { |
+ if (!render_widget_host_) |
+ return; |
+ cc::CompositorFrameAck ack; |
+ ack.last_software_frame_id = frame_id; |
+ RenderWidgetHostImpl::SendReclaimCompositorResources( |
+ render_widget_host_->GetRoutingID(), |
+ output_surface_id, |
+ render_widget_host_->GetProcess()->GetID(), |
+ ack); |
+} |
+ |
+void RenderWidgetHostViewMac::ReleaseReferencesToSoftwareFrame() { |
+ DestroySoftwareLayer(); |
+} |
+ |
void RenderWidgetHostViewMac::ShutdownHost() { |
weak_factory_.InvalidateWeakPtrs(); |
render_widget_host_->Shutdown(); |
@@ -1469,6 +2050,33 @@ |
delegated_frame_host_.reset(); |
root_layer_.reset(); |
browser_compositor_view_placeholder_.reset(); |
+} |
+ |
+void RenderWidgetHostViewMac::GotAcceleratedFrame() { |
+ EnsureCompositedIOSurfaceLayer(); |
+ SendVSyncParametersToRenderer(); |
+ |
+ // Delete software backingstore and layer. |
+ software_frame_manager_->DiscardCurrentFrame(); |
+ DestroySoftwareLayer(); |
+} |
+ |
+void RenderWidgetHostViewMac::GotSoftwareFrame() { |
+ TRACE_EVENT0("browser", "RenderWidgetHostViewMac::GotSoftwareFrame"); |
+ |
+ if (!render_widget_host_) |
+ return; |
+ |
+ EnsureSoftwareLayer(); |
+ LayoutLayers(); |
+ SendVSyncParametersToRenderer(); |
+ |
+ // Draw the contents of the frame immediately. It is critical that this |
+ // happen before the frame be acked, otherwise the new frame will likely be |
+ // ready before the drawing is complete, thrashing the browser main thread. |
+ [software_layer_ displayIfNeeded]; |
+ |
+ DestroyCompositedIOSurfaceAndLayer(); |
} |
void RenderWidgetHostViewMac::SetActive(bool active) { |
@@ -1579,6 +2187,52 @@ |
SpeakText(text); |
} |
+gfx::Rect RenderWidgetHostViewMac::GetScaledOpenGLPixelRect( |
+ const gfx::Rect& rect) { |
+ gfx::Rect src_gl_subrect = rect; |
+ src_gl_subrect.set_y(GetViewBounds().height() - rect.bottom()); |
+ |
+ return gfx::ToEnclosingRect(gfx::ScaleRect(src_gl_subrect, |
+ ViewScaleFactor())); |
+} |
+ |
+void RenderWidgetHostViewMac::AddPendingLatencyInfo( |
+ const std::vector<ui::LatencyInfo>& latency_info) { |
+ for (size_t i = 0; i < latency_info.size(); i++) { |
+ pending_latency_info_.push_back(latency_info[i]); |
+ } |
+} |
+ |
+void RenderWidgetHostViewMac::SendPendingLatencyInfoToHost() { |
+ for (size_t i = 0; i < pending_latency_info_.size(); i++) { |
+ pending_latency_info_[i].AddLatencyNumber( |
+ ui::INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT, 0, 0); |
+ render_widget_host_->FrameSwapped(pending_latency_info_[i]); |
+ } |
+ pending_latency_info_.clear(); |
+} |
+ |
+void RenderWidgetHostViewMac::AddPendingSwapAck( |
+ int32 route_id, int gpu_host_id, int32 renderer_id) { |
+ // Note that multiple un-acked swaps can come in the event of a GPU process |
+ // loss. Drop the old acks. |
+ pending_swap_ack_.reset(new PendingSwapAck( |
+ route_id, gpu_host_id, renderer_id)); |
+} |
+ |
+void RenderWidgetHostViewMac::SendPendingSwapAck() { |
+ if (!pending_swap_ack_) |
+ return; |
+ |
+ AcceleratedSurfaceMsg_BufferPresented_Params ack_params; |
+ ack_params.sync_point = 0; |
+ ack_params.renderer_id = pending_swap_ack_->renderer_id; |
+ RenderWidgetHostImpl::AcknowledgeBufferPresent(pending_swap_ack_->route_id, |
+ pending_swap_ack_->gpu_host_id, |
+ ack_params); |
+ pending_swap_ack_.reset(); |
+} |
+ |
void RenderWidgetHostViewMac::PauseForPendingResizeOrRepaintsAndDraw() { |
if (!render_widget_host_ || render_widget_host_->is_hidden()) |
return; |
@@ -1587,6 +2241,11 @@ |
// This may lead to large delays, causing overlaps. See crbug.com/352020. |
if (!allow_pause_for_resize_or_repaint_) |
return; |
+ |
+ // Ensure that all frames are acked before waiting for a frame to come in. |
+ // Note that we will draw a frame at the end of this function, so it is safe |
+ // to ack a never-drawn frame here. |
+ SendPendingSwapAck(); |
// Wait for a frame of the right size to come in. |
if (browser_compositor_view_) |
@@ -1594,6 +2253,58 @@ |
render_widget_host_->PauseForPendingResizeOrRepaints(); |
if (browser_compositor_view_) |
browser_compositor_view_->EndPumpingFrames(); |
+ |
+ // Immediately draw any frames that haven't been drawn yet. This is necessary |
+ // to keep the window and the window's contents in sync. |
+ [cocoa_view_ displayIfNeeded]; |
+ [software_layer_ displayIfNeeded]; |
+ [compositing_iosurface_layer_ displayIfNeededAndAck]; |
+} |
+ |
+void RenderWidgetHostViewMac::LayoutLayers() { |
+ if (delegated_frame_host_) { |
+ return; |
+ } |
+ |
+ // Disable animation of the layer's resizing or change in contents scale. |
+ ScopedCAActionDisabler disabler; |
+ |
+ // Dynamically calling setContentsScale on a CAOpenGLLayer for which |
+ // setAsynchronous is dynamically toggled can result in flashes of corrupt |
+ // content. Work around this by replacing the entire layer when the scale |
+ // factor changes. |
+ if (compositing_iosurface_ && |
+ [compositing_iosurface_layer_ |
+ respondsToSelector:(@selector(contentsScale))]) { |
+ if (compositing_iosurface_->scale_factor() != |
+ [compositing_iosurface_layer_ contentsScale]) { |
+ DestroyCompositedIOSurfaceLayer(kRemoveLayerFromHierarchy); |
+ EnsureCompositedIOSurfaceLayer(); |
+ } |
+ } |
+ if (compositing_iosurface_ && |
+ compositing_iosurface_->HasIOSurface() && |
+ compositing_iosurface_layer_) { |
+ CGRect layer_bounds = CGRectMake( |
+ 0, |
+ 0, |
+ compositing_iosurface_->dip_io_surface_size().width(), |
+ compositing_iosurface_->dip_io_surface_size().height()); |
+ bool bounds_changed = !CGRectEqualToRect( |
+ layer_bounds, [compositing_iosurface_layer_ bounds]); |
+ [compositing_iosurface_layer_ setBounds:layer_bounds]; |
+ |
+ // If the bounds changed, then draw the frame immediately, to ensure that |
+ // content displayed is in sync with the window size. |
+ if (bounds_changed) { |
+ // Also, sometimes, especially when infobars are being removed, the |
+ // setNeedsDisplay calls are dropped on the floor, and stale content is |
+ // displayed. Calling displayIfNeeded will ensure that the right size |
+ // frame is drawn to the screen. |
+ // http://crbug.com/350817 |
+ [compositing_iosurface_layer_ setNeedsDisplayAndDisplayAndAck]; |
+ } |
+ } |
} |
SkColorType RenderWidgetHostViewMac::PreferredReadbackFormat() { |
@@ -1617,8 +2328,13 @@ |
// http://crbug.com/350410 |
// If tab capture isn't active then only ack frames when we draw them. |
- if (delegated_frame_host_ && !delegated_frame_host_->HasFrameSubscriber()) |
- return false; |
+ if (delegated_frame_host_) { |
+ if (!delegated_frame_host_->HasFrameSubscriber()) |
+ return false; |
+ } else { |
+ if (!frame_subscriber_) |
+ return false; |
+ } |
NSWindow* window = [cocoa_view_ window]; |
// If the view isn't even in the heirarchy then frames will never be drawn, |
@@ -1645,9 +2361,20 @@ |
} |
void RenderWidgetHostViewMac::AcceleratedLayerDidDrawFrame() { |
+ if (!render_widget_host_) |
+ return; |
+ |
+ SendPendingLatencyInfoToHost(); |
+ SendPendingSwapAck(); |
} |
void RenderWidgetHostViewMac::AcceleratedLayerHitError() { |
+ if (!render_widget_host_) |
+ return; |
+ // Perform all acks that would have been done if the frame had succeeded, to |
+ // un-block the renderer. |
+ AcceleratedLayerDidDrawFrame(); |
+ GotAcceleratedCompositingError(); |
} |
//////////////////////////////////////////////////////////////////////////////// |
@@ -1687,12 +2414,15 @@ |
renderWidgetHostView_.reset(r); |
canBeKeyView_ = YES; |
focusedPluginIdentifier_ = -1; |
+ renderWidgetHostView_->backing_store_scale_factor_ = |
+ ui::GetScaleFactorForNativeView(self); |
// OpenGL support: |
if ([self respondsToSelector: |
@selector(setWantsBestResolutionOpenGLSurface:)]) { |
[self setWantsBestResolutionOpenGLSurface:YES]; |
} |
+ handlingGlobalFrameDidChange_ = NO; |
[[NSNotificationCenter defaultCenter] |
addObserver:self |
selector:@selector(didChangeScreenParameters:) |
@@ -2405,6 +3135,11 @@ |
if (!renderWidgetHostView_->render_widget_host_) |
return; |
+ |
+ // Move the CALayers to their positions in the new view size. Note that |
+ // this will not draw anything because the non-background layers' sizes |
+ // didn't actually change. |
+ renderWidgetHostView_->LayoutLayers(); |
renderWidgetHostView_->render_widget_host_->SendScreenRects(); |
renderWidgetHostView_->render_widget_host_->WasResized(); |