| // Copyright 2019 The ChromiumOS Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <fcntl.h> |
| #include <sys/mman.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <sys/ioctl.h> |
| #include <linux/dma-buf.h> |
| |
| #include <base/check_op.h> |
| #include <base/containers/contains.h> |
| #include <base/files/scoped_file.h> |
| #include <base/posix/eintr_wrapper.h> |
| #include <base/scoped_generic.h> |
| #include <base/strings/stringprintf.h> |
| #include <gbm.h> |
| #include <gtest/gtest.h> |
| #include <xf86drm.h> |
| #include <xf86drmMode.h> |
| |
| namespace { |
| |
| constexpr size_t kBytesPerPixel = sizeof(uint32_t); |
| |
| namespace internal { |
| struct ScopedDrmFDCloseTraits { |
| static int InvalidValue() { return -1; } |
| static void Free(int fd) { drmClose(fd); } |
| }; |
| } // namespace internal |
| using ScopedDrmFD = base::ScopedGeneric<int, internal::ScopedDrmFDCloseTraits>; |
| |
| namespace internal { |
| struct DrmModeResourcesDeleter { |
| void operator()(drmModeRes* ptr) { drmModeFreeResources(ptr); } |
| }; |
| } // namespace internal |
| using ScopedDrmModeResources = |
| std::unique_ptr<drmModeRes, internal::DrmModeResourcesDeleter>; |
| |
| namespace internal { |
| struct DrmModeConnectorDeleter { |
| void operator()(drmModeConnector* ptr) { drmModeFreeConnector(ptr); } |
| }; |
| } // namespace internal |
| using ScopedDrmModeConnector = |
| std::unique_ptr<drmModeConnector, internal::DrmModeConnectorDeleter>; |
| |
| namespace internal { |
| struct GbmDeviceDeleter { |
| void operator()(gbm_device* gbm) { |
| if (gbm) |
| gbm_device_destroy(gbm); |
| } |
| }; |
| } // namespace internal |
| using ScopedGbmDevice = |
| std::unique_ptr<gbm_device, internal::GbmDeviceDeleter>; |
| |
| namespace internal { |
| struct GbmBoDeleter { |
| void operator()(gbm_bo* bo) { |
| if (bo) |
| gbm_bo_destroy(bo); |
| } |
| }; |
| } // namespace internal |
| using ScopedGbmBo = std::unique_ptr<gbm_bo, internal::GbmBoDeleter>; |
| |
| class ScopedGbmBoMap { |
| public: |
| ScopedGbmBoMap() = default; |
| ScopedGbmBoMap(void* map, gbm_bo* bo) : map_(map), bo_(bo) {} |
| |
| ~ScopedGbmBoMap() { |
| if (map_) |
| gbm_bo_unmap(bo_, map_); |
| } |
| |
| private: |
| void* map_ = nullptr; |
| gbm_bo* bo_ = nullptr; |
| }; |
| |
| class ScopedMmapMemory { |
| public: |
| ScopedMmapMemory() = default; |
| ScopedMmapMemory(void* addr, size_t length) |
| : addr_(addr), length_(length) {} |
| |
| ~ScopedMmapMemory() { |
| if (addr_ != MAP_FAILED) |
| munmap(addr_, length_); |
| } |
| |
| bool is_valid() { return addr_ != MAP_FAILED; } |
| void* get() { return addr_; } |
| |
| private: |
| void* addr_ = MAP_FAILED; |
| size_t length_ = 0; |
| }; |
| |
| constexpr uint32_t kFormatList[] = { |
| GBM_FORMAT_C8, |
| GBM_FORMAT_RGB332, |
| GBM_FORMAT_BGR233, |
| GBM_FORMAT_XRGB4444, |
| GBM_FORMAT_XBGR4444, |
| GBM_FORMAT_RGBX4444, |
| GBM_FORMAT_BGRX4444, |
| GBM_FORMAT_ARGB4444, |
| GBM_FORMAT_ABGR4444, |
| GBM_FORMAT_RGBA4444, |
| GBM_FORMAT_BGRA4444, |
| GBM_FORMAT_XRGB1555, |
| GBM_FORMAT_XBGR1555, |
| GBM_FORMAT_RGBX5551, |
| GBM_FORMAT_BGRX5551, |
| GBM_FORMAT_ARGB1555, |
| GBM_FORMAT_ABGR1555, |
| GBM_FORMAT_RGBA5551, |
| GBM_FORMAT_BGRA5551, |
| GBM_FORMAT_RGB565, |
| GBM_FORMAT_BGR565, |
| GBM_FORMAT_RGB888, |
| GBM_FORMAT_BGR888, |
| GBM_FORMAT_XRGB8888, |
| GBM_FORMAT_XBGR8888, |
| GBM_FORMAT_RGBX8888, |
| GBM_FORMAT_BGRX8888, |
| GBM_FORMAT_ARGB8888, |
| GBM_FORMAT_ABGR8888, |
| GBM_FORMAT_RGBA8888, |
| GBM_FORMAT_BGRA8888, |
| GBM_FORMAT_XRGB2101010, |
| GBM_FORMAT_XBGR2101010, |
| GBM_FORMAT_RGBX1010102, |
| GBM_FORMAT_BGRX1010102, |
| GBM_FORMAT_ARGB2101010, |
| GBM_FORMAT_ABGR2101010, |
| GBM_FORMAT_RGBA1010102, |
| GBM_FORMAT_BGRA1010102, |
| GBM_FORMAT_YUYV, |
| GBM_FORMAT_YVYU, |
| GBM_FORMAT_UYVY, |
| GBM_FORMAT_VYUY, |
| GBM_FORMAT_AYUV, |
| GBM_FORMAT_NV12, |
| GBM_FORMAT_YVU420, |
| }; |
| |
| std::string FormatToString(uint32_t format) { |
| switch (format) { |
| #define CASE(f) case f: return #f |
| |
| CASE(GBM_FORMAT_C8); |
| CASE(GBM_FORMAT_RGB332); |
| CASE(GBM_FORMAT_BGR233); |
| CASE(GBM_FORMAT_XRGB4444); |
| CASE(GBM_FORMAT_XBGR4444); |
| CASE(GBM_FORMAT_RGBX4444); |
| CASE(GBM_FORMAT_BGRX4444); |
| CASE(GBM_FORMAT_ARGB4444); |
| CASE(GBM_FORMAT_ABGR4444); |
| CASE(GBM_FORMAT_RGBA4444); |
| CASE(GBM_FORMAT_BGRA4444); |
| CASE(GBM_FORMAT_XRGB1555); |
| CASE(GBM_FORMAT_XBGR1555); |
| CASE(GBM_FORMAT_RGBX5551); |
| CASE(GBM_FORMAT_BGRX5551); |
| CASE(GBM_FORMAT_ARGB1555); |
| CASE(GBM_FORMAT_ABGR1555); |
| CASE(GBM_FORMAT_RGBA5551); |
| CASE(GBM_FORMAT_BGRA5551); |
| CASE(GBM_FORMAT_RGB565); |
| CASE(GBM_FORMAT_BGR565); |
| CASE(GBM_FORMAT_RGB888); |
| CASE(GBM_FORMAT_BGR888); |
| CASE(GBM_FORMAT_XRGB8888); |
| CASE(GBM_FORMAT_XBGR8888); |
| CASE(GBM_FORMAT_RGBX8888); |
| CASE(GBM_FORMAT_BGRX8888); |
| CASE(GBM_FORMAT_ARGB8888); |
| CASE(GBM_FORMAT_ABGR8888); |
| CASE(GBM_FORMAT_RGBA8888); |
| CASE(GBM_FORMAT_BGRA8888); |
| CASE(GBM_FORMAT_XRGB2101010); |
| CASE(GBM_FORMAT_XBGR2101010); |
| CASE(GBM_FORMAT_RGBX1010102); |
| CASE(GBM_FORMAT_BGRX1010102); |
| CASE(GBM_FORMAT_ARGB2101010); |
| CASE(GBM_FORMAT_ABGR2101010); |
| CASE(GBM_FORMAT_RGBA1010102); |
| CASE(GBM_FORMAT_BGRA1010102); |
| CASE(GBM_FORMAT_YUYV); |
| CASE(GBM_FORMAT_YVYU); |
| CASE(GBM_FORMAT_UYVY); |
| CASE(GBM_FORMAT_VYUY); |
| CASE(GBM_FORMAT_AYUV); |
| CASE(GBM_FORMAT_NV12); |
| CASE(GBM_FORMAT_YVU420); |
| #undef CASE |
| |
| default: |
| return "unknown format: " + std::to_string(format); |
| } |
| } |
| |
| constexpr uint32_t kUsageList[] = { |
| GBM_BO_USE_SCANOUT, |
| GBM_BO_USE_CURSOR_64X64, |
| GBM_BO_USE_RENDERING, |
| GBM_BO_USE_LINEAR, |
| GBM_BO_USE_SW_READ_OFTEN, |
| GBM_BO_USE_SW_READ_RARELY, |
| GBM_BO_USE_SW_WRITE_OFTEN, |
| GBM_BO_USE_SW_WRITE_RARELY, |
| }; |
| |
| std::string UsageToString(uint32_t usage) { |
| switch(usage) { |
| #define CASE(f) case f: return #f |
| |
| CASE(GBM_BO_USE_SCANOUT); |
| CASE(GBM_BO_USE_CURSOR_64X64); |
| CASE(GBM_BO_USE_RENDERING); |
| CASE(GBM_BO_USE_LINEAR); |
| CASE(GBM_BO_USE_SW_READ_OFTEN); |
| CASE(GBM_BO_USE_SW_READ_RARELY); |
| CASE(GBM_BO_USE_SW_WRITE_OFTEN); |
| CASE(GBM_BO_USE_SW_WRITE_RARELY); |
| #undef CASE |
| |
| default: |
| return "unknown usage: " + std::to_string(usage); |
| } |
| } |
| |
| constexpr uint32_t kBufferList[] = { |
| GBM_BO_USE_SCANOUT | GBM_BO_USE_SW_READ_RARELY | GBM_BO_USE_SW_WRITE_RARELY, |
| GBM_BO_USE_RENDERING | GBM_BO_USE_SW_READ_RARELY | GBM_BO_USE_SW_WRITE_RARELY, |
| GBM_BO_USE_SW_READ_RARELY | GBM_BO_USE_SW_WRITE_RARELY, |
| GBM_BO_USE_SW_READ_RARELY | GBM_BO_USE_SW_WRITE_RARELY | GBM_BO_USE_TEXTURING, |
| GBM_BO_USE_SW_READ_RARELY | GBM_BO_USE_SW_WRITE_RARELY | GBM_BO_USE_TEXTURING, |
| GBM_BO_USE_RENDERING | GBM_BO_USE_SW_READ_RARELY | |
| GBM_BO_USE_SW_WRITE_RARELY | GBM_BO_USE_TEXTURING, |
| GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT | GBM_BO_USE_SW_READ_RARELY | |
| GBM_BO_USE_SW_WRITE_RARELY, |
| GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT | GBM_BO_USE_SW_READ_RARELY | |
| GBM_BO_USE_SW_WRITE_RARELY | GBM_BO_USE_TEXTURING, |
| }; |
| |
| void ExpectBo(gbm_bo* bo) { |
| ASSERT_TRUE(bo); |
| EXPECT_GE(gbm_bo_get_width(bo), 0); |
| EXPECT_GE(gbm_bo_get_height(bo), 0); |
| EXPECT_GE(gbm_bo_get_stride(bo), gbm_bo_get_width(bo)); |
| |
| const uint32_t format = gbm_bo_get_format(bo); |
| |
| EXPECT_TRUE(base::Contains(kFormatList, format)) << format; |
| |
| const size_t num_planes = gbm_bo_get_plane_count(bo); |
| switch (format) { |
| case GBM_FORMAT_NV12: |
| EXPECT_EQ(2, num_planes); |
| break; |
| case GBM_FORMAT_YVU420: |
| EXPECT_EQ(3, num_planes); |
| break; |
| default: |
| EXPECT_EQ(1, num_planes); |
| break; |
| } |
| |
| EXPECT_EQ(gbm_bo_get_handle_for_plane(bo, 0).u32, gbm_bo_get_handle(bo).u32); |
| |
| EXPECT_EQ(0, gbm_bo_get_offset(bo, 0)); |
| EXPECT_GE(gbm_bo_get_plane_size(bo, 0), |
| gbm_bo_get_width(bo) * gbm_bo_get_height(bo)); |
| EXPECT_EQ(gbm_bo_get_stride_for_plane(bo, 0), gbm_bo_get_stride(bo)); |
| |
| for (size_t plane = 0; plane < num_planes; ++plane) { |
| EXPECT_GT(gbm_bo_get_handle_for_plane(bo, plane).u32, 0); |
| { |
| base::ScopedFD fd(gbm_bo_get_plane_fd(bo, plane)); |
| EXPECT_TRUE(fd.is_valid()); |
| } |
| gbm_bo_get_offset(bo, plane); // Make sure no crash. |
| EXPECT_GT(gbm_bo_get_plane_size(bo, plane), 0); |
| EXPECT_GT(gbm_bo_get_stride_for_plane(bo, plane), 0); |
| } |
| } |
| |
| #define EXPECT_BO(expr) \ |
| EXPECT_NO_FATAL_FAILURE(ExpectBo((expr))) |
| |
| // Fails with any unexpected behavior for the given bo. |
| #define ASSERT_BO(expr) \ |
| do { \ |
| ExpectBo((expr)); \ |
| ASSERT_FALSE(::testing::Test::HasFailure()); \ |
| } while(0) |
| |
| bool HasConnectedConnector(int fd, const drmModeRes* resources) { |
| for (int i = 0; i < resources->count_connectors; ++i) { |
| ScopedDrmModeConnector connector( |
| drmModeGetConnector(fd, resources->connectors[i])); |
| if (!connector) |
| continue; |
| if (connector->count_modes > 0 && |
| connector->connection == DRM_MODE_CONNECTED) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| ScopedDrmFD DrmOpen() { |
| // Find the first drm device with a connected display. |
| for (int i = 0; i < DRM_MAX_MINOR; ++i) { |
| auto dev_name = base::StringPrintf(DRM_DEV_NAME, DRM_DIR_NAME, i); |
| ScopedDrmFD fd(HANDLE_EINTR(open(dev_name.c_str(), O_RDWR))); |
| if (!fd.is_valid()) |
| continue; |
| |
| ScopedDrmModeResources resources(drmModeGetResources(fd.get())); |
| if (!resources) |
| continue; |
| |
| if (resources->count_crtcs > 0 && |
| HasConnectedConnector(fd.get(), resources.get())) { |
| return fd; |
| } |
| } |
| |
| // If no drm device has a connected display, fall back to the first |
| // drm device. |
| for (int i = 0; i < DRM_MAX_MINOR; ++i) { |
| auto dev_name = base::StringPrintf(DRM_DEV_NAME, DRM_DIR_NAME, i); |
| ScopedDrmFD fd(HANDLE_EINTR(open(dev_name.c_str(), O_RDWR))); |
| if (fd.is_valid()) |
| return fd; |
| } |
| |
| return {}; |
| } |
| |
| class GraphicsGbmTest : public testing::Test { |
| public: |
| GraphicsGbmTest() : fd_(DrmOpen()), gbm_(gbm_create_device(fd_.get())) {} |
| ~GraphicsGbmTest() = default; |
| |
| void SetUp() override { |
| ASSERT_TRUE(fd_.is_valid()); |
| ASSERT_TRUE(gbm_.get()); |
| } |
| |
| protected: |
| ScopedDrmFD fd_; |
| ScopedGbmDevice gbm_; |
| }; |
| |
| TEST_F(GraphicsGbmTest, BackendName) { |
| EXPECT_TRUE(gbm_device_get_backend_name(gbm_.get())); |
| } |
| |
| TEST_F(GraphicsGbmTest, Reinit) { |
| gbm_.reset(); |
| fd_.reset(); |
| |
| fd_ = DrmOpen(); |
| ASSERT_TRUE(fd_.is_valid()); |
| gbm_.reset(gbm_create_device(fd_.get())); |
| ASSERT_TRUE(gbm_.get()); |
| |
| EXPECT_TRUE(gbm_device_get_backend_name(gbm_.get())); |
| |
| ScopedGbmBo bo(gbm_bo_create( |
| gbm_.get(), 1024, 1024, GBM_FORMAT_XRGB8888, GBM_BO_USE_RENDERING)); |
| EXPECT_BO(bo.get()); |
| } |
| |
| // Tests repeated alloc/free. |
| TEST_F(GraphicsGbmTest, AllocFree) { |
| for (int i = 0; i < 1000; ++i) { |
| ScopedGbmBo bo(gbm_bo_create( |
| gbm_.get(), 1024, 1024, GBM_FORMAT_XRGB8888, GBM_BO_USE_RENDERING)); |
| EXPECT_BO(bo.get()); |
| } |
| } |
| |
| // Tests that we can allocate different buffer dimensions. |
| TEST_F(GraphicsGbmTest, AllocFreeSizes) { |
| // Test i * i size. |
| for (int i = 1; i < 1920; ++i) { |
| SCOPED_TRACE("i: " + std::to_string(i)); |
| ScopedGbmBo bo(gbm_bo_create( |
| gbm_.get(), i, i, GBM_FORMAT_XRGB8888, GBM_BO_USE_RENDERING)); |
| EXPECT_BO(bo.get()); |
| } |
| |
| // Test i * 1 size. |
| for (int i = 1; i < 1920; ++i) { |
| SCOPED_TRACE("size: " + std::to_string(i) + " x 1"); |
| ScopedGbmBo bo(gbm_bo_create( |
| gbm_.get(), i, 1, GBM_FORMAT_XRGB8888, GBM_BO_USE_RENDERING)); |
| EXPECT_BO(bo.get()); |
| } |
| |
| // Test 1 * i size. |
| for (int i = 1; i < 1920; ++i) { |
| SCOPED_TRACE("size: 1 x " + std::to_string(i)); |
| ScopedGbmBo bo(gbm_bo_create( |
| gbm_.get(), 1, i, GBM_FORMAT_XRGB8888, GBM_BO_USE_RENDERING)); |
| EXPECT_BO(bo.get()); |
| } |
| } |
| |
| // Tests that we can allocate different buffer formats. |
| TEST_F(GraphicsGbmTest, AllocFreeFormats) { |
| for (const auto format : kFormatList) { |
| if (!gbm_device_is_format_supported( |
| gbm_.get(), format, GBM_BO_USE_RENDERING)) { |
| continue; |
| } |
| SCOPED_TRACE("Format: " + FormatToString(format)); |
| ScopedGbmBo bo(gbm_bo_create( |
| gbm_.get(), 1024, 1024, format, GBM_BO_USE_RENDERING)); |
| EXPECT_BO(bo.get()); |
| } |
| } |
| |
| // Tests that we find at least one working format for each usage. |
| TEST_F(GraphicsGbmTest, AllocFreeUsage) { |
| for (const auto usage : kUsageList) { |
| SCOPED_TRACE("Usage: " + UsageToString(usage)); |
| bool found = false; |
| const uint32_t size = usage == GBM_BO_USE_CURSOR_64X64 ? 64 : 1024; |
| for (const auto format : kFormatList) { |
| if (!gbm_device_is_format_supported(gbm_.get(), format, usage)) |
| continue; |
| SCOPED_TRACE("Format: " + FormatToString(format)); |
| ScopedGbmBo bo(gbm_bo_create(gbm_.get(), size, size, format, usage)); |
| EXPECT_BO(bo.get()); |
| found = true; |
| } |
| EXPECT_TRUE(found) << "Available format is not found"; |
| } |
| } |
| |
| // Tests user data. |
| TEST_F(GraphicsGbmTest, UserData) { |
| ScopedGbmBo bo1(gbm_bo_create( |
| gbm_.get(), 1024, 1024, GBM_FORMAT_XRGB8888, GBM_BO_USE_RENDERING)); |
| ASSERT_BO(bo1.get()); |
| ScopedGbmBo bo2(gbm_bo_create( |
| gbm_.get(), 1024, 1024, GBM_FORMAT_XRGB8888, GBM_BO_USE_RENDERING)); |
| ASSERT_BO(bo2.get()); |
| |
| bool destroyed1 = false; |
| bool destroyed2 = false; |
| auto destroy = [](struct gbm_bo* bo, void* data) { |
| *static_cast<bool*>(data) = true; |
| }; |
| gbm_bo_set_user_data(bo1.get(), &destroyed1, destroy); |
| gbm_bo_set_user_data(bo2.get(), &destroyed2, destroy); |
| |
| EXPECT_EQ(gbm_bo_get_user_data(bo1.get()), &destroyed1); |
| EXPECT_EQ(gbm_bo_get_user_data(bo2.get()), &destroyed2); |
| |
| bo1.reset(); |
| EXPECT_TRUE(destroyed1); |
| |
| gbm_bo_set_user_data(bo2.get(), nullptr, nullptr); |
| bo2.reset(); |
| EXPECT_FALSE(destroyed2); |
| } |
| |
| // Tests prime export. |
| TEST_F(GraphicsGbmTest, Export) { |
| ScopedGbmBo bo(gbm_bo_create( |
| gbm_.get(), 1024, 1024, GBM_FORMAT_XRGB8888, GBM_BO_USE_RENDERING)); |
| ASSERT_BO(bo.get()); |
| |
| base::ScopedFD prime_fd(gbm_bo_get_fd(bo.get())); |
| EXPECT_TRUE(prime_fd.is_valid()); |
| } |
| |
| // Tests prime import using dma-buf API. |
| TEST_F(GraphicsGbmTest, ImportDmabuf) { |
| constexpr uint32_t kWidth = 123; |
| constexpr uint32_t kHeight = 456; |
| |
| ScopedGbmBo bo1(gbm_bo_create( |
| gbm_.get(), kWidth, kHeight, GBM_FORMAT_XRGB8888, GBM_BO_USE_RENDERING)); |
| ASSERT_BO(bo1.get()); |
| |
| base::ScopedFD prime_fd(gbm_bo_get_fd(bo1.get())); |
| ASSERT_TRUE(prime_fd.is_valid()); |
| |
| struct gbm_import_fd_data fd_data; |
| fd_data.fd = prime_fd.get(); |
| fd_data.width = kWidth; |
| fd_data.height = kHeight; |
| fd_data.stride = gbm_bo_get_stride(bo1.get()); |
| fd_data.format = GBM_FORMAT_XRGB8888; |
| |
| bo1.reset(); |
| |
| ScopedGbmBo bo2(gbm_bo_import( |
| gbm_.get(), GBM_BO_IMPORT_FD, &fd_data, GBM_BO_USE_RENDERING)); |
| ASSERT_BO(bo2.get()); |
| EXPECT_EQ(kWidth, gbm_bo_get_width(bo2.get())); |
| EXPECT_EQ(kHeight, gbm_bo_get_height(bo2.get())); |
| EXPECT_EQ(fd_data.stride, gbm_bo_get_stride(bo2.get())); |
| } |
| |
| // Tests GBM_BO_IMPORT_FD_MODIFIER entry point. |
| TEST_F(GraphicsGbmTest, ImportModifier) { |
| constexpr uint32_t kWidth = 567; |
| constexpr uint32_t kHeight = 891; |
| |
| for (const auto format : kFormatList) { |
| if (!gbm_device_is_format_supported( |
| gbm_.get(), format, GBM_BO_USE_RENDERING)) { |
| continue; |
| } |
| SCOPED_TRACE("Format: " + std::to_string(format)); |
| ScopedGbmBo bo1(gbm_bo_create( |
| gbm_.get(), kWidth, kHeight, format, GBM_BO_USE_RENDERING)); |
| const size_t num_planes = gbm_bo_get_plane_count(bo1.get()); |
| |
| std::vector<base::ScopedFD> fds; |
| for (size_t p = 0; p < num_planes; ++p) |
| fds.emplace_back(gbm_bo_get_plane_fd(bo1.get(), p)); |
| |
| struct gbm_import_fd_modifier_data fd_data; |
| fd_data.num_fds = num_planes; |
| for (size_t p = 0; p < num_planes; ++p) { |
| fd_data.fds[p] = fds[p].get(); |
| fd_data.strides[p] = gbm_bo_get_stride_for_plane(bo1.get(), p); |
| fd_data.offsets[p] = gbm_bo_get_offset(bo1.get(), p); |
| } |
| fd_data.modifier = gbm_bo_get_modifier(bo1.get()); |
| fd_data.width = kWidth; |
| fd_data.height = kHeight; |
| fd_data.format = format; |
| |
| bo1.reset(); |
| |
| ScopedGbmBo bo2(gbm_bo_import( |
| gbm_.get(), GBM_BO_IMPORT_FD_MODIFIER, &fd_data, GBM_BO_USE_RENDERING)); |
| |
| ASSERT_BO(bo2.get()); |
| EXPECT_EQ(kWidth, gbm_bo_get_width(bo2.get())); |
| EXPECT_EQ(kHeight, gbm_bo_get_height(bo2.get())); |
| EXPECT_EQ(fd_data.modifier, gbm_bo_get_modifier(bo2.get())); |
| |
| for (size_t p = 0; p < num_planes; ++p) { |
| EXPECT_EQ(fd_data.strides[p], gbm_bo_get_stride_for_plane(bo2.get(), p)) |
| << "Unexpected stride at " << p; |
| EXPECT_EQ(fd_data.offsets[p], gbm_bo_get_offset(bo2.get(), p)) |
| << "Unexpected offset at " << p; |
| } |
| } |
| } |
| |
| TEST_F(GraphicsGbmTest, GemMap) { |
| constexpr uint32_t kWidth = 666; |
| constexpr uint32_t kHeight = 777; |
| |
| ScopedGbmBo bo(gbm_bo_create( |
| gbm_.get(), kWidth, kHeight, GBM_FORMAT_ARGB8888, |
| GBM_BO_USE_SW_READ_RARELY | GBM_BO_USE_SW_WRITE_RARELY)); |
| ASSERT_BO(bo.get()); |
| |
| { |
| void* raw_map_data = nullptr; |
| uint32_t stride = 0; |
| void* addr = gbm_bo_map(bo.get(), 0, 0, kWidth, kHeight, |
| GBM_BO_TRANSFER_READ_WRITE, &stride, &raw_map_data); |
| ASSERT_NE(MAP_FAILED, addr); |
| ASSERT_TRUE(raw_map_data); |
| ScopedGbmBoMap map_data(raw_map_data, bo.get()); |
| EXPECT_GT(stride, 0); |
| |
| uint32_t* pixel = static_cast<uint32_t*>(addr); |
| pixel[(kHeight / 2) * (stride / kBytesPerPixel) + kWidth / 2] = 0xABBAABBA; |
| } |
| |
| // Remap and verify written previously data. |
| { |
| void* raw_map_data = nullptr; |
| uint32_t stride = 0; |
| void* addr = gbm_bo_map(bo.get(), 0, 0, kWidth, kHeight, |
| GBM_BO_TRANSFER_READ_WRITE, &stride, &raw_map_data); |
| ASSERT_NE(MAP_FAILED, addr); |
| ASSERT_TRUE(raw_map_data); |
| ScopedGbmBoMap map_data(raw_map_data, bo.get()); |
| EXPECT_GT(stride, 0); |
| uint32_t* pixel = static_cast<uint32_t*>(addr); |
| EXPECT_EQ(0xABBAABBA, |
| pixel[(kHeight / 2) * (stride / kBytesPerPixel) + kWidth / 2]); |
| } |
| } |
| |
| TEST_F(GraphicsGbmTest, DmabufMap) { |
| constexpr uint32_t kWidth = 666; |
| constexpr uint32_t kHeight = 777; |
| |
| ScopedGbmBo bo(gbm_bo_create( |
| gbm_.get(), kWidth, kHeight, GBM_FORMAT_ARGB8888, GBM_BO_USE_LINEAR)); |
| ASSERT_BO(bo.get()); |
| |
| { |
| base::ScopedFD prime_fd(gbm_bo_get_fd(bo.get())); |
| ASSERT_TRUE(prime_fd.is_valid()); |
| |
| uint32_t stride = gbm_bo_get_stride(bo.get()); |
| ASSERT_GT(stride, 0); |
| uint32_t length = gbm_bo_get_plane_size(bo.get(), 0); |
| ASSERT_GT(length, 0); |
| |
| ScopedMmapMemory addr( |
| mmap(nullptr, length, (PROT_READ | PROT_WRITE), MAP_SHARED, |
| prime_fd.get(), 0), |
| length); |
| ASSERT_TRUE(addr.is_valid()); |
| |
| uint32_t* pixel = static_cast<uint32_t*>(addr.get()); |
| size_t stride_pixels = stride / kBytesPerPixel; |
| |
| struct dma_buf_sync sync_start = { |
| DMA_BUF_SYNC_START | DMA_BUF_SYNC_WRITE |
| }; |
| ASSERT_EQ( |
| 0, |
| HANDLE_EINTR(ioctl(prime_fd.get(), DMA_BUF_IOCTL_SYNC, &sync_start))); |
| |
| for (uint32_t y = 0; y < kHeight; ++y) { |
| for (uint32_t x = 0; x < kWidth; ++x) |
| pixel[y * stride_pixels + x] = ((y << 16) | x); |
| } |
| |
| struct dma_buf_sync sync_end = { DMA_BUF_SYNC_END | DMA_BUF_SYNC_WRITE}; |
| ASSERT_EQ( |
| 0, |
| HANDLE_EINTR(ioctl(prime_fd.get(), DMA_BUF_IOCTL_SYNC, &sync_end))); |
| } |
| |
| { |
| base::ScopedFD prime_fd(gbm_bo_get_fd(bo.get())); |
| ASSERT_TRUE(prime_fd.is_valid()); |
| |
| uint32_t stride = gbm_bo_get_stride(bo.get()); |
| ASSERT_GT(stride, 0); |
| uint32_t length = gbm_bo_get_plane_size(bo.get(), 0); |
| ASSERT_GT(length, 0); |
| |
| ScopedMmapMemory addr( |
| mmap(nullptr, length, (PROT_READ | PROT_WRITE), MAP_SHARED, |
| prime_fd.get(), 0), |
| length); |
| ASSERT_TRUE(addr.is_valid()); |
| |
| uint32_t* pixel = static_cast<uint32_t*>(addr.get()); |
| size_t stride_pixels = stride / kBytesPerPixel; |
| |
| struct dma_buf_sync sync_start = { |
| DMA_BUF_SYNC_START | DMA_BUF_SYNC_WRITE |
| }; |
| ASSERT_EQ( |
| 0, |
| HANDLE_EINTR(ioctl(prime_fd.get(), DMA_BUF_IOCTL_SYNC, &sync_start))); |
| |
| for (uint32_t y = 0; y < kHeight; ++y) { |
| for (uint32_t x = 0; x < kWidth; ++x) { |
| EXPECT_EQ(((y << 16) | x), pixel[y * stride_pixels + x]) |
| << "Pixel mismatch at (" << x << ", " << y << ")"; |
| } |
| } |
| |
| struct dma_buf_sync sync_end = { DMA_BUF_SYNC_END | DMA_BUF_SYNC_WRITE }; |
| ASSERT_EQ( |
| 0, |
| HANDLE_EINTR(ioctl(prime_fd.get(), DMA_BUF_IOCTL_SYNC, &sync_end))); |
| } |
| |
| void* raw_map_data = nullptr; |
| uint32_t stride = 0; |
| void* addr = gbm_bo_map(bo.get(), 0, 0, kWidth, kHeight, |
| GBM_BO_TRANSFER_READ_WRITE, &stride, &raw_map_data); |
| ASSERT_NE(MAP_FAILED, addr); |
| ASSERT_TRUE(raw_map_data); |
| ScopedGbmBoMap map_data(raw_map_data, bo.get()); |
| EXPECT_GT(stride, 0); |
| uint32_t* pixel = static_cast<uint32_t*>(addr); |
| size_t stride_pixels = stride / kBytesPerPixel; |
| |
| for (uint32_t y = 0; y < kHeight; ++y) { |
| for (uint32_t x = 0; x < kWidth; ++x) { |
| EXPECT_EQ(((y << 16) | x), pixel[y * stride_pixels + x]) |
| << "Pixel mismatch at (" << x << ", " << y << ")"; |
| } |
| } |
| } |
| |
| TEST_F(GraphicsGbmTest, GemMapTiling) { |
| // TODO(crbug.com/752669) |
| if (strcmp(gbm_device_get_backend_name(gbm_.get()), "tegra")) |
| return; |
| |
| constexpr uint32_t kWidth = 666; |
| constexpr uint32_t kHeight = 777; |
| |
| for (const auto buffer_create_flag : kBufferList) { |
| ScopedGbmBo bo(gbm_bo_create( |
| gbm_.get(), kWidth, kHeight, GBM_FORMAT_ARGB8888, buffer_create_flag)); |
| ASSERT_BO(bo.get()); |
| |
| { |
| void* raw_map_data = nullptr; |
| uint32_t stride = 0; |
| void* addr = gbm_bo_map(bo.get(), 0, 0, kWidth, kHeight, |
| GBM_BO_TRANSFER_WRITE, &stride, &raw_map_data); |
| ASSERT_NE(MAP_FAILED, addr); |
| ASSERT_TRUE(addr); |
| ASSERT_TRUE(raw_map_data); |
| ScopedGbmBoMap map_data(raw_map_data, bo.get()); |
| |
| uint32_t* pixel = static_cast<uint32_t*>(addr); |
| const uint32_t stride_pixels = stride / kBytesPerPixel; |
| |
| for (uint32_t y = 0; y < kHeight; ++y) { |
| for (uint32_t x = 0; x < kWidth; ++x) |
| pixel[y * stride_pixels + x] = ((y << 16 | x)); |
| } |
| } |
| |
| // Remap and verify written previously data. |
| { |
| void* raw_map_data = nullptr; |
| uint32_t stride = 0; |
| void* addr = gbm_bo_map(bo.get(), 0, 0, kWidth, kHeight, |
| GBM_BO_TRANSFER_WRITE, &stride, &raw_map_data); |
| ASSERT_NE(MAP_FAILED, addr); |
| ASSERT_TRUE(addr); |
| ASSERT_TRUE(raw_map_data); |
| ScopedGbmBoMap map_data(raw_map_data, bo.get()); |
| |
| uint32_t* pixel = static_cast<uint32_t*>(addr); |
| const uint32_t stride_pixels = stride / kBytesPerPixel; |
| |
| for (uint32_t y = 0; y < kHeight; ++y) { |
| for (uint32_t x = 0; x < kWidth; ++x) { |
| EXPECT_EQ(((y << 16) | x), pixel[y * stride_pixels + x]) |
| << "Pixel mismatch at (" << x << ", " << y << ")"; |
| } |
| } |
| } |
| } |
| } |
| |
| } // namespace |
| |
| int main(int argc, char** argv) { |
| testing::InitGoogleTest(&argc, argv); |
| return RUN_ALL_TESTS(); |
| } |