| // 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 "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" |
| |
| namespace gbm_wrapper { |
| |
| class Buffer final : public ui::GbmBuffer { |
| public: |
| Buffer(struct gbm_bo* bo, |
| uint32_t format, |
| uint32_t flags, |
| uint64_t modifier, |
| std::vector<base::ScopedFD> fds, |
| const gfx::Size& size, |
| std::vector<gfx::NativePixmapPlane> planes) |
| : bo_(bo), |
| format_(format), |
| format_modifier_(modifier), |
| flags_(flags), |
| fds_(std::move(fds)), |
| size_(size), |
| planes_(std::move(planes)) { |
| DCHECK_EQ(fds_.size(), planes_.size()); |
| } |
| |
| ~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 (fds_.empty()) |
| return false; |
| |
| for (const auto& fd : fds_) { |
| if (fd.get() == -1) |
| return false; |
| } |
| return true; |
| } |
| size_t GetNumPlanes() const override { return planes_.size(); } |
| int GetPlaneFd(size_t plane) const override { |
| DCHECK_LT(plane, fds_.size()); |
| return fds_[plane].get(); |
| } |
| int GetPlaneStride(size_t plane) const override { |
| DCHECK_LT(plane, planes_.size()); |
| return planes_[plane].stride; |
| } |
| int GetPlaneOffset(size_t plane) const override { |
| DCHECK_LT(plane, planes_.size()); |
| return planes_[plane].offset; |
| } |
| size_t GetPlaneSize(size_t plane) const override { |
| DCHECK_LT(plane, planes_.size()); |
| return planes_[plane].size; |
| } |
| uint32_t GetPlaneHandle(size_t plane) const override { |
| DCHECK_LT(plane, planes_.size()); |
| return gbm_bo_get_plane_handle(bo_, plane).u32; |
| } |
| uint32_t GetHandle() const override { return gbm_bo_get_handle(bo_).u32; } |
| gfx::NativePixmapHandle ExportHandle() const override { |
| gfx::NativePixmapHandle handle; |
| gfx::BufferFormat format = ui::GetBufferFormatFromFourCCFormat(format_); |
| // TODO(dcastagna): Use gbm_bo_get_num_planes once all the formats we use |
| // are supported by gbm. |
| for (size_t i = 0; i < gfx::NumberOfPlanesForBufferFormat(format); ++i) { |
| base::ScopedFD scoped_fd(HANDLE_EINTR(dup(GetPlaneFd(i)))); |
| if (!scoped_fd.is_valid()) { |
| PLOG(ERROR) << "dup"; |
| return gfx::NativePixmapHandle(); |
| } |
| handle.fds.emplace_back( |
| base::FileDescriptor(scoped_fd.release(), true /* auto_close */)); |
| handle.planes.emplace_back(GetPlaneStride(i), GetPlaneOffset(i), |
| GetPlaneSize(i), GetFormatModifier()); |
| } |
| return handle; |
| } |
| |
| sk_sp<SkSurface> GetSurface() override { |
| DCHECK(!mmap_data_); |
| uint32_t stride; |
| void* addr; |
| addr = 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); |
| |
| if (!addr) |
| return nullptr; |
| SkImageInfo info = |
| SkImageInfo::MakeN32Premul(size_.width(), size_.height()); |
| return SkSurface::MakeRasterDirectReleaseProc(info, addr, stride, |
| &Buffer::UnmapGbmBo, this); |
| } |
| |
| private: |
| gbm_bo* bo_ = nullptr; |
| void* mmap_data_ = nullptr; |
| |
| uint32_t format_ = 0; |
| uint64_t format_modifier_ = 0; |
| uint32_t flags_ = 0; |
| |
| std::vector<base::ScopedFD> fds_; |
| |
| gfx::Size size_; |
| |
| std::vector<gfx::NativePixmapPlane> planes_; |
| |
| 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; |
| } |
| |
| 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); |
| std::vector<base::ScopedFD> fds; |
| std::vector<gfx::NativePixmapPlane> planes; |
| |
| const uint64_t modifier = gbm_bo_get_format_modifier(bo); |
| for (size_t i = 0; i < gbm_bo_get_num_planes(bo); ++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(gbm_bo_get_plane_fd(bo, i)); |
| |
| if (!fd.is_valid()) { |
| PLOG(ERROR) << "Failed to export buffer to dma_buf"; |
| gbm_bo_destroy(bo); |
| return nullptr; |
| } |
| fds.emplace_back(std::move(fd)); |
| |
| planes.emplace_back(gbm_bo_get_plane_stride(bo, i), |
| gbm_bo_get_plane_offset(bo, i), |
| gbm_bo_get_plane_size(bo, i), modifier); |
| } |
| return std::make_unique<Buffer>(bo, format, flags, modifier, std::move(fds), |
| size, std::move(planes)); |
| } |
| |
| 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> CreateBufferFromFds( |
| uint32_t format, |
| const gfx::Size& size, |
| std::vector<base::ScopedFD> fds, |
| const std::vector<gfx::NativePixmapPlane>& planes) override { |
| DCHECK_LE(fds.size(), planes.size()); |
| DCHECK_EQ(planes[0].offset, 0); |
| |
| // Try to use scanout if supported. |
| int gbm_flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_TEXTURING; |
| 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_planar_data fd_data; |
| fd_data.width = size.width(); |
| fd_data.height = size.height(); |
| fd_data.format = format; |
| |
| DCHECK_LE(planes.size(), 3u); |
| for (size_t i = 0; i < planes.size(); ++i) { |
| fd_data.fds[i] = fds[i < fds.size() ? i : 0].get(); |
| fd_data.strides[i] = planes[i].stride; |
| fd_data.offsets[i] = planes[i].offset; |
| fd_data.format_modifiers[i] = planes[i].modifier; |
| } |
| |
| // 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_PLANAR, &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, planes[0].modifier, |
| std::move(fds), size, std::move(planes)); |
| } |
| |
| private: |
| gbm_device* 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 |