-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Conversation
b80f8fe
to
e99d6c5
Compare
@tobrun Could you help out defining the initial API on the Android side here? |
e99d6c5
to
a12ed5a
Compare
For a initial implementation I believe changing the camera would be most usefull?
This leaves us with a choice, we either expose the same models as the SDK (eg. CameraPosition and LatLngBounds) or we keep the api in line what core exposes? A second iteration could bring support for Layer/Sources? Sidenote: I'm not aware of any requirements around items as showing the mapbox logo? |
Have you thought about implementing this as a service?
|
A good benchmark for this feature would be to have a grid or a list which loads map images on the fly. |
85fdee6
to
6c73a9a
Compare
Added:
I like the idea of a Service or threadpool somehow. Maybe separate from this PR though as it could just re-use these building blocks?
I assumed we need to add the logo somehow. cc @zugaldia |
6c73a9a
to
b5dc09c
Compare
Yes. We should include the logo in a way that both a regular map and a snapshot render in exactly the same manner. That way it's easier to set up transitions between the two that flow naturally. |
b5dc09c
to
9e8310c
Compare
Is there a reason we can't reuse // .hpp
class SnapshotCallback {
public:
void operator()(std::exception_ptr, PremultipliedImage);
};
class MapSnapshotter {
public:
MapSnapshotter(const std::string& styleURL,
const Size&,
const float pixelRatio,
const CameraOptions&);
void snapshot(ActorRef<SnapshotCallback>);
private:
class Impl;
Thread<Impl> impl;
};
// .cpp
MapSnapshotter::MapSnapshotter(...)
: impl(...) {
}
MapSnapshotter::snapshot(ActorRef<SnapshotCallback> callback) {
impl.invoke(&Impl::snapshot, callback);
}
class Impl {
public:
Impl(...);
void snapshot(ActorRef<SnapshotCallback>);
private:
HeadlessFrontend frontend;
Map map;
}
Impl::Impl(...)
: frontend(...)
, map(...) {
map.setStyle(...);
map.setCamera(...);
}
Impl::snapshot(ActorRef<SnapshotCallback> callback) {
map.renderStill([callback, &] (std::exception_ptr error) {
callback.invoke(&SnapshotCallback::operator(), error, frontend.readStillImage());
});
} In other words: put everything -- frontend, map, backend, renderer -- on the background snapshot thread, and invoke the callback when done. No thread pausing/resuming needed. |
9e8310c
to
14eba10
Compare
14eba10
to
413c660
Compare
413c660
to
77a646f
Compare
@jfirebaugh Thanks, that makes it indeed a lot simpler. I've added a implementation for SnapshotCallback that takes a lambda so platforms don't have to implement it. Besides some fixes, I also took the opportunity to enable destruction of the Java object through regular garbage collection. By making This makes the API much more natural on the Java side and there is no need for explicit destruction other than to abort prematurely (now called cancel). The only pre-requisite now for this PR is #9816 as cd8eb13 prevents the still image generation from rendering. |
|
||
if (err) { | ||
try { | ||
std::rethrow_exception(err); |
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 can use
inline std::string toString(std::exception_ptr error) { |
But it seems like we should propagate errors to the caller, via an onSnapshotError
method or something, no?
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.
Thanks. I will add an additional callback. I refrained from doing so initially as there is not much you can do in these situations.
class Size; | ||
class LatLngBounds; | ||
|
||
class SnapshotCallback { |
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 might be able to do using SnapshotCallback = std::function<void (std::exception_ptr, PremultipliedImage)>
and avoid the wrapper class.
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 left the wrapper class as it makes clear on which thread the callback is going to happen when requiring an ActorRef
in MapSnapshotter::snapshot
. If we require only the lambda, we would still internally use an actor/mailbox to ensure the callback happens on the original thread right?
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 mean keep ActorRef<SnapshotCallback>
as it is, but have SnapshotCallback
just be a std::function
typedef. I believe that's possible with your change in 68f470f.
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.
Yes, you're totally right. Very nice!
77a646f
to
b1dae4a
Compare
b1dae4a
to
69050dd
Compare
if (region) { | ||
mbgl::EdgeInsets insets = { 0, 0, 0, 0 }; | ||
std::vector<LatLng> latLngs = { region->southwest(), region->northeast() }; | ||
map.jumpTo(map.cameraForLatLngs(latLngs, insets)); |
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.
In case we have a region
set, can we avoid calling the first jumpTo
?
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.
Besides the coordinate, this also sets the pitch, etc. Since the map doesn't do anything significant before the render is started in Still
mode I didn't see it as significant. (the onUpdate does not trigger an actual update yet).
@@ -61,8 +61,6 @@ class Thread : public Scheduler { | |||
} | |||
|
|||
~Thread() override { | |||
MBGL_VERIFY_THREAD(tid); |
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.
Curious, what prompted this removal?
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 is c607c03. Enables the thread to be destructed from other threads (eg, the Finalizer thread on Android). Otherwise the client side api is quite awkward as the Java object needs to be explicitly "destructed" on the creating thread.
|
||
private: | ||
class Impl; | ||
std::unique_ptr<util::Thread<Impl>> impl; |
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.
Given that this is in a unique_ptr
anyway, can we instead forward declare util::Thread
with
namespace util {
template <typename T> class Thread;
} // namespace util
instead of exposing it publicly and including it here?
: impl(std::make_unique<util::Thread<MapSnapshotter::Impl>>("Map Snapshotter", fileSource, scheduler, styleURL, size, pixelRatio, cameraOptions, region, programCacheDir)) { | ||
} | ||
|
||
MapSnapshotter::~MapSnapshotter() = default; |
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 destructor synchronously destroys the thread. If a render is in progress, this could potentially take a few dozen or even hundred milliseconds. We could introduce a similar system like the tile parsing obsolete
atomic bool that we check after every layer and abort rendering in case we try to abort the rendering.
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.
Could be worth looking into. This doesn't add too much overhead in the "regular" case, it does add a check every frame for every layer in Still and Continuous mode.
Initial implementation for client side static maps (#9340).
This implementation uses a object on the main thread and manages an internal thread for rendering. #9340 mentions the ability to render on arbitrary threads, but I think this brings more complexity than it's worth as the result has to be forwarded to the main thread by the users. Instead, this API offers a simple callback that is called when the image is ready and handles the cross-thread communication internally.
Todo: