-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Conversation
@brunoabinader, thanks for your PR! By analyzing this pull request, we identified @tmpsantos, @1ec5 and @jfirebaugh to be potential reviewers. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I disagree with this change. util::Size
is used internally in Mapbox GL, and converting platform-specific size types to util::Size
is the platform binding's responsibility: The Qt wrapper should convert QSize
to util::Size
and back.
56572b1
to
b076cf5
Compare
@kkaefer I've added a sanitizer for the Qt wrapper when converting from |
b076cf5
to
6701aed
Compare
include/mbgl/util/projection.hpp
Outdated
@@ -13,7 +13,7 @@ namespace mbgl { | |||
class Projection { | |||
public: | |||
// Map pixel width at given scale. | |||
static double worldSize(double scale) { | |||
static constexpr double worldSize(double scale) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the rationale for this change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nothing special besides the fact util::tileSize
is already known in compile time - as requested below I'll subtract this cosmetic change from this PR 👍
Actually this is needed if we want to keep TransformState
ctors constexpr - Projection::worldSize
is used to initialize Bc
and Cc
here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a need for TransformState
to be constexpr
? Based on the history with GCC 4.9 it sounded like something we should avoid using unless it's truly necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jfirebaugh my understanding is that using constexpr
allows clearer understanding of what we want to do, and what the compiler is possible of calculating in compile time - I personally find class/struct ctors using constexpr
to be a more elegant pattern too.
There are specific cases where constexpr
usage is bogus on GCC 4.9 - though this is not the case for simple cases like the ones above i.e. we still use plain constexpr
in lots of places in our https://github.com/mapbox/mapbox-gl-native/tree/qt-staging branch.
As example, there are cases where constexpr
works fine in GCC 4.9 - like this example, and cases where it doesn't.
Probably the best way to make sure we're compliant with GCC 4.9 is to have a GCC 4.9-based build bot running as part of our CI, but I guess that's out of scope for this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
using
constexpr
allows clearer understanding of what we want to do
How so? We don't inherently intend that these calculations or constructors happen at compile time, and the fact that they (currently) can is not an essential feature of the code.
I don't think we should go around adding constexpr
everywhere it's possible to do so. If we're talking about introducing coding guidelines for C++ features, then I would say my guideline for constexpr
is "use only in cases where compile-time evaluation is an intended or essential feature of the code, or for general-purpose library functions which may need to be used in constexpr
contexts."
src/mbgl/map/transform.cpp
Outdated
@@ -46,6 +46,10 @@ Transform::Transform(MapObserver& observer_, | |||
#pragma mark - Map View | |||
|
|||
bool Transform::resize(const Size size) { | |||
if (!size) { | |||
return false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If (0, 0) is an invalid size, shouldn't this throw rather than silently doing nothing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I supposed that was the meaning of the return bool
- should we make it void
instead and throw on such cases?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think at one point the return value was used to check if the size was changing, but it looks like this may be used only in tests. If so, yes, void
and throw on invalid input.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can use try/catch in the tests 👍
src/mbgl/map/transform_state.hpp
Outdated
TransformState(ConstrainMode = ConstrainMode::HeightOnly, ViewportMode = ViewportMode::Default); | ||
constexpr TransformState() = default; | ||
|
||
constexpr TransformState(ConstrainMode constrain_, ViewportMode viewport_) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This removes the ability to construct with a single argument. (Could you please limit your changes to fixing the main issue, and propose cosmetic changes in a separate PR?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This removes the ability to construct with a single argument.
Do we have a need for this? If so, we could add an extra ctor that receives a single ConstrainMode
.
Could you please limit your changes to fixing the main issue, and propose cosmetic changes in a separate PR?
Fair enough - though I've seen this as a common behavior in some of our PRs.
src/mbgl/map/transform_state.hpp
Outdated
double min_scale = std::pow(2, 0); | ||
double max_scale = std::pow(2, 20); | ||
double min_scale = 1.0; // 2^0 | ||
double max_scale = 1048576.0; // 2^20 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the rationale for this change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These values are known and std::pow
is not constexpr
, causing the TransformState
ctor to not be constexpr
-able too.
src/mbgl/map/transform_state.hpp
Outdated
@@ -74,13 +77,17 @@ class TransformState { | |||
double zoomScale(double zoom) const; | |||
double scaleZoom(double scale) const; | |||
|
|||
constexpr explicit operator bool() const { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's not use operator bool()
for validity checks. If we need a validity check, name it bool valid() const
. But first, try to eliminate the possibility of an invalid state.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's not use operator bool() for validity checks.
We've discussed this before, yet we still use explicit operator bool
in other parts of the code e.g. Size::operator bool.
I don't have a strong opining about replacing it with something like valid()
, however I'd prefer to have a separate PR that replaces every explicit operator bool
usage for validation in our code 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👉 #8601.
{ | ||
mapObj = std::make_unique<mbgl::Map>( | ||
*this, sanitizedSize(size), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is size
invalid here and needs to be sanitized in the first place?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because QSize
default constructor initializes witdh and height with -1. This causes an overflow when converting from int
to uint32_t
- using std::numeric_limits<uint32_t>::max()
for width and height has proven to cause undefined behavior in places like tileCover.
Qt sometimes initializes a dummy map plugin as means of discovering which map plugins it has available - when it does, it uses a default QSize
for initializing the item size.
src/mbgl/map/transform.cpp
Outdated
@@ -46,6 +46,10 @@ Transform::Transform(MapObserver& observer_, | |||
#pragma mark - Map View | |||
|
|||
bool Transform::resize(const Size size) { | |||
if (!size) { | |||
return false; | |||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need to return early here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because if we allow Transform
to have a zero-ed size, it can result in division-by-zero issues e.g. here.
6701aed
to
05d1f9f
Compare
@jfirebaugh I've |
a44bbf9
to
c67c189
Compare
d0a8a74
to
6ab94e9
Compare
413e34c
to
94ecc6a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Android changes look 👍
94ecc6a
to
28ac4c4
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Qt 👍
platform/qt/src/qmapboxgl.cpp
Outdated
mbgl::Size sanitizedSize(const QSize& size) { | ||
return mbgl::Size { | ||
size.width() <= 0 ? 0 : static_cast<uint32_t>(size.width()), | ||
size.height() <= 0 ? 0 : static_cast<uint32_t>(size.height()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could use std::max
here, but I'm OK with ternary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, I'll make the change 👍
28ac4c4
to
d94cb91
Compare
d94cb91
to
b05e149
Compare
@kkaefer @jfirebaugh can you please re-review? |
Can you replace |
b05e149
to
7bcb6b7
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 on operator bool
⇢ isEmpty
changes. IIRC @1ec5 had some concerns about the 64⨯64 minimum size, so let's me sure he weighs in.
On iOS and macOS, it’s possible to create a view with zero size then give it size at some point before the view appears on screen, such as after the view is added to the view hierarchy. This pattern is especially common in iOS applications that build the interface programmatically using manual layout such as As reported in #1572, the current implementation on iOS and macOS accommodates this pattern only partially. On master, the following code: MGLMapView *mapView = [[MGLMapView alloc] initWithFrame:CGRectZero];
mapView.frame = CGRectMake(0, 0, 300, 300);
[self.view addSubview:mapView]; results in the following console spew:
but otherwise everything is perfectly fine. However, swap two lines so that the view is added to the view hierarchy before it gets some size, and the application crashes with an OpenGL error:
As I understand it, this PR would ensure that the crash occurs earlier with a more informative error message. Unfortunately, it also causes the first case (setting the size right after initialization) to crash in the same way:
I think this would be a surprising change for developers who build their interface programmatically. Is there any way to modify If not, I suppose we could implement #1572 (comment) by making 64×64 points the minimum size in diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm
index d80e741c0..67b5ac09f 100644
--- a/platform/ios/src/MGLMapView.mm
+++ b/platform/ios/src/MGLMapView.mm
@@ -576,8 +576,12 @@ public:
- (mbgl::Size)size
{
- return { static_cast<uint32_t>(self.bounds.size.width),
- static_cast<uint32_t>(self.bounds.size.height) };
+ CGSize size = CGSizeMake(MAX(self.bounds.size.width, 64),
+ MAX(self.bounds.size.height, 64));
+ return {
+ static_cast<uint32_t>(size.width),
+ static_cast<uint32_t>(size.height),
+ };
}
- (mbgl::Size)framebufferSize
diff --git a/platform/macos/src/MGLMapView.mm b/platform/macos/src/MGLMapView.mm
index e19755044..bccc7cff9 100644
--- a/platform/macos/src/MGLMapView.mm
+++ b/platform/macos/src/MGLMapView.mm
@@ -307,8 +307,12 @@ public:
}
- (mbgl::Size)size {
- return { static_cast<uint32_t>(self.bounds.size.width),
- static_cast<uint32_t>(self.bounds.size.height) };
+ CGSize size = CGSizeMake(MAX(self.bounds.size.width, 64),
+ MAX(self.bounds.size.height, 64));
+ return {
+ static_cast<uint32_t>(size.width),
+ static_cast<uint32_t>(size.height),
+ };
}
- (mbgl::Size)framebufferSize { Methods like /cc @boundsj |
Note that, even with the diff proposed in #8562 (comment), the console spew about failing to bind the drawable persists but appears to be harmless:
|
We are not going to modify this pattern - as you mentioned the size can be silently updated to 64x64 inside The suggested patch for iOS is no different from the approach took by MGLStyleTests and MGLSourceQueryTests.
Initializing a map with a valid (by valid I mean at least the minimum texture size as specified by the OpenGL ES 2.0 which is 64x64) size is currently mandatory in our code - because we are immediately supposed to be able to call Complexity aside, I believe the less painful change would be to just set the size to 64x64 silently when passing it to the |
Alright, let’s go with silently upping the size to 64×64 points. Does |
The only restriction I'm aware of for framebuffer sizes is that their width and height values must be a power of 2 for OpenGL versions prior to 2.0. |
7bcb6b7
to
f044cb4
Compare
Some platforms provide their own size type defaulting to -1 in both width and height e.g. Qt's QSize. We should special case this and make this part of the assertion check.