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..487e851b3f5de01852a23a5116794f99d844b84b 100644 |
--- a/content/common/gpu/image_transport_surface_overlay_mac.mm |
+++ b/content/common/gpu/image_transport_surface_overlay_mac.mm |
@@ -4,27 +4,66 @@ |
#include "content/common/gpu/image_transport_surface_overlay_mac.h" |
-#include <OpenGL/gl.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 "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; |
+ |
+ // 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 +76,98 @@ 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(); |
+ 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 +175,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; |
tapted
2015/08/06 00:56:49
nit: indent 2 extra spaces
ccameron
2015/08/06 01:42:47
Done.
|
+ delay = target - now; |
+ force_immediate_display = false; |
+ } |
+ } |
+ base::MessageLoop::current()->PostDelayedTask( |
+ FROM_HERE, |
+ base::Bind(&ImageTransportSurfaceOverlayMac::CheckAndDisplayPendingSwaps, |
+ weak_factory_.GetWeakPtr(), force_immediate_display), |
+ delay); |
Ken Russell (switch to Gerrit)
2015/08/05 21:55:23
This will busy-loop while the browser is waiting f
ccameron
2015/08/06 01:42:47
That could very well happen -- the effect of it wo
|
+} |
+ |
+void ImageTransportSurfaceOverlayMac::FinishAllPendingSwaps() { |
+ while (!pending_swaps_.empty()) |
+ CheckAndDisplayPendingSwaps(true); |
Ken Russell (switch to Gerrit)
2015/08/05 21:55:23
Would it better match the intended behavior to act
ccameron
2015/08/06 01:42:47
Doing a finish on the GLFenceAPPLE would have this
|
+} |
+ |
+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 +257,16 @@ 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) |
+ return; |
+ display_link_mac_ = ui::DisplayLinkMac::GetForDisplay(params.display_id); |
+} |
void ImageTransportSurfaceOverlayMac::OnResize(gfx::Size pixel_size, |
float scale_factor) { |
+ // Flush through any pending frames. |
+ FinishAllPendingSwaps(); |
pixel_size_ = pixel_size; |
scale_factor_ = scale_factor; |
} |