Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't keep pointer to QgsPointCloudLayer in QgsPointCloudLayerRenderer and QgsPointCloudLayerProfileGenerator #60086

Merged
merged 6 commits into from
Jan 25, 2025

Conversation

dvdkon
Copy link
Contributor

@dvdkon dvdkon commented Jan 8, 2025

Description

This is a quick change that refactors a small problem in QgsPointCloudLayerRenderer. Previously render() used a saved QgsPointCloudLayer object (and called methods on it and QgsPointCloudDataProvider, generating warnings), but that object is not thread safe. Instead we now get everything we need up-front and only keep a refcounted pointer to the thread-safe layer index.

EDIT: I've also made a similar change to QgsPointCloudLayerProfileGenerator, since it was pointed out it had the same issue.

@github-actions github-actions bot added this to the 3.42.0 milestone Jan 8, 2025
@dvdkon dvdkon marked this pull request as draft January 8, 2025 17:57
@dvdkon dvdkon force-pushed the point-cloud-dont-keep-layer branch 2 times, most recently from da23ec8 to 5e92e01 Compare January 8, 2025 19:16
Copy link

github-actions bot commented Jan 8, 2025

🪟 Windows builds

Download Windows builds of this PR for testing.
Debug symbols for this build are available here.
(Built from commit ed28933)

🪟 Windows Qt6 builds

Download Windows Qt6 builds of this PR for testing.
(Built from commit ed28933)

Copy link

github-actions bot commented Jan 8, 2025

Tests failed for Qt 5

One or more tests failed using the build from commit a3866f4

classified_render_edit_1 (testModifyAttributeValue)

classified_render_edit_1

Test failed at testModifyAttributeValue at tests/src/core/testqgspointcloudediting.cpp:193

Rendered image did not match tests/testdata/control_images/pointcloud_editing/expected_classified_render_edit_1/expected_classified_render_edit_1.png (found 490 pixels different)

The full test report (included comparison of rendered vs expected images) can be found here.

Further documentation on the QGIS test infrastructure can be found in the Developer's Guide.

Copy link

github-actions bot commented Jan 8, 2025

Tests failed for Qt 6

One or more tests failed using the build from commit a3866f4

classified_render_edit_1 (testModifyAttributeValue)

classified_render_edit_1

Test failed at testModifyAttributeValue at tests/src/core/testqgspointcloudediting.cpp:193

Rendered image did not match tests/testdata/control_images/pointcloud_editing/expected_classified_render_edit_1/expected_classified_render_edit_1.png (found 490 pixels different)

The full test report (included comparison of rendered vs expected images) can be found here.

Further documentation on the QGIS test infrastructure can be found in the Developer's Guide.

@dvdkon dvdkon force-pushed the point-cloud-dont-keep-layer branch from 5e92e01 to 5777b8e Compare January 8, 2025 21:05
@dvdkon dvdkon marked this pull request as ready for review January 9, 2025 09:08
@uclaros
Copy link
Contributor

uclaros commented Jan 9, 2025

Same change needs to be applied to the profile renderer too.
And probably then we can switch from QGIS_PROTECT_QOBJECT_THREAD_ACCESS_NON_FATAL to QGIS_PROTECT_QOBJECT_THREAD_ACCESS in QgsPointCloudLayer::dataProvider()

const QVector< QgsPointCloudSubIndex > mSubIndexes;
std::optional<QgsPointCloudIndex> mOverviewIndex;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious, is there a benefit of using std::optional instead of just a QgsPointCloudIndex() / QgsPointCloudIndex( nullptr ), which evaluates as false?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. There isn't, I just personally default to using std::optional when a member can be explicitly left out, and don't much like putting nullptr in smart pointers.

It is redundant (and probably inconsistent with the rest of the codebase), so if you think it's better, I'll change it to the bare index object.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a nice habit I guess!
It would be ever slightly more readable without it, as optional is barely used in the codebase, but I don't have a strong opinion :)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd personally love to see optional used more often -- it's quite an elegant solution to some tricky API considerations... just so long as we don't need to worry about exposure to Python 🤣

@dvdkon
Copy link
Contributor Author

dvdkon commented Jan 9, 2025

Same change needs to be applied to the profile renderer too. And probably then we can switch from QGIS_PROTECT_QOBJECT_THREAD_ACCESS_NON_FATAL to QGIS_PROTECT_QOBJECT_THREAD_ACCESS in QgsPointCloudLayer::dataProvider()

Do you mean QgsPointCloudLayerProfileGenerator? As far as I can see, QgsProfilePlotRenderer doesn't keep any layer object. And in the generator it's more complicated, because it calls identify() on the data provider, which is also not thread-safe. So we can't really fix the issue in full by just shuffling objects around like in this PR.

@uclaros
Copy link
Contributor

uclaros commented Jan 9, 2025

Do you mean QgsPointCloudLayerProfileGenerator? As far as I can see, QgsProfilePlotRenderer doesn't keep any layer object. And in the generator it's more complicated, because it calls identify() on the data provider, which is also not thread-safe. So we can't really fix the issue in full by just shuffling objects around like in this PR.

Yes, I mean QgsPointCloudLayerProfileGenerator::generateProfile(), where I believe the main issue lies, as it is called in separate threads for rendering (the comment there pin points the issue as the logic was copied from the 2d renderer).

We can ignore the identify() part for now, it is probably called from the main thread.

@dvdkon
Copy link
Contributor Author

dvdkon commented Jan 13, 2025

Yes, I mean QgsPointCloudLayerProfileGenerator::generateProfile(), where I believe the main issue lies, as it is called in separate threads for rendering (the comment there pin points the issue as the logic was copied from the 2d renderer).

Thanks, I see it now. I've fixed this method as well and made the dataProvider() method fail on non-main-thread access.

@dvdkon dvdkon changed the title Don't keep pointer to QgsPointCloudLayer in QgsPointCloudLayerRenderer Don't keep pointer to QgsPointCloudLayer in QgsPointCloudLayerRenderer and QgsPointCloudLayerProfileGenerator Jan 13, 2025
@dvdkon dvdkon force-pushed the point-cloud-dont-keep-layer branch from 467bfd6 to a3866f4 Compare January 13, 2025 11:00
@uclaros
Copy link
Contributor

uclaros commented Jan 13, 2025

Thanks, I see it now. I've fixed this method as well and made the dataProvider() method fail on non-main-thread access.

Nice!
You will also need to change layer->dataProvider()->index() to the new method layer->index(), which returns a different index (editing index) when layer is in editing mode.
Then that method can also change to the FATAL variant!

@dvdkon
Copy link
Contributor Author

dvdkon commented Jan 22, 2025

You will also need to change layer->dataProvider()->index() to the new method layer->index(), which returns a different index (editing index) when layer is in editing mode.

Thanks, I didn't notice that. The test now passes on my machine.

Then that method can also change to the FATAL variant!

Done.

@nyalldawson nyalldawson merged commit 5bcde82 into qgis:master Jan 25, 2025
31 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants