Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

Use a host interface for CustomLayer instead of function pointers #11553

Merged
merged 6 commits into from
Apr 2, 2018

Conversation

asheemmamoowala
Copy link
Contributor

@asheemmamoowala asheemmamoowala commented Mar 29, 2018

Fixes #11143.
Fixes #10771.

CustomLayers are a special class of style layer where the core API delegates rendering of the layer to external code.

With the change to asynchronous rendering, the RenderCustomLayer may call out to the external code to initialize, render, or clean-up outside of the CustomLayer's lifecycle. For example, a custom layer added to the current map style, followed by a different style being set on the map.

In addition to #11143, a number of other issues related to custom layers have been opened/addressed recently: #10771, #10755, #10765, #11343.

The existing API using avoid * context object and function pointers, doesn't provide any lifecycle information. The context object needs to exist for the lifetime of the CustomLayer (CustomLayer::Impl to be more specific), and the RenderCustomLayer. This PR proposes using a shared_ptr<> to ensure that the context object has the required lifetime.

Since a shared_ptr<> cannot be used with void *, a host interface CustomLayerHost with the former function pointers converted to equivalent events.

On iOS/macOS, the SDK uses the peer style layer MGLOpenGLStyleLayer as the context object, creating a tenuous chain of ownership. The implementation of CustomLayerHost addresses the retain/release issue described in this comment.

RenderCustomLayer calls CustomLayerHost::initialize() in its constructor to ensure that the peer style layer is retained, even if the style (and CustomLayer::Impl) are changed prematurely.

TODO

  • Pull in integration tests spread across a couple other branches
  • Document and communicate API changes

cc @julianrex @lilykaiser @jfirebaugh @akitchen @1ec5

Copy link
Contributor

@jfirebaugh jfirebaugh left a comment

Choose a reason for hiding this comment

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

I like this idea, but doesn't the shared_ptr< CustomLayerHost> need to be used throughout the whole stack -- CustomLayer, CustomLayer::Impl -- not just RenderCustomLayer?

@asheemmamoowala
Copy link
Contributor Author

The shared_ptr<> is used in CustomLayer::Impl as well as the RenderCustomLayer - they are the shared owners. CustomLayer passes the raw pointer down to the Impl and doesn't hold on to it.

@jfirebaugh
Copy link
Contributor

Constructing a shared_ptr<> from a raw pointer is almost always incorrect. If ownership is being transferred into CustomLayer::Impl/RenderCustomLayer from CustomLayer, then use an ownership type (unique_ptr or shared_ptr) to indicate that.

@asheemmamoowala asheemmamoowala force-pushed the ahm-custom-layer-api-change branch 3 times, most recently from f198d30 to b0f68d3 Compare March 29, 2018 23:50
Andrew Kitchen and others added 5 commits March 29, 2018 17:22
Unfortunately a brief spin of the run loop is required for the layer reuse test. I've tried to tune this down for my machine; YMMV
… pointers

Co-authored-by: Julian Rex <julian.rex@mapbox.com>
…style layer, and upgrade to a strong layer when initialized by the renderer

Co-authored-by: Julian Rex <julian.rex@mapbox.com>
@asheemmamoowala asheemmamoowala force-pushed the ahm-custom-layer-api-change branch from b0f68d3 to 46383bb Compare March 30, 2018 00:26
@asheemmamoowala asheemmamoowala changed the base branch from master to release-boba March 30, 2018 00:27
}

void deinitialize() {

Copy link
Member

Choose a reason for hiding this comment

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

nit additional return vs contextLost()

Copy link
Member

@tobrun tobrun left a comment

Choose a reason for hiding this comment

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

tested on Android device and code is looking 👍

@@ -2,18 +2,11 @@

#include <mbgl/style/layer.hpp>

#include <functional>
Copy link
Contributor

Choose a reason for hiding this comment

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

Unnecessary?

@@ -28,6 +21,18 @@ struct CustomLayerRenderParameters {
double fieldOfView;
};

class CustomLayerHost {
public:
virtual ~CustomLayerHost() = default;
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: indentation.

* main thread, at a point when the GL context is active but before rendering for the first
* time.
*
* Resources that are acquired in this method must be released in the UninitializeFunction.
Copy link
Contributor

Choose a reason for hiding this comment

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

"in the UninitializeFunction" → "in deinitialize"

@@ -36,38 +41,29 @@ struct CustomLayerRenderParameters {
* Make sure that you are drawing your fragments with a z value of 1 to take advantage of the
* opaque fragment culling in case there are opaque layers above your custom layer.
*/
using CustomLayerRenderFunction = void (*)(void* context, const CustomLayerRenderParameters&);
virtual void render(const CustomLayerRenderParameters&) = 0;

/**
* Called when the system has destroyed the underlying GL context. The
* `CustomLayerDeinitializeFunction` will not be called in this case, however
Copy link
Contributor

Choose a reason for hiding this comment

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

Update docs.


/**
* Called when the system has destroyed the underlying GL context. The
* `CustomLayerDeinitializeFunction` will not be called in this case, however
* `CustomLayerInitializeFunction` will be called instead to prepare for a new render.
*
*/
using CustomLayerContextLostFunction = void (*)(void* context);
virtual void contextLost() = 0;

/**
* Destroy any GL state needed by the custom layer, and deallocate context, if necessary. This
* method is called once, from the main thread, at a point when the GL context is active.
*
* Note that it may be called even when the InitializeFunction has not been called.
Copy link
Contributor

Choose a reason for hiding this comment

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

Update docs.

@@ -116,7 +114,9 @@ - (void)setStyle:(MGLStyle *)style {
[NSException raise:@"MGLLayerReuseException"
format:@"%@ cannot be added to more than one MGLStyle at a time.", self];
}
_style.openGLLayers[self.identifier] = nil;
Copy link
Contributor

Choose a reason for hiding this comment

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

What's this for?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Clears the entry from the previous style when calling [styleLayer removeFromStyle:]. I had removed this in #10765, but I think it needs to be returned. In the Obj-C SDK, the style and the CustomLayerHost-implementation share ownership of the peer layer.

@@ -12,17 +12,16 @@ namespace mbgl {
using namespace style;

RenderCustomLayer::RenderCustomLayer(Immutable<style::CustomLayer::Impl> _impl)
: RenderLayer(LayerType::Custom, _impl) {
: RenderLayer(LayerType::Custom, _impl), host(_impl->host) {
host->initialize();
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a reason you're changing the timing of when the custom layer is initialized? Are you sure the GL context is guaranteed to be active at this point? If so, add assert(BackendScope::exists()); like in the destructor.

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 moved this to the constructor in an attempt to initialize and retain the Obj-C peer layer sooner. This was to address a race condition where a custom layer is added to a style, but before the next frame renders, the style is swapped. In this case, the peer layer was released too soon.
This fix didn't address that issue, but I decided to keep it in, since the render layers are created in Renderer::Impl and rendered in the same render pass. Adding the assert for protection.

@@ -38,7 +26,6 @@ std::unique_ptr<Layer> CustomLayer::cloneRef(const std::string&) const {
}

// Visibility

Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: revert whitespace change.

@@ -34,7 +34,7 @@ void main() {
// layer implementation because it is intended to reflect how someone using custom layers
// might actually write their own implementation.

class TestLayer {
class TestLayer : public mbgl::style::CustomLayerHost {
public:
~TestLayer() {
if (program) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This needs to move to deinitialize.

}
if (contextDestroyed) {
host->contextLost();
} else if (!contextDestroyed) {
Copy link
Contributor

Choose a reason for hiding this comment

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

The conditional is redundant.

@friedbunny friedbunny added Core The cross-platform C++ core, aka mbgl iOS Mapbox Maps SDK for iOS macOS Mapbox Maps SDK for macOS Android Mapbox Maps SDK for Android Qt Mapbox Maps SDK for Qt, aka Qt Location Mapbox GL labels Apr 2, 2018
Copy link
Contributor

@fabian-guerra fabian-guerra left a comment

Choose a reason for hiding this comment

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

I tested the changes on macOS. Besides adding the blurb to the changelogs (iOS, macOS & Android) and some nits LGTM.

@@ -172,6 +170,23 @@ class ExampleCustomLayer {

}

void contextLost() {
__android_log_write(ANDROID_LOG_INFO, LOG_TAG, "ContextLost");
if (program) {
Copy link
Contributor

Choose a reason for hiding this comment

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

In the context lost case, the system has already destroyed all the GL resources, and glDeleteBuffers etc should not be called (IIRC, they'll actually produce errors). So all this should do is clear program.

@asheemmamoowala asheemmamoowala force-pushed the ahm-custom-layer-api-change branch from b2e7d7e to c08c88c Compare April 2, 2018 22:22
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Android Mapbox Maps SDK for Android Core The cross-platform C++ core, aka mbgl iOS Mapbox Maps SDK for iOS macOS Mapbox Maps SDK for macOS Qt Mapbox Maps SDK for Qt, aka Qt Location Mapbox GL
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants