Chromium Code Reviews| Index: content/common/gpu/image_transport_surface_overlay_mac.mm |
| diff --git a/content/common/gpu/image_transport_surface_overlay_mac.mm b/content/common/gpu/image_transport_surface_overlay_mac.mm |
| index 1a528333529330c9b8872a8f6c71be59970c170b..15287525bab27cf17fcd0d67d40f7d8feb1c093c 100644 |
| --- a/content/common/gpu/image_transport_surface_overlay_mac.mm |
| +++ b/content/common/gpu/image_transport_surface_overlay_mac.mm |
| @@ -4,27 +4,70 @@ |
| #include "content/common/gpu/image_transport_surface_overlay_mac.h" |
| -#include <OpenGL/gl.h> |
| +#include <IOSurface/IOSurface.h> |
| +#include <OpenGL/CGLTypes.h> |
| +// This type consistently causes problem on Mac, and needs to be dealt with |
| +// in a systemic way. |
| +// http://crbug.com/517208 |
| +#ifndef GL_OES_EGL_image |
| +typedef void* GLeglImageOES; |
| +#endif |
| + |
| +#include "base/mac/scoped_cftyperef.h" |
| #include "content/common/gpu/gpu_messages.h" |
| #include "ui/accelerated_widget_mac/surface_handle_types.h" |
| #include "ui/base/cocoa/animation_utils.h" |
| #include "ui/base/cocoa/remote_layer_api.h" |
| #include "ui/gfx/geometry/dip_util.h" |
| +#include "ui/gl/gl_fence_apple.h" |
| #include "ui/gl/gl_image_io_surface.h" |
| +#include "ui/gl/scoped_api.h" |
| +#include "ui/gl/scoped_cgl.h" |
| + |
| +namespace { |
| + |
| +void CheckGLErrors(const char* msg) { |
| + GLenum gl_error; |
| + while ((gl_error = glGetError()) != GL_NO_ERROR) { |
| + LOG(ERROR) << "OpenGL error hit " << msg << ": " << gl_error; |
| + } |
| +} |
| + |
| +} // namespace |
| namespace content { |
| +class ImageTransportSurfaceOverlayMac::PendingSwap : |
| + public base::RefCounted<PendingSwap> { |
| + public: |
| + PendingSwap() {} |
| + |
| + // The IOSurface with new content for this swap. |
| + base::ScopedCFTypeRef<IOSurfaceRef> io_surface; |
| + |
| + // A fence object, and the CGL context it was issued in. |
| + base::ScopedTypeRef<CGLContextObj> cgl_context; |
| + scoped_ptr<gfx::GLFenceAPPLE> fence; |
|
jbauman
2015/08/06 01:53:09
I'd prefer if you put a std::vector<ui::LatencyInf
ccameron
2015/08/06 02:27:39
Good point -- done.
|
| + |
| + // The size of the full frame, in dip. |
| + gfx::Size dip_size; |
| + |
| + protected: |
| + friend class base::RefCounted<PendingSwap>; |
| + |
| + virtual ~PendingSwap() { DCHECK(!fence); } |
| +}; |
| + |
| ImageTransportSurfaceOverlayMac::ImageTransportSurfaceOverlayMac( |
| GpuChannelManager* manager, |
| GpuCommandBufferStub* stub, |
| gfx::PluginWindowHandle handle) |
| - : scale_factor_(1), pending_overlay_image_(nullptr) { |
| + : scale_factor_(1), pending_overlay_image_(nullptr), weak_factory_(this) { |
| helper_.reset(new ImageTransportHelper(this, manager, stub, handle)); |
| } |
| ImageTransportSurfaceOverlayMac::~ImageTransportSurfaceOverlayMac() { |
| - gfx::GLImageIOSurface::SetLayerForWidget(widget_, nil); |
| } |
| bool ImageTransportSurfaceOverlayMac::Initialize() { |
| @@ -37,46 +80,100 @@ bool ImageTransportSurfaceOverlayMac::Initialize() { |
| ca_context_.reset( |
| [[CAContext contextWithCGSConnection:connection_id options:@{}] retain]); |
| layer_.reset([[CALayer alloc] init]); |
| + [layer_ setGeometryFlipped:YES]; |
| [ca_context_ setLayer:layer_]; |
| - // Register the CALayer so that it can be picked up in GLImageIOSurface. |
| - static intptr_t previous_widget = 0; |
| - previous_widget += 1; |
| - widget_ = reinterpret_cast<gfx::AcceleratedWidget>(previous_widget); |
| - gfx::GLImageIOSurface::SetLayerForWidget(widget_, layer_); |
| - |
| return true; |
| } |
| -void ImageTransportSurfaceOverlayMac::Destroy() {} |
| +void ImageTransportSurfaceOverlayMac::Destroy() { |
| + FinishAllPendingSwaps(); |
| +} |
| bool ImageTransportSurfaceOverlayMac::IsOffscreen() { |
| return false; |
| } |
| -gfx::SwapResult ImageTransportSurfaceOverlayMac::SwapBuffers() { |
| +gfx::SwapResult ImageTransportSurfaceOverlayMac::SwapBuffersInternal( |
| + const gfx::Rect& pixel_damage_rect) { |
| TRACE_EVENT0("gpu", "ImageTransportSurfaceOverlayMac::SwapBuffers"); |
| - // A flush is required to ensure that all content appears in the layer. |
| - { |
| - TRACE_EVENT0("gpu", "ImageTransportSurfaceOverlayMac::glFlush"); |
| - glFlush(); |
| - } |
| + // The remainder of the function will populate the PendingSwap structure and |
| + // then enqueue it. |
| + scoped_refptr<PendingSwap> this_swap(new PendingSwap); |
| // There should exist only one overlay image, and it should cover the whole |
| // surface. |
| DCHECK(pending_overlay_image_); |
| if (pending_overlay_image_) { |
| - TRACE_EVENT0("gpu", "ImageTransportSurfaceOverlayMac::setContents"); |
| - ScopedCAActionDisabler disabler; |
| - gfx::Rect dip_bounds = gfx::ConvertRectToDIP( |
| - scale_factor_, gfx::Rect(pixel_size_)); |
| - gfx::RectF crop_rect(0, 0, 1, 1); |
| - pending_overlay_image_->ScheduleOverlayPlane( |
| - widget_, 0, gfx::OVERLAY_TRANSFORM_NONE, dip_bounds, crop_rect); |
| + gfx::GLImageIOSurface* pending_overlay_image_io_surface = |
| + static_cast<gfx::GLImageIOSurface*>(pending_overlay_image_); |
| + this_swap->io_surface = pending_overlay_image_io_surface->io_surface(); |
| pending_overlay_image_ = nullptr; |
| } |
| + // A flush is required to ensure that all content appears in the layer. |
| + { |
| + gfx::ScopedSetGLToRealGLApi scoped_set_gl_api; |
| + TRACE_EVENT1("gpu", "ImageTransportSurfaceOverlayMac::glFlush", "surface", |
| + this_swap->io_surface.get()); |
| + CheckGLErrors("before flushing frame"); |
| + this_swap->cgl_context.reset(CGLGetCurrentContext(), |
| + base::scoped_policy::RETAIN); |
| + this_swap->fence.reset(new gfx::GLFenceAPPLE); |
| + CheckGLErrors("while flushing frame"); |
| + } |
| + |
| + this_swap->dip_size = gfx::ConvertSizeToDIP(scale_factor_, pixel_size_); |
| + pending_swaps_.push_back(this_swap); |
| + |
| + PostCheckAndDisplayPendingSwaps(); |
| + return gfx::SwapResult::SWAP_ACK; |
| +} |
| + |
| +void ImageTransportSurfaceOverlayMac::CheckAndDisplayPendingSwaps( |
| + bool force_immediate_display) { |
| + TRACE_EVENT0("gpu", |
| + "ImageTransportSurfaceOverlayMac::CheckAndDisplayPendingSwaps"); |
| + if (pending_swaps_.empty()) |
| + return; |
| + |
| + // Check if the pending work has finished (and early-out if it is not, and |
| + // this is not forced). |
| + scoped_refptr<PendingSwap> this_swap = pending_swaps_.front(); |
| + { |
| + gfx::ScopedSetGLToRealGLApi scoped_set_gl_api; |
| + gfx::ScopedCGLSetCurrentContext scoped_set_current(this_swap->cgl_context); |
| + |
| + CheckGLErrors("before testing fence"); |
| + bool result = this_swap->fence->HasCompleted(); |
| + CheckGLErrors("after testing fence"); |
| + if (!result && !force_immediate_display) { |
| + PostCheckAndDisplayPendingSwaps(); |
| + TRACE_EVENT1("gpu", "ImageTransportSurfaceOverlayMac::fenceNotReady", |
| + "surface", this_swap->io_surface.get()); |
| + return; |
| + } |
| + this_swap->fence.reset(); |
| + CheckGLErrors("while deleting fence"); |
| + } |
| + |
| + // Update the CALayer hierarchy. |
| + { |
| + TRACE_EVENT1("gpu", "ImageTransportSurfaceOverlayMac::setContents", |
| + "surface", this_swap->io_surface.get()); |
| + ScopedCAActionDisabler disabler; |
| + id new_contents = static_cast<id>(this_swap->io_surface.get()); |
| + [layer_ setContents:new_contents]; |
| + CGRect new_frame = gfx::Rect(this_swap->dip_size).ToCGRect(); |
| + if (!CGRectEqualToRect([layer_ frame], new_frame)) |
| + [layer_ setFrame:new_frame]; |
| + } |
| + |
| + // Remove this swap from the queue. |
| + pending_swaps_.pop_front(); |
| + |
| + // Send acknowledgement to the browser. |
| GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params; |
| params.surface_handle = |
| ui::SurfaceHandleFromCAContextID([ca_context_ contextId]); |
| @@ -84,14 +181,51 @@ gfx::SwapResult ImageTransportSurfaceOverlayMac::SwapBuffers() { |
| params.scale_factor = scale_factor_; |
| params.latency_info.swap(latency_info_); |
| helper_->SendAcceleratedSurfaceBuffersSwapped(params); |
| - return gfx::SwapResult::SWAP_ACK; |
| +} |
| + |
| +void ImageTransportSurfaceOverlayMac::PostCheckAndDisplayPendingSwaps() { |
| + bool force_immediate_display = true; |
| + base::TimeDelta delay; |
| + if (display_link_mac_) { |
| + base::TimeTicks timebase; |
| + base::TimeDelta interval; |
| + if (display_link_mac_->GetVSyncParameters(&timebase, &interval)) { |
| + base::TimeTicks now = base::TimeTicks::Now(); |
| + // Make the timer fire halfway through the vsync interval (since that |
| + // makes actual vsync alignment the most likely). |
| + timebase -= interval / 2; |
| + // Make sure that timebase is before now (to simplify the below |
| + // computations). |
| + if (timebase >= now) |
| + timebase -= (1 + (timebase - now) / interval) * interval; |
| + // Compute the target time. |
| + base::TimeTicks target = |
| + timebase + (1 + (now - timebase) / interval) * interval; |
| + delay = target - now; |
| + force_immediate_display = false; |
| + } |
| + } |
| + base::MessageLoop::current()->PostDelayedTask( |
| + FROM_HERE, |
| + base::Bind(&ImageTransportSurfaceOverlayMac::CheckAndDisplayPendingSwaps, |
| + weak_factory_.GetWeakPtr(), force_immediate_display), |
| + delay); |
| +} |
| + |
| +void ImageTransportSurfaceOverlayMac::FinishAllPendingSwaps() { |
| + while (!pending_swaps_.empty()) |
| + CheckAndDisplayPendingSwaps(true); |
| +} |
| + |
| +gfx::SwapResult ImageTransportSurfaceOverlayMac::SwapBuffers() { |
| + return SwapBuffersInternal(gfx::Rect(pixel_size_)); |
| } |
| gfx::SwapResult ImageTransportSurfaceOverlayMac::PostSubBuffer(int x, |
| int y, |
| int width, |
| int height) { |
| - return SwapBuffers(); |
| + return SwapBuffersInternal(gfx::Rect(x, y, width, height)); |
| } |
| bool ImageTransportSurfaceOverlayMac::SupportsPostSubBuffer() { |
| @@ -129,10 +263,18 @@ bool ImageTransportSurfaceOverlayMac::IsSurfaceless() const { |
| } |
| void ImageTransportSurfaceOverlayMac::OnBufferPresented( |
| - const AcceleratedSurfaceMsg_BufferPresented_Params& params) {} |
| + const AcceleratedSurfaceMsg_BufferPresented_Params& params) { |
| + if (display_link_mac_ && |
| + display_link_mac_->display_id() == params.display_id_for_vsync) |
| + return; |
| + display_link_mac_ = ui::DisplayLinkMac::GetForDisplay( |
| + params.display_id_for_vsync); |
| +} |
| void ImageTransportSurfaceOverlayMac::OnResize(gfx::Size pixel_size, |
| float scale_factor) { |
| + // Flush through any pending frames. |
| + FinishAllPendingSwaps(); |
| pixel_size_ = pixel_size; |
| scale_factor_ = scale_factor; |
| } |