File indexing completed on 2024-05-19 16:35:21
0001 /* 0002 SPDX-FileCopyrightText: 2018 Fredrik Höglund <fredrik@kde.org> 0003 SPDX-FileCopyrightText: 2019 Roman Gilg <subdiff@gmail.com> 0004 SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org> 0005 SPDX-FileCopyrightText: 2021 Xaver Hugl <xaver.hugl@gmail.com> 0006 0007 Based on the libweston implementation, 0008 SPDX-FileCopyrightText: 2014, 2015 Collabora, Ltd. 0009 0010 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0011 */ 0012 0013 #include "linuxdmabufv1clientbuffer.h" 0014 #include "linuxdmabufv1clientbuffer_p.h" 0015 #include "surface_interface_p.h" 0016 #include "utils/common.h" 0017 0018 #include <errno.h> 0019 #include <fcntl.h> 0020 #include <unistd.h> 0021 0022 namespace KWaylandServer 0023 { 0024 static const int s_version = 4; 0025 0026 LinuxDmaBufV1ClientBufferIntegrationPrivate::LinuxDmaBufV1ClientBufferIntegrationPrivate(LinuxDmaBufV1ClientBufferIntegration *q, Display *display) 0027 : QtWaylandServer::zwp_linux_dmabuf_v1(*display, s_version) 0028 , q(q) 0029 , defaultFeedback(new LinuxDmaBufV1Feedback(this)) 0030 { 0031 } 0032 0033 void LinuxDmaBufV1ClientBufferIntegrationPrivate::zwp_linux_dmabuf_v1_bind_resource(Resource *resource) 0034 { 0035 if (resource->version() < ZWP_LINUX_DMABUF_V1_GET_DEFAULT_FEEDBACK_SINCE_VERSION) { 0036 for (auto it = supportedModifiers.constBegin(); it != supportedModifiers.constEnd(); ++it) { 0037 const uint32_t &format = it.key(); 0038 const auto &modifiers = it.value(); 0039 for (const uint64_t &modifier : std::as_const(modifiers)) { 0040 if (resource->version() >= ZWP_LINUX_DMABUF_V1_MODIFIER_SINCE_VERSION) { 0041 const uint32_t modifier_lo = modifier & 0xffffffff; 0042 const uint32_t modifier_hi = modifier >> 32; 0043 send_modifier(resource->handle, format, modifier_hi, modifier_lo); 0044 } else if (modifier == DRM_FORMAT_MOD_LINEAR || modifier == DRM_FORMAT_MOD_INVALID) { 0045 send_format(resource->handle, format); 0046 } 0047 } 0048 } 0049 } 0050 } 0051 0052 void LinuxDmaBufV1ClientBufferIntegrationPrivate::zwp_linux_dmabuf_v1_get_default_feedback(Resource *resource, uint32_t id) 0053 { 0054 LinuxDmaBufV1FeedbackPrivate::get(defaultFeedback.get())->add(resource->client(), id, resource->version()); 0055 } 0056 0057 void LinuxDmaBufV1ClientBufferIntegrationPrivate::zwp_linux_dmabuf_v1_get_surface_feedback(Resource *resource, uint32_t id, wl_resource *surfaceResource) 0058 { 0059 auto surface = SurfaceInterface::get(surfaceResource); 0060 if (!surface) { 0061 qCWarning(KWIN_CORE) << "requested surface feedback for nonexistant surface!"; 0062 return; 0063 } 0064 auto surfacePrivate = SurfaceInterfacePrivate::get(surface); 0065 if (!surfacePrivate->dmabufFeedbackV1) { 0066 surfacePrivate->dmabufFeedbackV1.reset(new LinuxDmaBufV1Feedback(this)); 0067 } 0068 LinuxDmaBufV1FeedbackPrivate::get(surfacePrivate->dmabufFeedbackV1.get())->add(resource->client(), id, resource->version()); 0069 } 0070 0071 void LinuxDmaBufV1ClientBufferIntegrationPrivate::zwp_linux_dmabuf_v1_destroy(Resource *resource) 0072 { 0073 wl_resource_destroy(resource->handle); 0074 } 0075 0076 void LinuxDmaBufV1ClientBufferIntegrationPrivate::zwp_linux_dmabuf_v1_create_params(Resource *resource, uint32_t params_id) 0077 { 0078 wl_resource *paramsResource = wl_resource_create(resource->client(), &zwp_linux_buffer_params_v1_interface, resource->version(), params_id); 0079 if (!paramsResource) { 0080 wl_resource_post_no_memory(resource->handle); 0081 return; 0082 } 0083 new LinuxDmaBufParamsV1(q, paramsResource); 0084 } 0085 0086 LinuxDmaBufParamsV1::LinuxDmaBufParamsV1(LinuxDmaBufV1ClientBufferIntegration *integration, ::wl_resource *resource) 0087 : QtWaylandServer::zwp_linux_buffer_params_v1(resource) 0088 , m_integration(integration) 0089 { 0090 } 0091 0092 void LinuxDmaBufParamsV1::zwp_linux_buffer_params_v1_destroy_resource(Resource *resource) 0093 { 0094 delete this; 0095 } 0096 0097 void LinuxDmaBufParamsV1::zwp_linux_buffer_params_v1_destroy(Resource *resource) 0098 { 0099 wl_resource_destroy(resource->handle); 0100 } 0101 0102 void LinuxDmaBufParamsV1::zwp_linux_buffer_params_v1_add(Resource *resource, 0103 int32_t fd, 0104 uint32_t plane_idx, 0105 uint32_t offset, 0106 uint32_t stride, 0107 uint32_t modifier_hi, 0108 uint32_t modifier_lo) 0109 { 0110 if (Q_UNLIKELY(m_isUsed)) { 0111 wl_resource_post_error(resource->handle, error_already_used, "the params object has already been used to create a wl_buffer"); 0112 close(fd); 0113 return; 0114 } 0115 0116 if (Q_UNLIKELY(plane_idx >= 4)) { 0117 wl_resource_post_error(resource->handle, error_plane_idx, "plane index %d is out of bounds", plane_idx); 0118 close(fd); 0119 return; 0120 } 0121 0122 if (Q_UNLIKELY(m_attrs.fd[plane_idx].isValid())) { 0123 wl_resource_post_error(resource->handle, error_plane_set, "the plane index %d was already set", plane_idx); 0124 close(fd); 0125 return; 0126 } 0127 m_attrs.fd[plane_idx] = KWin::FileDescriptor{fd}; 0128 m_attrs.offset[plane_idx] = offset; 0129 m_attrs.pitch[plane_idx] = stride; 0130 m_attrs.modifier = (quint64(modifier_hi) << 32) | modifier_lo; 0131 m_attrs.planeCount++; 0132 } 0133 0134 void LinuxDmaBufParamsV1::zwp_linux_buffer_params_v1_create(Resource *resource, int32_t width, int32_t height, uint32_t format, uint32_t flags) 0135 { 0136 if (Q_UNLIKELY(m_isUsed)) { 0137 wl_resource_post_error(resource->handle, error_already_used, "the params object has already been used to create a wl_buffer"); 0138 return; 0139 } 0140 0141 if (Q_UNLIKELY(!test(resource, width, height))) { 0142 return; 0143 } 0144 0145 m_isUsed = true; 0146 0147 m_attrs.width = width; 0148 m_attrs.height = height; 0149 m_attrs.format = format; 0150 0151 LinuxDmaBufV1ClientBuffer *clientBuffer = m_integration->rendererInterface()->importBuffer(std::move(m_attrs), flags); 0152 if (!clientBuffer) { 0153 send_failed(resource->handle); 0154 return; 0155 } 0156 0157 wl_resource *bufferResource = wl_resource_create(resource->client(), &wl_buffer_interface, 1, 0); 0158 if (!bufferResource) { 0159 delete clientBuffer; 0160 wl_resource_post_no_memory(resource->handle); 0161 return; 0162 } 0163 0164 clientBuffer->initialize(bufferResource); 0165 send_created(resource->handle, bufferResource); 0166 0167 DisplayPrivate *displayPrivate = DisplayPrivate::get(m_integration->display()); 0168 displayPrivate->registerClientBuffer(clientBuffer); 0169 } 0170 0171 void LinuxDmaBufParamsV1::zwp_linux_buffer_params_v1_create_immed(Resource *resource, 0172 uint32_t buffer_id, 0173 int32_t width, 0174 int32_t height, 0175 uint32_t format, 0176 uint32_t flags) 0177 { 0178 if (Q_UNLIKELY(m_isUsed)) { 0179 wl_resource_post_error(resource->handle, error_already_used, "the params object has already been used to create a wl_buffer"); 0180 return; 0181 } 0182 0183 if (Q_UNLIKELY(!test(resource, width, height))) { 0184 return; 0185 } 0186 0187 m_isUsed = true; 0188 0189 m_attrs.width = width; 0190 m_attrs.height = height; 0191 m_attrs.format = format; 0192 0193 LinuxDmaBufV1ClientBuffer *clientBuffer = m_integration->rendererInterface()->importBuffer(std::move(m_attrs), flags); 0194 if (!clientBuffer) { 0195 wl_resource_post_error(resource->handle, error_invalid_wl_buffer, "importing the supplied dmabufs failed"); 0196 return; 0197 } 0198 0199 wl_resource *bufferResource = wl_resource_create(resource->client(), &wl_buffer_interface, 1, buffer_id); 0200 if (!bufferResource) { 0201 delete clientBuffer; 0202 wl_resource_post_no_memory(resource->handle); 0203 return; 0204 } 0205 0206 clientBuffer->initialize(bufferResource); 0207 0208 DisplayPrivate *displayPrivate = DisplayPrivate::get(m_integration->display()); 0209 displayPrivate->registerClientBuffer(clientBuffer); 0210 } 0211 0212 bool LinuxDmaBufParamsV1::test(Resource *resource, uint32_t width, uint32_t height) 0213 { 0214 if (Q_UNLIKELY(!m_attrs.planeCount)) { 0215 wl_resource_post_error(resource->handle, error_incomplete, "no planes have been specified"); 0216 return false; 0217 } 0218 0219 // Check for holes in the dmabuf set (e.g. [0, 1, 3]). 0220 for (int i = 0; i < m_attrs.planeCount; ++i) { 0221 if (!m_attrs.fd[i].isValid()) { 0222 wl_resource_post_error(resource->handle, error_incomplete, "no dmabuf has been added for plane %d", i); 0223 return false; 0224 } 0225 } 0226 0227 if (Q_UNLIKELY(width == 0 || height == 0)) { 0228 wl_resource_post_error(resource->handle, error_invalid_dimensions, "invalid width %d or height %d", width, height); 0229 return false; 0230 } 0231 0232 for (int i = 0; i < m_attrs.planeCount; ++i) { 0233 // Check for overflows. 0234 if (Q_UNLIKELY(uint64_t(m_attrs.offset[i]) + m_attrs.pitch[i] > UINT32_MAX)) { 0235 wl_resource_post_error(resource->handle, error_out_of_bounds, "size overflow for plane %d", i); 0236 return false; 0237 } 0238 0239 if (Q_UNLIKELY(i == 0 && uint64_t(m_attrs.offset[i]) + uint64_t(m_attrs.pitch[i]) * height > UINT32_MAX)) { 0240 wl_resource_post_error(resource->handle, error_out_of_bounds, "size overflow for plane %d", i); 0241 return false; 0242 } 0243 0244 // Don't report an error as it might be caused by the kernel not supporting 0245 // seeking on dmabuf. 0246 const off_t size = lseek(m_attrs.fd[i].get(), 0, SEEK_END); 0247 if (size == -1) { 0248 continue; 0249 } 0250 0251 if (Q_UNLIKELY(m_attrs.offset[i] >= size)) { 0252 wl_resource_post_error(resource->handle, error_out_of_bounds, "invalid offset %i for plane %d", m_attrs.offset[i], i); 0253 return false; 0254 } 0255 0256 if (Q_UNLIKELY(m_attrs.offset[i] + m_attrs.pitch[i] > size)) { 0257 wl_resource_post_error(resource->handle, error_out_of_bounds, "invalid stride %i for plane %d", m_attrs.pitch[i], i); 0258 return false; 0259 } 0260 0261 // Only valid for first plane as other planes might be sub-sampled according to 0262 // fourcc format. 0263 if (Q_UNLIKELY(i == 0 && m_attrs.offset[i] + m_attrs.pitch[i] * height > size)) { 0264 wl_resource_post_error(resource->handle, error_out_of_bounds, "invalid buffer stride of height for plane %d", i); 0265 return false; 0266 } 0267 } 0268 0269 return true; 0270 } 0271 0272 LinuxDmaBufV1ClientBufferIntegration::LinuxDmaBufV1ClientBufferIntegration(Display *display) 0273 : ClientBufferIntegration(display) 0274 , d(new LinuxDmaBufV1ClientBufferIntegrationPrivate(this, display)) 0275 { 0276 } 0277 0278 LinuxDmaBufV1ClientBufferIntegration::~LinuxDmaBufV1ClientBufferIntegration() 0279 { 0280 } 0281 0282 LinuxDmaBufV1ClientBufferIntegration::RendererInterface *LinuxDmaBufV1ClientBufferIntegration::rendererInterface() const 0283 { 0284 return d->rendererInterface; 0285 } 0286 0287 void LinuxDmaBufV1ClientBufferIntegration::setRendererInterface(RendererInterface *rendererInterface) 0288 { 0289 d->rendererInterface = rendererInterface; 0290 } 0291 0292 bool operator==(const LinuxDmaBufV1Feedback::Tranche &t1, const LinuxDmaBufV1Feedback::Tranche &t2) 0293 { 0294 return t1.device == t2.device && t1.flags == t2.flags && t1.formatTable == t2.formatTable; 0295 } 0296 0297 void LinuxDmaBufV1ClientBufferIntegration::setSupportedFormatsWithModifiers(const QVector<LinuxDmaBufV1Feedback::Tranche> &tranches) 0298 { 0299 if (LinuxDmaBufV1FeedbackPrivate::get(d->defaultFeedback.get())->m_tranches != tranches) { 0300 QHash<uint32_t, QVector<uint64_t>> set; 0301 for (const auto &tranche : tranches) { 0302 set.insert(tranche.formatTable); 0303 } 0304 d->supportedModifiers = set; 0305 d->mainDevice = tranches.first().device; 0306 d->table.reset(new LinuxDmaBufV1FormatTable(set)); 0307 d->defaultFeedback->setTranches(tranches); 0308 } 0309 } 0310 0311 static bool testAlphaChannel(uint32_t drmFormat) 0312 { 0313 switch (drmFormat) { 0314 case DRM_FORMAT_ARGB4444: 0315 case DRM_FORMAT_ABGR4444: 0316 case DRM_FORMAT_RGBA4444: 0317 case DRM_FORMAT_BGRA4444: 0318 0319 case DRM_FORMAT_ARGB1555: 0320 case DRM_FORMAT_ABGR1555: 0321 case DRM_FORMAT_RGBA5551: 0322 case DRM_FORMAT_BGRA5551: 0323 0324 case DRM_FORMAT_ARGB8888: 0325 case DRM_FORMAT_ABGR8888: 0326 case DRM_FORMAT_RGBA8888: 0327 case DRM_FORMAT_BGRA8888: 0328 0329 case DRM_FORMAT_ARGB2101010: 0330 case DRM_FORMAT_ABGR2101010: 0331 case DRM_FORMAT_RGBA1010102: 0332 case DRM_FORMAT_BGRA1010102: 0333 0334 case DRM_FORMAT_XRGB8888_A8: 0335 case DRM_FORMAT_XBGR8888_A8: 0336 case DRM_FORMAT_RGBX8888_A8: 0337 case DRM_FORMAT_BGRX8888_A8: 0338 case DRM_FORMAT_RGB888_A8: 0339 case DRM_FORMAT_BGR888_A8: 0340 case DRM_FORMAT_RGB565_A8: 0341 case DRM_FORMAT_BGR565_A8: 0342 return true; 0343 default: 0344 return false; 0345 } 0346 } 0347 0348 void LinuxDmaBufV1ClientBufferPrivate::buffer_destroy(Resource *resource) 0349 { 0350 wl_resource_destroy(resource->handle); 0351 } 0352 0353 LinuxDmaBufV1ClientBuffer::LinuxDmaBufV1ClientBuffer(KWin::DmaBufAttributes &&attrs, quint32 flags) 0354 : ClientBuffer(*new LinuxDmaBufV1ClientBufferPrivate) 0355 { 0356 Q_D(LinuxDmaBufV1ClientBuffer); 0357 d->attrs = std::move(attrs); 0358 d->flags = flags; 0359 d->hasAlphaChannel = testAlphaChannel(attrs.format); 0360 } 0361 0362 LinuxDmaBufV1ClientBuffer::~LinuxDmaBufV1ClientBuffer() = default; 0363 0364 void LinuxDmaBufV1ClientBuffer::initialize(wl_resource *resource) 0365 { 0366 Q_D(LinuxDmaBufV1ClientBuffer); 0367 d->init(resource); 0368 ClientBuffer::initialize(resource); 0369 } 0370 0371 quint32 LinuxDmaBufV1ClientBuffer::format() const 0372 { 0373 Q_D(const LinuxDmaBufV1ClientBuffer); 0374 return d->attrs.format; 0375 } 0376 0377 quint32 LinuxDmaBufV1ClientBuffer::flags() const 0378 { 0379 Q_D(const LinuxDmaBufV1ClientBuffer); 0380 return d->flags; 0381 } 0382 0383 const KWin::DmaBufAttributes &LinuxDmaBufV1ClientBuffer::attributes() const 0384 { 0385 Q_D(const LinuxDmaBufV1ClientBuffer); 0386 return d->attrs; 0387 } 0388 0389 QSize LinuxDmaBufV1ClientBuffer::size() const 0390 { 0391 Q_D(const LinuxDmaBufV1ClientBuffer); 0392 return QSize(d->attrs.width, d->attrs.height); 0393 } 0394 0395 bool LinuxDmaBufV1ClientBuffer::hasAlphaChannel() const 0396 { 0397 Q_D(const LinuxDmaBufV1ClientBuffer); 0398 return d->hasAlphaChannel; 0399 } 0400 0401 ClientBuffer::Origin LinuxDmaBufV1ClientBuffer::origin() const 0402 { 0403 Q_D(const LinuxDmaBufV1ClientBuffer); 0404 if (d->flags & QtWaylandServer::zwp_linux_buffer_params_v1::flags_y_invert) { 0405 return ClientBuffer::Origin::BottomLeft; 0406 } else { 0407 return ClientBuffer::Origin::TopLeft; 0408 } 0409 } 0410 0411 LinuxDmaBufV1Feedback::LinuxDmaBufV1Feedback(LinuxDmaBufV1ClientBufferIntegrationPrivate *integration) 0412 : d(new LinuxDmaBufV1FeedbackPrivate(integration)) 0413 { 0414 } 0415 0416 LinuxDmaBufV1Feedback::~LinuxDmaBufV1Feedback() = default; 0417 0418 void LinuxDmaBufV1Feedback::setTranches(const QVector<Tranche> &tranches) 0419 { 0420 if (d->m_tranches != tranches) { 0421 d->m_tranches = tranches; 0422 const auto &map = d->resourceMap(); 0423 for (const auto &resource : map) { 0424 d->send(resource); 0425 } 0426 } 0427 } 0428 0429 LinuxDmaBufV1FeedbackPrivate *LinuxDmaBufV1FeedbackPrivate::get(LinuxDmaBufV1Feedback *q) 0430 { 0431 return q->d.get(); 0432 } 0433 0434 LinuxDmaBufV1FeedbackPrivate::LinuxDmaBufV1FeedbackPrivate(LinuxDmaBufV1ClientBufferIntegrationPrivate *bufferintegration) 0435 : m_bufferintegration(bufferintegration) 0436 { 0437 } 0438 0439 void LinuxDmaBufV1FeedbackPrivate::send(Resource *resource) 0440 { 0441 send_format_table(resource->handle, m_bufferintegration->table->file.fd(), m_bufferintegration->table->file.size()); 0442 QByteArray bytes; 0443 bytes.append(reinterpret_cast<const char *>(&m_bufferintegration->mainDevice), sizeof(dev_t)); 0444 send_main_device(resource->handle, bytes); 0445 const auto &sendTranche = [this, resource](const LinuxDmaBufV1Feedback::Tranche &tranche) { 0446 QByteArray targetDevice; 0447 targetDevice.append(reinterpret_cast<const char *>(&tranche.device), sizeof(dev_t)); 0448 QByteArray indices; 0449 for (auto it = tranche.formatTable.begin(); it != tranche.formatTable.end(); it++) { 0450 const uint32_t format = it.key(); 0451 for (const auto &mod : std::as_const(it.value())) { 0452 uint16_t index = m_bufferintegration->table->indices[std::pair<uint32_t, uint64_t>(format, mod)]; 0453 indices.append(reinterpret_cast<const char *>(&index), 2); 0454 } 0455 } 0456 send_tranche_target_device(resource->handle, targetDevice); 0457 send_tranche_formats(resource->handle, indices); 0458 send_tranche_flags(resource->handle, static_cast<uint32_t>(tranche.flags)); 0459 send_tranche_done(resource->handle); 0460 }; 0461 for (const auto &tranche : std::as_const(m_tranches)) { 0462 sendTranche(tranche); 0463 } 0464 // send default hints as the last fallback tranche 0465 const auto defaultFeedbackPrivate = get(m_bufferintegration->defaultFeedback.get()); 0466 if (this != defaultFeedbackPrivate) { 0467 for (const auto &tranche : std::as_const(defaultFeedbackPrivate->m_tranches)) { 0468 sendTranche(tranche); 0469 } 0470 } 0471 send_done(resource->handle); 0472 } 0473 0474 void LinuxDmaBufV1FeedbackPrivate::zwp_linux_dmabuf_feedback_v1_bind_resource(Resource *resource) 0475 { 0476 send(resource); 0477 } 0478 0479 void LinuxDmaBufV1FeedbackPrivate::zwp_linux_dmabuf_feedback_v1_destroy(Resource *resource) 0480 { 0481 wl_resource_destroy(resource->handle); 0482 } 0483 0484 struct linux_dmabuf_feedback_v1_table_entry 0485 { 0486 uint32_t format; 0487 uint32_t pad; // unused 0488 uint64_t modifier; 0489 }; 0490 0491 LinuxDmaBufV1FormatTable::LinuxDmaBufV1FormatTable(const QHash<uint32_t, QVector<uint64_t>> &supportedModifiers) 0492 { 0493 QVector<linux_dmabuf_feedback_v1_table_entry> data; 0494 for (auto it = supportedModifiers.begin(); it != supportedModifiers.end(); it++) { 0495 const uint32_t format = it.key(); 0496 for (const uint64_t &mod : *it) { 0497 indices.insert({format, mod}, data.size()); 0498 data.append({format, 0, mod}); 0499 } 0500 } 0501 0502 const auto size = data.size() * sizeof(linux_dmabuf_feedback_v1_table_entry); 0503 file = KWin::RamFile("kwin-dmabuf-feedback-table", data.constData(), size, KWin::RamFile::Flag::SealWrite); 0504 if (!file.isValid()) { 0505 qCCritical(KWIN_CORE) << "Failed to create RamFile for LinuxDmaBufV1FormatTable"; 0506 return; 0507 } 0508 } 0509 0510 } // namespace KWaylandServer