Changeset View
Changeset View
Standalone View
Standalone View
plugins/platforms/drm/drm_backend.cpp
Show First 20 Lines • Show All 83 Lines • ▼ Show 20 Line(s) | 83 | if (m_gbmDevice) { | |||
---|---|---|---|---|---|
84 | gbm_device_destroy(m_gbmDevice); | 84 | gbm_device_destroy(m_gbmDevice); | ||
85 | } | 85 | } | ||
86 | #endif | 86 | #endif | ||
87 | if (m_fd >= 0) { | 87 | if (m_fd >= 0) { | ||
88 | // wait for pageflips | 88 | // wait for pageflips | ||
89 | while (m_pageFlipsPending != 0) { | 89 | while (m_pageFlipsPending != 0) { | ||
90 | QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents); | 90 | QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents); | ||
91 | } | 91 | } | ||
92 | qDeleteAll(m_planes); | | |||
93 | qDeleteAll(m_outputs); | 92 | qDeleteAll(m_outputs); | ||
93 | qDeleteAll(m_planes); | ||||
94 | qDeleteAll(m_crtcs); | ||||
95 | qDeleteAll(m_connectors); | ||||
94 | delete m_cursor[0]; | 96 | delete m_cursor[0]; | ||
95 | delete m_cursor[1]; | 97 | delete m_cursor[1]; | ||
96 | close(m_fd); | 98 | close(m_fd); | ||
97 | } | 99 | } | ||
98 | } | 100 | } | ||
99 | 101 | | |||
100 | void DrmBackend::init() | 102 | void DrmBackend::init() | ||
101 | { | 103 | { | ||
▲ Show 20 Lines • Show All 63 Lines • ▼ Show 20 Line(s) | 164 | { | |||
165 | } | 167 | } | ||
166 | m_active = true; | 168 | m_active = true; | ||
167 | if (!usesSoftwareCursor()) { | 169 | if (!usesSoftwareCursor()) { | ||
168 | DrmBuffer *c = m_cursor[(m_cursorIndex + 1) % 2]; | 170 | DrmBuffer *c = m_cursor[(m_cursorIndex + 1) % 2]; | ||
169 | const QPoint cp = Cursor::pos() - softwareCursorHotspot(); | 171 | const QPoint cp = Cursor::pos() - softwareCursorHotspot(); | ||
170 | for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) { | 172 | for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) { | ||
171 | DrmOutput *o = *it; | 173 | DrmOutput *o = *it; | ||
172 | o->pageFlipped(); | 174 | o->pageFlipped(); | ||
173 | o->blank(); | 175 | o->m_crtc->blank(); | ||
174 | o->showCursor(c); | 176 | o->showCursor(c); | ||
175 | o->moveCursor(cp); | 177 | o->moveCursor(cp); | ||
176 | } | 178 | } | ||
177 | } | 179 | } | ||
178 | // restart compositor | 180 | // restart compositor | ||
179 | m_pageFlipsPending = 0; | 181 | m_pageFlipsPending = 0; | ||
180 | if (Compositor *compositor = Compositor::self()) { | 182 | if (Compositor *compositor = Compositor::self()) { | ||
181 | compositor->bufferSwapComplete(); | 183 | compositor->bufferSwapComplete(); | ||
Show All 9 Lines | 189 | { | |||
191 | // block compositor | 193 | // block compositor | ||
192 | if (m_pageFlipsPending == 0 && Compositor::self()) { | 194 | if (m_pageFlipsPending == 0 && Compositor::self()) { | ||
193 | Compositor::self()->aboutToSwapBuffers(); | 195 | Compositor::self()->aboutToSwapBuffers(); | ||
194 | } | 196 | } | ||
195 | // hide cursor and disable | 197 | // hide cursor and disable | ||
196 | for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) { | 198 | for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) { | ||
197 | DrmOutput *o = *it; | 199 | DrmOutput *o = *it; | ||
198 | o->hideCursor(); | 200 | o->hideCursor(); | ||
199 | o->restoreSaved(); | | |||
200 | } | 201 | } | ||
201 | m_active = false; | 202 | m_active = false; | ||
202 | } | 203 | } | ||
203 | 204 | | |||
204 | void DrmBackend::pageFlipHandler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, void *data) | 205 | void DrmBackend::pageFlipHandler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, void *data) | ||
205 | { | 206 | { | ||
206 | Q_UNUSED(fd) | 207 | Q_UNUSED(fd) | ||
207 | Q_UNUSED(frame) | 208 | Q_UNUSED(frame) | ||
▲ Show 20 Lines • Show All 75 Lines • ▼ Show 20 Line(s) | 282 | if (m_planes.isEmpty()) { | |||
283 | m_atomicModeSetting = false; | 284 | m_atomicModeSetting = false; | ||
284 | } | 285 | } | ||
285 | } | 286 | } | ||
286 | } else { | 287 | } else { | ||
287 | qCWarning(KWIN_DRM) << "drmSetClientCap for Atomic Mode Setting failed. Using legacy mode."; | 288 | qCWarning(KWIN_DRM) << "drmSetClientCap for Atomic Mode Setting failed. Using legacy mode."; | ||
288 | } | 289 | } | ||
289 | } | 290 | } | ||
290 | 291 | | |||
292 | ScopedDrmPointer<_drmModeRes, &drmModeFreeResources> resources(drmModeGetResources(m_fd)); | ||||
293 | drmModeRes *res = resources.data(); | ||||
294 | if (!resources) { | ||||
295 | qCWarning(KWIN_DRM) << "drmModeGetResources failed"; | ||||
296 | return; | ||||
297 | } | ||||
298 | | ||||
299 | for (int i = 0; i < res->count_connectors; ++i) { | ||||
300 | m_connectors << new DrmConnector(res->connectors[i], m_fd); | ||||
301 | } | ||||
302 | for (int i = 0; i < res->count_crtcs; ++i) { | ||||
303 | m_crtcs << new DrmCrtc(res->crtcs[i], m_fd, i); | ||||
304 | } | ||||
305 | | ||||
306 | if (m_atomicModeSetting) { | ||||
307 | auto tryInit = [] (DrmObject *o) -> bool { | ||||
308 | if (o->init()) { | ||||
309 | return false; | ||||
310 | } else { | ||||
311 | delete o; | ||||
312 | return true; | ||||
313 | } | ||||
314 | }; | ||||
315 | m_connectors.erase(std::remove_if(m_connectors.begin(), m_connectors.end(), tryInit), m_connectors.end()); | ||||
316 | m_crtcs.erase(std::remove_if(m_crtcs.begin(), m_crtcs.end(), tryInit), m_crtcs.end()); | ||||
anthonyfieroni: You mean std::erase(std::remove_if(...), ....end()) otherwise you have dangling pointer(s) in… | |||||
If remove_if is true, the object already got deleted in tryInit. remove_if then just removes the dangling pointer entry from the QVector. Afterwards everything is supposed to be clean, is it? romangg: If remove_if is true, the object already got deleted in tryInit. remove_if then just removes… | |||||
http://www.cplusplus.com/reference/algorithm/remove_if/ anthonyfieroni: http://www.cplusplus.com/reference/algorithm/remove_if/
remove_if does not remove anything from… | |||||
Thanks! So like this? m_connectors.erase(std::remove_if(m_connectors.begin(), m_connectors.end(), tryInit), m_connectors.end()); m_crtcs.erase(std::remove_if(m_crtcs.begin(), m_crtcs.end(), tryInit), m_crtcs.end()); romangg: Thanks! So like this?
```
m_connectors.erase(std::remove_if(m_connectors.begin(), m_connectors. | |||||
anthonyfieroni: Yes. | |||||
317 | } | ||||
318 | | ||||
291 | queryResources(); | 319 | queryResources(); | ||
320 | | ||||
292 | if (m_outputs.isEmpty()) { | 321 | if (m_outputs.isEmpty()) { | ||
293 | qCWarning(KWIN_DRM) << "No outputs, cannot render, will terminate now"; | 322 | qCWarning(KWIN_DRM) << "No outputs, cannot render, will terminate now"; | ||
294 | emit initFailed(); | 323 | emit initFailed(); | ||
295 | return; | 324 | return; | ||
296 | } | 325 | } | ||
297 | 326 | | |||
298 | // setup udevMonitor | 327 | // setup udevMonitor | ||
299 | if (m_udevMonitor) { | 328 | if (m_udevMonitor) { | ||
Show All 26 Lines | |||||
326 | initCursor(); | 355 | initCursor(); | ||
327 | } | 356 | } | ||
328 | 357 | | |||
329 | void DrmBackend::queryResources() | 358 | void DrmBackend::queryResources() | ||
330 | { | 359 | { | ||
331 | if (m_fd < 0) { | 360 | if (m_fd < 0) { | ||
332 | return; | 361 | return; | ||
333 | } | 362 | } | ||
363 | | ||||
334 | ScopedDrmPointer<_drmModeRes, &drmModeFreeResources> resources(drmModeGetResources(m_fd)); | 364 | ScopedDrmPointer<_drmModeRes, &drmModeFreeResources> resources(drmModeGetResources(m_fd)); | ||
335 | if (!resources) { | 365 | if (!resources) { | ||
336 | qCWarning(KWIN_DRM) << "drmModeGetResources failed"; | 366 | qCWarning(KWIN_DRM) << "drmModeGetResources failed"; | ||
337 | return; | 367 | return; | ||
338 | } | 368 | } | ||
339 | 369 | | |||
340 | QVector<DrmOutput*> connectedOutputs; | 370 | QVector<DrmOutput*> connectedOutputs; | ||
341 | for (int i = 0; i < resources->count_connectors; ++i) { | 371 | QVector<DrmConnector*> pendingConnectors; | ||
342 | const auto id = resources->connectors[i]; | 372 | | ||
343 | ScopedDrmPointer<_drmModeConnector, &drmModeFreeConnector> connector(drmModeGetConnector(m_fd, id)); | 373 | // split up connected connectors in already or not yet assigned ones | ||
344 | if (!connector) { | 374 | for (DrmConnector *con : qAsConst(m_connectors)) { | ||
graesslin: qAsConst(m_connectors) | |||||
375 | if (!con->isConnected()) { | ||||
345 | continue; | 376 | continue; | ||
346 | } | 377 | } | ||
347 | if (connector->connection != DRM_MODE_CONNECTED) { | 378 | | ||
379 | if (DrmOutput *o = findOutput(con->id())) { | ||||
380 | connectedOutputs << o; | ||||
381 | } else { | ||||
382 | pendingConnectors << con; | ||||
383 | } | ||||
384 | } | ||||
385 | | ||||
386 | // check for outputs which got removed | ||||
387 | auto it = m_outputs.begin(); | ||||
388 | while (it != m_outputs.end()) { | ||||
389 | if (connectedOutputs.contains(*it)) { | ||||
390 | it++; | ||||
348 | continue; | 391 | continue; | ||
349 | } | 392 | } | ||
350 | if (connector->count_modes == 0) { | 393 | DrmOutput *removed = *it; | ||
394 | it = m_outputs.erase(it); | ||||
395 | emit outputRemoved(removed); | ||||
396 | delete removed; | ||||
397 | } | ||||
398 | | ||||
399 | // now check new connections | ||||
400 | for (DrmConnector *con : qAsConst(pendingConnectors)) { | ||||
401 | ScopedDrmPointer<_drmModeConnector, &drmModeFreeConnector> connector(drmModeGetConnector(m_fd, con->id())); | ||||
402 | if (!connector) { | ||||
351 | continue; | 403 | continue; | ||
352 | } | 404 | } | ||
353 | if (DrmOutput *o = findOutput(connector->connector_id)) { | 405 | if (connector->count_modes == 0) { | ||
354 | connectedOutputs << o; | | |||
355 | continue; | 406 | continue; | ||
356 | } | 407 | } | ||
357 | bool crtcFound = false; | 408 | bool outputDone = false; | ||
358 | const quint32 crtcId = findCrtc(resources.data(), connector.data(), &crtcFound); | 409 | | ||
359 | if (!crtcFound) { | 410 | QVector<uint32_t> encoders = con->encoders(); | ||
graesslin: qAsConst in case encoders() doesn't return a const ref. | |||||
411 | for (auto encId : qAsConst(encoders)) { | ||||
412 | ScopedDrmPointer<_drmModeEncoder, &drmModeFreeEncoder> encoder(drmModeGetEncoder(m_fd, encId)); | ||||
413 | if (!encoder) { | ||||
360 | continue; | 414 | continue; | ||
361 | } | 415 | } | ||
graesslin: qAsConst | |||||
362 | ScopedDrmPointer<_drmModeCrtc, &drmModeFreeCrtc> crtc(drmModeGetCrtc(m_fd, crtcId)); | 416 | for (DrmCrtc *crtc : qAsConst(m_crtcs)) { | ||
363 | if (!crtc) { | 417 | if (!(encoder->possible_crtcs & (1 << crtc->resIndex()))) { | ||
364 | continue; | 418 | continue; | ||
365 | } | 419 | } | ||
366 | DrmOutput *drmOutput = new DrmOutput(this); | | |||
367 | connect(drmOutput, &DrmOutput::dpmsChanged, this, &DrmBackend::outputDpmsChanged); | | |||
368 | drmOutput->m_crtcId = crtcId; | | |||
369 | drmOutput->m_connector = connector->connector_id; | | |||
370 | 420 | | |||
371 | if (m_atomicModeSetting) { | 421 | // check if crtc isn't used yet -- currently we don't allow multiple outputs on one crtc (cloned mode) | ||
372 | drmOutput->m_crtc = new DrmCrtc(crtcId, m_fd); | 422 | auto it = std::find_if(connectedOutputs.constBegin(), connectedOutputs.constEnd(), | ||
373 | if (drmOutput->m_crtc->init()) { | 423 | [crtc] (DrmOutput *o) { | ||
374 | drmOutput->m_crtc->setOutput(drmOutput); | 424 | return o->m_crtc == crtc; | ||
375 | } else { | 425 | } | ||
376 | qCWarning(KWIN_DRM) << "Crtc object failed, skipping output on connector" << connector->connector_id; | 426 | ); | ||
377 | delete drmOutput->m_crtc; | 427 | if (it != connectedOutputs.constEnd()) { | ||
378 | delete drmOutput; | | |||
379 | continue; | 428 | continue; | ||
380 | } | 429 | } | ||
381 | 430 | | |||
382 | drmOutput->m_conn = new DrmConnector(connector->connector_id, m_fd); | 431 | // we found a suitable encoder+crtc | ||
383 | if (drmOutput->m_conn->init()) { | 432 | // TODO: we could avoid these lib drm calls if we store all struct data in DrmCrtc and DrmConnector in the beginning | ||
384 | drmOutput->m_conn->setOutput(drmOutput); | 433 | ScopedDrmPointer<_drmModeCrtc, &drmModeFreeCrtc> modeCrtc(drmModeGetCrtc(m_fd, crtc->id())); | ||
385 | } else { | 434 | if (!modeCrtc) { | ||
386 | qCWarning(KWIN_DRM) << "Connector object failed, skipping output on connector" << connector->connector_id; | | |||
387 | delete drmOutput->m_conn; | | |||
388 | delete drmOutput; | | |||
389 | continue; | 435 | continue; | ||
390 | } | 436 | } | ||
391 | } | | |||
392 | 437 | | |||
393 | if (crtc->mode_valid) { | 438 | DrmOutput *output = new DrmOutput(this); | ||
394 | drmOutput->m_mode = crtc->mode; | 439 | con->setOutput(output); | ||
440 | output->m_conn = con; | ||||
441 | crtc->setOutput(output); | ||||
442 | output->m_crtc = crtc; | ||||
443 | connect(output, &DrmOutput::dpmsChanged, this, &DrmBackend::outputDpmsChanged); | ||||
444 | | ||||
445 | if (modeCrtc->mode_valid) { | ||||
446 | output->m_mode = modeCrtc->mode; | ||||
395 | } else { | 447 | } else { | ||
396 | drmOutput->m_mode = connector->modes[0]; | 448 | output->m_mode = connector->modes[0]; | ||
397 | } | 449 | } | ||
398 | qCDebug(KWIN_DRM) << "For new output use mode " << drmOutput->m_mode.name; | 450 | qCDebug(KWIN_DRM) << "For new output use mode " << output->m_mode.name; | ||
399 | 451 | | |||
400 | if (!drmOutput->init(connector.data())) { | 452 | if (!output->init(connector.data())) { | ||
401 | qCWarning(KWIN_DRM) << "Failed to create output for connector " << connector->connector_id; | 453 | qCWarning(KWIN_DRM) << "Failed to create output for connector " << con->id(); | ||
402 | delete drmOutput; | 454 | con->setOutput(nullptr); | ||
This part looks incorrect to me, you need to set m_crtc and m_conn for init but if it fails then you go to outer loop it leave con and crtc with dangling output. anthonyfieroni: This part looks incorrect to me, you need to set m_crtc and m_conn for init but if it fails… | |||||
455 | crtc->setOutput(nullptr); | ||||
456 | delete output; | ||||
403 | continue; | 457 | continue; | ||
404 | } | 458 | } | ||
405 | qCDebug(KWIN_DRM) << "Found new output with uuid" << drmOutput->uuid(); | 459 | qCDebug(KWIN_DRM) << "Found new output with uuid" << output->uuid(); | ||
406 | connectedOutputs << drmOutput; | 460 | | ||
407 | } | 461 | connectedOutputs << output; | ||
408 | std::sort(connectedOutputs.begin(), connectedOutputs.end(), [] (DrmOutput *a, DrmOutput *b) { return a->m_connector < b->m_connector; }); | 462 | emit outputAdded(output); | ||
409 | // check for outputs which got removed | 463 | outputDone = true; | ||
410 | auto it = m_outputs.begin(); | 464 | break; | ||
411 | while (it != m_outputs.end()) { | | |||
412 | if (connectedOutputs.contains(*it)) { | | |||
413 | it++; | | |||
414 | continue; | | |||
415 | } | 465 | } | ||
416 | DrmOutput *removed = *it; | 466 | if (outputDone) { | ||
417 | it = m_outputs.erase(it); | 467 | break; | ||
418 | emit outputRemoved(removed); | | |||
419 | delete removed; | | |||
420 | } | 468 | } | ||
421 | for (auto it = connectedOutputs.constBegin(); it != connectedOutputs.constEnd(); ++it) { | | |||
422 | if (!m_outputs.contains(*it)) { | | |||
423 | emit outputAdded(*it); | | |||
424 | } | 469 | } | ||
425 | } | 470 | } | ||
471 | std::sort(connectedOutputs.begin(), connectedOutputs.end(), [] (DrmOutput *a, DrmOutput *b) { return a->m_conn->id() < b->m_conn->id(); }); | ||||
426 | m_outputs = connectedOutputs; | 472 | m_outputs = connectedOutputs; | ||
427 | readOutputsConfiguration(); | 473 | readOutputsConfiguration(); | ||
428 | if (!m_outputs.isEmpty()) { | 474 | if (!m_outputs.isEmpty()) { | ||
429 | emit screensQueried(); | 475 | emit screensQueried(); | ||
430 | } | 476 | } | ||
431 | } | 477 | } | ||
432 | 478 | | |||
433 | void DrmBackend::readOutputsConfiguration() | 479 | void DrmBackend::readOutputsConfiguration() | ||
▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 Line(s) | 516 | for (auto it = changes.begin(); it != changes.end(); it++) { | |||
479 | drmoutput->setChanges(changeset); | 525 | drmoutput->setChanges(changeset); | ||
480 | } | 526 | } | ||
481 | emit screens()->changed(); | 527 | emit screens()->changed(); | ||
482 | } | 528 | } | ||
483 | 529 | | |||
484 | DrmOutput *DrmBackend::findOutput(quint32 connector) | 530 | DrmOutput *DrmBackend::findOutput(quint32 connector) | ||
485 | { | 531 | { | ||
486 | auto it = std::find_if(m_outputs.constBegin(), m_outputs.constEnd(), [connector] (DrmOutput *o) { | 532 | auto it = std::find_if(m_outputs.constBegin(), m_outputs.constEnd(), [connector] (DrmOutput *o) { | ||
487 | return o->m_connector == connector; | 533 | return o->m_conn->id() == connector; | ||
488 | }); | 534 | }); | ||
489 | if (it != m_outputs.constEnd()) { | 535 | if (it != m_outputs.constEnd()) { | ||
490 | return *it; | 536 | return *it; | ||
491 | } | 537 | } | ||
492 | return nullptr; | 538 | return nullptr; | ||
493 | } | 539 | } | ||
494 | 540 | | |||
495 | DrmOutput *DrmBackend::findOutput(const QByteArray &uuid) | 541 | DrmOutput *DrmBackend::findOutput(const QByteArray &uuid) | ||
496 | { | 542 | { | ||
497 | auto it = std::find_if(m_outputs.constBegin(), m_outputs.constEnd(), [uuid] (DrmOutput *o) { | 543 | auto it = std::find_if(m_outputs.constBegin(), m_outputs.constEnd(), [uuid] (DrmOutput *o) { | ||
498 | return o->m_uuid == uuid; | 544 | return o->m_uuid == uuid; | ||
499 | }); | 545 | }); | ||
500 | if (it != m_outputs.constEnd()) { | 546 | if (it != m_outputs.constEnd()) { | ||
501 | return *it; | 547 | return *it; | ||
502 | } | 548 | } | ||
503 | return nullptr; | 549 | return nullptr; | ||
504 | } | 550 | } | ||
505 | 551 | | |||
506 | quint32 DrmBackend::findCrtc(drmModeRes *res, drmModeConnector *connector, bool *ok) | | |||
507 | { | | |||
508 | if (ok) { | | |||
509 | *ok = false; | | |||
510 | } | | |||
511 | ScopedDrmPointer<_drmModeEncoder, &drmModeFreeEncoder> encoder(drmModeGetEncoder(m_fd, connector->encoder_id)); | | |||
512 | if (encoder) { | | |||
513 | if (!crtcIsUsed(encoder->crtc_id)) { | | |||
514 | if (ok) { | | |||
515 | *ok = true; | | |||
516 | } | | |||
517 | return encoder->crtc_id; | | |||
518 | } | | |||
519 | } | | |||
520 | // let's iterate over all encoders to find a suitable crtc | | |||
521 | for (int i = 0; i < connector->count_encoders; ++i) { | | |||
522 | ScopedDrmPointer<_drmModeEncoder, &drmModeFreeEncoder> encoder(drmModeGetEncoder(m_fd, connector->encoders[i])); | | |||
523 | if (!encoder) { | | |||
524 | continue; | | |||
525 | } | | |||
526 | for (int j = 0; j < res->count_crtcs; ++j) { | | |||
527 | if (!(encoder->possible_crtcs & (1 << j))) { | | |||
528 | continue; | | |||
529 | } | | |||
530 | if (!crtcIsUsed(res->crtcs[j])) { | | |||
531 | if (ok) { | | |||
532 | *ok = true; | | |||
533 | } | | |||
534 | return res->crtcs[j]; | | |||
535 | } | | |||
536 | } | | |||
537 | } | | |||
538 | return 0; | | |||
539 | } | | |||
540 | | ||||
541 | bool DrmBackend::crtcIsUsed(quint32 crtc) | | |||
542 | { | | |||
543 | auto it = std::find_if(m_outputs.constBegin(), m_outputs.constEnd(), | | |||
544 | [crtc] (DrmOutput *o) { | | |||
545 | return o->m_crtcId == crtc; | | |||
546 | } | | |||
547 | ); | | |||
548 | return it != m_outputs.constEnd(); | | |||
549 | } | | |||
550 | | ||||
551 | void DrmBackend::present(DrmBuffer *buffer, DrmOutput *output) | 552 | void DrmBackend::present(DrmBuffer *buffer, DrmOutput *output) | ||
552 | { | 553 | { | ||
553 | if (output->present(buffer)) { | 554 | if (output->present(buffer)) { | ||
554 | m_pageFlipsPending++; | 555 | m_pageFlipsPending++; | ||
555 | if (m_pageFlipsPending == 1 && Compositor::self()) { | 556 | if (m_pageFlipsPending == 1 && Compositor::self()) { | ||
556 | Compositor::self()->aboutToSwapBuffers(); | 557 | Compositor::self()->aboutToSwapBuffers(); | ||
557 | } | 558 | } | ||
558 | } | 559 | } | ||
▲ Show 20 Lines • Show All 168 Lines • Show Last 20 Lines |
You mean std::erase(std::remove_if(...), ....end()) otherwise you have dangling pointer(s) in conllection