| 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..2287b4aa736a824764badb027d3e8ffcc08f20c1 100644
|
| --- a/content/common/gpu/image_transport_surface_overlay_mac.mm
|
| +++ b/content/common/gpu/image_transport_surface_overlay_mac.mm
|
| @@ -4,27 +4,72 @@
|
|
|
| #include "content/common/gpu/image_transport_surface_overlay_mac.h"
|
|
|
| -#include <OpenGL/gl.h>
|
| +#include <IOSurface/IOSurface.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 "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;
|
| +
|
| + // The size of the full frame, in dip.
|
| + gfx::Size dip_size;
|
| +
|
| + std::vector<ui::LatencyInfo> latency_info;
|
| +
|
| + 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,61 +82,153 @@ 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);
|
| + this_swap->latency_info.swap(latency_info_);
|
|
|
| // 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]);
|
| params.size = pixel_size_;
|
| params.scale_factor = scale_factor_;
|
| - params.latency_info.swap(latency_info_);
|
| + params.latency_info.swap(this_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 +266,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;
|
| }
|
|
|