blob: 9b752e3ca23f2b5883dc7a9a65efaacd1ed3241a [file] [log] [blame]
/*
* Copyright 2021 The Chromium OS 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 "gpu/shared_image.h"
#include <utility>
#include <drm_fourcc.h>
#include <hardware/gralloc.h>
#include "cros-camera/camera_buffer_manager.h"
#include "cros-camera/common.h"
namespace cros {
namespace {
bool IsSupportedFormat(uint32_t buffer_format) {
switch (buffer_format) {
case DRM_FORMAT_NV12:
case DRM_FORMAT_P010:
return true;
}
return false;
}
uint32_t GetDrmPixelFormatForSubplane(buffer_handle_t buffer, int plane) {
uint32_t buffer_format = CameraBufferManager::GetDrmPixelFormat(buffer);
switch (buffer_format) {
case DRM_FORMAT_NV12:
return (plane == 0) ? DRM_FORMAT_R8 : DRM_FORMAT_GR88;
case DRM_FORMAT_P010:
return (plane == 0) ? DRM_FORMAT_R16 : DRM_FORMAT_GR1616;
}
NOTREACHED() << "Unsupported format: " << FormatToString(buffer_format);
return 0;
}
}; // namespace
// static
SharedImage SharedImage::CreateFromBuffer(buffer_handle_t buffer,
Texture2D::Target texture_target,
bool separate_yuv_textures) {
std::vector<EglImage> egl_images;
std::vector<Texture2D> textures;
if (!separate_yuv_textures) {
egl_images.emplace_back(EglImage::FromBuffer(buffer));
if (!egl_images[0].IsValid()) {
LOGF(ERROR) << "Failed to create EGLimage for buffer";
return SharedImage();
}
textures.emplace_back(texture_target, egl_images[0]);
if (!textures[0].IsValid()) {
LOGF(ERROR) << "Failed to bind EGLimage to texture";
return SharedImage();
}
} else {
uint32_t buffer_format = CameraBufferManager::GetDrmPixelFormat(buffer);
if (!IsSupportedFormat(buffer_format)) {
LOGF(ERROR) << "Unsupported format: " << FormatToString(buffer_format);
return SharedImage();
}
int buffer_width = static_cast<int>(CameraBufferManager::GetWidth(buffer));
int buffer_height =
static_cast<int>(CameraBufferManager::GetHeight(buffer));
egl_images.emplace_back(
EglImage::FromBufferPlane(buffer, 0, buffer_width, buffer_height,
GetDrmPixelFormatForSubplane(buffer, 0)));
if (!egl_images[0].IsValid()) {
LOGF(ERROR) << "Failed to create EGLimage for Y plane";
return SharedImage();
}
egl_images.emplace_back(EglImage::FromBufferPlane(
buffer, 1, buffer_width / 2, buffer_height / 2,
GetDrmPixelFormatForSubplane(buffer, 1)));
if (!egl_images[1].IsValid()) {
LOGF(ERROR) << "Failed to create EGLimage for UV plane";
return SharedImage();
}
textures.emplace_back(Texture2D::Target::kTarget2D, egl_images[0]);
textures.emplace_back(Texture2D::Target::kTarget2D, egl_images[1]);
if (!textures[0].IsValid() || !textures[1].IsValid()) {
LOGF(ERROR) << "Failed to bind EGLimage to texture";
return SharedImage();
}
}
return SharedImage(buffer, std::move(egl_images), std::move(textures));
}
// static
SharedImage SharedImage::CreateFromGpuTexture(GLenum gl_format,
int width,
int height) {
std::vector<Texture2D> textures;
textures.emplace_back(gl_format, width, height);
if (!textures[0].IsValid()) {
LOGF(ERROR) << "Failed to create texture";
return SharedImage();
}
return SharedImage(nullptr, std::vector<EglImage>(), std::move(textures));
}
SharedImage::SharedImage(buffer_handle_t buffer,
std::vector<EglImage> egl_images,
std::vector<Texture2D> textures)
: buffer_(buffer),
egl_images_(std::move(egl_images)),
textures_(std::move(textures)) {
CHECK(textures_[0].IsValid());
}
SharedImage::SharedImage(SharedImage&& other) {
*this = std::move(other);
}
SharedImage& SharedImage::operator=(SharedImage&& other) {
if (this != &other) {
Invalidate();
buffer_ = other.buffer_;
egl_images_ = std::move(other.egl_images_);
textures_ = std::move(other.textures_);
destruction_callback_ = std::move(other.destruction_callback_);
other.buffer_ = nullptr;
}
return *this;
}
SharedImage::~SharedImage() {
Invalidate();
}
const Texture2D& SharedImage::texture() const {
CHECK_EQ(textures_.size(), 1);
return textures_[0];
}
const Texture2D& SharedImage::y_texture() const {
CHECK_EQ(textures_.size(), 2);
return textures_[0];
}
const Texture2D& SharedImage::uv_texture() const {
CHECK_EQ(textures_.size(), 2);
return textures_[1];
}
void SharedImage::SetDestructionCallback(base::OnceClosure callback) {
destruction_callback_ = std::move(callback);
}
void SharedImage::Invalidate() {
buffer_ = nullptr;
egl_images_.clear();
textures_.clear();
if (!destruction_callback_.is_null()) {
std::move(destruction_callback_).Run();
}
}
} // namespace cros