11#include <config-kwin.h>
34#include <drm_fourcc.h>
36#include <libdrm/drm_mode.h>
38#include <xf86drmMode.h>
40#ifndef DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT
41#define DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT 6
49 , m_deviceId(deviceId)
51 , m_atomicModeSetting(false)
52 , m_gbmDevice(nullptr)
55 uint64_t capability = 0;
57 if (
drmGetCap(
fd, DRM_CAP_CURSOR_WIDTH, &capability) == 0) {
58 m_cursorSize.setWidth(capability);
60 m_cursorSize.setWidth(64);
63 if (
drmGetCap(
fd, DRM_CAP_CURSOR_HEIGHT, &capability) == 0) {
64 m_cursorSize.setHeight(capability);
66 m_cursorSize.setHeight(64);
69 int ret =
drmGetCap(
fd, DRM_CAP_TIMESTAMP_MONOTONIC, &capability);
70 if (ret == 0 && capability == 1) {
71 m_presentationClock = CLOCK_MONOTONIC;
73 m_presentationClock = CLOCK_REALTIME;
76 m_addFB2ModifiersSupported =
drmGetCap(
fd, DRM_CAP_ADDFB2_MODIFIERS, &capability) == 0 && capability == 1;
77 qCDebug(KWIN_DRM) <<
"drmModeAddFB2WithModifiers is" << (m_addFB2ModifiersSupported ?
"supported" :
"not supported") <<
"on GPU" << m_devNode;
81 m_isI915 = strstr(
version->name,
"i915");
82 m_isNVidia = strstr(
version->name,
"nvidia-drm");
83 m_isVirtualMachine = strstr(
version->name,
"virtio") || strstr(
version->name,
"qxl")
84 || strstr(
version->name,
"vmwgfx") || strstr(
version->name,
"vboxvideo");
90 drmGetMagic(m_gbmFd.
get(), &magic);
91 drmAuthMagic(m_fd, magic);
92 m_gbmDevice = gbm_create_device(m_gbmFd.
get());
94 qCCritical(KWIN_DRM) <<
"gbm_create_device() failed";
96 m_allocator = std::make_unique<GbmGraphicsBufferAllocator>(m_gbmDevice);
100 m_socketNotifier = std::make_unique<QSocketNotifier>(
fd, QSocketNotifier::Read);
101 connect(m_socketNotifier.get(), &QSocketNotifier::activated,
this, &DrmGpu::dispatchEvents);
105 if (m_atomicModeSetting ==
false) {
107 m_asyncPageflipSupported =
drmGetCap(
fd, DRM_CAP_ASYNC_PAGE_FLIP, &capability) == 0 && capability == 1;
114 m_eglDisplay.reset();
116 m_connectors.clear();
118 m_socketNotifier.reset();
121 gbm_device_destroy(m_gbmDevice);
129 char *path = drmGetDeviceNameFromFd2(m_fd);
132 qCWarning(KWIN_DRM) <<
"Could not open DRM fd for leasing!" << strerror(errno);
134 if (drmIsMaster(
fd.get())) {
135 if (drmDropMaster(
fd.get()) != 0) {
136 qCWarning(KWIN_DRM) <<
"Could not create a non-master DRM fd for leasing!" << strerror(errno);
146 return m_presentationClock;
149void DrmGpu::initDrmResources()
152 bool isEnvVarSet =
false;
154 const bool noAMS = qEnvironmentVariableIntValue(
"KWIN_DRM_NO_AMS", &isEnvVarSet) != 0 && isEnvVarSet;
155 if (m_isVirtualMachine && !supportsVmCursorHotspot && !isEnvVarSet) {
156 qCWarning(KWIN_DRM,
"Atomic Mode Setting disabled on GPU %s because of cursor offset issues in virtual machines", qPrintable(m_devNode));
158 qCWarning(KWIN_DRM) <<
"Atomic Mode Setting requested off via environment variable. Using legacy mode on GPU" << m_devNode;
160 qCWarning(KWIN_DRM) <<
"drmSetClientCap for Atomic Mode Setting failed. Using legacy mode on GPU" << m_devNode;
163 if (planeResources) {
164 qCDebug(KWIN_DRM) <<
"Using Atomic Mode Setting on gpu" << m_devNode;
165 qCDebug(KWIN_DRM) <<
"Number of planes on GPU" << m_devNode <<
":" << planeResources->count_planes;
167 for (
unsigned int i = 0; i < planeResources->count_planes; ++i) {
168 DrmUniquePtr<drmModePlane> kplane(
drmModeGetPlane(m_fd, planeResources->planes[i]));
169 auto plane = std::make_unique<DrmPlane>(
this, kplane->plane_id);
171 m_allObjects << plane.get();
172 m_planes.push_back(std::move(plane));
175 if (m_planes.empty()) {
176 qCWarning(KWIN_DRM) <<
"Failed to create any plane. Falling back to legacy mode on GPU " << m_devNode;
179 qCWarning(KWIN_DRM) <<
"Failed to get plane resources. Falling back to legacy mode on GPU " << m_devNode;
182 m_atomicModeSetting = !m_planes.empty();
186 qCCritical(KWIN_DRM) <<
"drmModeGetResources for getting CRTCs failed on GPU" << m_devNode;
189 QList<DrmPlane *> assignedPlanes;
190 for (
int i = 0; i < resources->count_crtcs; ++i) {
191 uint32_t crtcId = resources->crtcs[i];
192 QList<DrmPlane *> primaryCandidates;
193 QList<DrmPlane *> cursorCandidates;
194 for (
const auto &plane : m_planes) {
195 if (plane->isCrtcSupported(i) && !assignedPlanes.contains(plane.get())) {
197 primaryCandidates.push_back(plane.get());
199 cursorCandidates.push_back(plane.get());
203 if (m_atomicModeSetting && primaryCandidates.empty()) {
204 qCWarning(KWIN_DRM) <<
"Could not find a suitable primary plane for crtc" << resources->crtcs[i];
207 const auto findBestPlane = [crtcId](
const QList<DrmPlane *> &list) {
209 const auto connected = std::find_if(list.begin(), list.end(), [crtcId](DrmPlane *plane) {
210 return plane->crtcId.value() == crtcId;
212 if (connected != list.end()) {
216 const auto notconnected = std::find_if(list.begin(), list.end(), [](DrmPlane *plane) {
217 return plane->crtcId.value() == 0;
219 if (notconnected != list.end()) {
220 return *notconnected;
222 return list.empty() ? nullptr : list.front();
224 DrmPlane *primary = findBestPlane(primaryCandidates);
225 DrmPlane *cursor = findBestPlane(cursorCandidates);
226 assignedPlanes.push_back(primary);
228 assignedPlanes.push_back(cursor);
230 auto crtc = std::make_unique<DrmCrtc>(
this, crtcId, i, primary, cursor);
234 m_allObjects << crtc.get();
235 m_crtcs.push_back(std::move(crtc));
247 qCWarning(KWIN_DRM) <<
"drmModeGetResources failed";
254 for (
const auto &output : std::as_const(m_drmOutputs)) {
255 if (output->lease()) {
256 bool leaseActive =
false;
257 for (uint i = 0; i < lessees->count; i++) {
258 if (lessees->lessees[i] == output->lease()->lesseeId()) {
264 Q_EMIT output->lease()->revokeRequested();
270 QList<DrmConnector *> existing;
271 QList<DrmOutput *> addedOutputs;
272 for (
int i = 0; i < resources->count_connectors; ++i) {
273 const uint32_t currentConnector = resources->connectors[i];
274 const auto it = std::find_if(m_connectors.begin(), m_connectors.end(), [currentConnector](
const auto &connector) {
275 return connector->id() == currentConnector;
277 if (it == m_connectors.end()) {
278 auto conn = std::make_shared<DrmConnector>(
this, currentConnector);
282 existing.push_back(conn.get());
283 m_allObjects.push_back(conn.get());
284 m_connectors.push_back(std::move(conn));
286 (*it)->updateProperties();
287 existing.push_back(it->get());
290 for (
auto it = m_connectors.begin(); it != m_connectors.end();) {
292 const auto output = findOutput(conn->
id());
293 const bool stillExists = existing.contains(conn);
296 removeOutput(output);
298 }
else if (!output) {
299 qCDebug(KWIN_DRM,
"New %soutput on GPU %s: %s", conn->
isNonDesktop() ?
"non-desktop " :
"", qPrintable(m_devNode), qPrintable(conn->
modelName()));
300 const auto pipeline = conn->
pipeline();
301 m_pipelines << pipeline;
303 m_drmOutputs << output;
304 addedOutputs << output;
308 pipeline->applyPendingChanges();
313 m_allObjects.removeOne(it->get());
314 it = m_connectors.erase(it);
319 for (
const auto &crtc : std::as_const(m_crtcs)) {
320 crtc->updateProperties();
323 for (
const auto &plane : std::as_const(m_planes)) {
324 plane->updateProperties();
328 for (
const auto &pipeline : std::as_const(m_pipelines)) {
329 pipeline->applyPendingChanges();
330 if (pipeline->output() && !pipeline->crtc()) {
331 pipeline->setEnable(
false);
332 pipeline->output()->updateEnabled(
false);
336 for (
const auto &pipeline : std::as_const(m_pipelines)) {
337 pipeline->revertPendingChanges();
339 for (
const auto &output : std::as_const(addedOutputs)) {
340 removeOutput(output);
341 const auto it = std::find_if(m_connectors.begin(), m_connectors.end(), [output](
const auto &conn) {
342 return conn.get() == output->connector();
344 Q_ASSERT(it != m_connectors.end());
345 m_allObjects.removeOne(it->get());
346 m_connectors.erase(it);
349 qCWarning(KWIN_DRM,
"Failed to find a working setup for new outputs!");
350 for (
const auto &pipeline : std::as_const(m_pipelines)) {
351 pipeline->revertPendingChanges();
353 for (
const auto &output : std::as_const(addedOutputs)) {
354 output->updateEnabled(
false);
355 output->pipeline()->setEnable(
false);
356 output->pipeline()->applyPendingChanges();
364 const auto outputs = m_drmOutputs;
365 for (
const auto &output : outputs) {
366 removeOutput(output);
374DrmPipeline::Error DrmGpu::checkCrtcAssignment(QList<DrmConnector *> connectors,
const QList<DrmCrtc *> &crtcs)
376 if (connectors.isEmpty() || crtcs.isEmpty()) {
377 if (m_pipelines.isEmpty()) {
382 for (
const auto &conn : std::as_const(connectors)) {
383 qCWarning(KWIN_DRM) <<
"disabling connector" << conn->modelName() <<
"without a crtc";
384 conn->pipeline()->setCrtc(
nullptr);
386 return testPipelines();
388 auto connector = connectors.takeFirst();
389 auto pipeline = connector->pipeline();
390 if (!pipeline->enabled() || !connector->isConnected()) {
392 pipeline->setCrtc(
nullptr);
393 return checkCrtcAssignment(connectors, crtcs);
395 DrmCrtc *currentCrtc =
nullptr;
396 if (m_atomicModeSetting) {
398 const uint32_t
id = connector->crtcId.value();
399 auto it = std::find_if(crtcs.begin(), crtcs.end(), [
id](
const auto &crtc) {
400 return id == crtc->id();
402 if (it != crtcs.end()) {
404 auto crtcsLeft = crtcs;
405 crtcsLeft.removeOne(currentCrtc);
406 pipeline->setCrtc(currentCrtc);
412 }
while (pipeline->pruneModifier());
415 for (
const auto &crtc : std::as_const(crtcs)) {
416 if (connector->isCrtcSupported(crtc) && crtc != currentCrtc) {
417 auto crtcsLeft = crtcs;
418 crtcsLeft.removeOne(crtc);
419 pipeline->setCrtc(crtc);
425 }
while (pipeline->pruneModifier());
433 QList<DrmConnector *> connectors;
434 QList<DrmCrtc *> crtcs;
436 for (
const auto &conn : m_connectors) {
437 bool isLeased = std::any_of(m_drmOutputs.cbegin(), m_drmOutputs.cend(), [&conn](
const auto output) {
438 return output->lease() && output->pipeline()->connector() == conn.get();
441 connectors.push_back(conn.get());
444 for (
const auto &crtc : m_crtcs) {
445 bool isLeased = std::any_of(m_drmOutputs.cbegin(), m_drmOutputs.cend(), [&crtc](
const auto output) {
446 return output->lease() && output->pipeline()->crtc() == crtc.get();
449 crtcs.push_back(crtc.get());
452 if (m_atomicModeSetting) {
454 std::sort(connectors.begin(), connectors.end(), [](
auto c1,
auto c2) {
455 return c1->crtcId.value() > c2->crtcId.value();
458 return checkCrtcAssignment(connectors, crtcs);
463 QList<DrmPipeline *> inactivePipelines;
464 std::copy_if(m_pipelines.constBegin(), m_pipelines.constEnd(), std::back_inserter(inactivePipelines), [](
const auto pipeline) {
465 return pipeline->enabled() && !pipeline->active();
471 for (
const auto pipeline : std::as_const(inactivePipelines)) {
472 pipeline->setActive(
true);
475 for (
const auto pipeline : std::as_const(inactivePipelines)) {
476 pipeline->setActive(
false);
482DrmOutput *DrmGpu::findOutput(quint32 connector)
484 auto it = std::find_if(m_drmOutputs.constBegin(), m_drmOutputs.constEnd(), [connector](DrmOutput *o) {
485 return o->connector()->id() == connector;
487 if (it != m_drmOutputs.constEnd()) {
495 m_socketNotifier->setEnabled(
false);
497 const bool idle = std::all_of(m_drmOutputs.constBegin(), m_drmOutputs.constEnd(), [](
DrmOutput *output) {
498 return !output->pipeline()->pageflipsPending();
505 pfds[0].events = POLLIN;
507 const int ready = poll(pfds, 1, 30000);
509 if (errno != EINTR) {
510 qCWarning(KWIN_DRM) << Q_FUNC_INFO <<
"poll() failed:" << strerror(errno);
513 }
else if (ready == 0) {
514 qCWarning(KWIN_DRM) <<
"No drm events for gpu" << m_devNode <<
"within last 30 seconds";
520 m_socketNotifier->setEnabled(
true);
523static std::chrono::nanoseconds convertTimestamp(
const timespec ×tamp)
525 return std::chrono::seconds(timestamp.tv_sec) + std::chrono::nanoseconds(timestamp.tv_nsec);
528static std::chrono::nanoseconds convertTimestamp(clockid_t sourceClock, clockid_t targetClock,
529 const timespec ×tamp)
531 if (sourceClock == targetClock) {
532 return convertTimestamp(timestamp);
535 timespec sourceCurrentTime = {};
536 timespec targetCurrentTime = {};
538 clock_gettime(sourceClock, &sourceCurrentTime);
539 clock_gettime(targetClock, &targetCurrentTime);
541 const auto delta = convertTimestamp(sourceCurrentTime) - convertTimestamp(timestamp);
542 return convertTimestamp(targetCurrentTime) - delta;
545void DrmGpu::pageFlipHandler(
int fd,
unsigned int sequence,
unsigned int sec,
unsigned int usec,
unsigned int crtc_id,
void *user_data)
547 const auto commit =
static_cast<DrmCommit *
>(user_data);
548 const auto gpu = commit->gpu();
555 std::chrono::nanoseconds timestamp = convertTimestamp(gpu->presentationClock(), CLOCK_MONOTONIC,
556 {static_cast<time_t>(sec), static_cast<long>(usec * 1000)});
557 if (timestamp == std::chrono::nanoseconds::zero()) {
558 qCDebug(KWIN_DRM,
"Got invalid timestamp (sec: %u, usec: %u) on gpu %s",
559 sec, usec, qPrintable(gpu->devNode()));
560 timestamp = std::chrono::steady_clock::now().time_since_epoch();
562 commit->pageFlipped(timestamp);
565void DrmGpu::dispatchEvents()
567 drmEventContext context = {};
569 context.page_flip_handler2 = pageFlipHandler;
573void DrmGpu::removeOutput(DrmOutput *output)
575 qCDebug(KWIN_DRM) <<
"Removing output" << output;
576 m_pipelines.removeOne(output->pipeline());
577 output->pipeline()->setLayers(
nullptr,
nullptr);
578 m_drmOutputs.removeOne(output);
582 m_forceModeset =
true;
598 m_virtualOutputs << output;
605 if (m_virtualOutputs.removeOne(output)) {
613 QList<uint32_t> objects;
615 if (output->lease() || !output->addLeaseObjects(objects)) {
623 qCWarning(KWIN_DRM) <<
"Could not create DRM lease!" << strerror(errno);
624 qCWarning(KWIN_DRM) <<
"Tried to lease the following" << objects.count() <<
"resources:";
625 for (
const auto &res : std::as_const(objects)) {
626 qCWarning(KWIN_DRM) << res;
630 qCDebug(KWIN_DRM) <<
"Created lease for" << objects.count() <<
"resources:";
631 for (
const auto &res : std::as_const(objects)) {
632 qCDebug(KWIN_DRM) << res;
634 return std::make_unique<DrmLease>(
this, std::move(
fd), lesseeId, outputs);
640 return m_virtualOutputs;
660 return m_atomicModeSetting;
675 return m_eglDisplay.get();
680 m_eglDisplay = std::move(display);
685 return m_addFB2ModifiersSupported;
690 return m_asyncPageflipSupported;
715 if (m_isActive != active) {
718 for (
const auto &output : std::as_const(m_drmOutputs)) {
719 output->renderLoop()->uninhibit();
723 for (
const auto &output : std::as_const(m_drmOutputs)) {
726 output->pipeline()->forceLegacyModeset();
730 for (
const auto &output : std::as_const(m_drmOutputs)) {
731 output->renderLoop()->inhibit();
745 return m_forceModeset || std::any_of(m_pipelines.constBegin(), m_pipelines.constEnd(), [](
const auto &pipeline) {
746 return pipeline->needsModeset();
753 for (
const auto &output : std::as_const(m_drmOutputs)) {
754 if (output->lease()) {
758 bool presentPendingForAll = std::all_of(
pipelines.constBegin(),
pipelines.constEnd(), [](
const auto &pipeline) {
759 return pipeline->modesetPresentPending() || !pipeline->activePending();
761 if (!presentPendingForAll) {
769 if (pipeline->modesetPresentPending()) {
770 pipeline->resetModesetPresentPending();
772 pipeline->output()->frameFailed();
776 m_forceModeset =
false;
787QList<DrmObject *> DrmGpu::unusedObjects()
const
789 QList<DrmObject *> ret = m_allObjects;
790 for (
const auto &pipeline : m_pipelines) {
791 ret.removeOne(pipeline->connector());
792 if (pipeline->crtc()) {
793 ret.removeOne(pipeline->crtc());
794 ret.removeOne(pipeline->crtc()->primaryPlane());
795 ret.removeOne(pipeline->crtc()->cursorPlane());
808 for (
const auto &plane : std::as_const(m_planes)) {
809 plane->releaseCurrentBuffer();
811 for (
const auto &crtc : std::as_const(m_crtcs)) {
812 crtc->releaseCurrentBuffer();
814 for (
const auto &pipeline : std::as_const(m_pipelines)) {
815 pipeline->primaryLayer()->releaseBuffers();
816 pipeline->cursorLayer()->releaseBuffers();
818 for (
const auto &output : std::as_const(m_virtualOutputs)) {
819 output->primaryLayer()->releaseBuffers();
825 for (
const auto &pipeline : std::as_const(m_pipelines)) {
827 pipeline->applyPendingChanges();
829 for (
const auto &output : std::as_const(m_virtualOutputs)) {
830 output->recreateSurface();
836 return m_allocator.get();
842 if (Q_UNLIKELY(!attributes)) {
846 uint32_t handles[] = {0, 0, 0, 0};
847 auto cleanup = qScopeGuard([
this, &handles]() {
848 for (
int i = 0; i < 4; ++i) {
849 if (handles[i] == 0) {
853 for (
int j = 0; j < i; ++j) {
854 if (handles[i] == handles[j]) {
862 drmCloseBufferHandle(m_fd, handles[i]);
865 for (
int i = 0; i < attributes->
planeCount; ++i) {
866 if (drmPrimeFDToHandle(m_fd, attributes->
fd[i].get(), &handles[i]) != 0) {
867 qCWarning(KWIN_DRM) <<
"drmPrimeFDToHandle() failed";
872 uint32_t framebufferId = 0;
875 uint64_t modifier[4] = {0, 0, 0, 0};
876 for (
int i = 0; i < attributes->
planeCount; ++i) {
884 attributes->
pitch.data(),
885 attributes->
offset.data(),
888 DRM_MODE_FB_MODIFIERS);
895 attributes->
pitch.data(),
896 attributes->
offset.data(),
899 if (ret == EOPNOTSUPP && attributes->
planeCount == 1) {
904 attributes->
pitch[0],
914 return std::make_shared<DrmFramebuffer>(
this, framebufferId, buffer, std::move(readFence));
919 , m_fd(std::move(fd))
920 , m_lesseeId(lesseeId)
923 for (
const auto output : m_outputs) {
924 output->leased(
this);
930 qCDebug(KWIN_DRM,
"Revoking lease with leaseID %d", m_lesseeId);
932 for (
const auto &output : m_outputs) {
933 output->leaseEnded();
948#include "moc_drm_gpu.cpp"
Session * session() const override
DrmRenderBackend * renderBackend() const
bool isNonDesktop() const
DrmPipeline * pipeline() const
QString modelName() const
bool asyncPageflipSupported() const
std::unique_ptr< DrmLease > leaseOutputs(const QList< DrmOutput * > &outputs)
void setActive(bool active)
QList< DrmOutput * > drmOutputs() const
void outputRemoved(DrmAbstractOutput *output)
void setEglDisplay(std::unique_ptr< EglDisplay > &&display)
DrmVirtualOutput * createVirtualOutput(const QString &name, const QSize &size, double scale)
EglDisplay * eglDisplay() const
void outputAdded(DrmAbstractOutput *output)
FileDescriptor createNonMasterFd() const
bool addFB2ModifiersSupported() const
const QList< DrmPipeline * > pipelines() const
clockid_t presentationClock() const
bool needsModeset() const
DrmBackend * platform() const
std::shared_ptr< DrmFramebuffer > importBuffer(GraphicsBuffer *buffer, FileDescriptor &&explicitFence)
void activeChanged(bool active)
GraphicsBufferAllocator * graphicsBufferAllocator() const
DrmGpu(DrmBackend *backend, const QString &devNode, int fd, dev_t deviceId)
DrmPipeline::Error testPendingConfiguration()
void removeVirtualOutput(DrmVirtualOutput *output)
QList< DrmVirtualOutput * > virtualOutputs() const
gbm_device * gbmDevice() const
bool atomicModeSetting() const
uint32_t lesseeId() const
DrmLease(DrmGpu *gpu, FileDescriptor &&fd, uint32_t lesseeId, const QList< DrmOutput * > &outputs)
static Error commitPipelines(const QList< DrmPipeline * > &pipelines, CommitMode mode, const QList< DrmObject * > &unusedObjects={})
virtual std::shared_ptr< DrmPipelineLayer > createCursorLayer(DrmPipeline *pipeline)=0
virtual std::shared_ptr< DrmPipelineLayer > createPrimaryLayer(DrmPipeline *pipeline)=0
virtual const DmaBufAttributes * dmabufAttributes() const
virtual void closeRestricted(int fileDescriptor)=0
#define DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT
int drmModeCreateLease(int fd, const uint32_t *objects, int num_objects, int flags, uint32_t *lessee_id)
drmModePlaneResPtr drmModeGetPlaneResources(int fd)
int drmHandleEvent(int fd, drmEventContextPtr evctx)
int drmModeAddFB(int fd, uint32_t width, uint32_t height, uint8_t depth, uint8_t bpp, uint32_t pitch, uint32_t bo_handle, uint32_t *buf_id)
drmModeResPtr drmModeGetResources(int fd)
int drmGetCap(int fd, uint64_t capability, uint64_t *value)
int drmModeAddFB2(int fd, uint32_t width, uint32_t height, uint32_t pixel_format, const uint32_t bo_handles[4], const uint32_t pitches[4], const uint32_t offsets[4], uint32_t *buf_id, uint32_t flags)
int drmModeRevokeLease(int fd, uint32_t lessee_id)
drmModeLesseeListPtr drmModeListLessees(int fd)
drmVersionPtr drmGetVersion(int fd)
int drmSetClientCap(int fd, uint64_t capability, uint64_t value)
drmModePlanePtr drmModeGetPlane(int fd, uint32_t plane_id)
int drmModeAddFB2WithModifiers(int fd, uint32_t width, uint32_t height, uint32_t pixel_format, const uint32_t bo_handles[4], const uint32_t pitches[4], const uint32_t offsets[4], const uint64_t modifier[4], uint32_t *buf_id, uint32_t flags)
std::unique_ptr< T, DrmDeleter< T > > DrmUniquePtr
std::array< uint32_t, 4 > offset
std::array< uint32_t, 4 > pitch
std::array< FileDescriptor, 4 > fd