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