| // Copyright 2017 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/capture/video/chromeos/local_gpu_memory_buffer_manager.h" |
| |
| #include <drm_fourcc.h> |
| #include <xf86drm.h> |
| #include <memory> |
| |
| #include "base/trace_event/memory_allocator_dump_guid.h" |
| #include "base/trace_event/process_memory_dump.h" |
| |
| namespace media { |
| |
| namespace { |
| |
| const int32_t kDrmNumNodes = 64; |
| const int32_t kMinNodeNumber = 128; |
| |
| gbm_device* CreateGbmDevice() { |
| int fd; |
| int32_t min_node = kMinNodeNumber; |
| int32_t max_node = kMinNodeNumber + kDrmNumNodes; |
| struct gbm_device* gbm = nullptr; |
| |
| for (int i = min_node; i < max_node; i++) { |
| fd = drmOpenRender(i); |
| if (fd < 0) { |
| continue; |
| } |
| |
| drmVersionPtr version = drmGetVersion(fd); |
| if (!strcmp("vgem", version->name)) { |
| drmFreeVersion(version); |
| close(fd); |
| continue; |
| } |
| |
| gbm = gbm_create_device(fd); |
| if (!gbm) { |
| drmFreeVersion(version); |
| close(fd); |
| continue; |
| } |
| |
| VLOG(1) << "Opened gbm device on render node " << version->name; |
| drmFreeVersion(version); |
| return gbm; |
| } |
| |
| return nullptr; |
| } |
| |
| uint32_t GetDrmFormat(gfx::BufferFormat gfx_format) { |
| switch (gfx_format) { |
| case gfx::BufferFormat::R_8: |
| return DRM_FORMAT_R8; |
| case gfx::BufferFormat::YUV_420_BIPLANAR: |
| return DRM_FORMAT_NV12; |
| // Add more formats when needed. |
| default: |
| return 0; |
| } |
| } |
| |
| class GpuMemoryBufferImplGbm : public gfx::GpuMemoryBuffer { |
| public: |
| GpuMemoryBufferImplGbm(gfx::BufferFormat format, gbm_bo* buffer_object) |
| : format_(format), buffer_object_(buffer_object), mapped_(false) { |
| handle_.type = gfx::NATIVE_PIXMAP; |
| // Set a dummy id since this is for testing only. |
| handle_.id = gfx::GpuMemoryBufferId(0); |
| for (size_t i = 0; i < gbm_bo_get_plane_count(buffer_object); ++i) { |
| handle_.native_pixmap_handle.planes.push_back(gfx::NativePixmapPlane( |
| gbm_bo_get_stride_for_plane(buffer_object, i), |
| gbm_bo_get_offset(buffer_object, i), |
| gbm_bo_get_plane_size(buffer_object, i), |
| base::ScopedFD(gbm_bo_get_plane_fd(buffer_object, i)))); |
| } |
| } |
| |
| ~GpuMemoryBufferImplGbm() override { |
| if (mapped_) { |
| Unmap(); |
| } |
| |
| gbm_bo_destroy(buffer_object_); |
| } |
| |
| bool Map() override { |
| if (mapped_) { |
| return true; |
| } |
| size_t num_planes = gbm_bo_get_plane_count(buffer_object_); |
| uint32_t stride; |
| mapped_planes_.resize(num_planes); |
| for (size_t i = 0; i < num_planes; ++i) { |
| void* mapped_data; |
| void* addr = |
| gbm_bo_map(buffer_object_, 0, 0, gbm_bo_get_width(buffer_object_), |
| gbm_bo_get_height(buffer_object_), |
| GBM_BO_TRANSFER_READ_WRITE, &stride, &mapped_data, i); |
| if (!addr) { |
| LOG(ERROR) << "Failed to map GpuMemoryBufferImplGbm plane " << i; |
| Unmap(); |
| return false; |
| } |
| mapped_planes_[i].addr = addr; |
| mapped_planes_[i].mapped_data = mapped_data; |
| } |
| mapped_ = true; |
| return true; |
| } |
| |
| void* memory(size_t plane) override { |
| if (!mapped_) { |
| LOG(ERROR) << "Buffer is not mapped"; |
| return nullptr; |
| } |
| if (plane > mapped_planes_.size()) { |
| LOG(ERROR) << "Invalid plane: " << plane; |
| return nullptr; |
| } |
| return mapped_planes_[plane].addr; |
| } |
| |
| void Unmap() override { |
| for (size_t i = 0; i < mapped_planes_.size(); ++i) { |
| if (mapped_planes_[i].addr) { |
| gbm_bo_unmap(buffer_object_, mapped_planes_[i].mapped_data); |
| mapped_planes_[i].addr = nullptr; |
| mapped_planes_[i].mapped_data = nullptr; |
| } |
| } |
| mapped_planes_.clear(); |
| mapped_ = false; |
| } |
| |
| gfx::Size GetSize() const override { |
| return gfx::Size(gbm_bo_get_width(buffer_object_), |
| gbm_bo_get_height(buffer_object_)); |
| } |
| |
| gfx::BufferFormat GetFormat() const override { return format_; } |
| |
| int stride(size_t plane) const override { |
| return gbm_bo_get_stride_for_plane(buffer_object_, plane); |
| } |
| |
| void SetColorSpace(const gfx::ColorSpace& color_space) override {} |
| |
| gfx::GpuMemoryBufferId GetId() const override { return handle_.id; } |
| |
| gfx::GpuMemoryBufferType GetType() const override { |
| return gfx::NATIVE_PIXMAP; |
| } |
| |
| gfx::GpuMemoryBufferHandle CloneHandle() const override { |
| DCHECK_EQ(handle_.type, gfx::NATIVE_PIXMAP); |
| gfx::GpuMemoryBufferHandle handle; |
| handle.type = gfx::NATIVE_PIXMAP; |
| handle.id = handle_.id; |
| handle.native_pixmap_handle = |
| gfx::CloneHandleForIPC(handle_.native_pixmap_handle); |
| return handle; |
| } |
| |
| ClientBuffer AsClientBuffer() override { |
| return reinterpret_cast<ClientBuffer>(this); |
| } |
| |
| void OnMemoryDump( |
| base::trace_event::ProcessMemoryDump* pmd, |
| const base::trace_event::MemoryAllocatorDumpGuid& buffer_dump_guid, |
| uint64_t tracing_process_id, |
| int importance) const override { |
| auto shared_buffer_guid = gfx::GetGenericSharedGpuMemoryGUIDForTracing( |
| tracing_process_id, GetId()); |
| pmd->CreateSharedGlobalAllocatorDump(shared_buffer_guid); |
| pmd->AddOwnershipEdge(buffer_dump_guid, shared_buffer_guid, importance); |
| } |
| |
| private: |
| struct MappedPlane { |
| void* addr; |
| void* mapped_data; |
| }; |
| |
| gfx::BufferFormat format_; |
| gbm_bo* buffer_object_; |
| gfx::GpuMemoryBufferHandle handle_; |
| bool mapped_; |
| std::vector<MappedPlane> mapped_planes_; |
| DISALLOW_IMPLICIT_CONSTRUCTORS(GpuMemoryBufferImplGbm); |
| }; |
| |
| } // namespace |
| |
| LocalGpuMemoryBufferManager::LocalGpuMemoryBufferManager() |
| : gbm_device_(CreateGbmDevice()) {} |
| |
| LocalGpuMemoryBufferManager::~LocalGpuMemoryBufferManager() { |
| if (gbm_device_) { |
| close(gbm_device_get_fd(gbm_device_)); |
| gbm_device_destroy(gbm_device_); |
| } |
| } |
| |
| std::unique_ptr<gfx::GpuMemoryBuffer> |
| LocalGpuMemoryBufferManager::CreateGpuMemoryBuffer( |
| const gfx::Size& size, |
| gfx::BufferFormat format, |
| gfx::BufferUsage usage, |
| gpu::SurfaceHandle surface_handle) { |
| if (usage != gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE && |
| usage != gfx::BufferUsage::CAMERA_AND_CPU_READ_WRITE) { |
| LOG(ERROR) << "Unsupported gfx::BufferUsage" << static_cast<int>(usage); |
| return std::unique_ptr<gfx::GpuMemoryBuffer>(); |
| } |
| if (!gbm_device_) { |
| LOG(ERROR) << "Invalid GBM device"; |
| return std::unique_ptr<gfx::GpuMemoryBuffer>(); |
| } |
| |
| uint32_t drm_format = GetDrmFormat(format); |
| uint32_t camera_gbm_usage = |
| GBM_BO_USE_LINEAR | GBM_BO_USE_CAMERA_READ | GBM_BO_USE_CAMERA_WRITE; |
| if (!drm_format) { |
| LOG(ERROR) << "Unable to convert gfx::BufferFormat " |
| << static_cast<int>(format) << " to DRM format"; |
| return std::unique_ptr<gfx::GpuMemoryBuffer>(); |
| } |
| |
| if (!gbm_device_is_format_supported(gbm_device_, drm_format, |
| camera_gbm_usage)) { |
| return std::unique_ptr<gfx::GpuMemoryBuffer>(); |
| } |
| |
| gbm_bo* buffer_object = gbm_bo_create( |
| gbm_device_, size.width(), size.height(), drm_format, camera_gbm_usage); |
| if (!buffer_object) { |
| LOG(ERROR) << "Failed to create GBM buffer object"; |
| return std::unique_ptr<gfx::GpuMemoryBuffer>(); |
| } |
| |
| return std::make_unique<GpuMemoryBufferImplGbm>(format, buffer_object); |
| } |
| |
| void LocalGpuMemoryBufferManager::SetDestructionSyncToken( |
| gfx::GpuMemoryBuffer* buffer, |
| const gpu::SyncToken& sync_token) {} |
| |
| } // namespace media |