| // Copyright 2018 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/ozone/common/linux/gbm_wrapper.h" |
| |
| #include <gbm.h> |
| #include <memory> |
| #include <utility> |
| |
| #include "base/posix/eintr_wrapper.h" |
| #include "third_party/skia/include/core/SkSurface.h" |
| #include "ui/gfx/buffer_format_util.h" |
| #include "ui/ozone/common/linux/drm_util_linux.h" |
| #include "ui/ozone/common/linux/gbm_buffer.h" |
| #include "ui/ozone/common/linux/gbm_device.h" |
| |
| #if !defined(MINIGBM) |
| #include <fcntl.h> |
| #include <xf86drm.h> |
| #endif |
| |
| namespace gbm_wrapper { |
| |
| namespace { |
| |
| int GetPlaneFdForBo(gbm_bo* bo, size_t plane) { |
| #if defined(MINIGBM) |
| return gbm_bo_get_plane_fd(bo, plane); |
| #else |
| const int plane_count = gbm_bo_get_plane_count(bo); |
| DCHECK(plane_count > 0 && plane < static_cast<size_t>(plane_count)); |
| |
| // System linux gbm (or Mesa gbm) does not provide fds per plane basis. Thus, |
| // get plane handle and use drm ioctl to get a prime fd out of it avoid having |
| // two different branches for minigbm and Mesa gbm here. |
| gbm_device* gbm_dev = gbm_bo_get_device(bo); |
| int dev_fd = gbm_device_get_fd(gbm_dev); |
| DCHECK_GE(dev_fd, 0); |
| |
| const uint32_t plane_handle = gbm_bo_get_handle_for_plane(bo, plane).u32; |
| int fd = -1; |
| int ret; |
| // Use DRM_RDWR to allow the fd to be mappable in another process. |
| ret = drmPrimeHandleToFD(dev_fd, plane_handle, DRM_CLOEXEC | DRM_RDWR, &fd); |
| |
| // Older DRM implementations blocked DRM_RDWR, but gave a read/write mapping |
| // anyways |
| if (ret) |
| ret = drmPrimeHandleToFD(dev_fd, plane_handle, DRM_CLOEXEC, &fd); |
| |
| return ret ? ret : fd; |
| #endif |
| } |
| |
| size_t GetSizeOfPlane(gbm_bo* bo, |
| uint32_t format, |
| const gfx::Size& size, |
| size_t plane) { |
| #if defined(MINIGBM) |
| return gbm_bo_get_plane_size(bo, plane); |
| #else |
| DCHECK(!size.IsEmpty()); |
| |
| // Get row size of the plane, stride and subsampled height to finally get the |
| // size of a plane in bytes. |
| const gfx::BufferFormat buffer_format = |
| ui::GetBufferFormatFromFourCCFormat(format); |
| const base::CheckedNumeric<size_t> stride_for_plane = |
| gbm_bo_get_stride_for_plane(bo, plane); |
| const base::CheckedNumeric<size_t> subsampled_height = |
| size.height() / |
| gfx::SubsamplingFactorForBufferFormat(buffer_format, plane); |
| |
| // Apply subsampling factor to get size in bytes. |
| const base::CheckedNumeric<size_t> checked_plane_size = |
| subsampled_height * stride_for_plane; |
| |
| return checked_plane_size.ValueOrDie(); |
| #endif |
| } |
| |
| } // namespace |
| |
| class Buffer final : public ui::GbmBuffer { |
| public: |
| Buffer(struct gbm_bo* bo, |
| uint32_t format, |
| uint32_t flags, |
| uint64_t modifier, |
| const gfx::Size& size, |
| gfx::NativePixmapHandle handle) |
| : bo_(bo), |
| format_(format), |
| format_modifier_(modifier), |
| flags_(flags), |
| size_(size), |
| handle_(std::move(handle)) {} |
| |
| ~Buffer() override { |
| DCHECK(!mmap_data_); |
| gbm_bo_destroy(bo_); |
| } |
| |
| uint32_t GetFormat() const override { return format_; } |
| uint64_t GetFormatModifier() const override { return format_modifier_; } |
| uint32_t GetFlags() const override { return flags_; } |
| // TODO(reveman): This should not be needed once crbug.com/597932 is fixed, |
| // as the size would be queried directly from the underlying bo. |
| gfx::Size GetSize() const override { return size_; } |
| gfx::BufferFormat GetBufferFormat() const override { |
| return ui::GetBufferFormatFromFourCCFormat(format_); |
| } |
| bool AreFdsValid() const override { |
| if (handle_.planes.empty()) |
| return false; |
| |
| for (const auto& plane : handle_.planes) { |
| if (!plane.fd.is_valid()) |
| return false; |
| } |
| return true; |
| } |
| size_t GetNumPlanes() const override { return handle_.planes.size(); } |
| int GetPlaneFd(size_t plane) const override { |
| DCHECK_LT(plane, handle_.planes.size()); |
| return handle_.planes[plane].fd.get(); |
| } |
| uint32_t GetPlaneStride(size_t plane) const override { |
| DCHECK_LT(plane, handle_.planes.size()); |
| return handle_.planes[plane].stride; |
| } |
| size_t GetPlaneOffset(size_t plane) const override { |
| DCHECK_LT(plane, handle_.planes.size()); |
| return handle_.planes[plane].offset; |
| } |
| size_t GetPlaneSize(size_t plane) const override { |
| DCHECK_LT(plane, handle_.planes.size()); |
| return static_cast<size_t>(handle_.planes[plane].size); |
| } |
| uint32_t GetPlaneHandle(size_t plane) const override { |
| DCHECK_LT(plane, handle_.planes.size()); |
| return gbm_bo_get_handle_for_plane(bo_, plane).u32; |
| } |
| uint32_t GetHandle() const override { return gbm_bo_get_handle(bo_).u32; } |
| gfx::NativePixmapHandle ExportHandle() const override { |
| return CloneHandleForIPC(handle_); |
| } |
| |
| sk_sp<SkSurface> GetSurface() override { |
| DCHECK(!mmap_data_); |
| uint32_t stride; |
| void* addr; |
| addr = |
| #if defined(MINIGBM) |
| gbm_bo_map(bo_, 0, 0, gbm_bo_get_width(bo_), gbm_bo_get_height(bo_), |
| GBM_BO_TRANSFER_READ_WRITE, &stride, &mmap_data_, 0); |
| #else |
| gbm_bo_map(bo_, 0, 0, gbm_bo_get_width(bo_), gbm_bo_get_height(bo_), |
| GBM_BO_TRANSFER_READ_WRITE, &stride, &mmap_data_); |
| #endif |
| |
| if (!addr) |
| return nullptr; |
| SkImageInfo info = |
| SkImageInfo::MakeN32Premul(size_.width(), size_.height()); |
| return SkSurface::MakeRasterDirectReleaseProc(info, addr, stride, |
| &Buffer::UnmapGbmBo, this); |
| } |
| |
| private: |
| static void UnmapGbmBo(void* pixels, void* context) { |
| Buffer* buffer = static_cast<Buffer*>(context); |
| gbm_bo_unmap(buffer->bo_, buffer->mmap_data_); |
| buffer->mmap_data_ = nullptr; |
| } |
| |
| gbm_bo* const bo_; |
| void* mmap_data_ = nullptr; |
| |
| const uint32_t format_; |
| const uint64_t format_modifier_; |
| const uint32_t flags_; |
| |
| const gfx::Size size_; |
| |
| const gfx::NativePixmapHandle handle_; |
| |
| DISALLOW_COPY_AND_ASSIGN(Buffer); |
| }; |
| |
| std::unique_ptr<Buffer> CreateBufferForBO(struct gbm_bo* bo, |
| uint32_t format, |
| const gfx::Size& size, |
| uint32_t flags) { |
| DCHECK(bo); |
| gfx::NativePixmapHandle handle; |
| |
| const uint64_t modifier = gbm_bo_get_modifier(bo); |
| const int plane_count = gbm_bo_get_plane_count(bo); |
| // The Mesa's gbm implementation explicitly checks whether plane count <= and |
| // returns 1 if the condition is true. Nevertheless, use a DCHECK here to make |
| // sure the condition is not broken there. |
| DCHECK_GT(plane_count, 0); |
| // Ensure there are no differences in integer signs by casting any possible |
| // values to size_t. |
| for (size_t i = 0; i < static_cast<size_t>(plane_count); ++i) { |
| // The fd returned by gbm_bo_get_fd is not ref-counted and need to be |
| // kept open for the lifetime of the buffer. |
| base::ScopedFD fd(GetPlaneFdForBo(bo, i)); |
| |
| if (!fd.is_valid()) { |
| PLOG(ERROR) << "Failed to export buffer to dma_buf"; |
| gbm_bo_destroy(bo); |
| return nullptr; |
| } |
| |
| handle.planes.emplace_back( |
| gbm_bo_get_stride_for_plane(bo, i), gbm_bo_get_offset(bo, i), |
| GetSizeOfPlane(bo, format, size, i), std::move(fd)); |
| } |
| |
| handle.modifier = modifier; |
| return std::make_unique<Buffer>(bo, format, flags, modifier, size, |
| std::move(handle)); |
| } |
| |
| class Device final : public ui::GbmDevice { |
| public: |
| Device(gbm_device* device) : device_(device) {} |
| ~Device() override { gbm_device_destroy(device_); } |
| |
| std::unique_ptr<ui::GbmBuffer> CreateBuffer(uint32_t format, |
| const gfx::Size& size, |
| uint32_t flags) override { |
| struct gbm_bo* bo = |
| gbm_bo_create(device_, size.width(), size.height(), format, flags); |
| if (!bo) |
| return nullptr; |
| |
| return CreateBufferForBO(bo, format, size, flags); |
| } |
| |
| std::unique_ptr<ui::GbmBuffer> CreateBufferWithModifiers( |
| uint32_t format, |
| const gfx::Size& size, |
| uint32_t flags, |
| const std::vector<uint64_t>& modifiers) override { |
| if (modifiers.empty()) |
| return CreateBuffer(format, size, flags); |
| struct gbm_bo* bo = gbm_bo_create_with_modifiers( |
| device_, size.width(), size.height(), format, modifiers.data(), |
| modifiers.size()); |
| if (!bo) |
| return nullptr; |
| |
| return CreateBufferForBO(bo, format, size, flags); |
| } |
| |
| std::unique_ptr<ui::GbmBuffer> CreateBufferFromHandle( |
| uint32_t format, |
| const gfx::Size& size, |
| gfx::NativePixmapHandle handle) override { |
| DCHECK_EQ(handle.planes[0].offset, 0u); |
| |
| // Try to use scanout if supported. |
| int gbm_flags = GBM_BO_USE_SCANOUT; |
| #if defined(MINIGBM) |
| gbm_flags |= GBM_BO_USE_TEXTURING; |
| #endif |
| if (!gbm_device_is_format_supported(device_, format, gbm_flags)) |
| gbm_flags &= ~GBM_BO_USE_SCANOUT; |
| |
| struct gbm_bo* bo = nullptr; |
| if (!gbm_device_is_format_supported(device_, format, gbm_flags)) { |
| LOG(ERROR) << "gbm format not supported: " << format; |
| return nullptr; |
| } |
| |
| struct gbm_import_fd_modifier_data fd_data; |
| fd_data.width = size.width(); |
| fd_data.height = size.height(); |
| fd_data.format = format; |
| fd_data.num_fds = handle.planes.size(); |
| fd_data.modifier = handle.modifier; |
| |
| DCHECK_LE(handle.planes.size(), 3u); |
| for (size_t i = 0; i < handle.planes.size(); ++i) { |
| fd_data.fds[i] = handle.planes[i < handle.planes.size() ? i : 0].fd.get(); |
| fd_data.strides[i] = handle.planes[i].stride; |
| fd_data.offsets[i] = handle.planes[i].offset; |
| } |
| |
| // The fd passed to gbm_bo_import is not ref-counted and need to be |
| // kept open for the lifetime of the buffer. |
| bo = gbm_bo_import(device_, GBM_BO_IMPORT_FD_MODIFIER, &fd_data, gbm_flags); |
| if (!bo) { |
| LOG(ERROR) << "nullptr returned from gbm_bo_import"; |
| return nullptr; |
| } |
| |
| return std::make_unique<Buffer>(bo, format, gbm_flags, handle.modifier, |
| size, std::move(handle)); |
| } |
| |
| private: |
| gbm_device* const device_; |
| |
| DISALLOW_COPY_AND_ASSIGN(Device); |
| }; |
| |
| } // namespace gbm_wrapper |
| |
| namespace ui { |
| |
| std::unique_ptr<GbmDevice> CreateGbmDevice(int fd) { |
| gbm_device* device = gbm_create_device(fd); |
| if (!device) |
| return nullptr; |
| return std::make_unique<gbm_wrapper::Device>(device); |
| } |
| |
| } // namespace ui |