Mac: Add partial swap support to NSOpenGLContext path

Add a damage rect parameter to the structure
GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params.
Because that structure and its IPC are Mac-only, wrap them
in ifdefs.

Add code in the IOSurface draw function to clear out the
difference between the IOSurface and its viewport.

BUG=496484

Review URL: https://codereview.chromium.org/1161853006

Cr-Commit-Position: refs/heads/master@{#333231}
diff --git a/ui/accelerated_widget_mac/accelerated_widget_mac.h b/ui/accelerated_widget_mac/accelerated_widget_mac.h
index 9b9cb53..d4646bf5 100644
--- a/ui/accelerated_widget_mac/accelerated_widget_mac.h
+++ b/ui/accelerated_widget_mac/accelerated_widget_mac.h
@@ -10,6 +10,7 @@
 
 #include "ui/accelerated_widget_mac/accelerated_widget_mac_export.h"
 #include "ui/events/latency_info.h"
+#include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/native_widget_types.h"
 
@@ -78,8 +79,9 @@
   void GotAcceleratedFrame(
       uint64 surface_handle,
       const std::vector<ui::LatencyInfo>& latency_info,
-      gfx::Size pixel_size,
+      const gfx::Size& pixel_size,
       float scale_factor,
+      const gfx::Rect& pixel_damage_rect,
       const base::Closure& drawn_callback);
 
   void GotSoftwareFrame(float scale_factor, SkCanvas* canvas);
@@ -90,14 +92,17 @@
   void IOSurfaceLayerDidDrawFrame() override;
   void IOSurfaceLayerHitError() override;
 
-  void GotAcceleratedCAContextFrame(
-      CAContextID ca_context_id, gfx::Size pixel_size, float scale_factor);
+  void GotAcceleratedCAContextFrame(CAContextID ca_context_id,
+                                    const gfx::Size& pixel_size,
+                                    float scale_factor);
 
-  void GotAcceleratedIOSurfaceFrame(
-      IOSurfaceID io_surface_id, gfx::Size pixel_size, float scale_factor);
+  void GotAcceleratedIOSurfaceFrame(IOSurfaceID io_surface_id,
+                                    const gfx::Size& pixel_size,
+                                    float scale_factor);
 
   void GotAcceleratedIOSurfaceFrameNSGL(
-      IOSurfaceID io_surface_id, gfx::Size pixel_size, float scale_factor);
+      IOSurfaceID io_surface_id, const gfx::Size& pixel_size,
+      float scale_factor, const gfx::Rect& pixel_damage_rect);
 
   void AcknowledgeAcceleratedFrame();
 
@@ -160,7 +165,9 @@
 void AcceleratedWidgetMacGotAcceleratedFrame(
     gfx::AcceleratedWidget widget, uint64 surface_handle,
     const std::vector<ui::LatencyInfo>& latency_info,
-    gfx::Size pixel_size, float scale_factor,
+    const gfx::Size& pixel_size,
+    float scale_factor,
+    const gfx::Rect& pixel_damage_rect,
     const base::Closure& drawn_callback,
     bool* disable_throttling, int* renderer_id);
 
diff --git a/ui/accelerated_widget_mac/accelerated_widget_mac.mm b/ui/accelerated_widget_mac/accelerated_widget_mac.mm
index 6a6433d..19beee03 100644
--- a/ui/accelerated_widget_mac/accelerated_widget_mac.mm
+++ b/ui/accelerated_widget_mac/accelerated_widget_mac.mm
@@ -131,7 +131,9 @@
 void AcceleratedWidgetMac::GotAcceleratedFrame(
     uint64 surface_handle,
     const std::vector<ui::LatencyInfo>& latency_info,
-    gfx::Size pixel_size, float scale_factor,
+    const gfx::Size& pixel_size,
+    float scale_factor,
+    const gfx::Rect& pixel_damage_rect,
     const base::Closure& drawn_callback) {
   static bool use_ns_gl_surfaces =
       base::CommandLine::ForCurrentProcess()->HasSwitch(
@@ -158,7 +160,7 @@
       IOSurfaceID io_surface_id = IOSurfaceIDFromSurfaceHandle(surface_handle);
       if (use_ns_gl_surfaces) {
         GotAcceleratedIOSurfaceFrameNSGL(
-            io_surface_id, pixel_size, scale_factor);
+            io_surface_id, pixel_size, scale_factor, pixel_damage_rect);
       } else {
         GotAcceleratedIOSurfaceFrame(io_surface_id, pixel_size, scale_factor);
       }
@@ -177,7 +179,7 @@
 
 void AcceleratedWidgetMac::GotAcceleratedCAContextFrame(
     CAContextID ca_context_id,
-    gfx::Size pixel_size,
+    const gfx::Size& pixel_size,
     float scale_factor) {
   // In the layer is replaced, keep the old one around until after the new one
   // is installed to avoid flashes.
@@ -209,7 +211,10 @@
 }
 
 void AcceleratedWidgetMac::GotAcceleratedIOSurfaceFrameNSGL(
-    IOSurfaceID io_surface_id, gfx::Size pixel_size, float scale_factor) {
+    IOSurfaceID io_surface_id,
+    const gfx::Size& pixel_size,
+    float scale_factor,
+    const gfx::Rect& pixel_damage_rect) {
   if (!io_surface_ns_gl_surface_) {
     io_surface_ns_gl_surface_.reset(
         IOSurfaceNSGLSurface::Create(view_->AcceleratedWidgetGetNSView()));
@@ -221,13 +226,14 @@
     return;
   }
 
-  io_surface_ns_gl_surface_->GotFrame(io_surface_id, pixel_size, scale_factor);
+  io_surface_ns_gl_surface_->GotFrame(
+      io_surface_id, pixel_size, scale_factor, pixel_damage_rect);
   AcknowledgeAcceleratedFrame();
 }
 
 void AcceleratedWidgetMac::GotAcceleratedIOSurfaceFrame(
     IOSurfaceID io_surface_id,
-    gfx::Size pixel_size,
+    const gfx::Size& pixel_size,
     float scale_factor) {
   // In the layer is replaced, keep the old one around until after the new one
   // is installed to avoid flashes.
@@ -389,14 +395,17 @@
 void AcceleratedWidgetMacGotAcceleratedFrame(
     gfx::AcceleratedWidget widget, uint64 surface_handle,
     const std::vector<ui::LatencyInfo>& latency_info,
-    gfx::Size pixel_size, float scale_factor,
+    const gfx::Size& pixel_size,
+    float scale_factor,
+    const gfx::Rect& pixel_damage_rect,
     const base::Closure& drawn_callback,
     bool* disable_throttling, int* renderer_id) {
   AcceleratedWidgetMac* accelerated_widget_mac =
       GetHelperFromAcceleratedWidget(widget);
   if (accelerated_widget_mac) {
     accelerated_widget_mac->GotAcceleratedFrame(
-        surface_handle, latency_info, pixel_size, scale_factor, drawn_callback);
+        surface_handle, latency_info, pixel_size, scale_factor,
+        pixel_damage_rect, drawn_callback);
     *disable_throttling =
         accelerated_widget_mac->IsRendererThrottlingDisabled();
     *renderer_id = accelerated_widget_mac->GetRendererID();
diff --git a/ui/accelerated_widget_mac/io_surface_ns_gl_surface.h b/ui/accelerated_widget_mac/io_surface_ns_gl_surface.h
index 4a9d6e0..7cfb4969 100644
--- a/ui/accelerated_widget_mac/io_surface_ns_gl_surface.h
+++ b/ui/accelerated_widget_mac/io_surface_ns_gl_surface.h
@@ -22,7 +22,8 @@
   // Called on the UI thread.
   bool GotFrame(IOSurfaceID io_surface_id,
                 gfx::Size pixel_size,
-                float scale_factor);
+                float scale_factor,
+                gfx::Rect pixel_damage_rect);
 
  private:
   explicit IOSurfaceNSGLSurface(
diff --git a/ui/accelerated_widget_mac/io_surface_ns_gl_surface.mm b/ui/accelerated_widget_mac/io_surface_ns_gl_surface.mm
index ed1050f..68d00bdf 100644
--- a/ui/accelerated_widget_mac/io_surface_ns_gl_surface.mm
+++ b/ui/accelerated_widget_mac/io_surface_ns_gl_surface.mm
@@ -65,7 +65,8 @@
 
 bool IOSurfaceNSGLSurface::GotFrame(IOSurfaceID io_surface_id,
                                     gfx::Size frame_pixel_size,
-                                    float frame_scale_factor) {
+                                    float frame_scale_factor,
+                                    gfx::Rect pixel_damage_rect) {
   // The OpenGL framebuffer's scale factor and pixel size are updated to match
   // the CALayer's contentsScale and bounds at setView. The pixel size is the
   // stored in the GL_VIEWPORT state of the context.
@@ -82,18 +83,26 @@
   // If the OpenGL framebuffer does not match the frame in scale factor or
   // pixel size, then re-latch them. Note that they will latch to the layer's
   // bounds, which will not necessarily match the frame's pixel size.
+  bool full_damage = false;
   if (frame_pixel_size != contents_pixel_size ||
       frame_scale_factor != contents_scale_factor) {
     ScopedCAActionDisabler disabler;
     [ns_gl_context_ clearDrawable];
     [[view_ layer] setContentsScale:frame_scale_factor];
     [ns_gl_context_ setView:view_];
+
+    // The front buffer may have been destroyed at re-creation, so re-draw
+    // everything.
+    full_damage = true;
   }
 
   bool result = true;
   [ns_gl_context_ makeCurrentContext];
   result &= iosurface_->SetIOSurface(io_surface_id, frame_pixel_size);
-  result &= iosurface_->DrawIOSurface();
+  if (full_damage)
+    result &= iosurface_->DrawIOSurface();
+  else
+    result &= iosurface_->DrawIOSurfaceWithDamageRect(pixel_damage_rect);
   glFlush();
   [NSOpenGLContext clearCurrentContext];
   return result;
diff --git a/ui/accelerated_widget_mac/io_surface_texture.h b/ui/accelerated_widget_mac/io_surface_texture.h
index 473e69cb..2cdd235 100644
--- a/ui/accelerated_widget_mac/io_surface_texture.h
+++ b/ui/accelerated_widget_mac/io_surface_texture.h
@@ -40,7 +40,6 @@
 class IOSurfaceTexture
     : public base::RefCounted<IOSurfaceTexture> {
  public:
-  // Returns NULL if IOSurfaceTexture or GL API calls fail.
   static scoped_refptr<IOSurfaceTexture> Create(
       bool needs_gl_finish_workaround);
 
@@ -54,6 +53,7 @@
   // larger than the IOSurface, the remaining right and bottom edges will be
   // white. |window_scale_factor| is 1 in normal views, 2 in HiDPI views.
   bool DrawIOSurface() WARN_UNUSED_RESULT;
+  bool DrawIOSurfaceWithDamageRect(gfx::Rect damage_rect) WARN_UNUSED_RESULT;
 
   // Returns true if the offscreen context used by this surface has been
   // poisoned.
@@ -67,6 +67,10 @@
       bool needs_gl_finish_workaround);
   ~IOSurfaceTexture();
 
+  // Draw the sepecified rect of the IOSurface. If |draw_boundary| is true,
+  // clear any overflow regions with white.
+  bool DrawIOSurfaceInternal(gfx::Rect damage_rect, bool draw_boundary);
+
   // Unref the IOSurfaceTexture and delete the associated GL texture. If the GPU
   // process is no longer referencing it, this will delete the IOSurface.
   void ReleaseIOSurfaceAndTexture();
diff --git a/ui/accelerated_widget_mac/io_surface_texture.mm b/ui/accelerated_widget_mac/io_surface_texture.mm
index 291fbdd..d3898a4 100644
--- a/ui/accelerated_widget_mac/io_surface_texture.mm
+++ b/ui/accelerated_widget_mac/io_surface_texture.mm
@@ -63,7 +63,16 @@
 }
 
 bool IOSurfaceTexture::DrawIOSurface() {
-  TRACE_EVENT0("browser", "IOSurfaceTexture::DrawIOSurface");
+  return DrawIOSurfaceInternal(gfx::Rect(pixel_size_), true);
+}
+
+bool IOSurfaceTexture::DrawIOSurfaceWithDamageRect(gfx::Rect damage_rect) {
+  return DrawIOSurfaceInternal(damage_rect, false);
+}
+
+bool IOSurfaceTexture::DrawIOSurfaceInternal(
+    gfx::Rect damage_rect, bool draw_boundary) {
+  TRACE_EVENT0("browser", "IOSurfaceTexture::DrawIOSurfaceInternal");
   DCHECK(CGLGetCurrentContext());
 
   // If we have release the IOSurface, clear the screen to light grey and
@@ -94,14 +103,14 @@
   glEnable(GL_TEXTURE_RECTANGLE_ARB);
   glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture_);
   glBegin(GL_QUADS);
-  glTexCoord2f(0, 0);
-  glVertex2f(0, 0);
-  glTexCoord2f(pixel_size_.width(), 0);
-  glVertex2f(pixel_size_.width(), 0);
-  glTexCoord2f(pixel_size_.width(), pixel_size_.height());
-  glVertex2f(pixel_size_.width(), pixel_size_.height());
-  glTexCoord2f(0, pixel_size_.height());
-  glVertex2f(0, pixel_size_.height());
+  glTexCoord2f(damage_rect.x(), damage_rect.y());
+  glVertex2f(damage_rect.x(), damage_rect.y());
+  glTexCoord2f(damage_rect.right(), damage_rect.y());
+  glVertex2f(damage_rect.right(), damage_rect.y());
+  glTexCoord2f(damage_rect.right(), damage_rect.bottom());
+  glVertex2f(damage_rect.right(), damage_rect.bottom());
+  glTexCoord2f(damage_rect.x(), damage_rect.bottom());
+  glVertex2f(damage_rect.x(), damage_rect.bottom());
   glEnd();
   glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
   glDisable(GL_TEXTURE_RECTANGLE_ARB);
@@ -112,6 +121,28 @@
   glBegin(GL_TRIANGLES);
   glEnd();
 
+  // If the viewport is larger than the texture, clear out the overflow to
+  // white.
+  if (draw_boundary) {
+    if (pixel_size_.width() < viewport_rect.width()) {
+      glBegin(GL_QUADS);
+      glVertex2f(pixel_size_.width(), 0);
+      glVertex2f(pixel_size_.width(), viewport_rect.height());
+      glVertex2f(viewport_rect.width(), viewport_rect.height());
+      glVertex2f(viewport_rect.width(), 0);
+      glEnd();
+    }
+    if (pixel_size_.height() < viewport_rect.height()) {
+      int non_surface_height = viewport_rect.height() - pixel_size_.height();
+      glBegin(GL_QUADS);
+      glVertex2f(0, 0);
+      glVertex2f(0, non_surface_height);
+      glVertex2f(pixel_size_.width(), non_surface_height);
+      glVertex2f(pixel_size_.width(), 0);
+      glEnd();
+    }
+  }
+
   if (needs_gl_finish_workaround_) {
     TRACE_EVENT0("gpu", "glFinish");
     glFinish();