blob: b747ae9f29912149fbce9e76f49bdce893163fea [file] [log] [blame]
// Copyright 2019 The Chromium OS 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 <fcntl.h>
#include <sys/stat.h>
#include <sys/types.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 {
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>;
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,
};
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);
// TODO(crbug.com/909719): Use base::ContainsValue, after uprev (which
// supports an array).
EXPECT_TRUE(
std::find(std::begin(kFormatList), std::end(kFormatList), format) !=
std::end(kFormatList)) << 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);
}
}
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() {
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;
}
}
return {};
}
class GraphicsGbmTest : public testing::Test {
public:
GraphicsGbmTest() : fd_(DrmOpen()), gbm_(gbm_create_device(fd_.get())) {
EXPECT_TRUE(fd_.is_valid());
EXPECT_TRUE(gbm_.get());
}
~GraphicsGbmTest() = default;
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));
ExpectBo(bo.get());
}
} // namespace
int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}