blob: 76f6b0bd5064c4acb797f44584997f1d42f4987f [file] [log] [blame]
// Copyright 2016 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/gfx/linux/client_native_pixmap_dmabuf.h"
#include <fcntl.h>
#include <linux/version.h>
#include <stddef.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <xf86drm.h>
#include "base/memory/ptr_util.h"
#include "base/posix/eintr_wrapper.h"
#include "base/process/memory.h"
#include "base/strings/stringprintf.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
#include <linux/dma-buf.h>
#else
#include <linux/types.h>
struct dma_buf_sync {
__u64 flags;
};
#define DMA_BUF_SYNC_READ (1 << 0)
#define DMA_BUF_SYNC_WRITE (2 << 0)
#define DMA_BUF_SYNC_RW (DMA_BUF_SYNC_READ | DMA_BUF_SYNC_WRITE)
#define DMA_BUF_SYNC_START (0 << 2)
#define DMA_BUF_SYNC_END (1 << 2)
#define DMA_BUF_BASE 'b'
#define DMA_BUF_IOCTL_SYNC _IOW(DMA_BUF_BASE, 0, struct dma_buf_sync)
#endif
namespace gfx {
namespace {
void PrimeSyncStart(int dmabuf_fd) {
struct dma_buf_sync sync_start = {0};
sync_start.flags = DMA_BUF_SYNC_START | DMA_BUF_SYNC_RW;
int rv = HANDLE_EINTR(ioctl(dmabuf_fd, DMA_BUF_IOCTL_SYNC, &sync_start));
PLOG_IF(ERROR, rv) << "Failed DMA_BUF_SYNC_START";
}
void PrimeSyncEnd(int dmabuf_fd) {
struct dma_buf_sync sync_end = {0};
sync_end.flags = DMA_BUF_SYNC_END | DMA_BUF_SYNC_RW;
int rv = HANDLE_EINTR(ioctl(dmabuf_fd, DMA_BUF_IOCTL_SYNC, &sync_end));
PLOG_IF(ERROR, rv) << "Failed DMA_BUF_SYNC_END";
}
} // namespace
// static
bool ClientNativePixmapDmaBuf::IsConfigurationSupported(
gfx::BufferFormat format,
gfx::BufferUsage usage) {
#if defined(CHROMECAST_BUILD)
switch (usage) {
case gfx::BufferUsage::GPU_READ_CPU_READ_WRITE:
case gfx::BufferUsage::GPU_READ_CPU_READ_WRITE_PERSISTENT:
// TODO(spang): Fix b/121148905 and turn these back on.
return false;
default:
break;
}
#endif
switch (usage) {
case gfx::BufferUsage::GPU_READ:
return format == gfx::BufferFormat::BGR_565 ||
format == gfx::BufferFormat::RGBA_8888 ||
format == gfx::BufferFormat::RGBX_8888 ||
format == gfx::BufferFormat::BGRA_8888 ||
format == gfx::BufferFormat::BGRX_8888 ||
format == gfx::BufferFormat::YVU_420;
case gfx::BufferUsage::SCANOUT:
return format == gfx::BufferFormat::BGRX_8888 ||
format == gfx::BufferFormat::RGBX_8888 ||
format == gfx::BufferFormat::RGBA_8888 ||
format == gfx::BufferFormat::BGRA_8888;
case gfx::BufferUsage::SCANOUT_CPU_READ_WRITE:
return
#if defined(ARCH_CPU_X86_FAMILY)
// Currently only Intel driver (i.e. minigbm and Mesa) supports R_8
// RG_88, NV12 and XB30. https://crbug.com/356871
format == gfx::BufferFormat::R_8 ||
format == gfx::BufferFormat::RG_88 ||
format == gfx::BufferFormat::YUV_420_BIPLANAR ||
format == gfx::BufferFormat::RGBX_1010102 ||
#endif
format == gfx::BufferFormat::BGRX_8888 ||
format == gfx::BufferFormat::BGRA_8888 ||
format == gfx::BufferFormat::RGBX_8888 ||
format == gfx::BufferFormat::RGBA_8888;
case gfx::BufferUsage::SCANOUT_VDA_WRITE:
return false;
case gfx::BufferUsage::GPU_READ_CPU_READ_WRITE:
case gfx::BufferUsage::GPU_READ_CPU_READ_WRITE_PERSISTENT:
return
#if defined(ARCH_CPU_X86_FAMILY)
// Currently only Intel driver (i.e. minigbm and
// Mesa) supports R_8 RG_88 and NV12.
// https://crbug.com/356871
format == gfx::BufferFormat::R_8 ||
format == gfx::BufferFormat::RG_88 ||
format == gfx::BufferFormat::YUV_420_BIPLANAR ||
#endif
format == gfx::BufferFormat::BGRA_8888;
case gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE:
// Each platform only supports one camera buffer type. We list the
// supported buffer formats on all platforms here. When allocating a
// camera buffer the caller is responsible for making sure a buffer is
// successfully allocated. For example, allocating YUV420_BIPLANAR
// for SCANOUT_CAMERA_READ_WRITE may only work on Intel boards.
return format == gfx::BufferFormat::YUV_420_BIPLANAR;
case gfx::BufferUsage::CAMERA_AND_CPU_READ_WRITE:
// R_8 is used as the underlying pixel format for BLOB buffers.
return format == gfx::BufferFormat::R_8;
}
NOTREACHED();
return false;
}
// static
std::unique_ptr<gfx::ClientNativePixmap>
ClientNativePixmapDmaBuf::ImportFromDmabuf(
const gfx::NativePixmapHandle& handle,
const gfx::Size& size) {
return base::WrapUnique(new ClientNativePixmapDmaBuf(handle, size));
}
ClientNativePixmapDmaBuf::ClientNativePixmapDmaBuf(
const gfx::NativePixmapHandle& handle,
const gfx::Size& size)
: pixmap_handle_(handle), size_(size), data_{0} {
TRACE_EVENT0("drm", "ClientNativePixmapDmaBuf");
// TODO(dcastagna): support multiple fds.
DCHECK_EQ(1u, handle.fds.size());
DCHECK_GE(handle.fds.front().fd, 0);
dmabuf_fd_.reset(handle.fds.front().fd);
DCHECK_GE(handle.planes.back().size, 0u);
size_t map_size = handle.planes.back().offset + handle.planes.back().size;
data_ = mmap(nullptr, map_size, (PROT_READ | PROT_WRITE), MAP_SHARED,
dmabuf_fd_.get(), 0);
if (data_ == MAP_FAILED) {
logging::SystemErrorCode mmap_error = logging::GetLastSystemErrorCode();
if (mmap_error == ENOMEM)
base::TerminateBecauseOutOfMemory(map_size);
CHECK(false) << "Failed to mmap dmabuf: "
<< logging::SystemErrorCodeToString(mmap_error);
}
}
ClientNativePixmapDmaBuf::~ClientNativePixmapDmaBuf() {
TRACE_EVENT0("drm", "~ClientNativePixmapDmaBuf");
size_t map_size =
pixmap_handle_.planes.back().offset + pixmap_handle_.planes.back().size;
int ret = munmap(data_, map_size);
DCHECK(!ret);
}
bool ClientNativePixmapDmaBuf::Map() {
TRACE_EVENT0("drm", "DmaBuf:Map");
if (data_ != nullptr) {
PrimeSyncStart(dmabuf_fd_.get());
return true;
}
return false;
}
void ClientNativePixmapDmaBuf::Unmap() {
TRACE_EVENT0("drm", "DmaBuf:Unmap");
PrimeSyncEnd(dmabuf_fd_.get());
}
void* ClientNativePixmapDmaBuf::GetMemoryAddress(size_t plane) const {
DCHECK_LT(plane, pixmap_handle_.planes.size());
uint8_t* address = reinterpret_cast<uint8_t*>(data_);
return address + pixmap_handle_.planes[plane].offset;
}
int ClientNativePixmapDmaBuf::GetStride(size_t plane) const {
DCHECK_LT(plane, pixmap_handle_.planes.size());
return pixmap_handle_.planes[plane].stride;
}
} // namespace gfx