Use a host interface for CustomLayer instead of function pointers (#11553)
Use a host interface for CustomLayer instead of function pointers Co-authored-by: Julian Rex <julian.rex@mapbox.com>
This commit is contained in:
@@ -47,10 +47,10 @@ protected:
|
||||
// as a matched pair, exclusively through BackendScope, in two situations:
|
||||
//
|
||||
// 1. When releasing GL resources during Renderer destruction
|
||||
// (Including calling CustomLayerDeinitializeFunction during RenderCustomLayer destruction)
|
||||
// (Including calling CustomLayerHost::deinitialize during RenderCustomLayer destruction)
|
||||
// 2. When renderering through Renderer::render()
|
||||
// (Including calling CustomLayerDeinitializeFunction for newly added custom layers and
|
||||
// CustomLayerDeinitializeFunction on layer removal)
|
||||
// (Including calling CustomLayerHost::initialize for newly added custom layers and
|
||||
// CustomLayerHost::deinitialize on layer removal)
|
||||
virtual void activate() = 0;
|
||||
virtual void deactivate() = 0;
|
||||
|
||||
|
||||
@@ -6,16 +6,7 @@ namespace mbgl {
|
||||
namespace style {
|
||||
|
||||
/**
|
||||
* Initialize any GL state needed by the custom layer. This method is called once, from the
|
||||
* 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.
|
||||
*/
|
||||
using CustomLayerInitializeFunction = void (*)(void* context);
|
||||
|
||||
/**
|
||||
* Parameters that define the current camera position for a CustomLayerRenderFunction.
|
||||
* Parameters that define the current camera position for a `CustomLayerHost::render()` function.
|
||||
*/
|
||||
struct CustomLayerRenderParameters {
|
||||
double width;
|
||||
@@ -28,46 +19,49 @@ struct CustomLayerRenderParameters {
|
||||
double fieldOfView;
|
||||
};
|
||||
|
||||
/**
|
||||
* Render the layer. This method is called once per frame. The implementation should not make
|
||||
* any assumptions about the GL state (other than that the correct context is active). It may
|
||||
* make changes to the state, and is not required to reset values such as the depth mask, stencil
|
||||
* mask, and corresponding test flags to their original values.
|
||||
* 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&);
|
||||
class CustomLayerHost {
|
||||
public:
|
||||
virtual ~CustomLayerHost() = default;
|
||||
/**
|
||||
* Initialize any GL state needed by the custom layer. This method is called once, from the
|
||||
* 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 `deinitialize` function.
|
||||
*/
|
||||
virtual void initialize() = 0;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
/**
|
||||
* Render the layer. This method is called once per frame. The implementation should not make
|
||||
* any assumptions about the GL state (other than that the correct context is active). It may
|
||||
* make changes to the state, and is not required to reset values such as the depth mask, stencil
|
||||
* mask, and corresponding test flags to their original values.
|
||||
* 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.
|
||||
*/
|
||||
virtual void render(const CustomLayerRenderParameters&) = 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.
|
||||
*/
|
||||
using CustomLayerDeinitializeFunction = void (*)(void* context);
|
||||
/**
|
||||
* Called when the system has destroyed the underlying GL context. The
|
||||
* `deinitialize` function will not be called in this case, however
|
||||
* `initialize` will be called instead to prepare for a new render.
|
||||
*
|
||||
*/
|
||||
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 `initialize` function has not been called.
|
||||
*/
|
||||
virtual void deinitialize() = 0;
|
||||
};
|
||||
|
||||
class CustomLayer : public Layer {
|
||||
public:
|
||||
CustomLayer(const std::string& id,
|
||||
CustomLayerInitializeFunction,
|
||||
CustomLayerRenderFunction,
|
||||
CustomLayerContextLostFunction,
|
||||
CustomLayerDeinitializeFunction,
|
||||
void* context);
|
||||
|
||||
CustomLayer(const std::string& id,
|
||||
CustomLayerInitializeFunction,
|
||||
CustomLayerRenderFunction,
|
||||
CustomLayerDeinitializeFunction,
|
||||
void* context);
|
||||
std::unique_ptr<CustomLayerHost> host);
|
||||
|
||||
~CustomLayer() final;
|
||||
|
||||
|
||||
@@ -9,20 +9,8 @@ package com.mapbox.mapboxsdk.style.layers;
|
||||
public class CustomLayer extends Layer {
|
||||
|
||||
public CustomLayer(String id,
|
||||
long context,
|
||||
long initializeFunction,
|
||||
long renderFunction,
|
||||
long deinitializeFunction) {
|
||||
this(id, context, initializeFunction, renderFunction, 0L, deinitializeFunction);
|
||||
}
|
||||
|
||||
public CustomLayer(String id,
|
||||
long context,
|
||||
long initializeFunction,
|
||||
long renderFunction,
|
||||
long contextLostFunction,
|
||||
long deinitializeFunction) {
|
||||
initialize(id, initializeFunction, renderFunction, contextLostFunction, deinitializeFunction, context);
|
||||
long host) {
|
||||
initialize(id, host);
|
||||
}
|
||||
|
||||
public CustomLayer(long nativePtr) {
|
||||
@@ -33,9 +21,7 @@ public class CustomLayer extends Layer {
|
||||
nativeUpdate();
|
||||
}
|
||||
|
||||
protected native void initialize(String id, long initializeFunction, long renderFunction,
|
||||
long contextLostFunction, long deinitializeFunction,
|
||||
long context);
|
||||
protected native void initialize(String id, long host);
|
||||
|
||||
protected native void nativeUpdate();
|
||||
|
||||
|
||||
@@ -58,11 +58,7 @@ public class CustomLayerActivity extends AppCompatActivity {
|
||||
fab.setImageResource(R.drawable.ic_layers);
|
||||
} else {
|
||||
customLayer = new CustomLayer("custom",
|
||||
ExampleCustomLayer.createContext(),
|
||||
ExampleCustomLayer.InitializeFunction,
|
||||
ExampleCustomLayer.RenderFunction,
|
||||
ExampleCustomLayer.ContextLostFunction, // Optional
|
||||
ExampleCustomLayer.DeinitializeFunction);
|
||||
ExampleCustomLayer.createContext());
|
||||
mapboxMap.addLayerBelow(customLayer, "building");
|
||||
fab.setImageResource(R.drawable.ic_layers_clear);
|
||||
}
|
||||
|
||||
@@ -112,18 +112,9 @@ void checkCompileStatus(GLuint shader) {
|
||||
static const GLchar * vertexShaderSource = "attribute vec2 a_pos; void main() { gl_Position = vec4(a_pos, 0, 1); }";
|
||||
static const GLchar * fragmentShaderSource = "uniform highp vec4 fill_color; void main() { gl_FragColor = fill_color; }";
|
||||
|
||||
class ExampleCustomLayer {
|
||||
class ExampleCustomLayer: mbgl::style::CustomLayerHost {
|
||||
public:
|
||||
~ExampleCustomLayer() {
|
||||
__android_log_write(ANDROID_LOG_INFO, LOG_TAG, "~ExampleCustomLayer");
|
||||
if (program) {
|
||||
glDeleteBuffers(1, &buffer);
|
||||
glDetachShader(program, vertexShader);
|
||||
glDetachShader(program, fragmentShader);
|
||||
glDeleteShader(vertexShader);
|
||||
glDeleteShader(fragmentShader);
|
||||
glDeleteProgram(program);
|
||||
}
|
||||
}
|
||||
|
||||
void initialize() {
|
||||
@@ -158,8 +149,15 @@ public:
|
||||
GL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, 8 * sizeof(GLfloat), background, GL_STATIC_DRAW));
|
||||
}
|
||||
|
||||
void render() {
|
||||
void render(const mbgl::style::CustomLayerRenderParameters&) {
|
||||
__android_log_write(ANDROID_LOG_INFO, LOG_TAG, "Render");
|
||||
glUseProgram(program);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, buffer);
|
||||
glEnableVertexAttribArray(a_pos);
|
||||
glVertexAttribPointer(a_pos, 2, GL_FLOAT, GL_FALSE, 0, NULL);
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glUniform4fv(fill_color, 1, color);
|
||||
|
||||
GL_CHECK_ERROR(glUseProgram(program));
|
||||
GL_CHECK_ERROR(glBindBuffer(GL_ARRAY_BUFFER, buffer));
|
||||
@@ -172,6 +170,23 @@ public:
|
||||
|
||||
}
|
||||
|
||||
void contextLost() {
|
||||
__android_log_write(ANDROID_LOG_INFO, LOG_TAG, "ContextLost");
|
||||
program = 0;
|
||||
}
|
||||
|
||||
void deinitialize() {
|
||||
__android_log_write(ANDROID_LOG_INFO, LOG_TAG, "DeInitialize");
|
||||
if (program) {
|
||||
glDeleteBuffers(1, &buffer);
|
||||
glDetachShader(program, vertexShader);
|
||||
glDetachShader(program, fragmentShader);
|
||||
glDeleteShader(vertexShader);
|
||||
glDeleteShader(fragmentShader);
|
||||
glDeleteProgram(program);
|
||||
}
|
||||
}
|
||||
|
||||
GLuint program = 0;
|
||||
GLuint vertexShader = 0;
|
||||
GLuint fragmentShader = 0;
|
||||
@@ -186,7 +201,8 @@ GLfloat ExampleCustomLayer::color[] = { 0.0f, 1.0f, 0.0f, 1.0f };
|
||||
|
||||
jlong JNICALL nativeCreateContext(JNIEnv*, jobject) {
|
||||
__android_log_write(ANDROID_LOG_INFO, LOG_TAG, "nativeCreateContext");
|
||||
return reinterpret_cast<jlong>(new ExampleCustomLayer());
|
||||
auto exampleCustomLayer = std::make_unique<ExampleCustomLayer>();
|
||||
return reinterpret_cast<jlong>(exampleCustomLayer.release());
|
||||
}
|
||||
|
||||
void JNICALL nativeSetColor(JNIEnv*, jobject, jfloat red, jfloat green, jfloat blue, jfloat alpha) {
|
||||
@@ -197,25 +213,6 @@ void JNICALL nativeSetColor(JNIEnv*, jobject, jfloat red, jfloat green, jfloat b
|
||||
ExampleCustomLayer::color[3] = alpha;
|
||||
}
|
||||
|
||||
void nativeInitialize(void *context) {
|
||||
__android_log_write(ANDROID_LOG_INFO, LOG_TAG, "nativeInitialize");
|
||||
reinterpret_cast<ExampleCustomLayer*>(context)->initialize();
|
||||
}
|
||||
|
||||
void nativeRender(void *context, const mbgl::style::CustomLayerRenderParameters& /*parameters*/) {
|
||||
__android_log_write(ANDROID_LOG_INFO, LOG_TAG, "nativeRender");
|
||||
reinterpret_cast<ExampleCustomLayer*>(context)->render();
|
||||
}
|
||||
|
||||
void nativeContextLost(void */*context*/) {
|
||||
__android_log_write(ANDROID_LOG_INFO, LOG_TAG, "nativeContextLost");
|
||||
}
|
||||
|
||||
void nativeDeinitialize(void *context) {
|
||||
__android_log_write(ANDROID_LOG_INFO, LOG_TAG, "nativeDeinitialize");
|
||||
delete reinterpret_cast<ExampleCustomLayer*>(context);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {
|
||||
__android_log_write(ANDROID_LOG_INFO, LOG_TAG, "OnLoad");
|
||||
|
||||
@@ -234,22 +231,6 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
env->SetStaticLongField(customLayerClass,
|
||||
env->GetStaticFieldID(customLayerClass, "InitializeFunction", "J"),
|
||||
reinterpret_cast<jlong>(nativeInitialize));
|
||||
|
||||
env->SetStaticLongField(customLayerClass,
|
||||
env->GetStaticFieldID(customLayerClass, "RenderFunction", "J"),
|
||||
reinterpret_cast<jlong>(nativeRender));
|
||||
|
||||
env->SetStaticLongField(customLayerClass,
|
||||
env->GetStaticFieldID(customLayerClass, "ContextLostFunction", "J"),
|
||||
reinterpret_cast<jlong>(nativeContextLost));
|
||||
|
||||
env->SetStaticLongField(customLayerClass,
|
||||
env->GetStaticFieldID(customLayerClass, "DeinitializeFunction", "J"),
|
||||
reinterpret_cast<jlong>(nativeDeinitialize));
|
||||
|
||||
return JNI_VERSION_1_6;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,14 +7,10 @@
|
||||
namespace mbgl {
|
||||
namespace android {
|
||||
|
||||
CustomLayer::CustomLayer(jni::JNIEnv& env, jni::String layerId, jni::jlong initializeFunction, jni::jlong renderFunction, jni::jlong contextLostFunction, jni::jlong deinitializeFunction, jni::jlong context)
|
||||
CustomLayer::CustomLayer(jni::JNIEnv& env, jni::String layerId, jni::jlong host)
|
||||
: Layer(env, std::make_unique<mbgl::style::CustomLayer>(
|
||||
jni::Make<std::string>(env, layerId),
|
||||
reinterpret_cast<mbgl::style::CustomLayerInitializeFunction>(initializeFunction),
|
||||
reinterpret_cast<mbgl::style::CustomLayerRenderFunction>(renderFunction),
|
||||
reinterpret_cast<mbgl::style::CustomLayerContextLostFunction>(contextLostFunction),
|
||||
reinterpret_cast<mbgl::style::CustomLayerDeinitializeFunction>(deinitializeFunction),
|
||||
reinterpret_cast<void*>(context))
|
||||
std::unique_ptr<mbgl::style::CustomLayerHost>(reinterpret_cast<mbgl::style::CustomLayerHost*>(host)))
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -53,7 +49,7 @@ namespace android {
|
||||
// Register the peer
|
||||
jni::RegisterNativePeer<CustomLayer>(
|
||||
env, CustomLayer::javaClass, "nativePtr",
|
||||
std::make_unique<CustomLayer, JNIEnv&, jni::String, jni::jlong, jni::jlong, jni::jlong, jni::jlong, jni::jlong>,
|
||||
std::make_unique<CustomLayer, JNIEnv&, jni::String, jni::jlong>,
|
||||
"initialize",
|
||||
"finalize",
|
||||
METHOD(&CustomLayer::update, "nativeUpdate"));
|
||||
|
||||
@@ -16,7 +16,7 @@ public:
|
||||
|
||||
static void registerNative(jni::JNIEnv&);
|
||||
|
||||
CustomLayer(jni::JNIEnv&, jni::String, jni::jlong, jni::jlong, jni::jlong, jni::jlong, jni::jlong);
|
||||
CustomLayer(jni::JNIEnv&, jni::String, jni::jlong);
|
||||
|
||||
CustomLayer(mbgl::Map&, mbgl::style::CustomLayer&);
|
||||
|
||||
|
||||
@@ -7,49 +7,47 @@
|
||||
#include <mbgl/style/layers/custom_layer.hpp>
|
||||
#include <mbgl/math/wrap.hpp>
|
||||
|
||||
/**
|
||||
Runs the preparation handler block contained in the given context, which is
|
||||
implicitly an instance of `MGLOpenGLStyleLayer`.
|
||||
class MGLOpenGLLayerHost : public mbgl::style::CustomLayerHost {
|
||||
public:
|
||||
MGLOpenGLLayerHost(MGLOpenGLStyleLayer *styleLayer) {
|
||||
layerRef = styleLayer;
|
||||
layer = nil;
|
||||
}
|
||||
|
||||
@param context An `MGLOpenGLStyleLayer` instance that was provided as context
|
||||
when creating an OpenGL style layer.
|
||||
*/
|
||||
void MGLPrepareCustomStyleLayer(void *context) {
|
||||
MGLOpenGLStyleLayer *layer = (__bridge MGLOpenGLStyleLayer *)context;
|
||||
[layer didMoveToMapView:layer.style.mapView];
|
||||
}
|
||||
void initialize() {
|
||||
if (layerRef == nil) return;
|
||||
else if (layer == nil) layer = layerRef;
|
||||
|
||||
/**
|
||||
Runs the drawing handler block contained in the given context, which is
|
||||
implicitly an instance of `MGLOpenGLStyleLayer`.
|
||||
[layer didMoveToMapView:layer.style.mapView];
|
||||
}
|
||||
|
||||
@param context An `MGLOpenGLStyleLayer` instance that was provided as context
|
||||
when creating an OpenGL style layer.
|
||||
*/
|
||||
void MGLDrawCustomStyleLayer(void *context, const mbgl::style::CustomLayerRenderParameters ¶ms) {
|
||||
MGLOpenGLStyleLayer *layer = (__bridge MGLOpenGLStyleLayer *)context;
|
||||
MGLStyleLayerDrawingContext drawingContext = {
|
||||
.size = CGSizeMake(params.width, params.height),
|
||||
.centerCoordinate = CLLocationCoordinate2DMake(params.latitude, params.longitude),
|
||||
.zoomLevel = params.zoom,
|
||||
.direction = mbgl::util::wrap(params.bearing, 0., 360.),
|
||||
.pitch = static_cast<CGFloat>(params.pitch),
|
||||
.fieldOfView = static_cast<CGFloat>(params.fieldOfView),
|
||||
};
|
||||
[layer drawInMapView:layer.style.mapView withContext:drawingContext];
|
||||
}
|
||||
void render(const mbgl::style::CustomLayerRenderParameters ¶ms) {
|
||||
if(!layer) return;
|
||||
|
||||
/**
|
||||
Runs the completion handler block contained in the given context, which is
|
||||
implicitly an instance of `MGLOpenGLStyleLayer`.
|
||||
MGLStyleLayerDrawingContext drawingContext = {
|
||||
.size = CGSizeMake(params.width, params.height),
|
||||
.centerCoordinate = CLLocationCoordinate2DMake(params.latitude, params.longitude),
|
||||
.zoomLevel = params.zoom,
|
||||
.direction = mbgl::util::wrap(params.bearing, 0., 360.),
|
||||
.pitch = static_cast<CGFloat>(params.pitch),
|
||||
.fieldOfView = static_cast<CGFloat>(params.fieldOfView),
|
||||
};
|
||||
[layer drawInMapView:layer.style.mapView withContext:drawingContext];
|
||||
}
|
||||
|
||||
@param context An `MGLOpenGLStyleLayer` instance that was provided as context
|
||||
when creating an OpenGL style layer.
|
||||
*/
|
||||
void MGLFinishCustomStyleLayer(void *context) {
|
||||
MGLOpenGLStyleLayer *layer = (__bridge_transfer MGLOpenGLStyleLayer *)context;
|
||||
[layer willMoveFromMapView:layer.style.mapView];
|
||||
}
|
||||
void contextLost() {}
|
||||
|
||||
void deinitialize() {
|
||||
if (layer == nil) return;
|
||||
|
||||
[layer willMoveFromMapView:layer.style.mapView];
|
||||
layerRef = layer;
|
||||
layer = nil;
|
||||
}
|
||||
private:
|
||||
__weak MGLOpenGLStyleLayer * layerRef;
|
||||
MGLOpenGLStyleLayer * layer = nil;
|
||||
};
|
||||
|
||||
/**
|
||||
An `MGLOpenGLStyleLayer` is a style layer that is rendered by OpenGL code that
|
||||
@@ -98,10 +96,7 @@ void MGLFinishCustomStyleLayer(void *context) {
|
||||
*/
|
||||
- (instancetype)initWithIdentifier:(NSString *)identifier {
|
||||
auto layer = std::make_unique<mbgl::style::CustomLayer>(identifier.UTF8String,
|
||||
MGLPrepareCustomStyleLayer,
|
||||
MGLDrawCustomStyleLayer,
|
||||
MGLFinishCustomStyleLayer,
|
||||
(__bridge_retained void *)self);
|
||||
std::make_unique<MGLOpenGLLayerHost>(self));
|
||||
return self = [super initWithPendingLayer:std::move(layer)];
|
||||
}
|
||||
|
||||
@@ -110,22 +105,15 @@ void MGLFinishCustomStyleLayer(void *context) {
|
||||
}
|
||||
|
||||
#pragma mark - Adding to and removing from a map view
|
||||
|
||||
- (void)setStyle:(MGLStyle *)style {
|
||||
if (_style && style) {
|
||||
[NSException raise:@"MGLLayerReuseException"
|
||||
format:@"%@ cannot be added to more than one MGLStyle at a time.", self];
|
||||
}
|
||||
_style = style;
|
||||
}
|
||||
|
||||
- (void)addToStyle:(MGLStyle *)style belowLayer:(MGLStyleLayer *)otherLayer {
|
||||
self.style = style;
|
||||
self.style.openGLLayers[self.identifier] = self;
|
||||
[super addToStyle:style belowLayer:otherLayer];
|
||||
}
|
||||
|
||||
- (void)removeFromStyle:(MGLStyle *)style {
|
||||
[super removeFromStyle:style];
|
||||
self.style.openGLLayers[self.identifier] = nil;
|
||||
self.style = nil;
|
||||
}
|
||||
|
||||
|
||||
@@ -82,6 +82,7 @@
|
||||
@property (nonatomic, readonly, weak) MGLMapView *mapView;
|
||||
@property (nonatomic, readonly) mbgl::style::Style *rawStyle;
|
||||
@property (readonly, copy, nullable) NSURL *URL;
|
||||
@property (nonatomic, readwrite, strong) NS_MUTABLE_DICTIONARY_OF(NSString *, MGLOpenGLStyleLayer *) *openGLLayers;
|
||||
@property (nonatomic) NS_MUTABLE_DICTIONARY_OF(NSString *, NS_DICTIONARY_OF(NSObject *, MGLTextLanguage *) *) *localizedLayersByIdentifier;
|
||||
|
||||
@end
|
||||
@@ -124,6 +125,7 @@ static_assert(6 == mbgl::util::default_styles::numOrderedStyles,
|
||||
if (self = [super init]) {
|
||||
_mapView = mapView;
|
||||
_rawStyle = rawStyle;
|
||||
_openGLLayers = [NSMutableDictionary dictionary];
|
||||
_localizedLayersByIdentifier = [NSMutableDictionary dictionary];
|
||||
}
|
||||
return self;
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace mbgl {
|
||||
@property (nonatomic, readonly) mbgl::style::Style *rawStyle;
|
||||
|
||||
- (nullable NS_ARRAY_OF(MGLAttributionInfo *) *)attributionInfosWithFontSize:(CGFloat)fontSize linkColor:(nullable MGLColor *)linkColor;
|
||||
|
||||
@property (nonatomic, readonly, strong) NS_MUTABLE_DICTIONARY_OF(NSString *, MGLOpenGLStyleLayer *) *openGLLayers;
|
||||
- (void)setStyleClasses:(NS_ARRAY_OF(NSString *) *)appliedClasses transitionDuration:(NSTimeInterval)transitionDuration;
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
@import Mapbox;
|
||||
#import <objc/message.h>
|
||||
#import <Mapbox/Mapbox.h>
|
||||
|
||||
@interface MBGLIntegrationTests : XCTestCase <MGLMapViewDelegate>
|
||||
|
||||
@@ -11,8 +11,11 @@
|
||||
|
||||
@implementation MBGLIntegrationTests {
|
||||
XCTestExpectation *_styleLoadingExpectation;
|
||||
XCTestExpectation *_renderFinishedExpectation;
|
||||
}
|
||||
|
||||
#pragma mark - Setup/Teardown
|
||||
|
||||
- (void)setUp {
|
||||
[super setUp];
|
||||
|
||||
@@ -32,6 +35,16 @@
|
||||
[window makeKeyAndVisible];
|
||||
}
|
||||
|
||||
- (void)tearDown {
|
||||
_styleLoadingExpectation = nil;
|
||||
self.mapView = nil;
|
||||
self.style = nil;
|
||||
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
#pragma mark - MGLMapViewDelegate
|
||||
|
||||
- (void)mapView:(MGLMapView *)mapView didFinishLoadingStyle:(MGLStyle *)style {
|
||||
XCTAssertNotNil(mapView.style);
|
||||
XCTAssertEqual(mapView.style, style);
|
||||
@@ -39,30 +52,51 @@
|
||||
[_styleLoadingExpectation fulfill];
|
||||
}
|
||||
|
||||
- (void)tearDown {
|
||||
_styleLoadingExpectation = nil;
|
||||
self.mapView = nil;
|
||||
- (void)mapViewDidFinishRenderingFrame:(MGLMapView *)mapView fullyRendered:(__unused BOOL)fullyRendered {
|
||||
[_renderFinishedExpectation fulfill];
|
||||
_renderFinishedExpectation = nil;
|
||||
}
|
||||
|
||||
[super tearDown];
|
||||
#pragma mark - Utilities
|
||||
|
||||
- (void)waitForMapViewToBeRendered {
|
||||
[self.mapView setNeedsDisplay];
|
||||
_renderFinishedExpectation = [self expectationWithDescription:@"Map view should be rendered"];
|
||||
[self waitForExpectations:@[_renderFinishedExpectation] timeout:1];
|
||||
}
|
||||
|
||||
- (MGLStyle *)style {
|
||||
return self.mapView.style;
|
||||
}
|
||||
|
||||
- (void)testAddingRemovingOpenGLLayer {
|
||||
#pragma mark - Tests
|
||||
|
||||
// This test does not strictly need to be in this test file/target. Including here for convenience.
|
||||
- (void)testOpenGLLayerDoesNotLeakWhenCreatedAndDestroyedWithoutAddingToStyle {
|
||||
MGLOpenGLStyleLayer *layer = [[MGLOpenGLStyleLayer alloc] initWithIdentifier:@"gl-layer"];
|
||||
__weak id weakLayer = layer;
|
||||
layer = nil;
|
||||
|
||||
XCTAssertNil(weakLayer);
|
||||
}
|
||||
|
||||
- (void)testAddingRemovingOpenGLLayerWithoutRendering {
|
||||
XCTAssertNotNil(self.style);
|
||||
|
||||
void(^addRemoveGLLayer)(void) = ^{
|
||||
MGLOpenGLStyleLayer *layer = [[MGLOpenGLStyleLayer alloc] initWithIdentifier:@"gl-layer"];
|
||||
[self.style insertLayer:layer atIndex:0];
|
||||
layer = nil;
|
||||
__weak id weakLayer = nil;
|
||||
|
||||
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate date]];
|
||||
@autoreleasepool {
|
||||
MGLOpenGLStyleLayer *layer = [[MGLOpenGLStyleLayer alloc] initWithIdentifier:@"gl-layer"];
|
||||
[self.style insertLayer:layer atIndex:0];
|
||||
weakLayer = layer;
|
||||
|
||||
id retrievedLayer = [self.style layerWithIdentifier:@"gl-layer"];
|
||||
XCTAssertNotNil(retrievedLayer);
|
||||
[self.style removeLayer:retrievedLayer];
|
||||
// Nil the layer prior to remove to ensure it's being retained
|
||||
layer = nil;
|
||||
[self.style removeLayer:weakLayer];
|
||||
}
|
||||
|
||||
XCTAssertNil(weakLayer);
|
||||
};
|
||||
|
||||
addRemoveGLLayer();
|
||||
@@ -70,8 +104,159 @@
|
||||
addRemoveGLLayer();
|
||||
}
|
||||
|
||||
//- (void)testOpenGLLayerDoesNotLeakWhenCreatedAndDestroyedWithoutAddingToStyle {
|
||||
// XCTFail(@"Not yet implemented");
|
||||
//}
|
||||
- (void)testReusingOpenGLLayerIdentifier {
|
||||
__weak MGLOpenGLStyleLayer *weakLayer2;
|
||||
|
||||
@autoreleasepool {
|
||||
MGLOpenGLStyleLayer *layer1 = [[MGLOpenGLStyleLayer alloc] initWithIdentifier:@"gl-layer"];
|
||||
[self.style insertLayer:layer1 atIndex:0];
|
||||
[self waitForMapViewToBeRendered];
|
||||
[self.style removeLayer:layer1];
|
||||
|
||||
MGLOpenGLStyleLayer *layer2 = [[MGLOpenGLStyleLayer alloc] initWithIdentifier:@"gl-layer"];
|
||||
weakLayer2 = layer2;
|
||||
|
||||
XCTAssertNotNil(layer2);
|
||||
XCTAssert(layer1 != layer2);
|
||||
|
||||
[self.style insertLayer:layer2 atIndex:0];
|
||||
[self waitForMapViewToBeRendered];
|
||||
[self.style removeLayer:layer2];
|
||||
|
||||
XCTAssertNil([layer1 style]);
|
||||
XCTAssertNil([layer2 style]);
|
||||
}
|
||||
|
||||
// At this point, layer2 (and layer1) should still be around,
|
||||
// since the render process needs to keep a reference to them.
|
||||
XCTAssertNotNil(weakLayer2);
|
||||
|
||||
// Let render loop run enough to release the layers
|
||||
[self waitForMapViewToBeRendered];
|
||||
XCTAssertNil(weakLayer2);
|
||||
}
|
||||
|
||||
- (void)testAddingRemovingOpenGLLayer {
|
||||
XCTAssertNotNil(self.style);
|
||||
|
||||
void(^addRemoveGLLayer)(void) = ^{
|
||||
|
||||
__weak id retrievedLayer = nil;
|
||||
|
||||
@autoreleasepool {
|
||||
MGLOpenGLStyleLayer *layer = [[MGLOpenGLStyleLayer alloc] initWithIdentifier:@"gl-layer"];
|
||||
[self.style insertLayer:layer atIndex:0];
|
||||
layer = nil;
|
||||
|
||||
[self waitForMapViewToBeRendered];
|
||||
|
||||
retrievedLayer = [self.style layerWithIdentifier:@"gl-layer"];
|
||||
XCTAssertNotNil(retrievedLayer);
|
||||
|
||||
[self.style removeLayer:retrievedLayer];
|
||||
[self waitForMapViewToBeRendered];
|
||||
}
|
||||
|
||||
XCTAssertNil(retrievedLayer);
|
||||
};
|
||||
|
||||
addRemoveGLLayer();
|
||||
addRemoveGLLayer();
|
||||
addRemoveGLLayer();
|
||||
}
|
||||
|
||||
- (void)testReusingOpenGLLayer {
|
||||
MGLOpenGLStyleLayer *layer = [[MGLOpenGLStyleLayer alloc] initWithIdentifier:@"gl-layer"];
|
||||
[self.style insertLayer:layer atIndex:0];
|
||||
[self waitForMapViewToBeRendered];
|
||||
|
||||
[self.style removeLayer:layer];
|
||||
[self waitForMapViewToBeRendered];
|
||||
|
||||
[self.style insertLayer:layer atIndex:0];
|
||||
[self waitForMapViewToBeRendered];
|
||||
|
||||
[self.style removeLayer:layer];
|
||||
[self waitForMapViewToBeRendered];
|
||||
}
|
||||
|
||||
- (void)testOpenGLLayerDoesNotLeakWhenRemovedFromStyle {
|
||||
__weak id weakLayer;
|
||||
@autoreleasepool {
|
||||
MGLOpenGLStyleLayer *layer = [[MGLOpenGLStyleLayer alloc] initWithIdentifier:@"gl-layer"];
|
||||
weakLayer = layer;
|
||||
[self.style insertLayer:layer atIndex:0];
|
||||
layer = nil;
|
||||
|
||||
[self waitForMapViewToBeRendered];
|
||||
[self.style removeLayer:[self.style layerWithIdentifier:@"gl-layer"]];
|
||||
}
|
||||
|
||||
MGLStyleLayer *layer2 = weakLayer;
|
||||
|
||||
XCTAssertNotNil(weakLayer);
|
||||
[self waitForMapViewToBeRendered];
|
||||
|
||||
layer2 = nil;
|
||||
XCTAssertNil(weakLayer);
|
||||
}
|
||||
|
||||
- (void)testOpenGLLayerDoesNotLeakWhenStyleChanged {
|
||||
__weak MGLOpenGLStyleLayer *weakLayer;
|
||||
|
||||
@autoreleasepool {
|
||||
{
|
||||
MGLOpenGLStyleLayer *layer = [[MGLOpenGLStyleLayer alloc] initWithIdentifier:@"gl-layer"];
|
||||
weakLayer = layer;
|
||||
[self.style insertLayer:layer atIndex:0];
|
||||
layer = nil;
|
||||
}
|
||||
}
|
||||
|
||||
XCTAssertNotNil(weakLayer);
|
||||
|
||||
[self waitForMapViewToBeRendered];
|
||||
|
||||
MGLStyleLayer *layer2 = [self.mapView.style layerWithIdentifier:@"gl-layer"];
|
||||
|
||||
NSURL *styleURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"one-liner" withExtension:@"json"];
|
||||
_styleLoadingExpectation = [self expectationWithDescription:@"Map view should finish loading style."];
|
||||
[self.mapView setStyleURL:styleURL];
|
||||
[self waitForExpectations:@[_styleLoadingExpectation] timeout:10];
|
||||
|
||||
// At this point the C++ CustomLayer will have been destroyed, and the rawLayer pointer has been NULLed
|
||||
XCTAssert(weakLayer == layer2);
|
||||
XCTAssertNotNil(weakLayer);
|
||||
|
||||
// Asking the style for the layer should return nil
|
||||
MGLStyleLayer *layer3 = [self.mapView.style layerWithIdentifier:@"gl-layer"];
|
||||
XCTAssertNil(layer3);
|
||||
}
|
||||
|
||||
|
||||
- (void)testOpenGLLayerDoesNotLeakWhenMapViewDeallocs {
|
||||
__weak id weakLayer;
|
||||
|
||||
@autoreleasepool {
|
||||
|
||||
NSURL *styleURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"one-liner" withExtension:@"json"];
|
||||
MGLMapView *mapView2 = [[MGLMapView alloc] initWithFrame:UIScreen.mainScreen.bounds styleURL:styleURL];
|
||||
mapView2.delegate = self;
|
||||
|
||||
XCTAssertNil(mapView2.style);
|
||||
|
||||
_styleLoadingExpectation = [self expectationWithDescription:@"Map view should finish loading style."];
|
||||
[self waitForExpectationsWithTimeout:1 handler:nil];
|
||||
|
||||
MGLOpenGLStyleLayer *layer = [[MGLOpenGLStyleLayer alloc] initWithIdentifier:@"gl-layer"];
|
||||
weakLayer = layer;
|
||||
[mapView2.style insertLayer:layer atIndex:0];
|
||||
layer = nil;
|
||||
|
||||
[self waitForMapViewToBeRendered];
|
||||
}
|
||||
XCTAssertNil(weakLayer);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -112,12 +112,16 @@ struct Q_MAPBOXGL_EXPORT CustomLayerRenderParameters {
|
||||
double zoom;
|
||||
double bearing;
|
||||
double pitch;
|
||||
double altitude;
|
||||
double fieldOfView;
|
||||
};
|
||||
|
||||
typedef void (*CustomLayerInitializeFunction)(void* context) ;
|
||||
typedef void (*CustomLayerRenderFunction)(void* context, const CustomLayerRenderParameters&);
|
||||
typedef void (*CustomLayerDeinitializeFunction)(void* context);
|
||||
class Q_MAPBOXGL_EXPORT CustomLayerHostInterface {
|
||||
public:
|
||||
virtual ~CustomLayerHostInterface() = default;
|
||||
virtual void initialize() = 0;
|
||||
virtual void render(const CustomLayerRenderParameters&) = 0;
|
||||
virtual void deinitialize() = 0;
|
||||
};
|
||||
|
||||
} // namespace QMapbox
|
||||
|
||||
|
||||
@@ -223,10 +223,7 @@ public:
|
||||
void removeImage(const QString &name);
|
||||
|
||||
void addCustomLayer(const QString &id,
|
||||
QMapbox::CustomLayerInitializeFunction,
|
||||
QMapbox::CustomLayerRenderFunction,
|
||||
QMapbox::CustomLayerDeinitializeFunction,
|
||||
void* context,
|
||||
QScopedPointer<QMapbox::CustomLayerHostInterface>& host,
|
||||
const QString& before = QString());
|
||||
void addLayer(const QVariantMap ¶ms, const QString& before = QString());
|
||||
bool layerExists(const QString &id);
|
||||
|
||||
@@ -158,27 +158,9 @@ namespace QMapbox {
|
||||
*/
|
||||
|
||||
/*!
|
||||
\typedef QMapbox::CustomLayerDeinitializeFunction
|
||||
\class QMapbox::CustomLayerHostInterface
|
||||
|
||||
Represents a callback to be called when destroying a custom layer.
|
||||
|
||||
\warning This is used for delegating the rendering of a layer to the user of
|
||||
this API and is not officially supported. Use at your own risk.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\typedef QMapbox::CustomLayerInitializeFunction
|
||||
|
||||
Represents a callback to be called when initializing a custom layer.
|
||||
|
||||
\warning This is used for delegating the rendering of a layer to the user of
|
||||
this API and is not officially supported. Use at your own risk.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\typedef QMapbox::CustomLayerRenderFunction
|
||||
|
||||
Represents a callback to be called on each render pass for a custom layer.
|
||||
Represents a host interface to be implemented for rendering custom layers.
|
||||
|
||||
\warning This is used for delegating the rendering of a layer to the user of
|
||||
this API and is not officially supported. Use at your own risk.
|
||||
|
||||
@@ -1343,20 +1343,43 @@ void QMapboxGL::removeSource(const QString& id)
|
||||
this API and is not officially supported. Use at your own risk.
|
||||
*/
|
||||
void QMapboxGL::addCustomLayer(const QString &id,
|
||||
QMapbox::CustomLayerInitializeFunction initFn,
|
||||
QMapbox::CustomLayerRenderFunction renderFn,
|
||||
QMapbox::CustomLayerDeinitializeFunction deinitFn,
|
||||
void *context,
|
||||
QScopedPointer<QMapbox::CustomLayerHostInterface>& host,
|
||||
const QString& before)
|
||||
{
|
||||
class HostWrapper : public mbgl::style::CustomLayerHost {
|
||||
public:
|
||||
QScopedPointer<QMapbox::CustomLayerHostInterface> ptr;
|
||||
HostWrapper(QScopedPointer<QMapbox::CustomLayerHostInterface>& p)
|
||||
: ptr(p.take()) {
|
||||
}
|
||||
|
||||
void initialize() {
|
||||
ptr->initialize();
|
||||
}
|
||||
|
||||
void render(const mbgl::style::CustomLayerRenderParameters& params) {
|
||||
QMapbox::CustomLayerRenderParameters renderParams;
|
||||
renderParams.width = params.width;
|
||||
renderParams.height = params.height;
|
||||
renderParams.latitude = params.latitude;
|
||||
renderParams.longitude = params.longitude;
|
||||
renderParams.zoom = params.zoom;
|
||||
renderParams.bearing = params.bearing;
|
||||
renderParams.pitch = params.pitch;
|
||||
renderParams.fieldOfView = params.fieldOfView;
|
||||
ptr->render(renderParams);
|
||||
}
|
||||
|
||||
void contextLost() { }
|
||||
|
||||
void deinitialize() {
|
||||
ptr->deinitialize();
|
||||
}
|
||||
};
|
||||
|
||||
d_ptr->mapObj->getStyle().addLayer(std::make_unique<mbgl::style::CustomLayer>(
|
||||
id.toStdString(),
|
||||
reinterpret_cast<mbgl::style::CustomLayerInitializeFunction>(initFn),
|
||||
// This cast is safe as long as both mbgl:: and QMapbox::
|
||||
// CustomLayerRenderParameters members remains the same.
|
||||
(mbgl::style::CustomLayerRenderFunction)renderFn,
|
||||
reinterpret_cast<mbgl::style::CustomLayerDeinitializeFunction>(deinitFn),
|
||||
context),
|
||||
std::make_unique<HostWrapper>(host)),
|
||||
before.isEmpty() ? mbgl::optional<std::string>() : mbgl::optional<std::string>(before.toStdString()));
|
||||
}
|
||||
|
||||
|
||||
@@ -12,17 +12,17 @@ namespace mbgl {
|
||||
using namespace style;
|
||||
|
||||
RenderCustomLayer::RenderCustomLayer(Immutable<style::CustomLayer::Impl> _impl)
|
||||
: RenderLayer(LayerType::Custom, _impl) {
|
||||
: RenderLayer(LayerType::Custom, _impl), host(_impl->host) {
|
||||
assert(BackendScope::exists());
|
||||
host->initialize();
|
||||
}
|
||||
|
||||
RenderCustomLayer::~RenderCustomLayer() {
|
||||
assert(BackendScope::exists());
|
||||
if (initialized) {
|
||||
if (contextDestroyed && impl().contextLostFn ) {
|
||||
impl().contextLostFn(impl().context);
|
||||
} else if (!contextDestroyed && impl().deinitializeFn) {
|
||||
impl().deinitializeFn(impl().context);
|
||||
}
|
||||
if (contextDestroyed) {
|
||||
host->contextLost();
|
||||
} else {
|
||||
host->deinitialize();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,15 +44,13 @@ std::unique_ptr<Bucket> RenderCustomLayer::createBucket(const BucketParameters&,
|
||||
}
|
||||
|
||||
void RenderCustomLayer::render(PaintParameters& paintParameters, RenderSource*) {
|
||||
if (context != impl().context || !initialized) {
|
||||
if (host != impl().host) {
|
||||
//If the context changed, deinitialize the previous one before initializing the new one.
|
||||
if (context && !contextDestroyed && impl().deinitializeFn) {
|
||||
MBGL_CHECK_ERROR(impl().deinitializeFn(context));
|
||||
if (host && !contextDestroyed) {
|
||||
MBGL_CHECK_ERROR(host->deinitialize());
|
||||
}
|
||||
context = impl().context;
|
||||
assert(impl().initializeFn);
|
||||
MBGL_CHECK_ERROR(impl().initializeFn(impl().context));
|
||||
initialized = true;
|
||||
host = impl().host;
|
||||
MBGL_CHECK_ERROR(host->initialize());
|
||||
}
|
||||
|
||||
gl::Context& glContext = paintParameters.context;
|
||||
@@ -75,8 +73,7 @@ void RenderCustomLayer::render(PaintParameters& paintParameters, RenderSource*)
|
||||
parameters.pitch = state.getPitch();
|
||||
parameters.fieldOfView = state.getFieldOfView();
|
||||
|
||||
assert(impl().renderFn);
|
||||
MBGL_CHECK_ERROR(impl().renderFn(context, parameters));
|
||||
MBGL_CHECK_ERROR(host->render(parameters));
|
||||
|
||||
// Reset the view back to our original one, just in case the CustomLayer changed
|
||||
// the viewport or Framebuffer.
|
||||
|
||||
@@ -24,9 +24,8 @@ public:
|
||||
};
|
||||
|
||||
private:
|
||||
bool initialized = false;
|
||||
bool contextDestroyed = false;
|
||||
void * context = nullptr;
|
||||
std::shared_ptr<style::CustomLayerHost> host;
|
||||
};
|
||||
|
||||
template <>
|
||||
|
||||
@@ -6,20 +6,8 @@ namespace mbgl {
|
||||
namespace style {
|
||||
|
||||
CustomLayer::CustomLayer(const std::string& layerID,
|
||||
CustomLayerInitializeFunction init,
|
||||
CustomLayerRenderFunction render,
|
||||
CustomLayerContextLostFunction contextLost,
|
||||
CustomLayerDeinitializeFunction deinit,
|
||||
void* context)
|
||||
: Layer(makeMutable<Impl>(layerID, init, render, contextLost, deinit, context)) {
|
||||
}
|
||||
|
||||
CustomLayer::CustomLayer(const std::string& layerID,
|
||||
CustomLayerInitializeFunction init,
|
||||
CustomLayerRenderFunction render,
|
||||
CustomLayerDeinitializeFunction deinit,
|
||||
void* context)
|
||||
: Layer(makeMutable<Impl>(layerID, init, render, nullptr, deinit, context)) {
|
||||
std::unique_ptr<CustomLayerHost> host)
|
||||
: Layer(makeMutable<Impl>(layerID, std::move(host))) {
|
||||
}
|
||||
|
||||
CustomLayer::~CustomLayer() = default;
|
||||
|
||||
@@ -4,17 +4,9 @@ namespace mbgl {
|
||||
namespace style {
|
||||
|
||||
CustomLayer::Impl::Impl(const std::string& id_,
|
||||
CustomLayerInitializeFunction initializeFn_,
|
||||
CustomLayerRenderFunction renderFn_,
|
||||
CustomLayerContextLostFunction contextLostFn_,
|
||||
CustomLayerDeinitializeFunction deinitializeFn_,
|
||||
void* context_)
|
||||
std::unique_ptr<CustomLayerHost> host_)
|
||||
: Layer::Impl(LayerType::Custom, id_, std::string()) {
|
||||
initializeFn = initializeFn_;
|
||||
renderFn = renderFn_;
|
||||
deinitializeFn = deinitializeFn_;
|
||||
contextLostFn = contextLostFn_;
|
||||
context = context_;
|
||||
host = std::move(host_);
|
||||
}
|
||||
|
||||
bool CustomLayer::Impl::hasLayoutDifference(const Layer::Impl&) const {
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include <mbgl/style/layer_impl.hpp>
|
||||
#include <mbgl/style/layers/custom_layer.hpp>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class TransformState;
|
||||
@@ -12,20 +14,12 @@ namespace style {
|
||||
class CustomLayer::Impl : public Layer::Impl {
|
||||
public:
|
||||
Impl(const std::string& id,
|
||||
CustomLayerInitializeFunction,
|
||||
CustomLayerRenderFunction,
|
||||
CustomLayerContextLostFunction,
|
||||
CustomLayerDeinitializeFunction,
|
||||
void* context);
|
||||
std::unique_ptr<CustomLayerHost> host);
|
||||
|
||||
bool hasLayoutDifference(const Layer::Impl&) const override;
|
||||
void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override;
|
||||
|
||||
CustomLayerInitializeFunction initializeFn = nullptr;
|
||||
CustomLayerRenderFunction renderFn = nullptr;
|
||||
CustomLayerContextLostFunction contextLostFn = nullptr;
|
||||
CustomLayerDeinitializeFunction deinitializeFn = nullptr;
|
||||
void* context = nullptr;
|
||||
std::shared_ptr<CustomLayerHost> host;
|
||||
};
|
||||
|
||||
} // namespace style
|
||||
|
||||
@@ -34,19 +34,8 @@ 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) {
|
||||
MBGL_CHECK_ERROR(glDeleteBuffers(1, &buffer));
|
||||
MBGL_CHECK_ERROR(glDetachShader(program, vertexShader));
|
||||
MBGL_CHECK_ERROR(glDetachShader(program, fragmentShader));
|
||||
MBGL_CHECK_ERROR(glDeleteShader(vertexShader));
|
||||
MBGL_CHECK_ERROR(glDeleteShader(fragmentShader));
|
||||
MBGL_CHECK_ERROR(glDeleteProgram(program));
|
||||
}
|
||||
}
|
||||
|
||||
void initialize() {
|
||||
program = MBGL_CHECK_ERROR(glCreateProgram());
|
||||
vertexShader = MBGL_CHECK_ERROR(glCreateShader(GL_VERTEX_SHADER));
|
||||
@@ -67,7 +56,7 @@ public:
|
||||
MBGL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, 6 * sizeof(GLfloat), triangle, GL_STATIC_DRAW));
|
||||
}
|
||||
|
||||
void render() {
|
||||
void render(const mbgl::style::CustomLayerRenderParameters&) {
|
||||
MBGL_CHECK_ERROR(glUseProgram(program));
|
||||
MBGL_CHECK_ERROR(glBindBuffer(GL_ARRAY_BUFFER, buffer));
|
||||
MBGL_CHECK_ERROR(glEnableVertexAttribArray(a_pos));
|
||||
@@ -75,6 +64,19 @@ public:
|
||||
MBGL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, 3));
|
||||
}
|
||||
|
||||
void contextLost() {}
|
||||
|
||||
void deinitialize() {
|
||||
if (program) {
|
||||
MBGL_CHECK_ERROR(glDeleteBuffers(1, &buffer));
|
||||
MBGL_CHECK_ERROR(glDetachShader(program, vertexShader));
|
||||
MBGL_CHECK_ERROR(glDetachShader(program, fragmentShader));
|
||||
MBGL_CHECK_ERROR(glDeleteShader(vertexShader));
|
||||
MBGL_CHECK_ERROR(glDeleteShader(fragmentShader));
|
||||
MBGL_CHECK_ERROR(glDeleteProgram(program));
|
||||
}
|
||||
}
|
||||
|
||||
GLuint program = 0;
|
||||
GLuint vertexShader = 0;
|
||||
GLuint fragmentShader = 0;
|
||||
@@ -95,15 +97,7 @@ TEST(CustomLayer, Basic) {
|
||||
map.setLatLngZoom({ 37.8, -122.5 }, 10);
|
||||
map.getStyle().addLayer(std::make_unique<CustomLayer>(
|
||||
"custom",
|
||||
[] (void* context) {
|
||||
reinterpret_cast<TestLayer*>(context)->initialize();
|
||||
},
|
||||
[] (void* context, const CustomLayerRenderParameters&) {
|
||||
reinterpret_cast<TestLayer*>(context)->render();
|
||||
},
|
||||
[] (void* context) {
|
||||
delete reinterpret_cast<TestLayer*>(context);
|
||||
}, new TestLayer()));
|
||||
std::make_unique<TestLayer>()));
|
||||
|
||||
auto layer = std::make_unique<FillLayer>("landcover", "mapbox");
|
||||
layer->setSourceLayer("landcover");
|
||||
|
||||
Reference in New Issue
Block a user