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

Prohibit inconsistent size state for SubViewport #66906

Merged

Conversation

Sauermann
Copy link
Contributor

Prohibit size changes of SubViewports with parent SubViewportContainers that have stretch mode enabled.

It is possible to bring the engine into an inconsistent state with the following tree:

  • SubViewportContainer (with stretch = true and size = Vector2i(200, 200))
    • SubViewport

by using the command SubViewport.set_size(Vector2i(100, 100))

SubViewportContainer.stretch = true means, that the SubViewport is automatically resized to the size of the SubViewportContainer and this command temporarily bypasses this automatism.
A MRP for this can be found in #64893

The inconsistency is:

  • The SubViewport is displayed stretched over the complete size of the SubViewportContainer
  • Events are transformed based on the size of SubViewport

This means that mouse events do not align with the display, until the next size-recalculation (for example by resizing the window).

This PR prevents this kind of size change be checking the parents stretch status.

An alternative would be, that set_size implicitly sets the parents stretch status to false, but that seems to be more error prone.

This PR does not affect #34805, because that issue is based on SubViewportContainer.stretch = false.

@Sauermann Sauermann requested review from a team as code owners October 4, 2022 19:58
@Sauermann Sauermann force-pushed the fix-prohibit-inconsistent-size-state branch from cce6bf0 to 00248e8 Compare October 4, 2022 21:06
@Sauermann Sauermann requested a review from a team as a code owner October 4, 2022 21:06
@Calinou Calinou added this to the 4.0 milestone Oct 4, 2022
Copy link
Member

@aaronfranke aaronfranke left a comment

Choose a reason for hiding this comment

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

I was experiencing this issue recently and I can confirm that this PR fixes it. With this PR it now shows a warning which tells you exactly what part of your code is causing the issue.

Comment on lines 4083 to 4099
SubViewportContainer *c = Object::cast_to<SubViewportContainer>(get_parent());
if (c && c->is_stretch_enabled()) {
WARN_PRINT("Can't change the size of a `SubViewport` with a `SubViewportContainer` parent that has `stretch` enabled. Set `SubViewportContainer.stretch` to `false` to allow changing the size manually.");
return;
}
Copy link
Member

@aaronfranke aaronfranke Oct 4, 2022

Choose a reason for hiding this comment

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

This can likely be improved. The first thing that occured to me is to only have this check on debug builds:

Suggested change
SubViewportContainer *c = Object::cast_to<SubViewportContainer>(get_parent());
if (c && c->is_stretch_enabled()) {
WARN_PRINT("Can't change the size of a `SubViewport` with a `SubViewportContainer` parent that has `stretch` enabled. Set `SubViewportContainer.stretch` to `false` to allow changing the size manually.");
return;
}
#ifdef DEBUG_ENABLED
SubViewportContainer *c = Object::cast_to<SubViewportContainer>(get_parent());
if (c && c->is_stretch_enabled()) {
WARN_PRINT("Can't change the size of a `SubViewport` with a `SubViewportContainer` parent that has `stretch` enabled. Set `SubViewportContainer.stretch` to `false` to allow changing the size manually.");
return;
}
#endif // DEBUG_ENABLED

Buut also, I see that the new force_set_size is already doing a get_parent() call and a SubViewportContainer cast, so maybe these should be combined somehow? I'd imagine an optional argument of bool p_force = false would break the property binding. Hmm... Maybe have internal_set_size(const Size2i &p_size, bool p_force = false) internally, and set_size(const Size2i &p_size) for the property binding which calls the internal one?

void SubViewport::internal_set_size(const Size2i &p_size, bool p_force) {
	SubViewportContainer *c = Object::cast_to<SubViewportContainer>(get_parent());
#ifdef DEBUG_ENABLED
	if (!p_force && c && c->is_stretch_enabled()) {
		WARN_PRINT("Can't change the size of a `SubViewport` with a `SubViewportContainer` parent that has `stretch` enabled. Set `SubViewportContainer.stretch` to `false` to allow changing the size manually.");
		return;
	}
#endif // DEBUG_ENABLED
	_set_size(p_size, _get_size_2d_override(), Rect2i(), _stretch_transform(), true);
	if (c) {
		c->update_minimum_size();
	}
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My first try was to add an optional argument bool p_force = false, but that did break the GDScript binding.

SubViewport::_set_size would still need to be public, so that it can be called from SubViewportContainer.
A different approach would be to make SubViewport::_set_size private, but then SubViewportContainer would need to be a friend class of SubViewport.

@aaronfranke Do you have suggestions, which of the two approaches is better?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The first thing that occured to me is to only have this check on debug builds

Would this message not also be helpful for people, who don't use a debug build, since this can be achieved via GDScript?
Also without this check, the inconsistent state can be reached, which I would like to prevent.

Copy link
Member

Choose a reason for hiding this comment

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

Sorry, I updated the message, the _set_size method is already taken by the parent class Viewport, so we would have to call the other one something else, like internal_set_size or I guess we could do Viewport::_set_size to refer to the base class but you're right that it might be confusing to have a public method that starts with an underscore.

Keep in mind that there are two levels of debug. DEBUG_ENABLED will show up in the editor and in debug templates (the old "release_debug"), but not in release templates (the old "release"). DEV_ENABLED is the one that will only show up when you compile yourself with full debugging for engine debugging (the old "debug"). If we have DEBUG_ENABLED it will still have the check for all editor builds.

What do you think about this? https://github.com/aaronfranke/godot/tree/fix-inconsistent-subviewport-size

Copy link
Contributor Author

@Sauermann Sauermann Oct 4, 2022

Choose a reason for hiding this comment

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

Thank you for the explanation of the Debug-Levels.

Your suggestions looks fine to me with one exception:
Even in a release template, the test should be executed in order to prevent the inconsistent state.

I have pushed a new version incorporating your changes but only put the warning message within DEV_ENABLED.

@Sauermann Sauermann force-pushed the fix-prohibit-inconsistent-size-state branch 3 times, most recently from 4f5c319 to 7fedd68 Compare October 4, 2022 22:25
Copy link
Member

@aaronfranke aaronfranke left a comment

Choose a reason for hiding this comment

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

The DEBUG_ENABLED thing can be decided by @akien-mga. I would wrap the whole if in DEBUG_ENABLED, but aside from that I approve.

Copy link
Member

@aaronfranke aaronfranke left a comment

Choose a reason for hiding this comment

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

Maybe docs could be improved though:

doc/classes/SubViewport.xml Outdated Show resolved Hide resolved
@Sauermann Sauermann force-pushed the fix-prohibit-inconsistent-size-state branch from 7fedd68 to 38a14b2 Compare October 5, 2022 07:38
scene/main/viewport.h Outdated Show resolved Hide resolved
@Sauermann Sauermann force-pushed the fix-prohibit-inconsistent-size-state branch from 38a14b2 to 8d568d7 Compare October 5, 2022 15:40
internal_set_size(p_size);
}

void SubViewport::internal_set_size(const Size2i &p_size, bool p_force) {
Copy link
Contributor Author

@Sauermann Sauermann Jan 31, 2023

Choose a reason for hiding this comment

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

PR-review-meeting:
Add public function set_size_force.
Use _internal_set_size as name and make it private.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have updated the PR with the requested changes from the PR-review-meeting.

Prohibit size changes of SubViewports with parent SubViewportContainers that have stretch mode enabled.
@Sauermann Sauermann force-pushed the fix-prohibit-inconsistent-size-state branch from 8d568d7 to decbda6 Compare January 31, 2023 21:53
Copy link
Member

@akien-mga akien-mga left a comment

Choose a reason for hiding this comment

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

Approved in PR review meeting.

@akien-mga akien-mga merged commit 51414fc into godotengine:master Feb 1, 2023
@akien-mga
Copy link
Member

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants