blob: e5b99084905444fd112898dbd07f171443af9e6a [file] [log] [blame]
// Copyright 2020 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 "media/gpu/vaapi/vaapi_picture_native_pixmap_angle.h"
#include "media/gpu/vaapi/va_surface.h"
#include "media/gpu/vaapi/vaapi_wrapper.h"
#include "ui/gfx/x/connection.h"
#include "ui/gfx/x/future.h"
#include "ui/gfx/x/xproto.h"
#include "ui/gl/gl_bindings.h"
#include "ui/gl/gl_image_egl_pixmap.h"
#include "ui/gl/scoped_binders.h"
namespace media {
namespace {
x11::Pixmap CreatePixmap(const gfx::Size& size) {
auto* connection = x11::Connection::Get();
if (!connection->Ready())
return x11::Pixmap::None;
auto root = connection->default_root();
uint8_t depth = 0;
if (auto reply = connection->GetGeometry(root).Sync())
depth = reply->depth;
else
return x11::Pixmap::None;
// TODO(tmathmeyer) should we use the depth from libva instead of root window?
auto pixmap = connection->GenerateId<x11::Pixmap>();
uint16_t pixmap_width, pixmap_height;
if (!base::CheckedNumeric<int>(size.width()).AssignIfValid(&pixmap_width) ||
!base::CheckedNumeric<int>(size.height()).AssignIfValid(&pixmap_height)) {
return x11::Pixmap::None;
}
auto req = connection->CreatePixmap(
{depth, pixmap, root, pixmap_width, pixmap_height});
if (req.Sync().error)
pixmap = x11::Pixmap::None;
return pixmap;
}
} // namespace
VaapiPictureNativePixmapAngle::VaapiPictureNativePixmapAngle(
scoped_refptr<VaapiWrapper> vaapi_wrapper,
const MakeGLContextCurrentCallback& make_context_current_cb,
const BindGLImageCallback& bind_image_cb,
int32_t picture_buffer_id,
const gfx::Size& size,
const gfx::Size& visible_size,
uint32_t service_texture_id,
uint32_t client_texture_id,
uint32_t texture_target)
: VaapiPictureNativePixmap(std::move(vaapi_wrapper),
make_context_current_cb,
bind_image_cb,
picture_buffer_id,
size,
visible_size,
service_texture_id,
client_texture_id,
texture_target) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Check that they're both not 0
DCHECK(service_texture_id);
DCHECK(client_texture_id);
}
VaapiPictureNativePixmapAngle::~VaapiPictureNativePixmapAngle() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (gl_image_ && make_context_current_cb_.Run()) {
gl_image_->ReleaseTexImage(texture_target_);
DCHECK_EQ(glGetError(), static_cast<GLenum>(GL_NO_ERROR));
}
if (x_pixmap_ != x11::Pixmap::None)
x11::Connection::Get()->FreePixmap({x_pixmap_});
}
Status VaapiPictureNativePixmapAngle::Allocate(gfx::BufferFormat format) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!(texture_id_ || client_texture_id_))
return StatusCode::kVaapiNoTexture;
if (!make_context_current_cb_ || !make_context_current_cb_.Run())
return StatusCode::kVaapiBadContext;
auto image =
base::MakeRefCounted<gl::GLImageEGLPixmap>(visible_size_, format);
if (!image)
return StatusCode::kVaapiNoImage;
x_pixmap_ = CreatePixmap(visible_size_);
if (x_pixmap_ == x11::Pixmap::None)
return StatusCode::kVaapiNoPixmap;
if (!image->Initialize(x_pixmap_))
return StatusCode::kVaapiFailedToInitializeImage;
gl::ScopedTextureBinder texture_binder(texture_target_, texture_id_);
if (!image->BindTexImage(texture_target_))
return StatusCode::kVaapiFailedToBindTexture;
gl_image_ = image;
DCHECK(bind_image_cb_);
if (!bind_image_cb_.Run(client_texture_id_, texture_target_, gl_image_,
/*can_bind_to_sampler=*/true)) {
return StatusCode::kVaapiFailedToBindImage;
}
return OkStatus();
}
bool VaapiPictureNativePixmapAngle::ImportGpuMemoryBufferHandle(
gfx::BufferFormat format,
gfx::GpuMemoryBufferHandle gpu_memory_buffer_handle) {
NOTREACHED();
return false;
}
bool VaapiPictureNativePixmapAngle::DownloadFromSurface(
scoped_refptr<VASurface> va_surface) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!make_context_current_cb_ || !make_context_current_cb_.Run())
return false;
DCHECK(texture_id_);
gl::ScopedTextureBinder texture_binder(texture_target_, texture_id_);
// GL needs to re-bind the texture after the pixmap content is updated so that
// the compositor sees the updated contents (we found this out experimentally)
gl_image_->ReleaseTexImage(texture_target_);
DCHECK(gfx::Rect(va_surface->size()).Contains(gfx::Rect(visible_size_)));
if (!vaapi_wrapper_->PutSurfaceIntoPixmap(va_surface->id(), x_pixmap_,
visible_size_)) {
return false;
}
return gl_image_->BindTexImage(texture_target_);
}
VASurfaceID VaapiPictureNativePixmapAngle::va_surface_id() const {
return VA_INVALID_ID;
}
} // namespace media