From 53b93dfe87fd10cced5d2a2a63072dfc7a2af6e4 Mon Sep 17 00:00:00 2001
From: Daniel Charles <daniel.charles@intel.com>
Date: Fri, 28 Jul 2017 16:31:47 -0700
Subject: [PATCH] Enable VAVDA, VAVEA and VAJDA on linux with VAAPI only

This patch contains all the changes necessary to use VA-API along with
vaapi-driver to run all media use cases supported with hardware acceleration.

It is intended to remain as experimental accessible from chrome://flags on linux.
It requires libva/intel-vaapi-driver to be installed on the system path where
chrome is executed.  Other drivers could be tested if available. Flags are
kept independent for linux, where this feature has to be enabled before
actually using it.  This should not change how other OSes use the flags
already, the new flags will show at the buttom on the section of unavailable
experiments

The changes cover a range of compiler pre-processor flags to enable the stack.
It moves the presandbox operations to the vaapi_wrapper class as the hook function
is available there. vaInit will open driver on the correct installed folder.

chrome flags consolidtation into only two flags for linux.  Mjpeg and accelerated
video are used.  The other flags are kept for ChromeOS and other OSes.

Developer testing was made on skylake hardware, ChromeOS and Ubuntu.

BUG=NONE
TEST="subjective testing with VAVDA,VAVEA and VAJDA, autotest for encoder"
TEST="and decoder hardware accelerated"
TEST="have libva/intel-vaapi-driver installed and not installed in the system"
TEST="repeat on different hardware families"
R=posciak@chromium.org
R=kcwu@chromium.org

Cq-Include-Trybots: master.tryserver.chromium.android:android_optional_gpu_tests_rel;master.tryserver.chromium.linux:linux_optional_gpu_tests_rel;master.tryserver.chromium.mac:mac_optional_gpu_tests_rel;master.tryserver.chromium.win:win_optional_gpu_tests_rel
Change-Id: Ifbbf5c9e5221a8b5733fc6d4d0cf984a1f103171
Signed-off-by: Daniel Charles <daniel.charles@intel.com>
---
Index: beta/ui/gfx/BUILD.gn
===================================================================
--- beta.orig/ui/gfx/BUILD.gn
+++ beta/ui/gfx/BUILD.gn
@@ -584,7 +584,7 @@
     "//ui/gfx/geometry",
   ]

-  if (is_linux && !use_qt) {
+  if (is_linux) {
     sources += [
       "linux/client_native_pixmap_dmabuf.cc",
       "linux/client_native_pixmap_dmabuf.h",
Index: beta/media/gpu/vaapi/BUILD.gn
===================================================================
--- beta.orig/media/gpu/vaapi/BUILD.gn
+++ beta/media/gpu/vaapi/BUILD.gn
@@ -117,12 +117,12 @@
     ]
   }
 
-  if (ozone_platform_gbm || use_egl) {
+  if (ozone_platform_gbm || use_egl || use_qt) {
     sources += [
       "vaapi_picture_native_pixmap.cc",
       "vaapi_picture_native_pixmap.h",
     ]
-    if (ozone_platform_gbm) {
+    if (ozone_platform_gbm || use_qt) {
       sources += [
         "vaapi_picture_native_pixmap_ozone.cc",
         "vaapi_picture_native_pixmap_ozone.h",
@@ -162,7 +162,7 @@
     "//ui/gfx:memory_buffer",
     "//ui/gl",
   ]
-  if (ozone_platform_gbm) {
+  if (ozone_platform_gbm || use_qt) {
     deps += [ "//ui/ozone" ]
   }
   
Index: beta/content/gpu/BUILD.gn
===================================================================
--- beta.orig/content/gpu/BUILD.gn
+++ beta/content/gpu/BUILD.gn
@@ -6,6 +6,7 @@ import("//build/config/jumbo.gni")
 import("//build/config/ui.gni")
 import("//gpu/vulkan/features.gni")
 import("//media/media_options.gni")
+import("//media/gpu/args.gni")
 import("//ui/ozone/ozone.gni")
 
 # See //content/BUILD.gn for how this works.
@@ -133,4 +134,8 @@ target(link_target_type, "gpu_sources")
       (!is_chromecast || is_cast_desktop_build)) {
     configs += [ "//build/config/linux/dri" ]
   }
+
+  if (is_desktop_linux && use_vaapi) {
+    public_configs = [ "//build/config/linux/libva" ]
+  }
 }
Index: beta/gpu/config/software_rendering_list.json
===================================================================
--- beta.orig/gpu/config/software_rendering_list.json
+++ beta/gpu/config/software_rendering_list.json
@@ -336,22 +336,6 @@
       ]
     },
     {
-      "id": 48,
-      "description": "Accelerated video decode is unavailable on Linux",
-      "cr_bugs": [137247, 1032907],
-      "os": {
-        "type": "linux"
-      },
-      "exceptions": [
-        {
-          "machine_model_name": ["Chromecast"]
-        }
-      ],
-      "features": [
-        "accelerated_video_decode"
-      ]
-    },
-    {
       "id": 50,
       "description": "Disable VMware software renderer on older Mesa",
       "cr_bugs": [145531, 332596, 571899, 629434],
Index: beta/media/base/media_switches.cc
===================================================================
--- beta.orig/media/base/media_switches.cc
+++ beta/media/base/media_switches.cc
@@ -718,7 +718,7 @@ bool IsVideoCaptureAcceleratedJpegDecodi
           switches::kUseFakeMjpegDecodeAccelerator)) {
     return true;
   }
-#if defined(OS_CHROMEOS)
+#if defined(OS_LINUX) && !defined(OS_ANDROID)
   return true;
 #endif
   return false;
Index: beta/media/gpu/BUILD.gn
===================================================================
--- beta.orig/media/gpu/BUILD.gn
+++ beta/media/gpu/BUILD.gn
@@ -460,6 +460,7 @@ if (use_v4l2_codec || use_vaapi || is_ma
     if (use_ozone) {
       deps += [ "//ui/ozone" ]
     }
+    public_configs = [ "//build/config/linux/libva" ]
   }
 }
 
Index: beta/media/gpu/gpu_video_decode_accelerator_factory.cc
===================================================================
--- beta.orig/media/gpu/gpu_video_decode_accelerator_factory.cc
+++ beta/media/gpu/gpu_video_decode_accelerator_factory.cc
@@ -186,6 +186,8 @@ GpuVideoDecodeAcceleratorFactory::Create
     vda = (this->*create_vda_function)(workarounds, gpu_preferences, media_log);
     if (vda && vda->Initialize(config, client))
       return vda;
+    else
+      LOG(ERROR) << "Initialization of one or more VDAs failed.";
   }
 
   return nullptr;
@@ -244,6 +246,7 @@ GpuVideoDecodeAcceleratorFactory::Create
     const gpu::GpuDriverBugWorkarounds& workarounds,
     const gpu::GpuPreferences& gpu_preferences,
     MediaLog* media_log) const {
+  LOG(WARNING) << "Initializing VAAPI VDA.";
   std::unique_ptr<VideoDecodeAccelerator> decoder;
   decoder.reset(new VaapiVideoDecodeAccelerator(make_context_current_cb_,
                                                 bind_image_cb_));
Index: beta/media/gpu/ipc/service/gpu_video_decode_accelerator.cc
===================================================================
--- beta.orig/media/gpu/ipc/service/gpu_video_decode_accelerator.cc
+++ beta/media/gpu/ipc/service/gpu_video_decode_accelerator.cc
@@ -381,6 +381,7 @@ bool GpuVideoDecodeAccelerator::Initiali
     LOG(ERROR) << "Failed creating the VDA factory";
     return false;
   }
+  LOG(WARNING) << "Created the VDA factory";
 
   const gpu::GpuDriverBugWorkarounds& gpu_workarounds =
       stub_->channel()->gpu_channel_manager()->gpu_driver_bug_workarounds();
@@ -401,6 +402,7 @@ bool GpuVideoDecodeAccelerator::Initiali
                << (config.is_encrypted() ? " with encryption" : "");
     return false;
   }
+  LOG(WARNING) << "Created VDA";
 
   // Attempt to set up performing decoding tasks on IO thread, if supported by
   // the VDA.
Index: beta/media/gpu/vaapi/vaapi_wrapper.cc
===================================================================
--- beta.orig/media/gpu/vaapi/vaapi_wrapper.cc
+++ beta/media/gpu/vaapi/vaapi_wrapper.cc
@@ -401,8 +401,18 @@
   base::File drm_file = base::File(
       base::FilePath::FromUTF8Unsafe(kDriRenderNode0Path),
       base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_WRITE);
-  if (drm_file.IsValid())
+  if (drm_file.IsValid()) {
     VADisplayState::Get()->SetDrmFd(drm_file.GetPlatformFile());
+    return;
+  }
+
+  const char kNvidiaPath[] = "/dev/dri/nvidiactl";
+  base::File nvidia_file = base::File(
+      base::FilePath::FromUTF8Unsafe(kNvidiaPath),
+      base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_WRITE);
+  if(nvidia_file.IsValid()) {
+    VADisplayState::Get()->SetDrmFd(nvidia_file.GetPlatformFile());
+  }
 }
 
 VADisplayState::VADisplayState()
@@ -430,10 +440,6 @@
 }
 
 bool VADisplayState::InitializeOnce() {
-  static_assert(
-      VA_MAJOR_VERSION >= 2 || (VA_MAJOR_VERSION == 1 && VA_MINOR_VERSION >= 1),
-      "Requires VA-API >= 1.1.0");
-
   switch (gl::GetGLImplementation()) {
     case gl::kGLImplementationEGLGLES2:
       va_display_ = vaGetDisplayDRM(drm_fd_.get());
@@ -441,10 +447,10 @@
     case gl::kGLImplementationDesktopGL:
 #if defined(USE_X11)
       va_display_ = vaGetDisplay(gfx::GetXDisplay());
-#else
-      LOG(WARNING) << "VAAPI video acceleration not available without "
-                      "DesktopGL (GLX).";
+      if (vaDisplayIsValid(va_display_))
+        break;
 #endif  // USE_X11
+      va_display_ = vaGetDisplayDRM(drm_fd_.get());
       break;
     // Cannot infer platform from GL, try all available displays
     case gl::kGLImplementationNone:
@@ -477,8 +483,19 @@
   int major_version, minor_version;
   VAStatus va_res = vaInitialize(va_display_, &major_version, &minor_version);
   if (va_res != VA_STATUS_SUCCESS) {
-    LOG(ERROR) << "vaInitialize failed: " << vaErrorStr(va_res);
-    return false;
+    LOG(ERROR) << "vaInitialize failed (ignore if using Wayland desktop environment): " << vaErrorStr(va_res);
+    va_display_ = vaGetDisplayDRM(drm_fd_.get());
+    if (!vaDisplayIsValid(va_display_)) {
+      LOG(ERROR) << "Could not get a valid DRM VA display";
+      return false;
+    }
+    va_res = vaInitialize(va_display_, &major_version, &minor_version);
+    if (va_res != VA_STATUS_SUCCESS) {
+      LOG(ERROR) << "vaInitialize failed using DRM: " << vaErrorStr(va_res);
+      return false;
+    } else {
+      LOG(WARNING) << "vaInitialize succeeded for DRM";
+    }
   }
 
   va_initialized_ = true;
@@ -486,7 +503,7 @@
   va_vendor_string_ = vaQueryVendorString(va_display_);
   DLOG_IF(WARNING, va_vendor_string_.empty())
       << "Vendor string empty or error reading.";
-  DVLOG(1) << "VAAPI version: " << major_version << "." << minor_version << " "
+  VLOG(1) << "VAAPI version: " << major_version << "." << minor_version << " "
            << va_vendor_string_;
 
   // The VAAPI version is determined from what is loaded on the system by
@@ -861,7 +878,7 @@
     if (attribs[i].type != required_attribs[i].type ||
         (attribs[i].value & required_attribs[i].value) !=
             required_attribs[i].value) {
-      DVLOG(1) << "Unsupported value " << required_attribs[i].value
+      VLOG(1) << "Unsupported value " << required_attribs[i].value
                << " for attribute type " << required_attribs[i].type;
       return false;
     }
  Index: beta/media/gpu/test/BUILD.gn
===================================================================
--- beta.orig/media/gpu/test/BUILD.gn
+++ beta/media/gpu/test/BUILD.gn
@@ -179,7 +179,7 @@ if (use_vaapi || use_v4l2_codec) {
   }
 }
 
-if (is_chromeos) {
+if (is_chromeos || use_vaapi) {
   static_library("local_gpu_memory_buffer_manager") {
     testonly = true
     sources = [
  Index: beta/media/gpu/vaapi/vaapi_video_decode_accelerator.cc
===================================================================
--- beta.orig/media/gpu/vaapi/vaapi_video_decode_accelerator.cc
+++ beta/media/gpu/vaapi/vaapi_video_decode_accelerator.cc
@@ -663,6 +663,7 @@ void VaapiVideoDecodeAccelerator::Assign
   // |vpp_vaapi_wrapper_| for VaapiPicture to DownloadFromSurface() the VA's
   // internal decoded frame.
   if (buffer_allocation_mode_ != BufferAllocationMode::kNone &&
+      buffer_allocation_mode_ != BufferAllocationMode::kWrapVdpau &&
       !vpp_vaapi_wrapper_) {
     vpp_vaapi_wrapper_ = VaapiWrapper::Create(
         VaapiWrapper::kVideoProcess, VAProfileNone,
@@ -694,7 +695,8 @@ void VaapiVideoDecodeAccelerator::Assign
             : gfx::Size();
 
     std::unique_ptr<VaapiPicture> picture = vaapi_picture_factory_->Create(
-        (buffer_allocation_mode_ == BufferAllocationMode::kNone)
+        ((buffer_allocation_mode_ == BufferAllocationMode::kNone) ||
+         (buffer_allocation_mode_ == BufferAllocationMode::kWrapVdpau))
             ? vaapi_wrapper_
             : vpp_vaapi_wrapper_,
         make_context_current_cb_, bind_image_cb_, buffer, size_to_bind);
@@ -1150,6 +1152,12 @@ VaapiVideoDecodeAccelerator::GetSupporte
 
 VaapiVideoDecodeAccelerator::BufferAllocationMode
 VaapiVideoDecodeAccelerator::DecideBufferAllocationMode() {
+  // NVIDIA blobs use VDPAU
+  if (VaapiWrapper::GetImplementationType() == VAImplementation::kNVIDIAVDPAU) {
+    LOG(INFO) << "VA-API driver on VDPAU backend";
+    return BufferAllocationMode::kWrapVdpau;
+  }
+
   // TODO(crbug.com/912295): Enable a better BufferAllocationMode for IMPORT
   // |output_mode_| as well.
   if (output_mode_ == VideoDecodeAccelerator::Config::OutputMode::IMPORT)
@@ -1162,7 +1170,7 @@ VaapiVideoDecodeAccelerator::DecideBuffe
   // depends on the bitstream and sometimes it's not enough to cover the amount
   // of frames needed by the client pipeline (see b/133733739).
   // TODO(crbug.com/911754): Enable for VP9 Profile 2.
-  if (IsGeminiLakeOrLater() &&
+  if (false && IsGeminiLakeOrLater() &&
       (profile_ == VP9PROFILE_PROFILE0 || profile_ == VP8PROFILE_ANY)) {
     // Add one to the reference frames for the one being currently egressed, and
     // an extra allocation for both |client_| and |decoder_|, see
Index: beta/media/gpu/vaapi/vaapi_video_decode_accelerator.h
===================================================================
--- beta.orig/media/gpu/vaapi/vaapi_video_decode_accelerator.h
+++ beta/media/gpu/vaapi/vaapi_video_decode_accelerator.h
@@ -210,6 +210,7 @@ class MEDIA_GPU_EXPORT VaapiVideoDecodeA
     // Using |client_|s provided PictureBuffers and as many internally
     // allocated.
     kNormal,
+    kWrapVdpau,
   };
 
   // Decides the concrete buffer allocation mode, depending on the hardware
Index: beta/media/gpu/vaapi/vaapi_wrapper.cc
===================================================================
--- beta.orig/media/gpu/vaapi/vaapi_wrapper.cc
+++ beta/media/gpu/vaapi/vaapi_wrapper.cc
@@ -131,6 +131,9 @@ media::VAImplementation VendorStringToIm
   } else if (base::StartsWith(va_vendor_string, "Intel iHD driver",
                               base::CompareCase::SENSITIVE)) {
     return media::VAImplementation::kIntelIHD;
+  } else if (base::StartsWith(va_vendor_string, "Splitted-Desktop Systems VDPAU",
+                              base::CompareCase::SENSITIVE)) {
+    return media::VAImplementation::kNVIDIAVDPAU;
   }
   return media::VAImplementation::kOther;
 }
@@ -1637,6 +1640,11 @@ VaapiWrapper::ExportVASurfaceAsNativePix
     return nullptr;
   }
 
+  if (GetImplementationType() == VAImplementation::kNVIDIAVDPAU) {
+    LOG(ERROR) << "Disabled due to potential breakage.";
+    return nullptr;
+  }
+
   VADRMPRIMESurfaceDescriptor descriptor;
   {
     base::AutoLock auto_lock(*va_lock_);
@@ -2283,32 +2291,43 @@ bool VaapiWrapper::CreateSurfaces(unsign
   DCHECK(va_surfaces->empty());
 
   va_surfaces->resize(num_surfaces);
+
   VASurfaceAttrib attribute{};
-  attribute.type = VASurfaceAttribUsageHint;
-  attribute.flags = VA_SURFACE_ATTRIB_SETTABLE;
-  attribute.value.type = VAGenericValueTypeInteger;
-  switch (usage_hint) {
-    case SurfaceUsageHint::kVideoDecoder:
-      attribute.value.value.i = VA_SURFACE_ATTRIB_USAGE_HINT_DECODER;
-      break;
-    case SurfaceUsageHint::kVideoEncoder:
-      attribute.value.value.i = VA_SURFACE_ATTRIB_USAGE_HINT_ENCODER;
-      break;
-    case SurfaceUsageHint::kVideoProcessWrite:
-      attribute.value.value.i = VA_SURFACE_ATTRIB_USAGE_HINT_VPP_WRITE;
-      break;
-    case SurfaceUsageHint::kGeneric:
-      attribute.value.value.i = VA_SURFACE_ATTRIB_USAGE_HINT_GENERIC;
-      break;
+  if (GetImplementationType() != VAImplementation::kNVIDIAVDPAU) {
+    // Nvidia's VAAPI-VDPAU driver doesn't support this attribute
+    attribute.type = VASurfaceAttribUsageHint;
+    attribute.flags = VA_SURFACE_ATTRIB_SETTABLE;
+    attribute.value.type = VAGenericValueTypeInteger;
+    switch (usage_hint) {
+      case SurfaceUsageHint::kVideoDecoder:
+        attribute.value.value.i = VA_SURFACE_ATTRIB_USAGE_HINT_DECODER;
+        break;
+      case SurfaceUsageHint::kVideoEncoder:
+        attribute.value.value.i = VA_SURFACE_ATTRIB_USAGE_HINT_ENCODER;
+        break;
+      case SurfaceUsageHint::kVideoProcessWrite:
+        attribute.value.value.i = VA_SURFACE_ATTRIB_USAGE_HINT_VPP_WRITE;
+        break;
+      case SurfaceUsageHint::kGeneric:
+        attribute.value.value.i = VA_SURFACE_ATTRIB_USAGE_HINT_GENERIC;
+        break;
+    }
   }
 
   VAStatus va_res;
   {
     base::AutoLock auto_lock(*va_lock_);
-    va_res = vaCreateSurfaces(
-        va_display_, va_format, base::checked_cast<unsigned int>(size.width()),
-        base::checked_cast<unsigned int>(size.height()), va_surfaces->data(),
-        num_surfaces, &attribute, 1u);
+    if (GetImplementationType() == VAImplementation::kNVIDIAVDPAU) {
+      va_res = vaCreateSurfaces(
+          va_display_, va_format, base::checked_cast<unsigned int>(size.width()),
+          base::checked_cast<unsigned int>(size.height()), va_surfaces->data(),
+          num_surfaces, NULL, 0);
+    } else {
+      va_res = vaCreateSurfaces(
+          va_display_, va_format, base::checked_cast<unsigned int>(size.width()),
+          base::checked_cast<unsigned int>(size.height()), va_surfaces->data(),
+          num_surfaces, &attribute, 1u);
+    }
   }
   VA_LOG_ON_ERROR(va_res, "vaCreateSurfaces (allocate mode)");
   return va_res == VA_STATUS_SUCCESS;
Index: beta/media/gpu/vaapi/vaapi_wrapper.h
===================================================================
--- beta.orig/media/gpu/vaapi/vaapi_wrapper.h
+++ beta/media/gpu/vaapi/vaapi_wrapper.h
@@ -80,6 +80,7 @@ enum class VAImplementation {
   kIntelIHD,
   kOther,
   kInvalid,
+  kNVIDIAVDPAU,
 };
 
 // This class handles VA-API calls and ensures proper locking of VA-API calls
