Ozone support for multiprocess graphics.

VgemPixmap allows scanout of gem handle imported by file descriptor.

Change-Id: I233b3992f2289754c904da94f98d4707cd227d0d

BUG=
R=cstout@chromium.org

Review URL: https://codereview.chromium.org/1582613004 .
diff --git a/ui/gfx/BUILD.gn b/ui/gfx/BUILD.gn
index 4044d81..922e4bd 100644
--- a/ui/gfx/BUILD.gn
+++ b/ui/gfx/BUILD.gn
@@ -127,6 +127,10 @@
     ]
   }
 
+  if (use_ozone) {
+    sources += [ "native_pixmap_handle_ozone.h" ]
+  }
+
   if (use_x11) {
     deps += [ "//ui/gfx/x" ]
   }
diff --git a/ui/gfx/native_pixmap_handle_ozone.h b/ui/gfx/native_pixmap_handle_ozone.h
new file mode 100644
index 0000000..14d5560
--- /dev/null
+++ b/ui/gfx/native_pixmap_handle_ozone.h
@@ -0,0 +1,22 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_NATIVE_PIXMAP_HANDLE_OZONE_H_
+#define UI_GFX_NATIVE_PIXMAP_HANDLE_OZONE_H_
+
+#include "base/file_descriptor_posix.h"
+
+namespace gfx {
+
+struct NativePixmapHandle {
+  // A file descriptor for the underlying memory object (usually dmabuf).
+  base::FileDescriptor fd;
+
+  // The stride to used when accessing the buffer via a memory mapping.
+  int32_t stride = 0;
+};
+
+}  // namespace gfx
+
+#endif  // UI_GFX_NATIVE_PIXMAP_HANDLE_OZONE_H_
diff --git a/ui/ozone/platform/drm/BUILD.gn b/ui/ozone/platform/drm/BUILD.gn
index 7333ad2..7c93434 100644
--- a/ui/ozone/platform/drm/BUILD.gn
+++ b/ui/ozone/platform/drm/BUILD.gn
@@ -116,13 +116,13 @@
 
   deps = [
     "//base",
-    "//skia",
     "//mojo/application",
     "//mojo/common",
     "//mojo/converters/geometry",
     "//mojo/converters/ozone_drm_gpu",
     "//mojo/services/ozone_drm_gpu/interfaces",
     "//mojo/services/ozone_drm_host/interfaces",
+    "//skia",
     "//ui/base",
     "//ui/display/types",
     "//ui/display/util",
@@ -154,10 +154,10 @@
       "//base",
       "//skia",
       "//ui/base",
-      "//ui/ozone:ozone_base",
       "//ui/events/ozone:events_ozone",
       "//ui/events/ozone:events_ozone_evdev",
       "//ui/events/ozone:events_ozone_layout",
+      "//ui/ozone:ozone_base",
     ]
   }
 }
@@ -196,6 +196,8 @@
 
   source_set("gbm") {
     sources = [
+      "gpu/drm_dmabuf_pixmap.cc",
+      "gpu/drm_dmabuf_pixmap.h",
       "gpu/gbm_buffer.cc",
       "gpu/gbm_buffer.h",
       "gpu/gbm_buffer_base.cc",
diff --git a/ui/ozone/platform/drm/gpu/drm_device.cc b/ui/ozone/platform/drm/gpu/drm_device.cc
index 1d80b68..7ec92a4 100644
--- a/ui/ozone/platform/drm/gpu/drm_device.cc
+++ b/ui/ozone/platform/drm/gpu/drm_device.cc
@@ -5,6 +5,7 @@
 #include "ui/ozone/platform/drm/gpu/drm_device.h"
 
 #include <fcntl.h>
+#include <i915_drm.h>
 #include <sys/mman.h>
 #include <unistd.h>
 #include <xf86drm.h>
@@ -615,4 +616,42 @@
                               &g[0], &b[0]) == 0);
 }
 
+bool DrmDevice::BufferHandleToFd(uint32_t handle, int* fd) {
+  int ret;
+
+  ret = drmPrimeHandleToFD(file_.GetPlatformFile(), handle, DRM_CLOEXEC, fd);
+  if (ret == -1)
+    return false;
+
+  return true;
+}
+
+bool DrmDevice::BufferFdToHandle(int fd, uint32_t* handle) {
+  int ret;
+
+  ret = drmPrimeFDToHandle(file_.GetPlatformFile(), fd, handle);
+  if (ret == -1)
+    return false;
+
+  return true;
+}
+
+bool DrmDevice::BufferHandleSetTiling(uint32_t handle,
+                                      uint32_t stride,
+                                      uint32_t tiling_mode) {
+  struct drm_i915_gem_set_tiling set_tiling;
+  int ret;
+
+  do {
+    set_tiling.handle = handle;
+    set_tiling.tiling_mode = tiling_mode;
+    set_tiling.stride = stride;
+
+    ret = drmIoctl(file_.GetPlatformFile(), DRM_IOCTL_I915_GEM_SET_TILING,
+                   &set_tiling);
+  } while (ret == -1 && (errno == EINTR || errno == EAGAIN));
+
+  return ret != -1;
+}
+
 }  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/drm_device.h b/ui/ozone/platform/drm/gpu/drm_device.h
index 012dde7..21d98d2 100644
--- a/ui/ozone/platform/drm/gpu/drm_device.h
+++ b/ui/ozone/platform/drm/gpu/drm_device.h
@@ -178,6 +178,12 @@
 
   HardwareDisplayPlaneManager* plane_manager() { return plane_manager_.get(); }
 
+  bool BufferHandleToFd(uint32_t handle, int* fd);
+  bool BufferFdToHandle(int fd, uint32_t* handle);
+  bool BufferHandleSetTiling(uint32_t handle,
+                             uint32_t stride,
+                             uint32_t tiling_mode);
+
  protected:
   friend class base::RefCountedThreadSafe<DrmDevice>;
 
diff --git a/ui/ozone/platform/drm/gpu/drm_dmabuf_pixmap.cc b/ui/ozone/platform/drm/gpu/drm_dmabuf_pixmap.cc
new file mode 100644
index 0000000..338e75a
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/drm_dmabuf_pixmap.cc
@@ -0,0 +1,110 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/drm/gpu/drm_dmabuf_pixmap.h"
+
+#include <i915_drm.h>
+#include "base/logging.h"
+#include "ui/ozone/platform/drm/gpu/drm_device.h"
+#include "ui/ozone/platform/drm/gpu/drm_window.h"
+
+namespace ui {
+
+namespace {
+void EmptyPageFlipCallback(gfx::SwapResult result) {}
+}  // namespace
+
+DrmDmabufPixmap::DrmDmabufPixmap(const scoped_refptr<DrmDevice>& drm)
+    : drm_(drm) {}
+
+DrmDmabufPixmap::~DrmDmabufPixmap() {
+  // TODO: finish
+  if (framebuffer_ && !drm_->RemoveFramebuffer(framebuffer_))
+    PLOG(ERROR) << "SharedBuffer: RemoveFramebuffer: fb " << framebuffer_;
+}
+
+bool DrmDmabufPixmap::Initialize(base::ScopedFD dma_buf,
+                                 int width,
+                                 int height,
+                                 uint32_t pitch) {
+  int fd = dma_buf.get();
+  if (!drm_->BufferFdToHandle(fd, &handle_)) {
+    PLOG(ERROR) << "SharedBuffer: Initialize: couldn't get handle from fd "
+                << fd;
+    return false;
+  }
+
+  // For scanout
+  if (!drm_->BufferHandleSetTiling(handle_, pitch, I915_TILING_X))
+    return false;
+
+  dma_buf_ = dma_buf.Pass();
+  dma_buf_pitch_ = pitch;
+
+  width_ = width;
+  height_ = height;
+
+  if (!drm_->AddFramebuffer(width_, height_, 24, 32, dma_buf_pitch_, handle_,
+                            &framebuffer_)) {
+    PLOG(ERROR) << "SharedBuffer: AddFramebuffer: handle " << handle_;
+    return false;
+  }
+
+  return true;
+}
+
+uint32_t DrmDmabufPixmap::GetHandle() const {
+  return handle_;
+}
+
+gfx::Size DrmDmabufPixmap::GetSize() const {
+  return gfx::Size(width_, height_);
+}
+
+uint32_t DrmDmabufPixmap::GetFramebufferId() const {
+  return framebuffer_;
+}
+
+int DrmDmabufPixmap::GetDmaBufFd() const {
+  return dma_buf_.get();
+}
+
+int DrmDmabufPixmap::GetDmaBufPitch() const {
+  return dma_buf_pitch_;
+}
+
+DrmDmabufPixmapWrapper::DrmDmabufPixmapWrapper(
+    ScreenManager* screen_manager,
+    const scoped_refptr<DrmDmabufPixmap>& pixmap)
+    : pixmap_(pixmap), screen_manager_(screen_manager) {}
+
+DrmDmabufPixmapWrapper::~DrmDmabufPixmapWrapper() {}
+
+bool DrmDmabufPixmapWrapper::ScheduleOverlayPlane(
+    gfx::AcceleratedWidget widget,
+    int plane_z_order,
+    gfx::OverlayTransform plane_transform,
+    const gfx::Rect& display_bounds,
+    const gfx::RectF& crop_rect) {
+  screen_manager_->GetWindow(widget)->QueueOverlayPlane(OverlayPlane(pixmap_));
+  // TODO(cstout): for the multi-overlay case, SchedulePageFlip should be called
+  // once after all overlay planes are scheduled.  Tracking issue mojo #540.
+  screen_manager_->GetWindow(widget)->SchedulePageFlip(
+      true /* is_sync */, base::Bind(&EmptyPageFlipCallback));
+  return true;
+}
+
+int DrmDmabufPixmapWrapper::GetDmaBufFd() {
+  return pixmap_->GetDmaBufFd();
+}
+
+int DrmDmabufPixmapWrapper::GetDmaBufPitch() {
+  return pixmap_->GetDmaBufPitch();
+}
+
+void* DrmDmabufPixmapWrapper::GetEGLClientBuffer() {
+  return nullptr;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/drm_dmabuf_pixmap.h b/ui/ozone/platform/drm/gpu/drm_dmabuf_pixmap.h
new file mode 100644
index 0000000..c3d29de
--- /dev/null
+++ b/ui/ozone/platform/drm/gpu/drm_dmabuf_pixmap.h
@@ -0,0 +1,79 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_DRM_GPU_DRM_DMABUF_PIXMAP_H_
+#define UI_OZONE_PLATFORM_DRM_GPU_DRM_DMABUF_PIXMAP_H_
+
+#include "base/files/scoped_file.h"
+#include "base/macros.h"
+#include "ui/ozone/platform/drm/gpu/scanout_buffer.h"
+#include "ui/ozone/platform/drm/gpu/screen_manager.h"
+#include "ui/ozone/public/native_pixmap.h"
+
+namespace ui {
+
+class DrmDevice;
+
+// DrmDmabufPixmap is a reference to a dmabuf file descriptor.
+// 'Pixmap' not 'buffer' because we have a size (width and height).
+class DrmDmabufPixmap : public ScanoutBuffer {
+ public:
+  DrmDmabufPixmap(const scoped_refptr<DrmDevice>& drm);
+  ~DrmDmabufPixmap() override;
+
+  bool Initialize(base::ScopedFD dma_buf,
+                  int width,
+                  int height,
+                  uint32_t pitch);
+
+  // ScanoutBuffer:
+  uint32_t GetFramebufferId() const override;
+  uint32_t GetHandle() const override;
+  gfx::Size GetSize() const override;
+
+  int GetDmaBufFd() const;
+  int GetDmaBufPitch() const;
+
+ private:
+  scoped_refptr<DrmDevice> drm_;
+
+  // PRIME file descriptor.
+  base::ScopedFD dma_buf_;
+  uint32_t dma_buf_pitch_ = -1;
+
+  // Local gem handle for the file descriptior.
+  uint32_t handle_ = 0;
+
+  // Framebuffer ID for scanout.
+  uint32_t framebuffer_ = 0;
+
+  int width_ = 0;
+  int height_ = 0;
+};
+
+class DrmDmabufPixmapWrapper : public NativePixmap {
+ public:
+  DrmDmabufPixmapWrapper(ScreenManager* screen_manager,
+                         const scoped_refptr<DrmDmabufPixmap>& pixmap);
+  ~DrmDmabufPixmapWrapper() override;
+
+  // NativePixmap
+  void* /* EGLClientBuffer */ GetEGLClientBuffer() override;
+  int GetDmaBufFd() override;
+  int GetDmaBufPitch() override;
+
+  bool ScheduleOverlayPlane(gfx::AcceleratedWidget widget,
+                            int plane_z_order,
+                            gfx::OverlayTransform plane_transform,
+                            const gfx::Rect& display_bounds,
+                            const gfx::RectF& crop_rect) override;
+
+ private:
+  scoped_refptr<DrmDmabufPixmap> pixmap_;
+  ScreenManager* screen_manager_;
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRM_GPU_DRM_DMABUF_PIXMAP_H_
diff --git a/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc b/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc
index 93e7793..80a9b1f 100644
--- a/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc
+++ b/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc
@@ -10,6 +10,7 @@
 #include "third_party/khronos/EGL/egl.h"
 #include "ui/ozone/common/egl_util.h"
 #include "ui/ozone/platform/drm/gpu/drm_device_manager.h"
+#include "ui/ozone/platform/drm/gpu/drm_dmabuf_pixmap.h"
 #include "ui/ozone/platform/drm/gpu/drm_window.h"
 #include "ui/ozone/platform/drm/gpu/gbm_buffer.h"
 #include "ui/ozone/platform/drm/gpu/gbm_device.h"
@@ -130,6 +131,22 @@
   return pixmap;
 }
 
+scoped_refptr<ui::NativePixmap> GbmSurfaceFactory::CreateNativePixmapFromHandle(
+    gfx::AcceleratedWidget widget,
+    gfx::Size size,
+    const gfx::NativePixmapHandle& handle) {
+  scoped_refptr<DrmDevice> drm =
+      drm_device_manager_->GetDrmDevice(widget).get();
+  DCHECK(drm);
+
+  scoped_refptr<VgemPixmap> pixmap(new VgemPixmap(drm));
+  pixmap->Initialize(base::ScopedFD(handle.fd.fd), size.width(), size.height(),
+                     handle.stride);
+
+  return scoped_refptr<VgemPixmapWrapper>(
+      new VgemPixmapWrapper(screen_manager_, pixmap));
+}
+
 bool GbmSurfaceFactory::CanShowPrimaryPlaneAsOverlay() {
   DCHECK(thread_checker_.CalledOnValidThread());
   return allow_surfaceless_;
diff --git a/ui/ozone/platform/drm/gpu/gbm_surface_factory.h b/ui/ozone/platform/drm/gpu/gbm_surface_factory.h
index 0bc01b3..b3b6676 100644
--- a/ui/ozone/platform/drm/gpu/gbm_surface_factory.h
+++ b/ui/ozone/platform/drm/gpu/gbm_surface_factory.h
@@ -39,6 +39,10 @@
       gfx::Size size,
       BufferFormat format,
       BufferUsage usage) override;
+  scoped_refptr<ui::NativePixmap> CreateNativePixmapFromHandle(
+      gfx::AcceleratedWidget widget,
+      gfx::Size size,
+      const gfx::NativePixmapHandle& handle) override;
   bool CanShowPrimaryPlaneAsOverlay() override;
   bool CanCreateNativePixmap(BufferUsage usage) override;
 
diff --git a/ui/ozone/public/surface_factory_ozone.cc b/ui/ozone/public/surface_factory_ozone.cc
index 70a81ad..42f44ca 100644
--- a/ui/ozone/public/surface_factory_ozone.cc
+++ b/ui/ozone/public/surface_factory_ozone.cc
@@ -52,7 +52,15 @@
     gfx::Size size,
     BufferFormat format,
     BufferUsage usage) {
-  return NULL;
+  return nullptr;
+}
+
+scoped_refptr<ui::NativePixmap>
+SurfaceFactoryOzone::CreateNativePixmapFromHandle(
+    gfx::AcceleratedWidget widget,
+    gfx::Size size,
+    const gfx::NativePixmapHandle& handle) {
+  return nullptr;
 }
 
 bool SurfaceFactoryOzone::CanShowPrimaryPlaneAsOverlay() {
diff --git a/ui/ozone/public/surface_factory_ozone.h b/ui/ozone/public/surface_factory_ozone.h
index 4dc5572..4a72f3f 100644
--- a/ui/ozone/public/surface_factory_ozone.h
+++ b/ui/ozone/public/surface_factory_ozone.h
@@ -9,9 +9,11 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/native_library.h"
 #include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/native_pixmap_handle_ozone.h"
 #include "ui/gfx/native_widget_types.h"
 #include "ui/gfx/overlay_transform.h"
 #include "ui/ozone/ozone_base_export.h"
+#include "ui/ozone/public/native_pixmap.h"
 
 namespace ui {
 
@@ -121,6 +123,13 @@
       BufferFormat format,
       BufferUsage usage);
 
+  // Create a single native buffer from an existing handle. Takes ownership of
+  // |handle| and can be called on any thread.
+  virtual scoped_refptr<NativePixmap> CreateNativePixmapFromHandle(
+      gfx::AcceleratedWidget widget,
+      gfx::Size size,
+      const gfx::NativePixmapHandle& handle);
+
   // Returns true if overlays can be shown at z-index 0, replacing the main
   // surface. Combined with surfaceless extensions, it allows for an
   // overlay-only mode.