Skip to content
This repository has been archived by the owner on Aug 20, 2020. It is now read-only.

Commit

Permalink
Enable zero buffer copy method for wayland VDA.
Browse files Browse the repository at this point in the history
1080p video playback is quite unsmooth on VTC-1010, with only 15 fps
and ~70% CPU usage. Root cause is current code spend too much time to
put output vasurface to GL texture for more than 25ms for each
frame, due to buffer copy between driver and user space. As a result,
GPU swap buffer operations are not executed timely and then block
render side composting.

This CL enables a zero buffer copy solution that will put vasurface to
GL texture inside GPU process. It creates an EGL image through
EGL_DRM_BUFFER_MESA target, then binds the EGL image to GL texture.

Local test result shows it gets 31 fps and ~25% CPU usage for 1080p playback.

BUG = XWALK-2069
  • Loading branch information
shaochangbin committed Aug 13, 2014
1 parent 9775269 commit 45f6768
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 43 deletions.
3 changes: 2 additions & 1 deletion media/va_wayland.sigs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ VAStatus vaSetDisplayAttributes(VADisplay dpy, VADisplayAttribute *attr_list, in
VAStatus vaSyncSurface(VADisplay dpy, VASurfaceID render_target);
VAStatus vaTerminate(VADisplay dpy);
VAStatus vaUnmapBuffer(VADisplay dpy, VABufferID buf_id);

VAStatus vaLockBuffer(VADisplay dpy, VABufferID buf_id, VABufferInfo *buf_info_ptr);
VAStatus vaUnlockBuffer(VADisplay dpy, VABufferID buf_id, VABufferInfo *buf_info_ptr);

#------------------------------------------------
# Functions from libva-wayland used in chromium code.
Expand Down
123 changes: 100 additions & 23 deletions media/vaapi_video_decode_accelerator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@
#include "content/common/gpu/gpu_channel.h"
#include "media/base/bind_to_current_loop.h"
#include "media/video/picture.h"
#include "ui/gl/scoped_binders.h"
#include "ui/gl/gl_bindings.h"
#include "ui/gl/gl_surface_egl.h"
#include "ui/gl/scoped_binders.h"

static void ReportToUMA(
media::VaapiH264Decoder::VAVDAH264DecoderFailure failure) {
Expand Down Expand Up @@ -88,8 +89,13 @@ class VaapiVideoDecodeAccelerator::TFPPicture : public base::NonThreadSafe {
return size_;
}

// Upload vaimage data to texture. Needs to be called every frame.
bool Upload(VASurfaceID id);
VAImage* va_image() {
return va_image_.get();
}

// Bind EGL image to texture. Needs to be called every frame.
bool Bind();
bool UpdateEGLImage(VASurfaceID id);

private:
TFPPicture(const base::Callback<bool(void)>& make_context_current, //NOLINT
Expand All @@ -100,6 +106,10 @@ class VaapiVideoDecodeAccelerator::TFPPicture : public base::NonThreadSafe {

bool Initialize();

EGLImageKHR CreateEGLImage(
EGLDisplay egl_display, VASurfaceID surface, VAImage* va_image);
bool DestroyEGLImage(EGLDisplay egl_display, EGLImageKHR egl_image);

base::Callback<bool(void)> make_context_current_; //NOLINT

VaapiWrapper* va_wrapper_;
Expand All @@ -109,7 +119,9 @@ class VaapiVideoDecodeAccelerator::TFPPicture : public base::NonThreadSafe {
uint32 texture_id_;

gfx::Size size_;
VAImage va_image_;
scoped_ptr<VAImage> va_image_;
EGLImageKHR egl_image_;
EGLDisplay egl_display_;

DISALLOW_COPY_AND_ASSIGN(TFPPicture);
};
Expand All @@ -124,9 +136,12 @@ VaapiVideoDecodeAccelerator::TFPPicture::TFPPicture(
va_wrapper_(va_wrapper),
picture_buffer_id_(picture_buffer_id),
texture_id_(texture_id),
size_(size) {
size_(size),
va_image_(new VAImage()),
egl_display_(gfx::GLSurfaceEGL::GetHardwareDisplay()) {
DCHECK(!make_context_current_.is_null());
};
DCHECK(va_image_);
}

linked_ptr<VaapiVideoDecodeAccelerator::TFPPicture>
VaapiVideoDecodeAccelerator::TFPPicture::Create(
Expand All @@ -150,7 +165,7 @@ bool VaapiVideoDecodeAccelerator::TFPPicture::Initialize() {
if (!make_context_current_.Run())
return false;

if (!va_wrapper_->CreateRGBImage(size_, &va_image_)) {
if (!va_wrapper_->CreateRGBImage(size_, va_image_.get())) {
DVLOG(1) << "Failed to create VAImage";
return false;
}
Expand All @@ -161,37 +176,86 @@ bool VaapiVideoDecodeAccelerator::TFPPicture::Initialize() {
VaapiVideoDecodeAccelerator::TFPPicture::~TFPPicture() {
DCHECK(CalledOnValidThread());

if (egl_image_ != EGL_NO_IMAGE_KHR)
DestroyEGLImage(egl_display_, egl_image_);

if (va_wrapper_) {
va_wrapper_->DestroyImage(&va_image_);
va_wrapper_->DestroyImage(va_image_.get());
}
}

bool VaapiVideoDecodeAccelerator::TFPPicture::Upload(VASurfaceID surface) {
bool VaapiVideoDecodeAccelerator::TFPPicture::UpdateEGLImage(
VASurfaceID surface) {
DCHECK(CalledOnValidThread());

if (!make_context_current_.Run())
return false;

if (!va_wrapper_->PutSurfaceIntoImage(surface, &va_image_)) {
DVLOG(1) << "Failed to put va surface to image";
if (egl_image_ != EGL_NO_IMAGE_KHR)
DestroyEGLImage(egl_display_, egl_image_);

egl_image_ = CreateEGLImage(egl_display_, surface, va_image_.get());
if (egl_image_ == EGL_NO_IMAGE_KHR) {
DVLOG(1) << "Failed to create EGL image";
return false;
}

void* buffer = NULL;
if (!va_wrapper_->MapImage(&va_image_, &buffer)) {
DVLOG(1) << "Failed to map VAImage";
return true;
}

bool VaapiVideoDecodeAccelerator::TFPPicture::Bind() {
DCHECK(CalledOnValidThread());
if (!make_context_current_.Run())
return false;
}

gfx::ScopedTextureBinder texture_binder(GL_TEXTURE_2D, texture_id_);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size_.width(), size_.height(),
0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, egl_image_);
return true;
}

va_wrapper_->UnmapImage(&va_image_);
EGLImageKHR VaapiVideoDecodeAccelerator::TFPPicture::CreateEGLImage(
EGLDisplay egl_display, VASurfaceID va_surface, VAImage* va_image) {
DCHECK(CalledOnValidThread());
DCHECK(va_image);

return true;
VABufferInfo buffer_info;
if (!va_wrapper_->LockBuffer(va_surface, va_image->buf, &buffer_info)) {
DVLOG(1) << "Failed to lock Buffer";
return EGL_NO_IMAGE_KHR;
}

EGLint attribs[] = {
EGL_WIDTH, 0,
EGL_HEIGHT, 0,
EGL_DRM_BUFFER_STRIDE_MESA, 0,
EGL_DRM_BUFFER_FORMAT_MESA,
EGL_DRM_BUFFER_FORMAT_ARGB32_MESA,
EGL_DRM_BUFFER_USE_MESA,
EGL_DRM_BUFFER_USE_SHARE_MESA,
EGL_NONE };
attribs[1] = va_image->width;
attribs[3] = va_image->height;
attribs[5] = va_image->pitches[0] / 4;

EGLImageKHR egl_image = eglCreateImageKHR(
egl_display,
EGL_NO_CONTEXT,
EGL_DRM_BUFFER_MESA,
(EGLClientBuffer) buffer_info.handle,
attribs);

if (va_wrapper_) {
va_wrapper_->UnlockBuffer(va_surface, va_image->buf, &buffer_info);
}

return egl_image;
}

bool VaapiVideoDecodeAccelerator::TFPPicture::DestroyEGLImage(
EGLDisplay egl_display, EGLImageKHR egl_image) {
return eglDestroyImageKHR(egl_display, egl_image);
}

VaapiVideoDecodeAccelerator::TFPPicture*
Expand Down Expand Up @@ -297,11 +361,24 @@ void VaapiVideoDecodeAccelerator::OutputPicture(
"output_id", output_id);

DVLOG(3) << "Outputting VASurface " << va_surface->id()
<< " into pixmap bound to picture buffer id " << output_id;
<< " into texture bound to picture buffer id " << output_id;

RETURN_AND_NOTIFY_ON_FAILURE(
vaapi_wrapper_->PutSurfaceIntoImage(
va_surface->id(),
tfp_picture->va_image()),
"Failed putting surface into vaimage",
PLATFORM_FAILURE, ); //NOLINT

RETURN_AND_NOTIFY_ON_FAILURE(
tfp_picture->UpdateEGLImage(va_surface->id()),
"Failed to update egl image per vaimage info",
PLATFORM_FAILURE, ); //NOLINT

RETURN_AND_NOTIFY_ON_FAILURE(tfp_picture->Upload(va_surface->id()),
"Failed to upload VASurface to texture",
PLATFORM_FAILURE, ); //NOLINT
RETURN_AND_NOTIFY_ON_FAILURE(
tfp_picture->Bind(),
"Failed to bind egl image to texture",
PLATFORM_FAILURE, ); //NOLINT

// Notify the client a picture is ready to be displayed.
++num_frames_at_client_;
Expand Down
47 changes: 31 additions & 16 deletions media/vaapi_wrapper.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "media/media/va_stubs.h"

#include "third_party/libva/va/wayland/va_wayland.h"
#include "third_party/libva/va/va_drmcommon.h"

using media_media::kModuleVa_wayland;
using media_media::InitializeStubs;
Expand Down Expand Up @@ -370,14 +371,15 @@ bool VaapiWrapper::CreateRGBImage(gfx::Size size, VAImage* image) {
base::AutoLock auto_lock(va_lock_);
VAStatus va_res;
VAImageFormat format;
format.fourcc = VA_FOURCC_RGBX;
format.fourcc = VA_FOURCC_BGRX;
format.byte_order = VA_LSB_FIRST;
format.bits_per_pixel = 32;
format.depth = 24;
format.red_mask = 0xff;
format.green_mask = 0xff00;
format.red_mask = 0x0000ff;
format.green_mask = 0x00ff00;
format.blue_mask = 0xff0000;
format.alpha_mask = 0;

va_res = vaCreateImage(va_display_,
&format,
size.width(),
Expand All @@ -392,19 +394,6 @@ void VaapiWrapper::DestroyImage(VAImage* image) {
vaDestroyImage(va_display_, image->image_id);
}

bool VaapiWrapper::MapImage(VAImage* image, void** buffer) {
base::AutoLock auto_lock(va_lock_);

VAStatus va_res = vaMapBuffer(va_display_, image->buf, buffer);
VA_SUCCESS_OR_RETURN(va_res, "Failed to map image", false);
return true;
}

void VaapiWrapper::UnmapImage(VAImage* image) {
base::AutoLock auto_lock(va_lock_);
vaUnmapBuffer(va_display_, image->buf);
}

bool VaapiWrapper::PutSurfaceIntoImage(VASurfaceID va_surface_id,
VAImage* image) {
base::AutoLock auto_lock(va_lock_);
Expand All @@ -421,6 +410,32 @@ bool VaapiWrapper::PutSurfaceIntoImage(VASurfaceID va_surface_id,
VA_SUCCESS_OR_RETURN(va_res, "Failed to put surface into image", false);
return true;
}

bool VaapiWrapper::LockBuffer(VASurfaceID va_surface_id,
VABufferID buf_id,
VABufferInfo* buf_info) {
DCHECK(buf_info);
base::AutoLock auto_lock(va_lock_);

buf_info->mem_type = VA_SURFACE_ATTRIB_MEM_TYPE_KERNEL_DRM;
VAStatus va_res = vaLockBuffer(va_display_, buf_id, buf_info);
VA_SUCCESS_OR_RETURN(va_res, "Failed to lock vabuffer", false);

return true;
}

bool VaapiWrapper::UnlockBuffer(VASurfaceID va_surface_id,
VABufferID buf_id,
VABufferInfo* buf_info) {
DCHECK(buf_info);
base::AutoLock auto_lock(va_lock_);
VAStatus va_res = vaUnlockBuffer(va_display_, buf_id, buf_info);
VA_SUCCESS_OR_RETURN(va_res, "Failed to unlock vabuffer", false);

return true;
}


bool VaapiWrapper::GetVaImageForTesting(VASurfaceID va_surface_id,
VAImage* image,
void** mem) {
Expand Down
11 changes: 8 additions & 3 deletions media/vaapi_wrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,17 @@ class CONTENT_EXPORT VaapiWrapper {
bool CreateRGBImage(gfx::Size size, VAImage* image);
void DestroyImage(VAImage* image);

bool MapImage(VAImage* image, void** buffer);
void UnmapImage(VAImage* image);

// Put data from |va_surface_id| into |va_image|, converting/scaling it.
bool PutSurfaceIntoImage(VASurfaceID va_surface_id,
VAImage* va_image);

bool LockBuffer(VASurfaceID va_surface_id,
VABufferID buf_id,
VABufferInfo* buf_info);
bool UnlockBuffer(VASurfaceID va_surface_id,
VABufferID buf_id,
VABufferInfo* buf_info);

// Returns true if the VAAPI version is less than the specified version.
bool VAAPIVersionLessThan(int major, int minor);

Expand Down

0 comments on commit 45f6768

Please sign in to comment.