From 526cbca855ea6310dc846cc05158d403aea456e1 Mon Sep 17 00:00:00 2001 From: "zejian.su" Date: Mon, 27 Nov 2023 18:02:17 +0800 Subject: [PATCH 1/2] In DRM preview, zoom the input image to fit the screen. 1. Use the atomic drm mode. 2. Find a mode in the connector with most closed area with the input image. 3. Set the plane property to scale the input image. Signed-off-by: zejian.su --- preview/drm_preview.cpp | 184 ++++++++++++++++++++++++++++++++-------- 1 file changed, 147 insertions(+), 37 deletions(-) diff --git a/preview/drm_preview.cpp b/preview/drm_preview.cpp index 38fc6a2..405bdca 100644 --- a/preview/drm_preview.cpp +++ b/preview/drm_preview.cpp @@ -17,6 +17,92 @@ #include "preview.hpp" +class DRMObject +{ +public: + DRMObject(int drmfd, uint32_t id, uint32_t type) + : id_(id), type_(type){ + drmModeObjectProperties *properties = drmModeObjectGetProperties(drmfd, id, type); + if (!properties) + return; + + drmModePropertyPtr property; + for (uint32_t i = 0; i < properties->count_props; i++) { + property = drmModeGetProperty(drmfd, properties->props[i]); + property_[std::string(property->name)] = property->prop_id; + drmModeFreeProperty(property); + } + + drmModeFreeObjectProperties(properties); + } + + uint32_t getID() const {return id_;}; + +public: + std::unordered_map property_; + +private: + uint32_t id_; + uint32_t type_; +}; + +class AtomicRequest +{ +public: + enum Flags { + FlagAllowModeset = (1 << 0), + FlagAsync = (1 << 1), + FlagTestOnly = (1 << 2), + }; + + AtomicRequest() : valid_(true) { + request_ = drmModeAtomicAlloc(); + if (!request_) + valid_ = false; + } + + ~AtomicRequest() { + if (request_) + drmModeAtomicFree(request_); + } + + bool isValid() const { return valid_; } + + int addProperty(int drmfd, const DRMObject *object, const std::string &propertyName, + uint64_t value) { + + if (!valid_) + return -EINVAL; + + auto it = object->property_.find(propertyName); + if(it == object->property_.end()) + return -EINVAL; + + if(drmModeAtomicAddProperty(request_, object->getID(), it->second, value) < 0) { + std::string errMsg = "drmModeAtomicAddProperty() failed -- object: " + + std::to_string(object->getID()) + ", property: " + std::to_string(it->second); + throw std::runtime_error(errMsg); + } + + return 0; + } + + int commit(int drmfd, unsigned int flags = 0) { + if(drmModeAtomicCommit(drmfd, request_, flags, NULL) < 0) + throw std::runtime_error("drmModeAtomicCommit() failed"); + return 0; + } + +private: + AtomicRequest(const AtomicRequest &) = delete; + AtomicRequest(const AtomicRequest &&) = delete; + AtomicRequest &operator=(const AtomicRequest &) = delete; + AtomicRequest &operator=(const AtomicRequest &&) = delete; + + bool valid_; + drmModeAtomicReq *request_; +}; + class DrmPreview : public Preview { public: @@ -48,6 +134,7 @@ private: void makeBuffer(int fd, size_t size, StreamInfo const &info, Buffer &buffer); void findCrtc(); void findPlane(); + void fitDisplaySize(); int drmfd_; int conId_; drmModeConnector *con_; @@ -61,11 +148,18 @@ private: unsigned int height_; unsigned int screen_width_; unsigned int screen_height_; + unsigned int display_x_; + unsigned int display_y_; + unsigned int display_w_; + unsigned int display_h_; std::map buffers_; // map the DMABUF's fd to the Buffer int last_fd_; unsigned int max_image_width_; unsigned int max_image_height_; bool first_time_; + + std::unique_ptr connector_; + std::unique_ptr crtc_; }; #define ERRSTR strerror(errno) @@ -96,10 +190,6 @@ void DrmPreview::findCrtc() if (con->encoder_id) { enc = drmModeGetEncoder(drmfd_, con->encoder_id); - //if (enc->crtc_id) - //{ - // crtc = drmModeGetCrtc(drmfd_, enc->crtc_id); - //} } else { enc = drmModeGetEncoder(drmfd_, con->encoders[0]); @@ -229,6 +319,7 @@ void DrmPreview::findPlane() drmModeFreePlane(plane); break; } + planeId_ = planes->planes[0]; } catch (std::exception const &e) { @@ -265,21 +356,16 @@ DrmPreview::DrmPreview(Options const *options) : Preview(options), last_fd_(-1), //out_fourcc_ = DRM_FORMAT_YUV420; out_fourcc_ = DRM_FORMAT_NV12; findPlane(); + + drmSetClientCap(drmfd_, DRM_CLIENT_CAP_ATOMIC, 1); + connector_ = std::make_unique(drmfd_, conId_, DRM_MODE_OBJECT_CONNECTOR); + crtc_ = std::make_unique(drmfd_, crtcId_, DRM_MODE_OBJECT_CRTC); } catch (std::exception const &e) { close(drmfd_); throw; } - - // Default behaviour here is to go fullscreen. - if (options_->fullscreen || width_ == 0 || height_ == 0 || x_ + width_ > screen_width_ || - y_ + height_ > screen_height_) - { - x_ = y_ = 0; - width_ = screen_width_; - height_ = screen_height_; - } } DrmPreview::~DrmPreview() @@ -365,9 +451,23 @@ static void setup_colour_space(int fd, int plane_id, std::optional (int64_t)screen_width_ * height_) { + display_x_ = 0, display_w_ = screen_width_; + display_h_ = ((int64_t)screen_width_ * height_) / (int64_t)width_; + display_y_ = (screen_height_ - display_h_) >> 1; + } else { + display_y_ = 0, display_h_ = screen_height_; + display_w_ = ((int64_t)width_ * screen_height_) / (int64_t)height_; + display_x_ = (screen_width_ - display_w_) >> 1; + } +} + void DrmPreview::makeBuffer(int fd, size_t size, StreamInfo const &info, Buffer &buffer) { unsigned int width = info.width, height = info.height, stride = info.stride; + int64_t targetArea = 0; if (first_time_) { first_time_ = false; @@ -379,6 +479,10 @@ void DrmPreview::makeBuffer(int fd, size_t size, StreamInfo const &info, Buffer buffer.size = size; buffer.info = info; + if(!width_ || !height) + width_ = info.width, height_ = info.height; + targetArea = (int64_t)width_ * height_; + if (drmPrimeFDToHandle(drmfd_, fd, &buffer.bo_handle)) throw std::runtime_error("drmPrimeFDToHandle failed for fd " + std::to_string(fd)); @@ -401,33 +505,46 @@ void DrmPreview::makeBuffer(int fd, size_t size, StreamInfo const &info, Buffer /* find preferred mode */ drmModeModeInfo *modeptr = NULL, *preferred = NULL; + int64_t minDiff = -1; for (int m = 0; m < con_->count_modes; m++) { + int64_t area, areaDiff; modeptr = &con_->modes[m]; - if (modeptr->hdisplay == width && modeptr->vdisplay == height) { + + area = (int64_t)modeptr->hdisplay * modeptr->vdisplay; + areaDiff = abs(targetArea - area); + if(-1 == minDiff) + minDiff = abs(targetArea - area), preferred = modeptr; + else { + if(areaDiff < minDiff) + minDiff = areaDiff, preferred = modeptr; + } + + if (!minDiff) { preferred = modeptr; std::cout << "find the matched mode, modes index= " << m << ", " << width << "x" << height << std::endl; break; } - if (modeptr->type & DRM_MODE_TYPE_PREFERRED) { - preferred = modeptr; - std::cout << "find perferred mode, modes index= " << m << std::endl; - } - } - - if (!preferred) - preferred = &con_->modes[0]; - - // set default - if (drmModeSetCrtc(drmfd_, crtcId_, buffer.fb_handle, 0, 0, - (uint32_t *)&conId_, 1, preferred)) { - throw std::runtime_error("drmModeSetCrtc() failed"); } screen_width_ = preferred->hdisplay; screen_height_ = preferred->vdisplay; - width_ = width; - height_ = height; + fitDisplaySize(); + + AtomicRequest ar; + uint32_t blob_id; + + drmModeCreatePropertyBlob(drmfd_, preferred, sizeof(con_->modes[0]), &blob_id); + ar.addProperty(drmfd_, connector_.get(), "CRTC_ID", crtcId_); + ar.addProperty(drmfd_, crtc_.get(), "ACTIVE", 1); + ar.addProperty(drmfd_, crtc_.get(), "MODE_ID", blob_id); + ar.commit(drmfd_, DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_ATOMIC_ALLOW_MODESET); + + // set default + //if (drmModeSetCrtc(drmfd_, crtcId_, buffer.fb_handle, 0, 0, + // (uint32_t *)&conId_, 1, preferred)) { + // throw std::runtime_error("drmModeSetCrtc() failed"); + //} } void DrmPreview::Show(int fd, libcamera::Span span, StreamInfo const &info) @@ -436,14 +553,7 @@ void DrmPreview::Show(int fd, libcamera::Span span, StreamInfo const &i if (buffer.fd == -1) makeBuffer(fd, span.size(), info, buffer); - unsigned int x_off = 0, y_off = 0; - unsigned int w = width_, h = height_; - if (info.width * height_ > width_ * info.height) - h = width_ * info.height / info.width, y_off = (height_ - h) / 2; - else - w = height_ * info.width / info.height, x_off = (width_ - w) / 2; - - if (drmModeSetPlane(drmfd_, planeId_, crtcId_, buffer.fb_handle, 0, x_off + x_, y_off + y_, w, h, 0, 0, + if (drmModeSetPlane(drmfd_, planeId_, crtcId_, buffer.fb_handle, 0, display_x_, display_y_, display_w_, display_h_, 0, 0, buffer.info.width << 16, buffer.info.height << 16)) throw std::runtime_error("drmModeSetPlane failed: " + std::string(ERRSTR)); if (last_fd_ >= 0) -- 2.34.1