[core] move gl::Context to Backend and refactor View

This commit is contained in:
Konstantin Käfer
2016-10-10 17:16:37 +02:00
parent 5cc390d694
commit a4d259c33f
76 changed files with 914 additions and 1024 deletions

View File

@@ -3,7 +3,7 @@
#include <mbgl/benchmark/util.hpp>
#include <mbgl/map/map.hpp>
#include <mbgl/platform/default/headless_backend.hpp>
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/platform/default/offscreen_view.hpp>
#include <mbgl/platform/default/thread_pool.hpp>
#include <mbgl/sprite/sprite_image.hpp>
#include <mbgl/storage/default_file_source.hpp>
@@ -29,17 +29,15 @@ public:
auto image = std::make_unique<SpriteImage>(std::move(decoded), 1.0);
map.addImage("test-icon", std::move(image));
view.resize(1000, 1000);
mbgl::benchmark::render(map);
mbgl::benchmark::render(map, view);
}
util::RunLoop loop;
HeadlessBackend backend;
HeadlessView view;
OffscreenView view{ backend.getContext(), {{ 1000, 1000 }} };
DefaultFileSource fileSource{ "benchmark/fixtures/api/cache.db", "." };
ThreadPool threadPool{ 4 };
Map map{ backend, view, view.getPixelRatio(), fileSource, threadPool, MapMode::Still };
Map map{ backend, view.getSize(), 1, fileSource, threadPool, MapMode::Still };
ScreenBox box{{ 0, 0 }, { 1000, 1000 }};
};

View File

@@ -1,16 +1,18 @@
#include <mbgl/benchmark/util.hpp>
#include <mbgl/platform/default/offscreen_view.hpp>
#include <mbgl/map/map.hpp>
#include <mbgl/map/view.hpp>
#include <mbgl/util/image.hpp>
#include <mbgl/util/run_loop.hpp>
namespace mbgl {
namespace benchmark {
void render(Map& map) {
void render(Map& map, OffscreenView& view) {
PremultipliedImage result;
map.renderStill([&result](std::exception_ptr, PremultipliedImage&& image) {
result = std::move(image);
map.renderStill(view, [&](std::exception_ptr) {
result = view.readStillImage();
});
while (!result.size()) {

View File

@@ -3,10 +3,11 @@
namespace mbgl {
class Map;
class OffscreenView;
namespace benchmark {
void render(Map&);
void render(Map&, OffscreenView&);
} // namespace benchmark
} // namespace mbgl

View File

@@ -13,6 +13,7 @@
#include <sstream>
#include <cstdlib>
#include <cstdio>
#include <array>
namespace {
@@ -119,7 +120,9 @@ int main(int argc, char *argv[]) {
mbgl::ThreadPool threadPool(4);
mbgl::Map map(backend, backend, backend.getPixelRatio(), fileSource, threadPool);
mbgl::Map map(backend, view->getSize(), view->getPixelRatio(), fileSource, threadPool);
backend.setMap(&map);
// Load settings
mbgl::Settings_JSON settings;

View File

@@ -4,7 +4,7 @@
#include <mbgl/util/run_loop.hpp>
#include <mbgl/platform/default/headless_backend.hpp>
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/platform/default/offscreen_view.hpp>
#include <mbgl/platform/default/thread_pool.hpp>
#include <mbgl/storage/default_file_source.hpp>
@@ -29,7 +29,6 @@ int main(int argc, char *argv[]) {
int width = 512;
int height = 512;
double pixelRatio = 1.0;
static std::string output = "out.png";
std::string cache_file = "cache.sqlite";
std::string asset_root = ".";
@@ -85,9 +84,9 @@ int main(int argc, char *argv[]) {
}
HeadlessBackend backend;
HeadlessView view(pixelRatio, width, height);
OffscreenView view(backend.getContext(), {{ static_cast<uint16_t>(width), static_cast<uint16_t>(height) }});
ThreadPool threadPool(4);
Map map(backend, view, view.getPixelRatio(), fileSource, threadPool, MapMode::Still);
Map map(backend, view.getSize(), 1, fileSource, threadPool, MapMode::Still);
map.setStyleJSON(style);
map.setClasses(classes);
@@ -100,7 +99,7 @@ int main(int argc, char *argv[]) {
map.setDebug(debug ? mbgl::MapDebugOptions::TileBorders | mbgl::MapDebugOptions::ParseStatus : mbgl::MapDebugOptions::NoDebug);
}
map.renderStill([&](std::exception_ptr error, PremultipliedImage&& image) {
map.renderStill(view, [&](std::exception_ptr error) {
try {
if (error) {
std::rethrow_exception(error);
@@ -110,7 +109,7 @@ int main(int argc, char *argv[]) {
exit(1);
}
util::write_file(output, encodePNG(image));
util::write_file(output, encodePNG(view.readStillImage()));
loop.stop();
});

View File

@@ -62,10 +62,12 @@ set(MBGL_CORE_FILES
src/mbgl/gl/debugging.hpp
src/mbgl/gl/extension.cpp
src/mbgl/gl/extension.hpp
src/mbgl/gl/framebuffer.hpp
src/mbgl/gl/gl.cpp
src/mbgl/gl/index_buffer.hpp
src/mbgl/gl/object.cpp
src/mbgl/gl/object.hpp
src/mbgl/gl/renderbuffer.hpp
src/mbgl/gl/shader.cpp
src/mbgl/gl/shader.hpp
src/mbgl/gl/state.hpp
@@ -97,7 +99,6 @@ set(MBGL_CORE_FILES
include/mbgl/map/camera.hpp
include/mbgl/map/map.hpp
include/mbgl/map/mode.hpp
include/mbgl/map/update.hpp
include/mbgl/map/view.hpp
src/mbgl/map/backend.cpp
src/mbgl/map/change.hpp
@@ -106,7 +107,7 @@ set(MBGL_CORE_FILES
src/mbgl/map/transform.hpp
src/mbgl/map/transform_state.cpp
src/mbgl/map/transform_state.hpp
src/mbgl/map/view.cpp
src/mbgl/map/update.hpp
src/mbgl/map/zoom_history.hpp
# math
@@ -135,8 +136,9 @@ set(MBGL_CORE_FILES
include/mbgl/platform/default/glfw_view.hpp
include/mbgl/platform/default/headless_backend.hpp
include/mbgl/platform/default/headless_display.hpp
include/mbgl/platform/default/headless_view.hpp
include/mbgl/platform/default/offscreen_view.hpp
include/mbgl/platform/default/settings_json.hpp
include/mbgl/platform/default/thread_pool.hpp
# renderer
src/mbgl/renderer/bucket.hpp

View File

@@ -36,7 +36,7 @@ target_include_directories(mbgl-core
target_add_mason_package(mbgl-core PUBLIC geometry)
target_add_mason_package(mbgl-core PUBLIC variant)
target_add_mason_package(mbgl-core PRIVATE unique_resource)
target_add_mason_package(mbgl-core PUBLIC unique_resource)
target_add_mason_package(mbgl-core PRIVATE rapidjson)
target_add_mason_package(mbgl-core PRIVATE boost)
target_add_mason_package(mbgl-core PRIVATE geojson)

View File

@@ -2,13 +2,21 @@
#include <mbgl/map/change.hpp>
#include <memory>
namespace mbgl {
class Map;
namespace gl {
class Context;
} // namespace gl
class Backend {
public:
virtual ~Backend() = default;
Backend();
virtual ~Backend();
// Returns the backend's context which manages OpenGL state.
gl::Context& getContext();
// Called when the backend's GL context needs to be made active or inactive. These are called,
// as a matched pair, in four situations:
@@ -29,6 +37,9 @@ public:
// Notifies a watcher of map x/y/scale/rotation changes.
virtual void notifyMapChange(MapChange change);
private:
const std::unique_ptr<gl::Context> context;
};
} // namespace mbgl

View File

@@ -2,8 +2,6 @@
#include <mbgl/util/optional.hpp>
#include <mbgl/util/chrono.hpp>
#include <mbgl/util/image.hpp>
#include <mbgl/map/update.hpp>
#include <mbgl/map/mode.hpp>
#include <mbgl/util/geo.hpp>
#include <mbgl/util/feature.hpp>
@@ -35,7 +33,7 @@ class Layer;
class Map : private util::noncopyable {
public:
explicit Map(Backend&,
View&,
std::array<uint16_t, 2> size,
float pixelRatio,
FileSource&,
Scheduler&,
@@ -47,14 +45,14 @@ public:
// Register a callback that will get called (on the render thread) when all resources have
// been loaded and a complete render occurs.
using StillImageCallback = std::function<void (std::exception_ptr, PremultipliedImage&&)>;
void renderStill(StillImageCallback callback);
using StillImageCallback = std::function<void (std::exception_ptr)>;
void renderStill(View&, StillImageCallback callback);
// Triggers a repaint.
void triggerRepaint();
// Main render function.
void render();
// Notifies the Map that the state has changed and an update might be necessary.
void update(Update update);
void render(View&);
// Styling
void addClass(const std::string&);
@@ -138,6 +136,7 @@ public:
ViewportMode getViewportMode() const;
// Size
void setSize(const std::array<uint16_t, 2>&);
uint16_t getWidth() const;
uint16_t getHeight() const;

View File

@@ -1,8 +1,6 @@
#pragma once
#include <mbgl/util/image.hpp>
#include <array>
#include <mbgl/util/noncopyable.hpp>
namespace mbgl {
@@ -12,29 +10,11 @@ class View : private util::noncopyable {
public:
virtual ~View() = default;
// Called when this View is associated with a Map object.
virtual void initialize(Map*);
// Called when this View is used for rendering. Implementations should ensure that a renderable
// object is bound and glClear/glDraw* calls can be done.
// object is bound and glClear/glDraw* calls can be done. They should also make sure that
// calling .bind() repeatedly is a no-op and that the appropriate gl::Context values are
// set to the current state.
virtual void bind() = 0;
// Called when the View signaled a dimension change. Must return the logical dimension
// of this map in pixels.
virtual std::array<uint16_t, 2> getSize() const = 0;
// Called for every frame that is being rendered. Must return the absolute dimensions of
// the current framebuffer. Typically, this is the logical width scaled by the pixel ratio,
// but in case the view was moved to display with a different pixel ratio, it can also be
// different from that rule.
virtual std::array<uint16_t, 2> getFramebufferSize() const = 0;
// Reads the pixel data from the current framebuffer. If your View implementation
// doesn't support reading from the framebuffer, return a null pointer.
virtual PremultipliedImage readStillImage(std::array<uint16_t, 2> size = {{ 0, 0 }});
protected:
mbgl::Map *map = nullptr;
};
} // namespace mbgl

View File

@@ -19,6 +19,8 @@ public:
float getPixelRatio() const;
void setMap(mbgl::Map*);
// Callback called when the user presses the key mapped to style change.
// The expected action is to set a new style, different to the current one.
void setChangeStyleCallback(std::function<void()> callback);
@@ -29,18 +31,18 @@ public:
void run();
private:
// mbgl::View implementation
void initialize(mbgl::Map*) override;
void updateViewBinding();
void bind() override;
std::array<uint16_t, 2> getSize() const override;
std::array<uint16_t, 2> getFramebufferSize() const override;
std::array<uint16_t, 2> getSize() const;
std::array<uint16_t, 2> getFramebufferSize() const;
// mbgl::Backend implementation
void activate() override;
void deactivate() override;
void invalidate() override;
private:
// Window callbacks
static void onKey(GLFWwindow *window, int key, int scancode, int action, int mods);
static void onScroll(GLFWwindow *window, double xoffset, double yoffset);
@@ -76,6 +78,8 @@ private:
std::function<void(mbgl::MapChange)> mapChangeCallback;
private:
mbgl::Map* map = nullptr;
bool fullscreen = false;
const bool benchmark = false;
bool tracking = false;

View File

@@ -1,41 +0,0 @@
#pragma once
#include <mbgl/map/view.hpp>
#include <mbgl/gl/types.hpp>
namespace mbgl {
class HeadlessView : public View {
public:
HeadlessView(float pixelRatio = 1, uint16_t width = 256, uint16_t height = 256);
~HeadlessView() override;
void bind() override;
std::array<uint16_t, 2> getSize() const override;
std::array<uint16_t, 2> getFramebufferSize() const override;
PremultipliedImage readStillImage(std::array<uint16_t, 2> size = {{ 0, 0 }}) override;
float getPixelRatio() const;
void resize(uint16_t width, uint16_t height);
private:
void clearBuffers();
void resizeFramebuffer();
void bindFramebuffer();
private:
const float pixelRatio;
std::array<uint16_t, 2> dimensions;
bool needsResize = false;
gl::FramebufferID fbo = 0;
gl::RenderbufferID fboDepthStencil = 0;
gl::RenderbufferID fboColor = 0;
};
} // namespace mbgl

View File

@@ -0,0 +1,33 @@
#pragma once
#include <mbgl/map/view.hpp>
#include <mbgl/gl/framebuffer.hpp>
#include <mbgl/gl/renderbuffer.hpp>
#include <mbgl/util/optional.hpp>
#include <mbgl/util/image.hpp>
namespace mbgl {
namespace gl {
class Context;
} // namespace gl
class OffscreenView : public View {
public:
OffscreenView(gl::Context&, std::array<uint16_t, 2> size = {{ 256, 256 }});
void bind() override;
PremultipliedImage readStillImage();
std::array<uint16_t, 2> getSize() const;
private:
gl::Context& context;
std::array<uint16_t, 2> size;
optional<gl::Framebuffer> framebuffer;
optional<gl::Renderbuffer<gl::RenderbufferType::RGBA>> color;
optional<gl::Renderbuffer<gl::RenderbufferType::DepthStencil>> depthStencil;
};
} // namespace mbgl

View File

@@ -103,7 +103,7 @@ global.propertyNativeType = function (property) {
if (/-translate-anchor$/.test(property.name)) {
return 'TranslateAnchorType';
}
if (/-(rotation|pitch)-alignment$/.test(property.name)) {
if (/-(rotation|pitch|illumination)-alignment$/.test(property.name)) {
return 'AlignmentType';
}
switch (property.type) {

View File

@@ -358,7 +358,7 @@ void nativeDestroySurface(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr
void nativeUpdate(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) {
assert(nativeMapViewPtr != 0);
NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
nativeMapView->getMap().update(mbgl::Update::Repaint);
nativeMapView->invalidate();
}
void nativeRender(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) {

View File

@@ -17,7 +17,9 @@
#include <mbgl/platform/log.hpp>
#include <mbgl/gl/extension.hpp>
#include <mbgl/gl/gl.hpp>
#include <mbgl/gl/context.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/util/image.hpp>
namespace mbgl {
namespace android {
@@ -80,7 +82,10 @@ NativeMapView::NativeMapView(JNIEnv *env_, jobject obj_, float pixelRatio, int a
mbgl::android::cachePath + "/mbgl-offline.db",
mbgl::android::apkPath);
map = std::make_unique<mbgl::Map>(*this, *this, pixelRatio, *fileSource, threadPool, MapMode::Continuous);
map = std::make_unique<mbgl::Map>(
*this,
std::array<uint16_t, 2>{{ static_cast<uint16_t>(width), static_cast<uint16_t>(height) }},
pixelRatio, *fileSource, threadPool, MapMode::Continuous);
float zoomFactor = map->getMaxZoom() - map->getMinZoom() + 1;
float cpuFactor = availableProcessors;
@@ -112,16 +117,14 @@ NativeMapView::~NativeMapView() {
vm = nullptr;
}
void NativeMapView::updateViewBinding() {
getContext().bindFramebuffer.setCurrentValue(0);
getContext().viewport.setCurrentValue({ 0, 0, static_cast<uint16_t>(fbWidth), static_cast<uint16_t>(fbHeight) });
}
void NativeMapView::bind() {
MBGL_CHECK_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, 0));
}
std::array<uint16_t, 2> NativeMapView::getSize() const {
return {{ static_cast<uint16_t>(width), static_cast<uint16_t>(height) }};
}
std::array<uint16_t, 2> NativeMapView::getFramebufferSize() const {
return {{ static_cast<uint16_t>(fbWidth), static_cast<uint16_t>(fbHeight) }};
getContext().bindFramebuffer = 0;
getContext().viewport = { 0, 0, static_cast<uint16_t>(fbWidth), static_cast<uint16_t>(fbHeight) };
}
void NativeMapView::activate() {
@@ -189,12 +192,8 @@ void NativeMapView::invalidate() {
void NativeMapView::render() {
activate();
if(sizeChanged){
sizeChanged = false;
glViewport(0, 0, fbWidth, fbHeight);
}
map->render();
updateViewBinding();
map->render(*this);
if(snapshot){
snapshot = false;
@@ -719,14 +718,13 @@ void NativeMapView::updateFps() {
void NativeMapView::resizeView(int w, int h) {
width = w;
height = h;
sizeChanged = true;
map->update(mbgl::Update::Dimensions);
map->setSize({{ static_cast<uint16_t>(width), static_cast<uint16_t>(height) }});
}
void NativeMapView::resizeFramebuffer(int w, int h) {
fbWidth = w;
fbHeight = h;
map->update(mbgl::Update::Repaint);
invalidate();
}
void NativeMapView::setInsets(mbgl::EdgeInsets insets_) {

View File

@@ -20,10 +20,9 @@ public:
NativeMapView(JNIEnv *env, jobject obj, float pixelRatio, int availableProcessors, size_t totalMemory);
virtual ~NativeMapView();
void updateViewBinding();
void bind() override;
std::array<uint16_t, 2> getSize() const override;
std::array<uint16_t, 2> getFramebufferSize() const override;
void activate() override;
void deactivate() override;
void invalidate() override;
@@ -83,7 +82,6 @@ private:
bool firstTime = false;
bool fpsEnabled = false;
bool sizeChanged = false;
bool snapshot = false;
double fps = 0.0;

View File

@@ -26,7 +26,7 @@ namespace android {
void CustomLayer::update(jni::JNIEnv&) {
Log::Debug(mbgl::Event::JNI, "Updating map");
if (map) {
map->update(mbgl::Update::Repaint);
map->triggerRepaint();
} else {
Log::Error(mbgl::Event::JNI, "No map reference, cannot update");
}

View File

@@ -40,11 +40,9 @@ public:
std::unique_ptr<mbgl::style::Source> releaseCoreSource();
protected:
std::unique_ptr<mbgl::style::Source> ownedSource;
mbgl::style::Source& source;
mbgl::Map* map;
};
} //android

View File

@@ -1,74 +0,0 @@
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/gl/gl.hpp>
#include <cassert>
namespace mbgl {
void HeadlessView::bindFramebuffer() {
assert(fbo);
MBGL_CHECK_ERROR(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo));
}
void HeadlessView::resizeFramebuffer() {
const unsigned int w = dimensions[0] * pixelRatio;
const unsigned int h = dimensions[1] * pixelRatio;
// Create depth/stencil buffer
MBGL_CHECK_ERROR(glGenRenderbuffersEXT(1, &fboDepthStencil));
MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fboDepthStencil));
MBGL_CHECK_ERROR(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, w, h));
MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0));
MBGL_CHECK_ERROR(glGenRenderbuffersEXT(1, &fboColor));
MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fboColor));
MBGL_CHECK_ERROR(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, w, h));
MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0));
MBGL_CHECK_ERROR(glGenFramebuffersEXT(1, &fbo));
MBGL_CHECK_ERROR(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo));
MBGL_CHECK_ERROR(glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, fboColor));
MBGL_CHECK_ERROR(glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER_EXT, fboDepthStencil));
GLenum status = MBGL_CHECK_ERROR(glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT));
if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
std::string error("Couldn't create framebuffer: ");
switch (status) {
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: (error += "incomplete attachment"); break;
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: error += "incomplete missing attachment"; break;
case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: error += "incomplete dimensions"; break;
case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: error += "incomplete formats"; break;
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: error += "incomplete draw buffer"; break;
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: error += "incomplete read buffer"; break;
case GL_FRAMEBUFFER_UNSUPPORTED: error += "unsupported"; break;
default: error += "other"; break;
}
throw std::runtime_error(error);
}
MBGL_CHECK_ERROR(glViewport(0, 0, w, h));
}
void HeadlessView::clearBuffers() {
MBGL_CHECK_ERROR(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
if (fbo) {
MBGL_CHECK_ERROR(glDeleteFramebuffersEXT(1, &fbo));
fbo = 0;
}
if (fboColor) {
MBGL_CHECK_ERROR(glDeleteRenderbuffersEXT(1, &fboColor));
fboColor = 0;
}
if (fboDepthStencil) {
MBGL_CHECK_ERROR(glDeleteRenderbuffersEXT(1, &fboDepthStencil));
fboDepthStencil = 0;
}
}
} // namespace mbgl

View File

@@ -1,72 +0,0 @@
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/gl/gl.hpp>
#include <cassert>
namespace mbgl {
void HeadlessView::bindFramebuffer() {
assert(fbo);
MBGL_CHECK_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, fbo));
}
void HeadlessView::resizeFramebuffer() {
const unsigned int w = dimensions[0] * pixelRatio;
const unsigned int h = dimensions[1] * pixelRatio;
// Create depth/stencil buffer
MBGL_CHECK_ERROR(glGenRenderbuffers(1, &fboDepthStencil));
MBGL_CHECK_ERROR(glBindRenderbuffer(GL_RENDERBUFFER, fboDepthStencil));
MBGL_CHECK_ERROR(glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, w, h));
MBGL_CHECK_ERROR(glBindRenderbuffer(GL_RENDERBUFFER, 0));
MBGL_CHECK_ERROR(glGenRenderbuffers(1, &fboColor));
MBGL_CHECK_ERROR(glBindRenderbuffer(GL_RENDERBUFFER, fboColor));
MBGL_CHECK_ERROR(glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8_OES, w, h));
MBGL_CHECK_ERROR(glBindRenderbuffer(GL_RENDERBUFFER, 0));
MBGL_CHECK_ERROR(glGenFramebuffers(1, &fbo));
MBGL_CHECK_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, fbo));
MBGL_CHECK_ERROR(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, fboColor));
MBGL_CHECK_ERROR(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fboDepthStencil));
MBGL_CHECK_ERROR(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fboDepthStencil));
GLenum status = MBGL_CHECK_ERROR(glCheckFramebufferStatus(GL_FRAMEBUFFER));
if (status != GL_FRAMEBUFFER_COMPLETE) {
std::string error("Couldn't create framebuffer: ");
switch (status) {
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: (error += "incomplete attachment"); break;
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: error += "incomplete missing attachment"; break;
case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: error += "incomplete dimensions"; break;
case GL_FRAMEBUFFER_UNSUPPORTED: error += "unsupported"; break;
default: error += "other"; break;
}
throw std::runtime_error(error);
}
MBGL_CHECK_ERROR(glViewport(0, 0, w, h));
}
void HeadlessView::clearBuffers() {
MBGL_CHECK_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, 0));
if (fbo) {
MBGL_CHECK_ERROR(glDeleteFramebuffers(1, &fbo));
fbo = 0;
}
if (fboColor) {
MBGL_CHECK_ERROR(glDeleteRenderbuffers(1, &fboColor));
fboColor = 0;
}
if (fboDepthStencil) {
MBGL_CHECK_ERROR(glDeleteRenderbuffers(1, &fboDepthStencil));
fboDepthStencil = 0;
}
}
} // namespace mbgl

View File

@@ -4,6 +4,7 @@
#include <mbgl/style/transition_options.hpp>
#include <mbgl/gl/gl.hpp>
#include <mbgl/gl/extension.hpp>
#include <mbgl/gl/context.hpp>
#include <mbgl/platform/log.hpp>
#include <mbgl/platform/platform.hpp>
#include <mbgl/util/string.hpp>
@@ -124,13 +125,21 @@ GLFWView::~GLFWView() {
glfwTerminate();
}
void GLFWView::initialize(mbgl::Map *map_) {
View::initialize(map_);
void GLFWView::setMap(mbgl::Map *map_) {
map = map_;
map->addAnnotationIcon("default_marker", makeSpriteImage(22, 22, 1));
}
void GLFWView::updateViewBinding() {
getContext().bindFramebuffer.setCurrentValue(0);
getContext().viewport.setCurrentValue(
{ 0, 0, static_cast<uint16_t>(fbWidth), static_cast<uint16_t>(fbHeight) });
}
void GLFWView::bind() {
MBGL_CHECK_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, 0));
getContext().bindFramebuffer = 0;
getContext().viewport = { 0, 0, static_cast<uint16_t>(fbWidth),
static_cast<uint16_t>(fbHeight) };
}
void GLFWView::onKey(GLFWwindow *window, int key, int /*scancode*/, int action, int mods) {
@@ -365,8 +374,8 @@ void GLFWView::onWindowResize(GLFWwindow *window, int width, int height) {
GLFWView *view = reinterpret_cast<GLFWView *>(glfwGetWindowUserPointer(window));
view->width = width;
view->height = height;
view->map->update(mbgl::Update::Dimensions);
view->map->setSize({{ static_cast<uint16_t>(view->width),
static_cast<uint16_t>(view->height) }});
}
void GLFWView::onFramebufferResize(GLFWwindow *window, int width, int height) {
@@ -374,7 +383,11 @@ void GLFWView::onFramebufferResize(GLFWwindow *window, int width, int height) {
view->fbWidth = width;
view->fbHeight = height;
view->map->update(mbgl::Update::Repaint);
// This is only triggered when the framebuffer is resized, but not the window. It can
// happen when you move the window between screens with a different pixel ratio.
// We are forcing a repaint my invalidating the view, which triggers a rerender with the
// new framebuffer dimensions.
view->invalidate();
}
void GLFWView::onMouseClick(GLFWwindow *window, int button, int action, int modifiers) {
@@ -440,15 +453,15 @@ void GLFWView::run() {
const double started = glfwGetTime();
glfwMakeContextCurrent(window);
glViewport(0, 0, fbWidth, fbHeight);
map->render();
updateViewBinding();
map->render(*this);
glfwSwapBuffers(window);
report(1000 * (glfwGetTime() - started));
if (benchmark) {
map->update(mbgl::Update::Repaint);
invalidate();
}
dirty = false;

View File

@@ -1,70 +0,0 @@
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/gl/gl.hpp>
#include <cstring>
namespace mbgl {
HeadlessView::HeadlessView(float pixelRatio_, uint16_t width, uint16_t height)
: pixelRatio(pixelRatio_), dimensions({ { width, height } }), needsResize(true) {
}
HeadlessView::~HeadlessView() {
clearBuffers();
}
void HeadlessView::bind() {
if (needsResize) {
clearBuffers();
resizeFramebuffer();
needsResize = false;
} else {
bindFramebuffer();
}
}
void HeadlessView::resize(const uint16_t width, const uint16_t height) {
if(dimensions[0] == width &&
dimensions[1] == height) {
return;
}
dimensions = {{ width, height }};
needsResize = true;
}
PremultipliedImage HeadlessView::readStillImage(std::array<uint16_t, 2> size) {
if (!size[0] || !size[1]) {
size[0] = dimensions[0] * pixelRatio;
size[1] = dimensions[1] * pixelRatio;
}
PremultipliedImage image { size[0], size[1] };
MBGL_CHECK_ERROR(glReadPixels(0, 0, size[0], size[1], GL_RGBA, GL_UNSIGNED_BYTE, image.data.get()));
const auto stride = image.stride();
auto tmp = std::make_unique<uint8_t[]>(stride);
uint8_t* rgba = image.data.get();
for (int i = 0, j = size[1] - 1; i < j; i++, j--) {
std::memcpy(tmp.get(), rgba + i * stride, stride);
std::memcpy(rgba + i * stride, rgba + j * stride, stride);
std::memcpy(rgba + j * stride, tmp.get(), stride);
}
return image;
}
float HeadlessView::getPixelRatio() const {
return pixelRatio;
}
std::array<uint16_t, 2> HeadlessView::getSize() const {
return dimensions;
}
std::array<uint16_t, 2> HeadlessView::getFramebufferSize() const {
return {{ static_cast<uint16_t>(dimensions[0] * pixelRatio),
static_cast<uint16_t>(dimensions[1] * pixelRatio) }};
}
} // namespace mbgl

View File

@@ -1,74 +0,0 @@
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/gl/gl.hpp>
#include <cassert>
namespace mbgl {
void HeadlessView::bindFramebuffer() {
assert(fbo);
MBGL_CHECK_ERROR(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo));
}
void HeadlessView::resizeFramebuffer() {
const unsigned int w = dimensions[0] * pixelRatio;
const unsigned int h = dimensions[1] * pixelRatio;
// Create depth/stencil buffer
MBGL_CHECK_ERROR(glGenRenderbuffersEXT(1, &fboDepthStencil));
MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fboDepthStencil));
MBGL_CHECK_ERROR(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, w, h));
MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0));
MBGL_CHECK_ERROR(glGenRenderbuffersEXT(1, &fboColor));
MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fboColor));
MBGL_CHECK_ERROR(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, w, h));
MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0));
MBGL_CHECK_ERROR(glGenFramebuffersEXT(1, &fbo));
MBGL_CHECK_ERROR(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo));
MBGL_CHECK_ERROR(glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, fboColor));
MBGL_CHECK_ERROR(glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER_EXT, fboDepthStencil));
GLenum status = MBGL_CHECK_ERROR(glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT));
if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
std::string error("Couldn't create framebuffer: ");
switch (status) {
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: (error += "incomplete attachment"); break;
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: error += "incomplete missing attachment"; break;
case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: error += "incomplete dimensions"; break;
case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: error += "incomplete formats"; break;
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: error += "incomplete draw buffer"; break;
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: error += "incomplete read buffer"; break;
case GL_FRAMEBUFFER_UNSUPPORTED: error += "unsupported"; break;
default: error += "other"; break;
}
throw std::runtime_error(error);
}
MBGL_CHECK_ERROR(glViewport(0, 0, w, h));
}
void HeadlessView::clearBuffers() {
MBGL_CHECK_ERROR(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
if (fbo) {
MBGL_CHECK_ERROR(glDeleteFramebuffersEXT(1, &fbo));
fbo = 0;
}
if (fboColor) {
MBGL_CHECK_ERROR(glDeleteRenderbuffersEXT(1, &fboColor));
fboColor = 0;
}
if (fboDepthStencil) {
MBGL_CHECK_ERROR(glDeleteRenderbuffersEXT(1, &fboDepthStencil));
fboDepthStencil = 0;
}
}
} // namespace mbgl

View File

@@ -0,0 +1,47 @@
#include <mbgl/platform/default/offscreen_view.hpp>
#include <mbgl/gl/context.hpp>
#include <mbgl/gl/gl.hpp>
#include <cstring>
#include <cassert>
namespace mbgl {
OffscreenView::OffscreenView(gl::Context& context_, std::array<uint16_t, 2> size_)
: context(context_), size(std::move(size_)) {
assert(size[0] > 0 && size[1] > 0);
}
void OffscreenView::bind() {
if (!framebuffer) {
color = context.createRenderbuffer<gl::RenderbufferType::RGBA>(size);
depthStencil = context.createRenderbuffer<gl::RenderbufferType::DepthStencil>(size);
framebuffer = context.createFramebuffer(*color, *depthStencil);
} else {
context.bindFramebuffer = framebuffer->framebuffer;
}
context.viewport = { 0, 0, size[0], size[1] };
}
PremultipliedImage OffscreenView::readStillImage() {
PremultipliedImage image { size[0], size[1] };
MBGL_CHECK_ERROR(glReadPixels(0, 0, size[0], size[1], GL_RGBA, GL_UNSIGNED_BYTE, image.data.get()));
const auto stride = image.stride();
auto tmp = std::make_unique<uint8_t[]>(stride);
uint8_t* rgba = image.data.get();
for (int i = 0, j = size[1] - 1; i < j; i++, j--) {
std::memcpy(tmp.get(), rgba + i * stride, stride);
std::memcpy(rgba + i * stride, rgba + j * stride, stride);
std::memcpy(rgba + j * stride, tmp.get(), stride);
}
return image;
}
std::array<uint16_t, 2> OffscreenView::getSize() const {
return size;
}
} // namespace mbgl

View File

@@ -38,10 +38,9 @@ macro(mbgl_platform_core)
# Headless view
PRIVATE platform/darwin/src/headless_backend_eagl.mm
PRIVATE platform/darwin/src/headless_view_eagl.cpp
PRIVATE platform/default/headless_backend.cpp
PRIVATE platform/default/headless_display.cpp
PRIVATE platform/default/headless_view.cpp
PRIVATE platform/default/offscreen_view.cpp
# Thread pool
PRIVATE platform/default/thread_pool.cpp

View File

@@ -2,6 +2,7 @@
#include <mbgl/platform/log.hpp>
#include <mbgl/gl/extension.hpp>
#include <mbgl/gl/context.hpp>
#import <GLKit/GLKit.h>
#import <OpenGLES/EAGL.h>
@@ -26,7 +27,7 @@
#include <mbgl/util/projection.hpp>
#include <mbgl/util/default_styles.hpp>
#include <mbgl/util/chrono.hpp>
#import <mbgl/util/run_loop.hpp>
#include <mbgl/util/run_loop.hpp>
#import "Mapbox.h"
#import "MGLFeature_Private.h"
@@ -398,10 +399,12 @@ public:
[[NSFileManager defaultManager] removeItemAtPath:fileCachePath error:NULL];
// setup mbgl map
const float scaleFactor = [UIScreen instancesRespondToSelector:@selector(nativeScale)] ? [[UIScreen mainScreen] nativeScale] : [[UIScreen mainScreen] scale];
const std::array<uint16_t, 2> size = {{ static_cast<uint16_t>(self.bounds.size.width),
static_cast<uint16_t>(self.bounds.size.height) }};
mbgl::DefaultFileSource *mbglFileSource = [MGLOfflineStorage sharedOfflineStorage].mbglFileSource;
const float scaleFactor = [UIScreen instancesRespondToSelector:@selector(nativeScale)] ? [[UIScreen mainScreen] nativeScale] : [[UIScreen mainScreen] scale];
_mbglThreadPool = new mbgl::ThreadPool(4);
_mbglMap = new mbgl::Map(*_mbglView, *_mbglView, scaleFactor, *mbglFileSource, *_mbglThreadPool, mbgl::MapMode::Continuous, mbgl::GLContextMode::Unique, mbgl::ConstrainMode::None, mbgl::ViewportMode::Default);
_mbglMap = new mbgl::Map(*_mbglView, size, scaleFactor, *mbglFileSource, *_mbglThreadPool, mbgl::MapMode::Continuous, mbgl::GLContextMode::Unique, mbgl::ConstrainMode::None, mbgl::ViewportMode::Default);
[self validateTileCacheSize];
// start paused if in IB
@@ -868,7 +871,8 @@ public:
{
if ( ! self.dormant)
{
_mbglMap->render();
_mbglView->updateViewBinding();
_mbglMap->render(*_mbglView);
[self updateUserLocationAnnotationView];
}
@@ -883,7 +887,8 @@ public:
if ( ! _isTargetingInterfaceBuilder)
{
_mbglMap->update(mbgl::Update::Dimensions);
_mbglMap->setSize({{ static_cast<uint16_t>(self.bounds.size.width),
static_cast<uint16_t>(self.bounds.size.height) }});
}
if (self.attributionSheet.visible)
@@ -4946,18 +4951,32 @@ public:
: nativeView(nativeView_) {
}
std::array<uint16_t, 2> getSize() const override {
return {{ static_cast<uint16_t>([nativeView bounds].size.width),
static_cast<uint16_t>([nativeView bounds].size.height) }};
mbgl::gl::value::Viewport::Type getViewport() const {
return { 0, 0, static_cast<uint16_t>(nativeView.glView.drawableWidth),
static_cast<uint16_t>(nativeView.glView.drawableHeight) };
}
std::array<uint16_t, 2> getFramebufferSize() const override {
return {{ static_cast<uint16_t>([[nativeView glView] drawableWidth]),
static_cast<uint16_t>([[nativeView glView] drawableHeight]) }};
/// This function is called before we start rendering, when iOS invokes our rendering method.
/// iOS already sets the correct framebuffer and viewport for us, so we need to update the
/// context state with the anticipated values.
void updateViewBinding() {
// We are using 0 as the placeholder value for the GLKView's framebuffer.
getContext().bindFramebuffer.setCurrentValue(0);
getContext().viewport.setCurrentValue(getViewport());
}
void bind() override {
[nativeView.glView bindDrawable];
if (getContext().bindFramebuffer != 0) {
// Something modified our state, and we need to bind the original drawable again.
// Doing this also sets the viewport to the full framebuffer.
// Note that in reality, iOS does not use the Framebuffer 0 (it's typically 1), and we
// only use this is a placeholder value.
[nativeView.glView bindDrawable];
updateViewBinding();
} else {
// Our framebuffer is still bound, but the viewport might have changed.
getContext().viewport = getViewport();
}
}
void notifyMapChange(mbgl::MapChange change) override
@@ -5195,7 +5214,7 @@ void MGLFinishCustomStyleLayer(void *context)
- (void)setCustomStyleLayersNeedDisplay
{
_mbglMap->update(mbgl::Update::Repaint);
[self setNeedsGLDisplay];
}
- (mbgl::Map *)mbglMap {

View File

@@ -42,10 +42,9 @@ macro(mbgl_platform_core)
# Headless view
PRIVATE platform/default/headless_backend_glx.cpp
PRIVATE platform/default/headless_view_glx.cpp
PRIVATE platform/default/headless_backend.cpp
PRIVATE platform/default/headless_display.cpp
PRIVATE platform/default/headless_view.cpp
PRIVATE platform/default/offscreen_view.cpp
# Thread pool
PRIVATE platform/default/thread_pool.cpp

View File

@@ -36,10 +36,9 @@ macro(mbgl_platform_core)
# Headless view
PRIVATE platform/darwin/src/headless_backend_cgl.cpp
PRIVATE platform/darwin/src/headless_view_cgl.cpp
PRIVATE platform/default/headless_backend.cpp
PRIVATE platform/default/headless_display.cpp
PRIVATE platform/default/headless_view.cpp
PRIVATE platform/default/offscreen_view.cpp
# Thread pool
PRIVATE platform/default/thread_pool.cpp

View File

@@ -25,6 +25,7 @@
#import <mbgl/platform/default/thread_pool.hpp>
#import <mbgl/gl/extension.hpp>
#import <mbgl/gl/gl.hpp>
#import <mbgl/gl/context.hpp>
#import <mbgl/map/backend.hpp>
#import <mbgl/sprite/sprite_image.hpp>
#import <mbgl/storage/default_file_source.hpp>
@@ -263,9 +264,12 @@ public:
NSURL *legacyCacheURL = [cachesDirectoryURL URLByAppendingPathComponent:@"cache.db"];
[[NSFileManager defaultManager] removeItemAtURL:legacyCacheURL error:NULL];
mbgl::DefaultFileSource *mbglFileSource = [MGLOfflineStorage sharedOfflineStorage].mbglFileSource;
mbgl::DefaultFileSource* mbglFileSource = [MGLOfflineStorage sharedOfflineStorage].mbglFileSource;
const std::array<uint16_t, 2> size = {{ static_cast<uint16_t>(self.bounds.size.width),
static_cast<uint16_t>(self.bounds.size.height) }};
_mbglThreadPool = new mbgl::ThreadPool(4);
_mbglMap = new mbgl::Map(*_mbglView, *_mbglView, [NSScreen mainScreen].backingScaleFactor, *mbglFileSource, *_mbglThreadPool, mbgl::MapMode::Continuous, mbgl::GLContextMode::Unique, mbgl::ConstrainMode::None, mbgl::ViewportMode::Default);
_mbglMap = new mbgl::Map(*_mbglView, size, [NSScreen mainScreen].backingScaleFactor, *mbglFileSource, *_mbglThreadPool, mbgl::MapMode::Continuous, mbgl::GLContextMode::Unique, mbgl::ConstrainMode::None, mbgl::ViewportMode::Default);
[self validateTileCacheSize];
// Install the OpenGL layer. Interface Builder’s synchronous drawing means
@@ -635,7 +639,8 @@ public:
[self validateTileCacheSize];
}
if (!_isTargetingInterfaceBuilder) {
_mbglMap->update(mbgl::Update::Dimensions);
_mbglMap->setSize({{ static_cast<uint16_t>(self.bounds.size.width),
static_cast<uint16_t>(self.bounds.size.height) }});
}
}
@@ -746,9 +751,8 @@ public:
return reinterpret_cast<mbgl::gl::glProc>(symbol);
});
_mbglView->updateFramebufferBinding();
_mbglMap->render();
_mbglView->updateViewBinding();
_mbglMap->render(*_mbglView);
if (_isPrinting) {
_isPrinting = NO;
@@ -2539,17 +2543,6 @@ public:
MGLMapViewImpl(MGLMapView *nativeView_)
: nativeView(nativeView_) {}
std::array<uint16_t, 2> getSize() const override {
return {{ static_cast<uint16_t>(nativeView.bounds.size.width),
static_cast<uint16_t>(nativeView.bounds.size.height) }};
}
std::array<uint16_t, 2> getFramebufferSize() const override {
NSRect bounds = [nativeView convertRectToBacking:nativeView.bounds];
return {{ static_cast<uint16_t>(bounds.size.width),
static_cast<uint16_t>(bounds.size.height) }};
}
void notifyMapChange(mbgl::MapChange change) override {
[nativeView notifyMapChange:change];
}
@@ -2567,26 +2560,34 @@ public:
[NSOpenGLContext clearCurrentContext];
}
void updateFramebufferBinding() {
MBGL_CHECK_ERROR(glGetIntegerv(GL_FRAMEBUFFER_BINDING, &fbo));
mbgl::gl::value::Viewport::Type getViewport() const {
return { 0, 0, static_cast<uint16_t>(nativeView.bounds.size.width),
static_cast<uint16_t>(nativeView.bounds.size.height) };
}
void updateViewBinding() {
fbo = mbgl::gl::value::BindFramebuffer::Get();
getContext().bindFramebuffer.setCurrentValue(fbo);
getContext().viewport.setCurrentValue(getViewport());
}
void bind() override {
MBGL_CHECK_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, fbo));
getContext().bindFramebuffer = fbo;
getContext().viewport = getViewport();
}
mbgl::PremultipliedImage readStillImage(std::array<uint16_t, 2> size = {{ 0, 0 }}) override {
if (!size[0] || !size[1]) {
size = getFramebufferSize();
}
mbgl::PremultipliedImage image { size[0], size[1] };
MBGL_CHECK_ERROR(glReadPixels(0, 0, size[0], size[1], GL_RGBA, GL_UNSIGNED_BYTE, image.data.get()));
mbgl::PremultipliedImage readStillImage() {
NSRect bounds = [nativeView convertRectToBacking:nativeView.bounds];
const uint16_t width = bounds.size.width;
const uint16_t height = bounds.size.height;
mbgl::PremultipliedImage image{ width, height };
MBGL_CHECK_ERROR(
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, image.data.get()));
const size_t stride = image.stride();
auto tmp = std::make_unique<uint8_t[]>(stride);
uint8_t *rgba = image.data.get();
for (int i = 0, j = size[1] - 1; i < j; i++, j--) {
for (int i = 0, j = height - 1; i < j; i++, j--) {
std::memcpy(tmp.get(), rgba + i * stride, stride);
std::memcpy(rgba + i * stride, rgba + j * stride, stride);
std::memcpy(rgba + j * stride, tmp.get(), stride);

View File

@@ -356,21 +356,28 @@ void NodeMap::Render(const Nan::FunctionCallbackInfo<v8::Value>& info) {
}
void NodeMap::startRender(NodeMap::RenderOptions options) {
view.resize(options.width, options.height);
map->update(mbgl::Update::Dimensions);
map->setSize(std::array<uint16_t, 2>{{ static_cast<uint16_t>(options.width),
static_cast<uint16_t>(options.height) }});
const std::array<uint16_t, 2> fbSize{{ static_cast<uint16_t>(options.width * pixelRatio),
static_cast<uint16_t>(options.height * pixelRatio) }};
if (!view || view->getSize() != fbSize) {
view.reset();
view = std::make_unique<mbgl::OffscreenView>(backend.getContext(), fbSize);
}
map->setClasses(options.classes);
map->setLatLngZoom(mbgl::LatLng(options.latitude, options.longitude), options.zoom);
map->setBearing(options.bearing);
map->setPitch(options.pitch);
map->setDebug(options.debugOptions);
map->renderStill([this](const std::exception_ptr eptr, mbgl::PremultipliedImage&& result) {
map->renderStill(*view, [this](const std::exception_ptr eptr) {
if (eptr) {
error = std::move(eptr);
uv_async_send(async);
} else {
assert(!image.data);
image = std::move(result);
image = view->readStillImage();
uv_async_send(async);
}
});
@@ -772,15 +779,23 @@ void NodeMap::QueryRenderedFeatures(const Nan::FunctionCallbackInfo<v8::Value>&
}
}
NodeMap::NodeMap(v8::Local<v8::Object> options) :
backend(sharedDisplay()),
view([&] {
Nan::HandleScope scope;
return Nan::Has(options, Nan::New("ratio").ToLocalChecked()).FromJust() ? Nan::Get(options, Nan::New("ratio").ToLocalChecked()).ToLocalChecked()->NumberValue() : 1.0;
}()),
threadpool(),
map(std::make_unique<mbgl::Map>(backend, view, view.getPixelRatio(), *this, threadpool, mbgl::MapMode::Still)),
async(new uv_async_t) {
NodeMap::NodeMap(v8::Local<v8::Object> options)
: pixelRatio([&] {
Nan::HandleScope scope;
return Nan::Has(options, Nan::New("ratio").ToLocalChecked()).FromJust()
? Nan::Get(options, Nan::New("ratio").ToLocalChecked())
.ToLocalChecked()
->NumberValue()
: 1.0;
}()),
backend(sharedDisplay()),
map(std::make_unique<mbgl::Map>(backend,
std::array<uint16_t, 2>{{ 256, 256 }},
pixelRatio,
*this,
threadpool,
mbgl::MapMode::Still)),
async(new uv_async_t) {
backend.setMapChangeCallback([&](mbgl::MapChange change) {
if (change == mbgl::MapChangeDidFailLoadingMap) {

View File

@@ -5,7 +5,7 @@
#include <mbgl/map/map.hpp>
#include <mbgl/storage/file_source.hpp>
#include <mbgl/platform/default/headless_backend.hpp>
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/platform/default/offscreen_view.hpp>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
@@ -54,8 +54,9 @@ public:
std::unique_ptr<mbgl::AsyncRequest> request(const mbgl::Resource&, mbgl::FileSource::Callback);
const float pixelRatio;
mbgl::HeadlessBackend backend;
mbgl::HeadlessView view;
std::unique_ptr<mbgl::OffscreenView> view;
NodeThreadPool threadpool;
std::unique_ptr<mbgl::Map> map;

View File

@@ -9,10 +9,15 @@
#include <QMouseEvent>
#include <QString>
#if QT_VERSION >= 0x050000
#include <QWindow>
#endif
int kAnimationDuration = 10000;
MapWindow::MapWindow(const QMapboxGLSettings &settings)
: m_map(nullptr, settings)
: m_map(nullptr, settings, size(), pixelRatio())
, m_bearingAnimation(&m_map, "bearing")
, m_zoomAnimation(&m_map, "zoom")
{
@@ -46,6 +51,17 @@ void MapWindow::selfTest()
m_zoomAnimation.start();
}
qreal MapWindow::pixelRatio() {
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
return devicePixelRatioF();
#elif (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
return devicePixelRatio();
#else
return 1;
#endif
}
void MapWindow::animationFinished()
{
qDebug() << "Animation ticks/s: " << m_animationTicks / static_cast<float>(kAnimationDuration) * 1000.;
@@ -288,16 +304,12 @@ void MapWindow::initializeGL()
QMapbox::initializeGLExtensions();
}
void MapWindow::resizeGL(int w, int h)
{
QSize size(w, h);
m_map.resize(size);
}
void MapWindow::paintGL()
{
m_frameDraws++;
m_map.resize(size(), size() * pixelRatio());
#if QT_VERSION < 0x050400 && defined(__APPLE__)
// XXX GL framebuffer is valid only after first attempt of painting on
// older versions of Qt on macOS.

View File

@@ -24,6 +24,7 @@ protected slots:
private:
void changeStyle();
qreal pixelRatio();
// QGLWidget implementation.
void keyPressEvent(QKeyEvent *ev) final;
@@ -32,7 +33,6 @@ private:
void wheelEvent(QWheelEvent *ev) final;
void initializeGL() final;
void resizeGL(int w, int h) final;
void paintGL() final;
QPointF m_lastPos;

View File

@@ -44,11 +44,10 @@ macro(mbgl_platform_test)
target_sources(mbgl-test
PRIVATE test/src/main.cpp
PRIVATE platform/qt/test/headless_backend_qt.cpp
PRIVATE platform/qt/test/headless_view_qt.cpp
PRIVATE platform/qt/test/qmapboxgl.cpp
PRIVATE platform/default/headless_backend.cpp
PRIVATE platform/default/headless_display.cpp
PRIVATE platform/default/headless_view.cpp
PRIVATE platform/default/offscreen_view.cpp
)
set_source_files_properties(

View File

@@ -3,6 +3,7 @@
#include <QMapbox>
#include <QObject>
#include <QSize>
#include <QPointF>
class QImage;
@@ -10,6 +11,7 @@ class QMargins;
class QSize;
class QString;
class QStringList;
class QOpenGLFramebufferObject;
class QMapboxGLPrivate;
@@ -95,7 +97,10 @@ public:
NorthLeftwards,
};
QMapboxGL(QObject *parent = 0, const QMapboxGLSettings& = QMapboxGLSettings());
QMapboxGL(QObject* parent = 0,
const QMapboxGLSettings& = QMapboxGLSettings(),
const QSize& size = QSize(),
qreal pixelRatio = 1);
virtual ~QMapboxGL();
void cycleDebugOptions();
@@ -167,7 +172,7 @@ public:
void scaleBy(double scale, const QPointF &center = QPointF());
void rotateBy(const QPointF &first, const QPointF &second);
void resize(const QSize &size);
void resize(const QSize &size, const QSize &framebufferSize);
void addAnnotationIcon(const QString &name, const QImage &sprite);
@@ -198,7 +203,11 @@ public:
void setFilter(const QString &layer, const QVariant &filter);
public slots:
#if QT_VERSION >= 0x050000
void render(QOpenGLFramebufferObject *fbo = NULL);
#else
void render();
#endif
void connectionEstablished();
signals:

View File

@@ -6,6 +6,7 @@
#include <mbgl/annotation/annotation.hpp>
#include <mbgl/gl/gl.hpp>
#include <mbgl/gl/context.hpp>
#include <mbgl/map/camera.hpp>
#include <mbgl/map/map.hpp>
#include <mbgl/style/conversion.hpp>
@@ -23,6 +24,7 @@
#if QT_VERSION >= 0x050000
#include <QGuiApplication>
#include <QWindow>
#include <QOpenGLFramebufferObject>
#else
#include <QCoreApplication>
#endif
@@ -271,7 +273,7 @@ void QMapboxGLSettings::setAccessToken(const QString &token)
Constructs a QMapboxGL object with \a settings and sets \a parent as the parent
object. The \a settings cannot be changed after the object is constructed.
*/
QMapboxGL::QMapboxGL(QObject *parent, const QMapboxGLSettings &settings)
QMapboxGL::QMapboxGL(QObject *parent, const QMapboxGLSettings &settings, const QSize& size, qreal pixelRatio)
: QObject(parent)
{
// Multiple QMapboxGL running on the same thread
@@ -280,7 +282,7 @@ QMapboxGL::QMapboxGL(QObject *parent, const QMapboxGLSettings &settings)
loop.setLocalData(std::make_shared<mbgl::util::RunLoop>());
}
d_ptr = new QMapboxGLPrivate(this, settings);
d_ptr = new QMapboxGLPrivate(this, settings, size, pixelRatio);
}
QMapboxGL::~QMapboxGL()
@@ -602,15 +604,14 @@ void QMapboxGL::rotateBy(const QPointF &first, const QPointF &second)
mbgl::ScreenCoordinate { second.x(), second.y() });
}
void QMapboxGL::resize(const QSize& size)
void QMapboxGL::resize(const QSize& size, const QSize& framebufferSize)
{
QSize converted = size / d_ptr->getPixelRatio();
if (d_ptr->size == converted) return;
if (d_ptr->size == size && d_ptr->fbSize == framebufferSize) return;
glViewport(0, 0, converted.width(), converted.height());
d_ptr->size = size;
d_ptr->fbSize = framebufferSize;
d_ptr->size = converted;
d_ptr->mapObj->update(mbgl::Update::Dimensions);
d_ptr->mapObj->setSize({{ static_cast<uint16_t>(size.width()), static_cast<uint16_t>(size.height()) }});
}
void QMapboxGL::addAnnotationIcon(const QString &name, const QImage &sprite)
@@ -793,19 +794,29 @@ void QMapboxGL::setFilter(const QString& layer_, const QVariant& filter_)
qWarning() << "Layer doesn't support filters";
}
#if QT_VERSION >= 0x050000
void QMapboxGL::render(QOpenGLFramebufferObject *fbo)
{
d_ptr->dirty = false;
d_ptr->updateFramebufferBinding(fbo);
d_ptr->mapObj->render(*d_ptr);
}
#else
void QMapboxGL::render()
{
d_ptr->dirty = false;
d_ptr->mapObj->render();
d_ptr->mapObj->render(*d_ptr);
}
#endif
void QMapboxGL::connectionEstablished()
{
d_ptr->connectionEstablished();
}
QMapboxGLPrivate::QMapboxGLPrivate(QMapboxGL *q, const QMapboxGLSettings &settings)
QMapboxGLPrivate::QMapboxGLPrivate(QMapboxGL *q, const QMapboxGLSettings &settings, const QSize &size_, qreal pixelRatio)
: QObject(q)
, size(size_)
, q_ptr(q)
, fileSourceObj(std::make_unique<mbgl::DefaultFileSource>(
settings.cacheDatabasePath().toStdString(),
@@ -813,7 +824,8 @@ QMapboxGLPrivate::QMapboxGLPrivate(QMapboxGL *q, const QMapboxGLSettings &settin
settings.cacheDatabaseMaximumSize()))
, threadPool(4)
, mapObj(std::make_unique<mbgl::Map>(
*this, *this, getPixelRatio(), *fileSourceObj, threadPool,
*this, std::array<uint16_t, 2>{{ static_cast<uint16_t>(size.width()), static_cast<uint16_t>(size.height()) }},
pixelRatio, *fileSourceObj, threadPool,
static_cast<mbgl::MapMode>(settings.mapMode()),
static_cast<mbgl::GLContextMode>(settings.contextMode()),
static_cast<mbgl::ConstrainMode>(settings.constrainMode()),
@@ -830,36 +842,42 @@ QMapboxGLPrivate::~QMapboxGLPrivate()
{
}
float QMapboxGLPrivate::getPixelRatio() const
{
#if QT_VERSION >= 0x050000
// QWindow is the most reliable pixel ratio because QGuiApplication returns
// the maximum pixel ratio of all available QScreen objects - this is not
// valid for cases e.g. where two or more QScreen objects with different
// pixel ratios are present and the window shows on the screen with lower
// pixel ratio.
static const float pixelRatio = QGuiApplication::allWindows().first()->devicePixelRatio();
#else
static const float pixelRatio = 1.0;
#endif
return pixelRatio;
void QMapboxGLPrivate::updateFramebufferBinding(QOpenGLFramebufferObject *fbo_)
{
fbo = fbo_;
if (fbo) {
getContext().bindFramebuffer.setDirty();
getContext().viewport.setCurrentValue(
{ 0, 0, static_cast<uint16_t>(fbo->width()), static_cast<uint16_t>(fbo->height()) });
} else {
getContext().bindFramebuffer.setCurrentValue(0);
getContext().viewport.setCurrentValue({ 0, 0, static_cast<uint16_t>(fbSize.width()),
static_cast<uint16_t>(fbSize.height()) });
}
}
void QMapboxGLPrivate::bind()
{
MBGL_CHECK_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, 0));
if (fbo) {
fbo->bind();
getContext().bindFramebuffer.setDirty();
getContext().viewport = { 0, 0, static_cast<uint16_t>(fbo->width()),
static_cast<uint16_t>(fbo->height()) };
} else {
getContext().bindFramebuffer = 0;
getContext().viewport = { 0, 0, static_cast<uint16_t>(fbSize.width()),
static_cast<uint16_t>(fbSize.height()) };
}
}
std::array<uint16_t, 2> QMapboxGLPrivate::getSize() const
#else
void QMapboxGLPrivate::bind()
{
return {{ static_cast<uint16_t>(size.width()), static_cast<uint16_t>(size.height()) }};
}
std::array<uint16_t, 2> QMapboxGLPrivate::getFramebufferSize() const
{
return {{ static_cast<uint16_t>(size.width() * getPixelRatio()),
static_cast<uint16_t>(size.height() * getPixelRatio()) }};
getContext().bindFramebuffer = 0;
getContext().viewport = { 0, 0, static_cast<uint16_t>(fbSize.width()),
static_cast<uint16_t>(fbSize.height()) };
}
#endif
void QMapboxGLPrivate::invalidate()
{

View File

@@ -17,22 +17,27 @@ class QMapboxGLPrivate : public QObject, public mbgl::View, public mbgl::Backend
Q_OBJECT
public:
explicit QMapboxGLPrivate(QMapboxGL *, const QMapboxGLSettings &);
explicit QMapboxGLPrivate(QMapboxGL *, const QMapboxGLSettings &, const QSize &size, qreal pixelRatio);
virtual ~QMapboxGLPrivate();
// mbgl::View implementation.
float getPixelRatio() const;
void bind() final;
std::array<uint16_t, 2> getSize() const final;
std::array<uint16_t, 2> getFramebufferSize() const final;
std::array<uint16_t, 2> getSize() const;
std::array<uint16_t, 2> getFramebufferSize() const;
void activate() final {}
void deactivate() final {}
void invalidate() final;
void notifyMapChange(mbgl::MapChange) final;
#if QT_VERSION >= 0x050000
void updateFramebufferBinding(QOpenGLFramebufferObject *);
#endif
mbgl::EdgeInsets margins;
QSize size { 0, 0 };
QSize fbSize { 0, 0 };
QMapboxGL *q_ptr { nullptr };
@@ -42,6 +47,8 @@ public:
bool dirty { false };
QOpenGLFramebufferObject *fbo { nullptr };
public slots:
void connectionEstablished();

View File

@@ -19,7 +19,7 @@ QQuickMapboxGLRenderer::QQuickMapboxGLRenderer()
settings.setCacheDatabaseMaximumSize(20 * 1024 * 1024);
settings.setViewportMode(QMapboxGLSettings::FlippedYViewport);
m_map.reset(new QMapboxGL(nullptr, settings));
m_map.reset(new QMapboxGL(nullptr, settings, QSize(256, 256), 1));
}
QQuickMapboxGLRenderer::~QQuickMapboxGLRenderer()
@@ -28,7 +28,7 @@ QQuickMapboxGLRenderer::~QQuickMapboxGLRenderer()
QOpenGLFramebufferObject* QQuickMapboxGLRenderer::createFramebufferObject(const QSize &size)
{
m_map->resize(size);
m_map->resize(size / m_pixelRatio, size);
QOpenGLFramebufferObjectFormat format;
format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
@@ -38,7 +38,7 @@ QOpenGLFramebufferObject* QQuickMapboxGLRenderer::createFramebufferObject(const
void QQuickMapboxGLRenderer::render()
{
m_map->render();
m_map->render(framebufferObject());
}
void QQuickMapboxGLRenderer::synchronize(QQuickFramebufferObject *item)
@@ -51,6 +51,12 @@ void QQuickMapboxGLRenderer::synchronize(QQuickFramebufferObject *item)
m_initialized = true;
}
if (auto window = quickMap->window()) {
m_pixelRatio = window->devicePixelRatio();
} else {
m_pixelRatio = 1;
}
auto syncStatus = quickMap->m_syncState;
quickMap->m_syncState = QQuickMapboxGL::NothingNeedsSync;

View File

@@ -29,6 +29,7 @@ signals:
private:
bool m_initialized = false;
qreal m_pixelRatio = 1;
QScopedPointer<QMapboxGL> m_map;
};

View File

@@ -1,82 +0,0 @@
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/gl/gl.hpp>
#include <QApplication>
#include <QGLContext>
#include <QGLWidget>
#if QT_VERSION >= 0x050000
#include <QOpenGLContext>
#endif
#include <cassert>
namespace mbgl {
void HeadlessView::bindFramebuffer() {
assert(fbo);
MBGL_CHECK_ERROR(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo));
}
void HeadlessView::resizeFramebuffer() {
const unsigned int w = dimensions[0] * pixelRatio;
const unsigned int h = dimensions[1] * pixelRatio;
// Create depth/stencil buffer
MBGL_CHECK_ERROR(glGenRenderbuffersEXT(1, &fboDepthStencil));
MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fboDepthStencil));
MBGL_CHECK_ERROR(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, w, h));
MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0));
MBGL_CHECK_ERROR(glGenRenderbuffersEXT(1, &fboColor));
MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fboColor));
MBGL_CHECK_ERROR(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, w, h));
MBGL_CHECK_ERROR(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0));
MBGL_CHECK_ERROR(glGenFramebuffersEXT(1, &fbo));
MBGL_CHECK_ERROR(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo));
MBGL_CHECK_ERROR(glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, fboColor));
MBGL_CHECK_ERROR(glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER_EXT, fboDepthStencil));
GLenum status = MBGL_CHECK_ERROR(glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT));
if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
std::string error("Couldn't create framebuffer: ");
switch (status) {
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: (error += "incomplete attachment"); break;
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: error += "incomplete missing attachment"; break;
case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: error += "incomplete dimensions"; break;
case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: error += "incomplete formats"; break;
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: error += "incomplete draw buffer"; break;
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: error += "incomplete read buffer"; break;
case GL_FRAMEBUFFER_UNSUPPORTED: error += "unsupported"; break;
default: error += "other"; break;
}
throw std::runtime_error(error);
}
MBGL_CHECK_ERROR(glViewport(0, 0, w, h));
}
void HeadlessView::clearBuffers() {
MBGL_CHECK_ERROR(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
if (fbo) {
MBGL_CHECK_ERROR(glDeleteFramebuffersEXT(1, &fbo));
fbo = 0;
}
if (fboColor) {
MBGL_CHECK_ERROR(glDeleteRenderbuffersEXT(1, &fboColor));
fboColor = 0;
}
if (fboDepthStencil) {
MBGL_CHECK_ERROR(glDeleteRenderbuffersEXT(1, &fboDepthStencil));
fboDepthStencil = 0;
}
}
} // namespace mbgl

View File

@@ -19,7 +19,7 @@ public:
widget.makeCurrent();
QMapbox::initializeGLExtensions();
map.resize(QSize(512, 512));
map.resize(QSize(512, 512), QSize(512, 512));
map.setCoordinateZoom(QMapbox::Coordinate(60.170448, 24.942046), 14);
}

View File

@@ -28,7 +28,7 @@ global.propertyType = function (property) {
if (/-translate-anchor$/.test(property.name)) {
return 'TranslateAnchorType';
}
if (/-(rotation|pitch)-alignment$/.test(property.name)) {
if (/-(rotation|pitch|illumination)-alignment$/.test(property.name)) {
return 'AlignmentType';
}
switch (property.type) {

View File

@@ -1,3 +1,4 @@
#include <mbgl/map/view.hpp>
#include <mbgl/gl/context.hpp>
#include <mbgl/gl/gl.hpp>
#include <mbgl/gl/vertex_array.hpp>
@@ -142,6 +143,113 @@ UniqueFramebuffer Context::createFramebuffer() {
return UniqueFramebuffer{ std::move(id), { this } };
}
UniqueRenderbuffer Context::createRenderbuffer(const RenderbufferType type,
const uint16_t width,
const uint16_t height) {
RenderbufferID id = 0;
MBGL_CHECK_ERROR(glGenRenderbuffers(1, &id));
UniqueRenderbuffer renderbuffer{ std::move(id), { this } };
bindRenderbuffer = renderbuffer;
MBGL_CHECK_ERROR(
glRenderbufferStorage(GL_RENDERBUFFER, static_cast<GLenum>(type), width, height));
return renderbuffer;
}
namespace {
void checkFramebuffer() {
GLenum status = MBGL_CHECK_ERROR(glCheckFramebufferStatus(GL_FRAMEBUFFER));
if (status != GL_FRAMEBUFFER_COMPLETE) {
switch (status) {
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
throw std::runtime_error("Couldn't create framebuffer: incomplete attachment");
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
throw std::runtime_error("Couldn't create framebuffer: incomplete missing attachment");
#ifdef GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
throw std::runtime_error("Couldn't create framebuffer: incomplete draw buffer");
#endif
#ifdef GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
throw std::runtime_error("Couldn't create framebuffer: incomplete read buffer");
#endif
#ifdef GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS
case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
throw std::runtime_error("Couldn't create framebuffer: incomplete dimensions");
#endif
case GL_FRAMEBUFFER_UNSUPPORTED:
throw std::runtime_error("Couldn't create framebuffer: unsupported");
default:
throw std::runtime_error("Couldn't create framebuffer: other");
}
}
}
void bindDepthStencilRenderbuffer(
const Renderbuffer<RenderbufferType::DepthStencil>& depthStencil) {
#ifdef GL_DEPTH_STENCIL_ATTACHMENT
MBGL_CHECK_ERROR(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
GL_RENDERBUFFER, depthStencil.renderbuffer));
#else
MBGL_CHECK_ERROR(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER,
depthStencil.renderbuffer));
MBGL_CHECK_ERROR(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
GL_RENDERBUFFER, depthStencil.renderbuffer));
#endif
}
} // namespace
Framebuffer
Context::createFramebuffer(const Renderbuffer<RenderbufferType::RGBA>& color,
const Renderbuffer<RenderbufferType::DepthStencil>& depthStencil) {
if (color.size != depthStencil.size) {
throw new std::runtime_error("Renderbuffer size mismatch");
}
auto fbo = createFramebuffer();
bindFramebuffer = fbo;
MBGL_CHECK_ERROR(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, color.renderbuffer));
bindDepthStencilRenderbuffer(depthStencil);
checkFramebuffer();
return { color.size, std::move(fbo) };
}
Framebuffer Context::createFramebuffer(const Renderbuffer<RenderbufferType::RGBA>& color) {
auto fbo = createFramebuffer();
bindFramebuffer = fbo;
MBGL_CHECK_ERROR(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, color.renderbuffer));
checkFramebuffer();
return { color.size, std::move(fbo) };
}
Framebuffer
Context::createFramebuffer(const Texture& color,
const Renderbuffer<RenderbufferType::DepthStencil>& depthStencil) {
if (color.size != depthStencil.size) {
throw new std::runtime_error("Renderbuffer size mismatch");
}
auto fbo = createFramebuffer();
bindFramebuffer = fbo;
MBGL_CHECK_ERROR(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
color.texture, 0));
bindDepthStencilRenderbuffer(depthStencil);
checkFramebuffer();
return { color.size, std::move(fbo) };
}
Framebuffer Context::createFramebuffer(const Texture& color) {
auto fbo = createFramebuffer();
bindFramebuffer = fbo;
MBGL_CHECK_ERROR(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
color.texture, 0));
checkFramebuffer();
return { color.size, std::move(fbo) };
}
UniqueTexture
Context::createTexture(uint16_t width, uint16_t height, const void* data, TextureUnit unit) {
auto obj = createTexture();
@@ -186,50 +294,37 @@ void Context::reset() {
performCleanup();
}
namespace {
template <typename Fn>
void applyStateFunction(Context& context, Fn&& fn) {
fn(context.stencilFunc);
fn(context.stencilMask);
fn(context.stencilTest);
fn(context.stencilOp);
fn(context.depthRange);
fn(context.depthMask);
fn(context.depthTest);
fn(context.depthFunc);
fn(context.blend);
fn(context.blendFunc);
fn(context.blendColor);
fn(context.colorMask);
fn(context.clearDepth);
fn(context.clearColor);
fn(context.clearStencil);
fn(context.program);
fn(context.lineWidth);
fn(context.activeTexture);
fn(context.bindFramebuffer);
fn(context.viewport);
#if not MBGL_USE_GLES2
fn(context.pixelZoom);
fn(context.rasterPos);
#endif // MBGL_USE_GLES2
for (auto& tex : context.texture) {
fn(tex);
}
fn(context.vertexBuffer);
fn(context.elementBuffer);
fn(context.vertexArrayObject);
}
} // namespace
void Context::resetState() {
applyStateFunction(*this, [](auto& state) { state.reset(); });
}
void Context::setDirtyState() {
applyStateFunction(*this, [](auto& state) { state.setDirty(); });
// Note: does not set viewport/bindFramebuffer to dirty since they are handled separately in
// the view object.
stencilFunc.setDirty();
stencilMask.setDirty();
stencilTest.setDirty();
stencilOp.setDirty();
depthRange.setDirty();
depthMask.setDirty();
depthTest.setDirty();
depthFunc.setDirty();
blend.setDirty();
blendFunc.setDirty();
blendColor.setDirty();
colorMask.setDirty();
clearDepth.setDirty();
clearColor.setDirty();
clearStencil.setDirty();
program.setDirty();
lineWidth.setDirty();
activeTexture.setDirty();
#if not MBGL_USE_GLES2
pixelZoom.setDirty();
rasterPos.setDirty();
#endif // MBGL_USE_GLES2
for (auto& tex : texture) {
tex.setDirty();
}
vertexBuffer.setDirty();
elementBuffer.setDirty();
vertexArrayObject.setDirty();
}
void Context::performCleanup() {
@@ -289,6 +384,12 @@ void Context::performCleanup() {
glDeleteFramebuffers(int(abandonedFramebuffers.size()), abandonedFramebuffers.data()));
abandonedFramebuffers.clear();
}
if (!abandonedRenderbuffers.empty()) {
MBGL_CHECK_ERROR(glDeleteRenderbuffers(int(abandonedRenderbuffers.size()),
abandonedRenderbuffers.data()));
abandonedRenderbuffers.clear();
}
}
} // namespace gl

View File

@@ -4,6 +4,8 @@
#include <mbgl/gl/state.hpp>
#include <mbgl/gl/value.hpp>
#include <mbgl/gl/texture.hpp>
#include <mbgl/gl/renderbuffer.hpp>
#include <mbgl/gl/framebuffer.hpp>
#include <mbgl/gl/vertex_buffer.hpp>
#include <mbgl/gl/index_buffer.hpp>
#include <mbgl/gl/attribute.hpp>
@@ -14,6 +16,9 @@
#include <array>
namespace mbgl {
class View;
namespace gl {
constexpr size_t TextureMax = 64;
@@ -27,7 +32,6 @@ public:
UniqueShader createFragmentShader();
UniqueTexture createTexture();
UniqueVertexArray createVertexArray();
UniqueFramebuffer createFramebuffer();
template <class V>
VertexBuffer<V> createVertexBuffer(std::vector<V>&& v) {
@@ -44,6 +48,20 @@ public:
};
}
template <RenderbufferType type>
Renderbuffer<type> createRenderbuffer(const std::array<uint16_t, 2>& size) {
static_assert(type == RenderbufferType::RGBA || type == RenderbufferType::DepthStencil,
"invalid renderbuffer type");
return { size, createRenderbuffer(type, size[0], size[1]) };
}
Framebuffer createFramebuffer(const Renderbuffer<RenderbufferType::RGBA>&,
const Renderbuffer<RenderbufferType::DepthStencil>&);
Framebuffer createFramebuffer(const Renderbuffer<RenderbufferType::RGBA>&);
Framebuffer createFramebuffer(const Texture&,
const Renderbuffer<RenderbufferType::DepthStencil>&);
Framebuffer createFramebuffer(const Texture&);
// Create a texture from an image with data.
template <typename Image>
Texture createTexture(const Image& image, TextureUnit unit = 0) {
@@ -87,8 +105,6 @@ public:
&& abandonedFramebuffers.empty();
}
void resetState();
void setDirtyState();
State<value::StencilFunc> stencilFunc;
@@ -111,6 +127,7 @@ public:
State<value::ActiveTexture> activeTexture;
State<value::BindFramebuffer> bindFramebuffer;
State<value::Viewport> viewport;
State<value::BindRenderbuffer> bindRenderbuffer;
#if not MBGL_USE_GLES2
State<value::PixelZoom> pixelZoom;
State<value::RasterPos> rasterPos;
@@ -124,6 +141,8 @@ private:
UniqueBuffer createVertexBuffer(const void* data, std::size_t size);
UniqueBuffer createIndexBuffer(const void* data, std::size_t size);
UniqueTexture createTexture(uint16_t width, uint16_t height, const void* data, TextureUnit);
UniqueFramebuffer createFramebuffer();
UniqueRenderbuffer createRenderbuffer(RenderbufferType, uint16_t width, uint16_t height);
void bindAttribute(const AttributeBinding&, std::size_t stride, const int8_t* offset);
friend detail::ProgramDeleter;
@@ -132,6 +151,7 @@ private:
friend detail::TextureDeleter;
friend detail::VertexArrayDeleter;
friend detail::FramebufferDeleter;
friend detail::RenderbufferDeleter;
std::vector<TextureID> pooledTextures;
@@ -141,6 +161,7 @@ private:
std::vector<TextureID> abandonedTextures;
std::vector<VertexArrayID> abandonedVertexArrays;
std::vector<FramebufferID> abandonedFramebuffers;
std::vector<RenderbufferID> abandonedRenderbuffers;
};
} // namespace gl

View File

@@ -0,0 +1,17 @@
#pragma once
#include <mbgl/gl/object.hpp>
#include <array>
namespace mbgl {
namespace gl {
class Framebuffer {
public:
std::array<uint16_t, 2> size;
gl::UniqueFramebuffer framebuffer;
};
} // namespace gl
} // namespace mbgl

View File

@@ -51,6 +51,7 @@ constexpr const char* stringFromError(GLenum err) {
} // namespace
void checkError(const char* cmd, const char* file, int line) {
// fprintf(stderr, "cmd: %s\n", cmd);
GLenum err = GL_NO_ERROR;
if ((err = glGetError()) != GL_NO_ERROR) {
std::string message = std::string(cmd) + ": Error " + stringFromError(err);

View File

@@ -41,6 +41,11 @@ void FramebufferDeleter::operator()(FramebufferID id) const {
context->abandonedFramebuffers.push_back(id);
}
void RenderbufferDeleter::operator()(RenderbufferID id) const {
assert(context);
context->abandonedRenderbuffers.push_back(id);
}
} // namespace detail
} // namespace gl
} // namespace mbgl

View File

@@ -41,6 +41,11 @@ struct FramebufferDeleter {
void operator()(FramebufferID) const;
};
struct RenderbufferDeleter {
Context* context;
void operator()(RenderbufferID) const;
};
} // namespace detail
using UniqueProgram = std_experimental::unique_resource<ProgramID, detail::ProgramDeleter>;
@@ -49,6 +54,7 @@ using UniqueBuffer = std_experimental::unique_resource<BufferID, detail::BufferD
using UniqueTexture = std_experimental::unique_resource<TextureID, detail::TextureDeleter>;
using UniqueVertexArray = std_experimental::unique_resource<VertexArrayID, detail::VertexArrayDeleter>;
using UniqueFramebuffer = std_experimental::unique_resource<FramebufferID, detail::FramebufferDeleter>;
using UniqueRenderbuffer = std_experimental::unique_resource<RenderbufferID, detail::RenderbufferDeleter>;
} // namespace gl
} // namespace mbgl

View File

@@ -0,0 +1,19 @@
#pragma once
#include <mbgl/gl/object.hpp>
#include <array>
namespace mbgl {
namespace gl {
template <RenderbufferType renderbufferType>
class Renderbuffer {
public:
using type = std::integral_constant<RenderbufferType, renderbufferType>;
std::array<uint16_t, 2> size;
gl::UniqueRenderbuffer renderbuffer;
};
} // namespace gl
} // namespace mbgl

View File

@@ -3,20 +3,6 @@
namespace mbgl {
namespace gl {
// Helper struct that allows obtaining the default value of a Value class
template <typename T, typename = void>
struct DefaultValue {
static typename T::Type Get() {
return T::Get();
}
};
template <typename T>
struct DefaultValue<T, decltype((void)T::Default, void())> {
static typename T::Type Get() {
return T::Default;
}
};
// Wraps a piece of OpenGL state and remember its value to avoid redundant state calls.
// Wrapped types need to implement to the Value class interface:
//
@@ -26,17 +12,12 @@ struct DefaultValue<T, decltype((void)T::Default, void())> {
// static void Set(const Type& value);
// static Type Get();
// };
//
// The Get() function is optional, but if it is omitted, you must provide a Default.
// Default is also optional, but if it is omitted, you must provide a Get() function.
// If both are present, DefaultValue<T>::Get() will use the Default member.
template <typename T>
class State {
public:
void operator=(const typename T::Type& value) {
if (*this != value) {
dirty = false;
currentValue = value;
setCurrentValue(value);
T::Set(currentValue);
}
}
@@ -49,9 +30,9 @@ public:
return dirty || currentValue != value;
}
// Explicitly resets the piece of OpenGL state to its default value.
void reset() {
*this = defaultValue;
void setCurrentValue(const typename T::Type& value) {
dirty = false;
currentValue = value;
}
// Mark the state as dirty. This means that the next time we are assigning a value to this
@@ -68,14 +49,9 @@ public:
return dirty;
}
void setDefaultValue(const typename T::Type& value) {
defaultValue = value;
}
private:
typename T::Type defaultValue = DefaultValue<T>::Get();
typename T::Type currentValue = defaultValue;
bool dirty = false;
typename T::Type currentValue = T::Default;
bool dirty = true;
};
// Helper struct that stores the current state and restores it upon destruction. You should not use

View File

@@ -48,6 +48,11 @@ enum class BufferType : uint32_t {
Element = 0x8893
};
enum class RenderbufferType : uint32_t {
RGBA = 0x8058,
DepthStencil = 0x88F0,
};
enum class TextureMipMap : bool { No = false, Yes = true };
enum class TextureFilter : bool { Nearest = false, Linear = true };

View File

@@ -241,6 +241,8 @@ ActiveTexture::Type ActiveTexture::Get() {
return static_cast<Type>(activeTexture - GL_TEXTURE0);
}
const constexpr Viewport::Type Viewport::Default;
void Viewport::Set(const Type& value) {
MBGL_CHECK_ERROR(glViewport(value.x, value.y, value.width, value.height));
}
@@ -252,6 +254,8 @@ Viewport::Type Viewport::Get() {
static_cast<uint16_t>(viewport[2]), static_cast<uint16_t>(viewport[3]) };
}
const constexpr BindFramebuffer::Type BindFramebuffer::Default;
void BindFramebuffer::Set(const Type& value) {
MBGL_CHECK_ERROR(glBindFramebuffer(GL_FRAMEBUFFER, value));
}
@@ -262,6 +266,18 @@ BindFramebuffer::Type BindFramebuffer::Get() {
return binding;
}
const constexpr BindRenderbuffer::Type BindRenderbuffer::Default;
void BindRenderbuffer::Set(const Type& value) {
MBGL_CHECK_ERROR(glBindRenderbuffer(GL_RENDERBUFFER, value));
}
BindRenderbuffer::Type BindRenderbuffer::Get() {
GLint binding;
MBGL_CHECK_ERROR(glGetIntegerv(GL_RENDERBUFFER_BINDING, &binding));
return binding;
}
const constexpr BindTexture::Type BindTexture::Default;
void BindTexture::Set(const Type& value) {

View File

@@ -180,6 +180,7 @@ struct Viewport {
uint16_t width;
uint16_t height;
};
static const constexpr Type Default = { 0, 0, 0, 0 };
static void Set(const Type&);
static Type Get();
};
@@ -190,6 +191,14 @@ constexpr bool operator!=(const Viewport::Type& a, const Viewport::Type& b) {
struct BindFramebuffer {
using Type = FramebufferID;
static const constexpr Type Default = 0;
static void Set(const Type&);
static Type Get();
};
struct BindRenderbuffer {
using Type = RenderbufferID;
static const constexpr Type Default = 0;
static void Set(const Type&);
static Type Get();
};

View File

@@ -1,9 +1,19 @@
#include <mbgl/map/backend.hpp>
#include <mbgl/gl/context.hpp>
#include <cassert>
namespace mbgl {
Backend::Backend() : context(std::make_unique<gl::Context>()) {
}
gl::Context& Backend::getContext() {
return *context;
}
Backend::~Backend() = default;
void Backend::notifyMapChange(MapChange) {
// no-op
}

View File

@@ -35,10 +35,18 @@ enum class RenderState : uint8_t {
Fully,
};
struct StillImageRequest {
StillImageRequest(View& view_, Map::StillImageCallback&& callback_)
: view(view_), callback(std::move(callback_)) {
}
View& view;
Map::StillImageCallback callback;
};
class Map::Impl : public style::Observer {
public:
Impl(Backend&,
View&,
float pixelRatio,
FileSource&,
Scheduler&,
@@ -54,12 +62,11 @@ public:
void onResourceError(std::exception_ptr) override;
void update();
void render();
void render(View&);
void loadStyleJSON(const std::string&);
Backend& backend;
View& view;
FileSource& fileSource;
Scheduler& scheduler;
@@ -85,14 +92,14 @@ public:
std::unique_ptr<AsyncRequest> styleRequest;
Map::StillImageCallback callback;
std::unique_ptr<StillImageRequest> stillImageRequest;
size_t sourceCacheSize;
TimePoint timePoint;
bool loading = false;
};
Map::Map(Backend& backend,
View& view,
const std::array<uint16_t, 2> size,
const float pixelRatio,
FileSource& fileSource,
Scheduler& scheduler,
@@ -101,7 +108,6 @@ Map::Map(Backend& backend,
ConstrainMode constrainMode,
ViewportMode viewportMode)
: impl(std::make_unique<Impl>(backend,
view,
pixelRatio,
fileSource,
scheduler,
@@ -109,12 +115,10 @@ Map::Map(Backend& backend,
contextMode,
constrainMode,
viewportMode)) {
view.initialize(this);
update(Update::Dimensions);
impl->transform.resize(size);
}
Map::Impl::Impl(Backend& backend_,
View& view_,
float pixelRatio_,
FileSource& fileSource_,
Scheduler& scheduler_,
@@ -123,7 +127,6 @@ Map::Impl::Impl(Backend& backend_,
ConstrainMode constrainMode_,
ViewportMode viewportMode_)
: backend(backend_),
view(view_),
fileSource(fileSource_),
scheduler(scheduler_),
transform([this](MapChange change) { backend.notifyMapChange(change); },
@@ -150,42 +153,38 @@ Map::~Map() {
impl->backend.deactivate();
}
void Map::renderStill(StillImageCallback callback) {
void Map::renderStill(View& view, StillImageCallback callback) {
if (!callback) {
Log::Error(Event::General, "StillImageCallback not set");
return;
}
if (impl->mode != MapMode::Still) {
callback(std::make_exception_ptr(util::MisuseException("Map is not in still image render mode")), {});
callback(std::make_exception_ptr(util::MisuseException("Map is not in still image render mode")));
return;
}
if (impl->callback) {
callback(std::make_exception_ptr(util::MisuseException("Map is currently rendering an image")), {});
if (impl->stillImageRequest) {
callback(std::make_exception_ptr(util::MisuseException("Map is currently rendering an image")));
return;
}
if (!impl->style) {
callback(std::make_exception_ptr(util::MisuseException("Map doesn't have a style")), {});
callback(std::make_exception_ptr(util::MisuseException("Map doesn't have a style")));
return;
}
if (impl->style->getLastError()) {
callback(impl->style->getLastError(), {});
callback(impl->style->getLastError());
return;
}
impl->callback = callback;
impl->stillImageRequest = std::make_unique<StillImageRequest>(view, std::move(callback));
impl->updateFlags |= Update::RenderStill;
impl->asyncUpdate.send();
}
void Map::update(Update flags) {
impl->onUpdate(flags);
}
void Map::render() {
void Map::render(View& view) {
if (!impl->style) {
return;
}
@@ -198,7 +197,7 @@ void Map::render() {
const Update flags = impl->transform.updateTransitions(Clock::now());
impl->render();
impl->render(view);
impl->backend.notifyMapChange(isFullyLoaded() ?
MapChangeDidFinishRenderingFrameFullyRendered :
@@ -218,16 +217,20 @@ void Map::render() {
// Triggers an asynchronous update, that eventually triggers a view
// invalidation, causing renderSync to be called again if in transition.
if (flags != Update::Nothing) {
update(flags);
impl->onUpdate(flags);
}
}
void Map::triggerRepaint() {
impl->backend.invalidate();
}
void Map::Impl::update() {
if (!style) {
updateFlags = Update::Nothing;
}
if (updateFlags == Update::Nothing || (mode == MapMode::Still && !callback)) {
if (updateFlags == Update::Nothing || (mode == MapMode::Still && !stillImageRequest)) {
return;
}
@@ -270,18 +273,19 @@ void Map::Impl::update() {
if (mode == MapMode::Continuous) {
backend.invalidate();
} else if (callback && style->isLoaded()) {
} else if (stillImageRequest && style->isLoaded()) {
// TODO: determine whether we need activate/deactivate
backend.activate();
render();
render(stillImageRequest->view);
backend.deactivate();
}
updateFlags = Update::Nothing;
}
void Map::Impl::render() {
void Map::Impl::render(View& view) {
if (!painter) {
painter = std::make_unique<Painter>(transform.getState());
painter = std::make_unique<Painter>(backend.getContext(), transform.getState());
}
FrameData frameData { timePoint,
@@ -296,8 +300,8 @@ void Map::Impl::render() {
annotationManager->getSpriteAtlas());
if (mode == MapMode::Still) {
callback(nullptr, view.readStillImage());
callback = nullptr;
auto request = std::move(stillImageRequest);
request->callback(nullptr);
}
painter->cleanup();
@@ -399,12 +403,12 @@ std::string Map::getStyleJSON() const {
void Map::cancelTransitions() {
impl->transform.cancelTransitions();
update(Update::Repaint);
impl->onUpdate(Update::Repaint);
}
void Map::setGestureInProgress(bool inProgress) {
impl->transform.setGestureInProgress(inProgress);
update(Update::Repaint);
impl->onUpdate(Update::Repaint);
}
bool Map::isGestureInProgress() const {
@@ -431,24 +435,24 @@ CameraOptions Map::getCameraOptions(optional<EdgeInsets> padding) const {
void Map::jumpTo(const CameraOptions& camera) {
impl->transform.jumpTo(camera);
update(camera.zoom ? Update::RecalculateStyle : Update::Repaint);
impl->onUpdate(camera.zoom ? Update::RecalculateStyle : Update::Repaint);
}
void Map::easeTo(const CameraOptions& camera, const AnimationOptions& animation) {
impl->transform.easeTo(camera, animation);
update(camera.zoom ? Update::RecalculateStyle : Update::Repaint);
impl->onUpdate(camera.zoom ? Update::RecalculateStyle : Update::Repaint);
}
void Map::flyTo(const CameraOptions& camera, const AnimationOptions& animation) {
impl->transform.flyTo(camera, animation);
update(Update::RecalculateStyle);
impl->onUpdate(Update::RecalculateStyle);
}
#pragma mark - Position
void Map::moveBy(const ScreenCoordinate& point, const Duration& duration) {
impl->transform.moveBy(point, duration);
update(Update::Repaint);
impl->onUpdate(Update::Repaint);
}
void Map::setLatLng(const LatLng& latLng, const Duration& duration) {
@@ -457,12 +461,12 @@ void Map::setLatLng(const LatLng& latLng, const Duration& duration) {
void Map::setLatLng(const LatLng& latLng, optional<EdgeInsets> padding, const Duration& duration) {
impl->transform.setLatLng(latLng, padding, duration);
update(Update::Repaint);
impl->onUpdate(Update::Repaint);
}
void Map::setLatLng(const LatLng& latLng, optional<ScreenCoordinate> anchor, const Duration& duration) {
impl->transform.setLatLng(latLng, anchor, duration);
update(Update::Repaint);
impl->onUpdate(Update::Repaint);
}
LatLng Map::getLatLng(optional<EdgeInsets> padding) const {
@@ -477,7 +481,7 @@ void Map::resetPosition(optional<EdgeInsets> padding) {
camera.padding = padding;
camera.zoom = 0;
impl->transform.jumpTo(camera);
update(Update::RecalculateStyle);
impl->onUpdate(Update::RecalculateStyle);
}
@@ -485,12 +489,12 @@ void Map::resetPosition(optional<EdgeInsets> padding) {
void Map::scaleBy(double ds, optional<ScreenCoordinate> anchor, const Duration& duration) {
impl->transform.scaleBy(ds, anchor, duration);
update(Update::RecalculateStyle);
impl->onUpdate(Update::RecalculateStyle);
}
void Map::setScale(double scale, optional<ScreenCoordinate> anchor, const Duration& duration) {
impl->transform.setScale(scale, anchor, duration);
update(Update::RecalculateStyle);
impl->onUpdate(Update::RecalculateStyle);
}
double Map::getScale() const {
@@ -503,7 +507,7 @@ void Map::setZoom(double zoom, const Duration& duration) {
void Map::setZoom(double zoom, optional<EdgeInsets> padding, const Duration& duration) {
impl->transform.setZoom(zoom, padding, duration);
update(Update::RecalculateStyle);
impl->onUpdate(Update::RecalculateStyle);
}
double Map::getZoom() const {
@@ -516,7 +520,7 @@ void Map::setLatLngZoom(const LatLng& latLng, double zoom, const Duration& durat
void Map::setLatLngZoom(const LatLng& latLng, double zoom, optional<EdgeInsets> padding, const Duration& duration) {
impl->transform.setLatLngZoom(latLng, zoom, padding, duration);
update(Update::RecalculateStyle);
impl->onUpdate(Update::RecalculateStyle);
}
CameraOptions Map::cameraForLatLngBounds(const LatLngBounds& bounds, optional<EdgeInsets> padding) const {
@@ -613,6 +617,11 @@ double Map::getMaxZoom() const {
#pragma mark - Size
void Map::setSize(const std::array<uint16_t, 2>& size) {
impl->transform.resize(size);
impl->onUpdate(Update::Repaint);
}
uint16_t Map::getWidth() const {
return impl->transform.getState().getWidth();
}
@@ -625,7 +634,7 @@ uint16_t Map::getHeight() const {
void Map::rotateBy(const ScreenCoordinate& first, const ScreenCoordinate& second, const Duration& duration) {
impl->transform.rotateBy(first, second, duration);
update(Update::Repaint);
impl->onUpdate(Update::Repaint);
}
void Map::setBearing(double degrees, const Duration& duration) {
@@ -634,12 +643,12 @@ void Map::setBearing(double degrees, const Duration& duration) {
void Map::setBearing(double degrees, optional<ScreenCoordinate> anchor, const Duration& duration) {
impl->transform.setAngle(-degrees * util::DEG2RAD, anchor, duration);
update(Update::Repaint);
impl->onUpdate(Update::Repaint);
}
void Map::setBearing(double degrees, optional<EdgeInsets> padding, const Duration& duration) {
impl->transform.setAngle(-degrees * util::DEG2RAD, padding, duration);
update(Update::Repaint);
impl->onUpdate(Update::Repaint);
}
double Map::getBearing() const {
@@ -648,7 +657,7 @@ double Map::getBearing() const {
void Map::resetNorth(const Duration& duration) {
impl->transform.setAngle(0, duration);
update(Update::Repaint);
impl->onUpdate(Update::Repaint);
}
#pragma mark - Pitch
@@ -659,7 +668,7 @@ void Map::setPitch(double pitch, const Duration& duration) {
void Map::setPitch(double pitch, optional<ScreenCoordinate> anchor, const Duration& duration) {
impl->transform.setPitch(pitch * util::DEG2RAD, anchor, duration);
update(Update::Repaint);
impl->onUpdate(Update::Repaint);
}
double Map::getPitch() const {
@@ -670,7 +679,7 @@ double Map::getPitch() const {
void Map::setNorthOrientation(NorthOrientation orientation) {
impl->transform.setNorthOrientation(orientation);
update(Update::Repaint);
impl->onUpdate(Update::Repaint);
}
NorthOrientation Map::getNorthOrientation() const {
@@ -681,7 +690,7 @@ NorthOrientation Map::getNorthOrientation() const {
void Map::setConstrainMode(mbgl::ConstrainMode mode) {
impl->transform.setConstrainMode(mode);
update(Update::Repaint);
impl->onUpdate(Update::Repaint);
}
ConstrainMode Map::getConstrainMode() const {
@@ -692,7 +701,7 @@ ConstrainMode Map::getConstrainMode() const {
void Map::setViewportMode(mbgl::ViewportMode mode) {
impl->transform.setViewportMode(mode);
update(Update::Repaint);
impl->onUpdate(Update::Repaint);
}
ViewportMode Map::getViewportMode() const {
@@ -737,17 +746,17 @@ double Map::getTopOffsetPixelsForAnnotationIcon(const std::string& name) {
AnnotationID Map::addAnnotation(const Annotation& annotation) {
auto result = impl->annotationManager->addAnnotation(annotation, getMaxZoom());
update(Update::AnnotationStyle | Update::AnnotationData);
impl->onUpdate(Update::AnnotationStyle | Update::AnnotationData);
return result;
}
void Map::updateAnnotation(AnnotationID id, const Annotation& annotation) {
update(impl->annotationManager->updateAnnotation(id, annotation, getMaxZoom()));
impl->onUpdate(impl->annotationManager->updateAnnotation(id, annotation, getMaxZoom()));
}
void Map::removeAnnotation(AnnotationID annotation) {
impl->annotationManager->removeAnnotation(annotation);
update(Update::AnnotationStyle | Update::AnnotationData);
impl->onUpdate(Update::AnnotationStyle | Update::AnnotationData);
}
#pragma mark - Feature query api
@@ -833,7 +842,7 @@ void Map::addLayer(std::unique_ptr<Layer> layer, const optional<std::string>& be
impl->backend.activate();
impl->style->addLayer(std::move(layer), before);
update(Update::Classes);
impl->onUpdate(Update::Classes);
impl->backend.deactivate();
}
@@ -847,7 +856,7 @@ void Map::removeLayer(const std::string& id) {
impl->backend.activate();
impl->style->removeLayer(id);
update(Update::Classes);
impl->onUpdate(Update::Classes);
impl->backend.deactivate();
}
@@ -861,7 +870,7 @@ void Map::addImage(const std::string& name, std::unique_ptr<const SpriteImage> i
impl->style->spriteAtlas->setSprite(name, std::move(image));
impl->style->spriteAtlas->updateDirty();
update(Update::Repaint);
impl->onUpdate(Update::Repaint);
}
void Map::removeImage(const std::string& name) {
@@ -873,7 +882,7 @@ void Map::removeImage(const std::string& name) {
impl->style->spriteAtlas->removeSprite(name);
impl->style->spriteAtlas->updateDirty();
update(Update::Repaint);
impl->onUpdate(Update::Repaint);
}
#pragma mark - Defaults
@@ -917,7 +926,7 @@ double Map::getDefaultPitch() const {
void Map::setDebug(MapDebugOptions debugOptions) {
impl->debugOptions = debugOptions;
update(Update::Repaint);
impl->onUpdate(Update::Repaint);
}
void Map::cycleDebugOptions() {
@@ -941,7 +950,7 @@ void Map::cycleDebugOptions() {
else
impl->debugOptions = MapDebugOptions::TileBorders;
update(Update::Repaint);
impl->onUpdate(Update::Repaint);
}
MapDebugOptions Map::getDebug() const {
@@ -954,20 +963,20 @@ bool Map::isFullyLoaded() const {
void Map::addClass(const std::string& className) {
if (impl->style && impl->style->addClass(className)) {
update(Update::Classes);
impl->onUpdate(Update::Classes);
}
}
void Map::removeClass(const std::string& className) {
if (impl->style && impl->style->removeClass(className)) {
update(Update::Classes);
impl->onUpdate(Update::Classes);
}
}
void Map::setClasses(const std::vector<std::string>& classNames) {
if (impl->style) {
impl->style->setClasses(classNames);
update(Update::Classes);
impl->onUpdate(Update::Classes);
}
}
@@ -1019,10 +1028,6 @@ void Map::Impl::onSourceAttributionChanged(style::Source&, const std::string&) {
}
void Map::Impl::onUpdate(Update flags) {
if (flags & Update::Dimensions) {
transform.resize(view.getSize());
}
updateFlags |= flags;
asyncUpdate.send();
}
@@ -1036,9 +1041,9 @@ void Map::Impl::onStyleError() {
}
void Map::Impl::onResourceError(std::exception_ptr error) {
if (mode == MapMode::Still && callback) {
callback(error, {});
callback = nullptr;
if (mode == MapMode::Still && stillImageRequest) {
auto request = std::move(stillImageRequest);
request->callback(error);
}
}

View File

@@ -6,7 +6,6 @@ namespace mbgl {
enum class Update {
Nothing = 0,
Dimensions = 1 << 1,
Classes = 1 << 2,
RecalculateStyle = 1 << 3,
RenderStill = 1 << 4,

View File

@@ -1,17 +0,0 @@
#include <mbgl/map/view.hpp>
#include <mbgl/map/map.hpp>
#include <cassert>
namespace mbgl {
PremultipliedImage View::readStillImage(std::array<uint16_t, 2>) {
return {};
}
void View::initialize(Map *map_) {
assert(map_);
map = map_;
}
} // namespace mbgl

View File

@@ -31,6 +31,8 @@
#include <mbgl/util/mat3.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/util/offscreen_texture.hpp>
#include <cassert>
#include <algorithm>
#include <iostream>
@@ -40,8 +42,9 @@ namespace mbgl {
using namespace style;
Painter::Painter(const TransformState& state_)
: state(state_),
Painter::Painter(gl::Context& context_, const TransformState& state_)
: context(context_),
state(state_),
tileTriangleVertexBuffer(context.createVertexBuffer(std::vector<FillVertex> {{
{ 0, 0 },
{ util::EXTENT, 0 },
@@ -71,9 +74,6 @@ Painter::Painter(const TransformState& state_)
#ifndef NDEBUG
overdrawShaders = std::make_unique<Shaders>(context, gl::Shader::Overdraw);
#endif
// Reset GL values
context.setDirtyState();
}
Painter::~Painter() = default;
@@ -93,9 +93,10 @@ void Painter::cleanup() {
}
void Painter::render(const Style& style, const FrameData& frame_, View& view, SpriteAtlas& annotationSpriteAtlas) {
context.viewport.setDefaultValue(
{ 0, 0, view.getFramebufferSize()[0], view.getFramebufferSize()[1] });
frame = frame_;
if (frame.contextMode == GLContextMode::Shared) {
context.setDirtyState();
}
PaintParameters parameters {
#ifndef NDEBUG
@@ -126,12 +127,14 @@ void Painter::render(const Style& style, const FrameData& frame_, View& view, Sp
frameHistory.record(frame.timePoint, state.getZoom(),
frame.mapMode == MapMode::Continuous ? util::DEFAULT_FADE_DURATION : Milliseconds(0));
// - UPLOAD PASS -------------------------------------------------------------------------------
// Uploads all required buffers and images before we do any actual rendering.
{
MBGL_DEBUG_GROUP("upload");
spriteAtlas->upload(context, 0);
lineAtlas->upload(context, 0);
glyphAtlas->upload(context, 0);
frameHistory.upload(context, 0);
@@ -149,10 +152,8 @@ void Painter::render(const Style& style, const FrameData& frame_, View& view, Sp
// tiles whatsoever.
{
MBGL_DEBUG_GROUP("clear");
context.bindFramebuffer.setDirty();
view.bind();
context.viewport.reset();
context.stencilFunc.reset();
context.stencilFunc = { gl::StencilTestFunction::Always, 0, ~0u };
context.stencilTest = true;
context.stencilMask = 0xFF;
context.depthTest = false;
@@ -249,10 +250,6 @@ void Painter::render(const Style& style, const FrameData& frame_, View& view, Sp
context.vertexArrayObject = 0;
}
if (frame.contextMode == GLContextMode::Shared) {
context.setDirtyState();
}
}
template <class Iterator>
@@ -296,17 +293,21 @@ void Painter::renderPass(PaintParameters& parameters,
renderBackground(parameters, *layer.as<BackgroundLayer>());
} else if (layer.is<CustomLayer>()) {
MBGL_DEBUG_GROUP(layer.baseImpl->id + " - custom");
// Reset GL state to a known state so the CustomLayer always has a clean slate.
context.vertexArrayObject = 0;
context.depthFunc = gl::DepthTestFunction::LessEqual;
context.depthTest = true;
context.depthMask = false;
context.stencilTest = false;
setDepthSublayer(0);
layer.as<CustomLayer>()->impl->render(state);
context.setDirtyState();
context.bindFramebuffer.setDirty();
// Reset the view back to our original one, just in case the CustomLayer changed
// the viewport or Framebuffer.
parameters.view.bind();
context.viewport.reset();
context.setDirtyState();
} else {
MBGL_DEBUG_GROUP(layer.baseImpl->id + " - " + util::toString(item.tile->id));
if (item.bucket->needsClipping()) {

View File

@@ -68,7 +68,7 @@ struct FrameData {
class Painter : private util::noncopyable {
public:
Painter(const TransformState&);
Painter(gl::Context&, const TransformState&);
~Painter();
void render(const style::Style&,
@@ -153,6 +153,9 @@ private:
}
#endif
private:
gl::Context& context;
mat4 projMatrix;
std::array<float, 2> pixelsToGLUnits;
@@ -169,8 +172,6 @@ private:
int indent = 0;
gl::Context context;
RenderPass pass = RenderPass::Opaque;
int numSublayers = 3;

View File

@@ -89,7 +89,7 @@ void Painter::renderDebugFrame(const mat4 &matrix) {
}
#ifndef NDEBUG
void Painter::renderClipMasks(PaintParameters& parameters) {
void Painter::renderClipMasks(PaintParameters&) {
context.stencilTest = false;
context.depthTest = false;
context.program = 0;
@@ -100,13 +100,13 @@ void Painter::renderClipMasks(PaintParameters& parameters) {
context.rasterPos = { -1, -1, 0, 0 };
// Read the stencil buffer
const auto& fbSize = parameters.view.getFramebufferSize();
auto pixels = std::make_unique<uint8_t[]>(fbSize[0] * fbSize[1]);
const auto viewport = context.viewport.getCurrentValue();
auto pixels = std::make_unique<uint8_t[]>(viewport.width * viewport.height);
MBGL_CHECK_ERROR(glReadPixels(
0, // GLint x
0, // GLint y
fbSize[0], // GLsizei width
fbSize[1], // GLsizei height
viewport.x, // GLint x
viewport.y, // GLint y
viewport.width, // GLsizei width
viewport.height, // GLsizei height
GL_STENCIL_INDEX, // GLenum format
GL_UNSIGNED_BYTE, // GLenum type
pixels.get() // GLvoid * data
@@ -114,22 +114,21 @@ void Painter::renderClipMasks(PaintParameters& parameters) {
// Scale the Stencil buffer to cover the entire color space.
auto it = pixels.get();
auto end = it + fbSize[0] * fbSize[1];
auto end = it + viewport.width * viewport.height;
const auto factor = 255.0f / *std::max_element(it, end);
for (; it != end; ++it) {
*it *= factor;
}
MBGL_CHECK_ERROR(glWindowPos2i(0, 0));
MBGL_CHECK_ERROR(glDrawPixels(fbSize[0], fbSize[1], GL_LUMINANCE, GL_UNSIGNED_BYTE, pixels.get()));
#else
(void)parameters;
MBGL_CHECK_ERROR(glWindowPos2i(viewport.x, viewport.y));
MBGL_CHECK_ERROR(glDrawPixels(viewport.width, viewport.height, GL_LUMINANCE, GL_UNSIGNED_BYTE,
pixels.get()));
#endif // MBGL_USE_GLES2
}
#endif // NDEBUG
#ifndef NDEBUG
void Painter::renderDepthBuffer(PaintParameters& parameters) {
void Painter::renderDepthBuffer(PaintParameters&) {
context.stencilTest = false;
context.depthTest = false;
context.program = 0;
@@ -140,27 +139,26 @@ void Painter::renderDepthBuffer(PaintParameters& parameters) {
context.rasterPos = { -1, -1, 0, 0 };
// Read the stencil buffer
const auto& fbSize = parameters.view.getFramebufferSize();
auto pixels = std::make_unique<GLubyte[]>(fbSize[0] * fbSize[1]);
const auto viewport = context.viewport.getCurrentValue();
auto pixels = std::make_unique<uint8_t[]>(viewport.width * viewport.height);
const double base = 1.0 / (1.0 - depthRangeSize);
glPixelTransferf(GL_DEPTH_SCALE, base);
glPixelTransferf(GL_DEPTH_BIAS, 1.0 - base);
MBGL_CHECK_ERROR(glReadPixels(
0, // GLint x
0, // GLint y
fbSize[0], // GLsizei width
fbSize[1], // GLsizei height
viewport.x, // GLint x
viewport.y, // GLint y
viewport.width, // GLsizei width
viewport.height, // GLsizei height
GL_DEPTH_COMPONENT, // GLenum format
GL_UNSIGNED_BYTE, // GLenum type
pixels.get() // GLvoid * data
));
MBGL_CHECK_ERROR(glWindowPos2i(0, 0));
MBGL_CHECK_ERROR(glDrawPixels(fbSize[0], fbSize[1], GL_LUMINANCE, GL_UNSIGNED_BYTE, pixels.get()));
#else
(void)parameters;
MBGL_CHECK_ERROR(glWindowPos2i(viewport.x, viewport.y));
MBGL_CHECK_ERROR(glDrawPixels(viewport.width, viewport.height, GL_LUMINANCE, GL_UNSIGNED_BYTE,
pixels.get()));
#endif // MBGL_USE_GLES2
}
#endif // NDEBUG

View File

@@ -30,7 +30,9 @@ void Painter::renderFill(PaintParameters& parameters,
const bool isOutlineColorDefined = !properties.fillOutlineColor.isUndefined();
Color strokeColor = isOutlineColorDefined? properties.fillOutlineColor : fillColor;
auto worldSize = util::convert<GLfloat>(parameters.view.getFramebufferSize());
const auto viewport = context.viewport.getCurrentValue();
const std::array<GLfloat, 2> worldSize{ { static_cast<GLfloat>(viewport.width),
static_cast<GLfloat>(viewport.height) } };
bool pattern = !properties.fillPattern.value.from.empty();
bool outline = properties.fillAntialias && !pattern && isOutlineColorDefined;

View File

@@ -1,70 +1,48 @@
#include <mbgl/util/offscreen_texture.hpp>
#include <mbgl/gl/context.hpp>
#include <mbgl/gl/gl.hpp>
#include <mbgl/util/offscreen_texture.hpp>
#include <cstring>
#include <cassert>
namespace mbgl {
void OffscreenTexture::bind(gl::Context& context,
std::array<uint16_t, 2> size) {
OffscreenTexture::OffscreenTexture(gl::Context& context_, std::array<uint16_t, 2> size_)
: context(context_), size(std::move(size_)) {
assert(size[0] > 0 && size[1] > 0);
}
if (!texture || texture->size != size) {
texture = context.createTexture(size);
}
void OffscreenTexture::bind() {
if (!framebuffer) {
framebuffer = context.createFramebuffer();
context.bindFramebuffer = *framebuffer;
MBGL_CHECK_ERROR(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
texture->texture, 0));
GLenum status = MBGL_CHECK_ERROR(glCheckFramebufferStatus(GL_FRAMEBUFFER));
if (status != GL_FRAMEBUFFER_COMPLETE) {
switch (status) {
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
throw std::runtime_error("Couldn't create framebuffer: incomplete attachment");
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
throw std::runtime_error(
"Couldn't create framebuffer: incomplete missing attachment");
#ifdef GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
throw std::runtime_error("Couldn't create framebuffer: incomplete draw buffer");
#endif
#ifdef GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
throw std::runtime_error("Couldn't create framebuffer: incomplete read buffer");
#endif
#ifdef GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS
case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
throw std::runtime_error("Couldn't create framebuffer: incomplete dimensions");
#endif
case GL_FRAMEBUFFER_UNSUPPORTED:
throw std::runtime_error("Couldn't create framebuffer: unsupported");
default:
throw std::runtime_error("Couldn't create framebuffer: other");
}
}
texture = context.createTexture(size);
framebuffer = context.createFramebuffer(*texture);
} else {
context.bindFramebuffer = *framebuffer;
context.bindFramebuffer = framebuffer->framebuffer;
}
context.viewport = { 0, 0, size[0], size[1] };
}
gl::Texture& OffscreenTexture::getTexture() {
assert(texture);
return *texture;
}
std::array<uint16_t, 2> OffscreenTexture::getSize() const {
if (texture) {
// Use explicit dereference instead of -> due to clang 3.5 bug
return (*texture).size;
} else {
return {{ 0, 0 }};
PremultipliedImage OffscreenTexture::readStillImage() {
PremultipliedImage image { size[0], size[1] };
MBGL_CHECK_ERROR(glReadPixels(0, 0, size[0], size[1], GL_RGBA, GL_UNSIGNED_BYTE, image.data.get()));
const auto stride = image.stride();
auto tmp = std::make_unique<uint8_t[]>(stride);
uint8_t* rgba = image.data.get();
for (int i = 0, j = size[1] - 1; i < j; i++, j--) {
std::memcpy(tmp.get(), rgba + i * stride, stride);
std::memcpy(rgba + i * stride, rgba + j * stride, stride);
std::memcpy(rgba + j * stride, tmp.get(), stride);
}
return image;
}
} // namespace mbgl

View File

@@ -1,6 +1,10 @@
#pragma once
#include <mbgl/map/view.hpp>
#include <mbgl/gl/framebuffer.hpp>
#include <mbgl/gl/texture.hpp>
#include <mbgl/util/optional.hpp>
#include <mbgl/util/image.hpp>
namespace mbgl {
@@ -8,15 +12,20 @@ namespace gl {
class Context;
} // namespace gl
class OffscreenTexture {
class OffscreenTexture : public View {
public:
void bind(gl::Context&, std::array<uint16_t, 2> size);
OffscreenTexture(gl::Context&, std::array<uint16_t, 2> size = {{ 256, 256 }});
void bind() override;
PremultipliedImage readStillImage();
gl::Texture& getTexture();
std::array<uint16_t, 2> getSize() const;
private:
optional<gl::UniqueFramebuffer> framebuffer;
gl::Context& context;
std::array<uint16_t, 2> size;
optional<gl::Framebuffer> framebuffer;
optional<gl::Texture> texture;
};

View File

@@ -6,7 +6,7 @@
#include <mbgl/sprite/sprite_image.hpp>
#include <mbgl/map/map.hpp>
#include <mbgl/platform/default/headless_backend.hpp>
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/platform/default/offscreen_view.hpp>
#include <mbgl/util/io.hpp>
#include <mbgl/util/run_loop.hpp>
#include <mbgl/util/color.hpp>
@@ -24,14 +24,14 @@ class AnnotationTest {
public:
util::RunLoop loop;
HeadlessBackend backend;
HeadlessView view;
OffscreenView view{ backend.getContext() };
StubFileSource fileSource;
ThreadPool threadPool { 4 };
Map map { backend, view, view.getPixelRatio(), fileSource, threadPool, MapMode::Still };
Map map { backend, view.getSize(), 1, fileSource, threadPool, MapMode::Still };
void checkRendering(const char * name) {
test::checkImage(std::string("test/fixtures/annotations/") + name,
test::render(map), 0.0002, 0.1);
test::render(map, view), 0.0002, 0.1);
}
};
@@ -162,7 +162,7 @@ TEST(Annotations, AddMultiple) {
test.map.addAnnotationIcon("default_marker", namedMarker("default_marker.png"));
test.map.addAnnotation(SymbolAnnotation { Point<double> { -10, 0 }, "default_marker" });
test::render(test.map);
test::render(test.map, test.view);
test.map.addAnnotation(SymbolAnnotation { Point<double> { 10, 0 }, "default_marker" });
test.checkRendering("add_multiple");
@@ -172,7 +172,7 @@ TEST(Annotations, NonImmediateAdd) {
AnnotationTest test;
test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
test::render(test.map);
test::render(test.map, test.view);
Polygon<double> polygon = {{ {{ { 0, 0 }, { 0, 45 }, { 45, 45 }, { 45, 0 } }} }};
FillAnnotation annotation { polygon };
@@ -190,7 +190,7 @@ TEST(Annotations, UpdateSymbolAnnotationGeometry) {
test.map.addAnnotationIcon("flipped_marker", namedMarker("flipped_marker.png"));
AnnotationID point = test.map.addAnnotation(SymbolAnnotation { Point<double> { 0, 0 }, "default_marker" });
test::render(test.map);
test::render(test.map, test.view);
test.map.updateAnnotation(point, SymbolAnnotation { Point<double> { -10, 0 }, "default_marker" });
test.checkRendering("update_point");
@@ -204,7 +204,7 @@ TEST(Annotations, UpdateSymbolAnnotationIcon) {
test.map.addAnnotationIcon("flipped_marker", namedMarker("flipped_marker.png"));
AnnotationID point = test.map.addAnnotation(SymbolAnnotation { Point<double> { 0, 0 }, "default_marker" });
test::render(test.map);
test::render(test.map, test.view);
test.map.updateAnnotation(point, SymbolAnnotation { Point<double> { 0, 0 }, "flipped_marker" });
test.checkRendering("update_icon");
@@ -220,7 +220,7 @@ TEST(Annotations, UpdateLineAnnotationGeometry) {
test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
AnnotationID line = test.map.addAnnotation(annotation);
test::render(test.map);
test::render(test.map, test.view);
annotation.geometry = LineString<double> {{ { 0, 0 }, { -45, -45 } }};
test.map.updateAnnotation(line, annotation);
@@ -237,7 +237,7 @@ TEST(Annotations, UpdateLineAnnotationStyle) {
test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
AnnotationID line = test.map.addAnnotation(annotation);
test::render(test.map);
test::render(test.map, test.view);
annotation.color = { { 0, 255, 0, 1 } };
annotation.width = { 2 };
@@ -254,7 +254,7 @@ TEST(Annotations, UpdateFillAnnotationGeometry) {
test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
AnnotationID fill = test.map.addAnnotation(annotation);
test::render(test.map);
test::render(test.map, test.view);
annotation.geometry = Polygon<double> {{ {{ { 0, 0 }, { 0, 45 }, { 45, 0 } }} }};
test.map.updateAnnotation(fill, annotation);
@@ -271,7 +271,7 @@ TEST(Annotations, UpdateFillAnnotationStyle) {
test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
AnnotationID fill = test.map.addAnnotation(annotation);
test::render(test.map);
test::render(test.map, test.view);
annotation.color = { { 0, 255, 0, 1 } };
test.map.updateAnnotation(fill, annotation);
@@ -285,7 +285,7 @@ TEST(Annotations, RemovePoint) {
test.map.addAnnotationIcon("default_marker", namedMarker("default_marker.png"));
AnnotationID point = test.map.addAnnotation(SymbolAnnotation { Point<double> { 0, 0 }, "default_marker" });
test::render(test.map);
test::render(test.map, test.view);
test.map.removeAnnotation(point);
test.checkRendering("remove_point");
@@ -302,7 +302,7 @@ TEST(Annotations, RemoveShape) {
test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
AnnotationID shape = test.map.addAnnotation(annotation);
test::render(test.map);
test::render(test.map, test.view);
test.map.removeAnnotation(shape);
test.checkRendering("remove_shape");
@@ -314,7 +314,7 @@ TEST(Annotations, ImmediateRemoveShape) {
test.map.removeAnnotation(test.map.addAnnotation(LineAnnotation { LineString<double>() }));
test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
test::render(test.map);
test::render(test.map, test.view);
}
TEST(Annotations, SwitchStyle) {
@@ -324,7 +324,7 @@ TEST(Annotations, SwitchStyle) {
test.map.addAnnotationIcon("default_marker", namedMarker("default_marker.png"));
test.map.addAnnotation(SymbolAnnotation { Point<double> { 0, 0 }, "default_marker" });
test::render(test.map);
test::render(test.map, test.view);
test.map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
test.checkRendering("switch_style");
@@ -338,7 +338,7 @@ TEST(Annotations, QueryRenderedFeatures) {
test.map.addAnnotation(SymbolAnnotation { Point<double> { 0, 0 }, "default_marker" });
test.map.addAnnotation(SymbolAnnotation { Point<double> { 0, 50 }, "default_marker" });
test::render(test.map);
test::render(test.map, test.view);
auto features = test.map.queryRenderedFeatures(test.map.pixelForLatLng({ 0, 0 }));
EXPECT_EQ(features.size(), 1u);
@@ -370,7 +370,7 @@ TEST(Annotations, QueryFractionalZoomLevels) {
test.map.setLatLngZoom({ 5, 5 }, 0);
for (uint16_t zoomSteps = 10; zoomSteps <= 20; ++zoomSteps) {
test.map.setZoom(zoomSteps / 10.0);
test::render(test.map);
test::render(test.map, test.view);
auto features = test.map.queryRenderedFeatures(box);
// Filter out repeated features.
@@ -402,7 +402,7 @@ TEST(Annotations, VisibleFeatures) {
// Change bearing *after* adding annotations causes them to be reordered.
test.map.setBearing(45);
test::render(test.map);
test::render(test.map, test.view);
auto features = test.map.queryRenderedFeatures(box);
auto sortID = [](const Feature& lhs, const Feature& rhs) { return lhs.id < rhs.id; };
@@ -413,7 +413,7 @@ TEST(Annotations, VisibleFeatures) {
test.map.setBearing(0);
test.map.setZoom(4);
test::render(test.map);
test::render(test.map, test.view);
features = test.map.queryRenderedFeatures(box);
std::sort(features.begin(), features.end(), sortID);
features.erase(std::unique(features.begin(), features.end(), sameID), features.end());

View File

@@ -4,7 +4,7 @@
#include <mbgl/map/map.hpp>
#include <mbgl/platform/default/headless_backend.hpp>
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/platform/default/offscreen_view.hpp>
#include <mbgl/storage/online_file_source.hpp>
#include <mbgl/platform/default/thread_pool.hpp>
#include <mbgl/util/exception.hpp>
@@ -21,14 +21,13 @@ TEST(API, RenderWithoutCallback) {
util::RunLoop loop;
HeadlessBackend backend;
HeadlessView view;
view.resize(128, 512);
OffscreenView view(backend.getContext(), {{ 128, 512 }});
StubFileSource fileSource;
ThreadPool threadPool(4);
std::unique_ptr<Map> map = std::make_unique<Map>(backend, view, view.getPixelRatio(),
fileSource, threadPool, MapMode::Still);
map->renderStill(nullptr);
std::unique_ptr<Map> map =
std::make_unique<Map>(backend, view.getSize(), 1, fileSource, threadPool, MapMode::Still);
map->renderStill(view, nullptr);
// Force Map thread to join.
map.reset();
@@ -47,15 +46,14 @@ TEST(API, RenderWithoutStyle) {
util::RunLoop loop;
HeadlessBackend backend;
HeadlessView view;
view.resize(128, 512);
OffscreenView view(backend.getContext(), {{ 128, 512 }});
StubFileSource fileSource;
ThreadPool threadPool(4);
Map map(backend, view, view.getPixelRatio(), fileSource, threadPool, MapMode::Still);
Map map(backend, view.getSize(), 1, fileSource, threadPool, MapMode::Still);
std::exception_ptr error;
map.renderStill([&](std::exception_ptr error_, PremultipliedImage&&) {
map.renderStill(view, [&](std::exception_ptr error_) {
error = error_;
loop.stop();
});

View File

@@ -3,7 +3,7 @@
#include <mbgl/gl/gl.hpp>
#include <mbgl/map/map.hpp>
#include <mbgl/platform/default/headless_backend.hpp>
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/platform/default/offscreen_view.hpp>
#include <mbgl/platform/default/thread_pool.hpp>
#include <mbgl/storage/default_file_source.hpp>
#include <mbgl/style/layers/custom_layer.hpp>
@@ -86,7 +86,7 @@ TEST(CustomLayer, Basic) {
util::RunLoop loop;
HeadlessBackend backend;
HeadlessView view;
OffscreenView view(backend.getContext());
#ifdef MBGL_ASSET_ZIP
// Regenerate with `cd test/fixtures/api/ && zip -r assets.zip assets/`
@@ -97,7 +97,7 @@ TEST(CustomLayer, Basic) {
ThreadPool threadPool(4);
Map map(backend, view, view.getPixelRatio(), fileSource, threadPool, MapMode::Still);
Map map(backend, view.getSize(), 1, fileSource, threadPool, MapMode::Still);
map.setStyleJSON(util::read_file("test/fixtures/api/water.json"));
map.setLatLngZoom({ 37.8, -122.5 }, 10);
map.addLayer(std::make_unique<CustomLayer>(
@@ -117,5 +117,5 @@ TEST(CustomLayer, Basic) {
layer->setFillColor(Color{ 1.0, 1.0, 0.0, 1.0 });
map.addLayer(std::move(layer));
test::checkImage("test/fixtures/custom_layer/basic", test::render(map), 0.0006, 0.1);
test::checkImage("test/fixtures/custom_layer/basic", test::render(map, view), 0.0006, 0.1);
}

View File

@@ -1,6 +1,6 @@
#include <mbgl/map/map.hpp>
#include <mbgl/platform/default/headless_backend.hpp>
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/platform/default/offscreen_view.hpp>
#include <mbgl/platform/default/thread_pool.hpp>
#include <mbgl/sprite/sprite_image.hpp>
#include <mbgl/test/stub_file_source.hpp>
@@ -22,15 +22,15 @@ public:
map.setStyleJSON(util::read_file("test/fixtures/api/query_style.json"));
map.addImage("test-icon", std::move(image));
test::render(map);
test::render(map, view);
}
util::RunLoop loop;
HeadlessBackend backend;
HeadlessView view;
OffscreenView view { backend.getContext() };
StubFileSource fileSource;
ThreadPool threadPool { 4 };
Map map { backend, view, view.getPixelRatio(), fileSource, threadPool, MapMode::Still };
Map map { backend, view.getSize(), 1, fileSource, threadPool, MapMode::Still };
};
} // end namespace

View File

@@ -3,7 +3,7 @@
#include <mbgl/map/map.hpp>
#include <mbgl/platform/default/headless_backend.hpp>
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/platform/default/offscreen_view.hpp>
#include <mbgl/platform/default/thread_pool.hpp>
#include <mbgl/storage/default_file_source.hpp>
#include <mbgl/util/image.hpp>
@@ -26,7 +26,7 @@ TEST(API, TEST_REQUIRES_SERVER(RenderMissingTile)) {
const auto style = util::read_file("test/fixtures/api/water_missing_tiles.json");
HeadlessBackend backend;
HeadlessView view(1, 256, 512);
OffscreenView view(backend.getContext(), {{ 256, 512 }});
#ifdef MBGL_ASSET_ZIP
// Regenerate with `cd test/fixtures/api/ && zip -r assets.zip assets/`
DefaultFileSource fileSource(":memory:", "test/fixtures/api/assets.zip");
@@ -38,14 +38,14 @@ TEST(API, TEST_REQUIRES_SERVER(RenderMissingTile)) {
Log::setObserver(std::make_unique<FixtureLogObserver>());
Map map(backend, view, view.getPixelRatio(), fileSource, threadPool, MapMode::Still);
Map map(backend, view.getSize(), 1, fileSource, threadPool, MapMode::Still);
std::string message;
// This host does not respond (== connection error).
// Are you seeing this test fail? Make sure you don't have a server running on port 3001!
map.setStyleJSON(style);
map.renderStill([&](std::exception_ptr err, PremultipliedImage&&) {
map.renderStill(view, [&](std::exception_ptr err) {
ASSERT_TRUE(err.operator bool());
try {
std::rethrow_exception(err);

View File

@@ -3,7 +3,7 @@
#include <mbgl/map/map.hpp>
#include <mbgl/platform/default/headless_backend.hpp>
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/platform/default/offscreen_view.hpp>
#include <mbgl/platform/default/thread_pool.hpp>
#include <mbgl/storage/default_file_source.hpp>
#include <mbgl/util/image.hpp>
@@ -20,7 +20,7 @@ TEST(API, RepeatedRender) {
const auto style = util::read_file("test/fixtures/api/water.json");
HeadlessBackend backend;
HeadlessView view(1, 256, 512);
OffscreenView view(backend.getContext(), {{ 256, 512 }});
#ifdef MBGL_ASSET_ZIP
// Regenerate with `cd test/fixtures/api/ && zip -r assets.zip assets/`
DefaultFileSource fileSource(":memory:", "test/fixtures/api/assets.zip");
@@ -32,13 +32,13 @@ TEST(API, RepeatedRender) {
Log::setObserver(std::make_unique<FixtureLogObserver>());
Map map(backend, view, view.getPixelRatio(), fileSource, threadPool, MapMode::Still);
Map map(backend, view.getSize(), 1, fileSource, threadPool, MapMode::Still);
{
map.setStyleJSON(style);
PremultipliedImage result;
map.renderStill([&result](std::exception_ptr, PremultipliedImage&& image) {
result = std::move(image);
map.renderStill(view, [&](std::exception_ptr) {
result = view.readStillImage();
});
while (!result.size()) {
@@ -55,8 +55,8 @@ TEST(API, RepeatedRender) {
{
map.setStyleJSON(style);
PremultipliedImage result;
map.renderStill([&result](std::exception_ptr, PremultipliedImage&& image) {
result = std::move(image);
map.renderStill(view, [&](std::exception_ptr) {
result = view.readStillImage();
});
while (!result.size()) {

View File

@@ -1,7 +1,7 @@
#include <mbgl/test/util.hpp>
#include <mbgl/platform/default/headless_backend.hpp>
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/platform/default/offscreen_view.hpp>
#include <mbgl/gl/gl.hpp>
#include <mbgl/gl/context.hpp>
@@ -43,11 +43,8 @@ TEST(GLObject, Value) {
auto object = std::make_unique<mbgl::gl::State<MockGLObject>>();
EXPECT_EQ(object->getCurrentValue(), false);
EXPECT_FALSE(object->isDirty());
EXPECT_FALSE(setFlag);
object->setDirty();
EXPECT_TRUE(object->isDirty());
EXPECT_FALSE(setFlag);
*object = false;
EXPECT_EQ(object->getCurrentValue(), false);
@@ -59,16 +56,11 @@ TEST(GLObject, Value) {
EXPECT_EQ(object->getCurrentValue(), true);
EXPECT_FALSE(object->isDirty());
EXPECT_TRUE(setFlag);
object->reset();
EXPECT_EQ(object->getCurrentValue(), false);
EXPECT_FALSE(object->isDirty());
EXPECT_TRUE(setFlag);
}
TEST(GLObject, Store) {
mbgl::HeadlessBackend backend;
mbgl::HeadlessView view;
mbgl::OffscreenView view(backend.getContext());
mbgl::gl::Context context;
EXPECT_TRUE(context.empty());

View File

@@ -5,7 +5,7 @@
#include <mbgl/map/map.hpp>
#include <mbgl/platform/default/headless_backend.hpp>
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/platform/default/offscreen_view.hpp>
#include <mbgl/platform/default/thread_pool.hpp>
#include <mbgl/sprite/sprite_image.hpp>
#include <mbgl/storage/network_status.hpp>
@@ -23,15 +23,14 @@ using namespace std::literals::string_literals;
struct MapTest {
util::RunLoop runLoop;
HeadlessBackend backend;
HeadlessView view;
OffscreenView view { backend.getContext() };
StubFileSource fileSource;
ThreadPool threadPool { 4 };
};
TEST(Map, LatLngBehavior) {
MapTest test;
Map map(test.backend, test.view, test.view.getPixelRatio(), test.fileSource, test.threadPool,
MapMode::Still);
Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still);
map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
@@ -65,12 +64,11 @@ TEST(Map, Offline) {
fileSource.put(Resource::glyphs(prefix + "{fontstack}/{range}.pbf", {{"Helvetica"}}, {0, 255}), expiredItem("glyph.pbf"));
NetworkStatus::Set(NetworkStatus::Status::Offline);
Map map(test.backend, test.view, test.view.getPixelRatio(), fileSource, test.threadPool,
MapMode::Still);
Map map(test.backend, test.view.getSize(), 1, fileSource, test.threadPool, MapMode::Still);
map.setStyleURL(prefix + "style.json");
test::checkImage("test/fixtures/map/offline",
test::render(map),
test::render(map, test.view),
0.0015,
0.1);
@@ -90,8 +88,8 @@ TEST(Map, SetStyleInvalidJSON) {
});
{
Map map(test.backend, test.view, test.view.getPixelRatio(), test.fileSource,
test.threadPool, MapMode::Still);
Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool,
MapMode::Still);
map.setStyleJSON("invalid");
}
@@ -122,8 +120,7 @@ TEST(Map, SetStyleInvalidURL) {
}
});
Map map(test.backend, test.view, test.view.getPixelRatio(), test.fileSource, test.threadPool,
MapMode::Still);
Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still);
map.setStyleURL("mapbox://bar");
test.runLoop.run();
@@ -132,8 +129,7 @@ TEST(Map, SetStyleInvalidURL) {
TEST(Map, DoubleStyleLoad) {
MapTest test;
Map map(test.backend, test.view, test.view.getPixelRatio(), test.fileSource, test.threadPool,
MapMode::Still);
Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still);
map.setStyleJSON("");
map.setStyleJSON("");
}
@@ -144,8 +140,7 @@ TEST(Map, StyleFresh) {
MapTest test;
FakeFileSource fileSource;
Map map(test.backend, test.view, test.view.getPixelRatio(), fileSource, test.threadPool,
MapMode::Still);
Map map(test.backend, test.view.getSize(), 1, fileSource, test.threadPool, MapMode::Still);
map.setStyleURL("mapbox://styles/test");
EXPECT_EQ(1u, fileSource.requests.size());
@@ -165,8 +160,7 @@ TEST(Map, StyleExpired) {
MapTest test;
FakeFileSource fileSource;
Map map(test.backend, test.view, test.view.getPixelRatio(), fileSource, test.threadPool,
MapMode::Still);
Map map(test.backend, test.view.getSize(), 1, fileSource, test.threadPool, MapMode::Still);
map.setStyleURL("mapbox://styles/test");
EXPECT_EQ(1u, fileSource.requests.size());
@@ -193,8 +187,7 @@ TEST(Map, StyleExpiredWithAnnotations) {
MapTest test;
FakeFileSource fileSource;
Map map(test.backend, test.view, test.view.getPixelRatio(), fileSource, test.threadPool,
MapMode::Still);
Map map(test.backend, test.view.getSize(), 1, fileSource, test.threadPool, MapMode::Still);
map.setStyleURL("mapbox://styles/test");
EXPECT_EQ(1u, fileSource.requests.size());
@@ -218,8 +211,7 @@ TEST(Map, StyleEarlyMutation) {
MapTest test;
FakeFileSource fileSource;
Map map(test.backend, test.view, test.view.getPixelRatio(), fileSource, test.threadPool,
MapMode::Still);
Map map(test.backend, test.view.getSize(), 1, fileSource, test.threadPool, MapMode::Still);
map.setStyleURL("mapbox://styles/test");
map.addLayer(std::make_unique<style::BackgroundLayer>("bg"));
@@ -233,8 +225,7 @@ TEST(Map, StyleEarlyMutation) {
TEST(Map, StyleLoadedSignal) {
MapTest test;
Map map(test.backend, test.view, test.view.getPixelRatio(), test.fileSource, test.threadPool,
MapMode::Still);
Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still);
// The map should emit a signal on style loaded
bool emitted = false;
@@ -255,22 +246,20 @@ TEST(Map, StyleLoadedSignal) {
TEST(Map, AddLayer) {
MapTest test;
Map map(test.backend, test.view, test.view.getPixelRatio(), test.fileSource, test.threadPool,
MapMode::Still);
Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still);
map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
auto layer = std::make_unique<BackgroundLayer>("background");
layer->setBackgroundColor({{ 1, 0, 0, 1 }});
layer->setBackgroundColor({ { 1, 0, 0, 1 } });
map.addLayer(std::move(layer));
test::checkImage("test/fixtures/map/add_layer", test::render(map));
test::checkImage("test/fixtures/map/add_layer", test::render(map, test.view));
}
TEST(Map, RemoveLayer) {
MapTest test;
Map map(test.backend, test.view, test.view.getPixelRatio(), test.fileSource, test.threadPool,
MapMode::Still);
Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still);
map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
auto layer = std::make_unique<BackgroundLayer>("background");
@@ -278,7 +267,7 @@ TEST(Map, RemoveLayer) {
map.addLayer(std::move(layer));
map.removeLayer("background");
test::checkImage("test/fixtures/map/remove_layer", test::render(map));
test::checkImage("test/fixtures/map/remove_layer", test::render(map, test.view));
}
TEST(Map, DisabledSources) {
@@ -295,8 +284,7 @@ TEST(Map, DisabledSources) {
return {};
};
Map map(test.backend, test.view, test.view.getPixelRatio(), test.fileSource, test.threadPool,
MapMode::Still);
Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still);
map.setZoom(1);
// This stylesheet has two raster layers, one that starts at zoom 1, the other at zoom 0.
@@ -338,16 +326,15 @@ TEST(Map, DisabledSources) {
}
)STYLE");
test::checkImage("test/fixtures/map/disabled_layers/first", test::render(map));
test::checkImage("test/fixtures/map/disabled_layers/first", test::render(map, test.view));
map.setZoom(0.5);
test::checkImage("test/fixtures/map/disabled_layers/second", test::render(map));
test::checkImage("test/fixtures/map/disabled_layers/second", test::render(map, test.view));
}
TEST(Map, Classes) {
MapTest test;
Map map(test.backend, test.view, test.view.getPixelRatio(), test.fileSource, test.threadPool,
MapMode::Still);
Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still);
map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"));
EXPECT_FALSE(map.getTransitionOptions().duration);
@@ -381,8 +368,7 @@ TEST(Map, Classes) {
TEST(Map, AddImage) {
MapTest test;
Map map(test.backend, test.view, test.view.getPixelRatio(), test.fileSource, test.threadPool,
MapMode::Still);
Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still);
auto decoded1 = decodeImage(util::read_file("test/fixtures/sprites/default_marker.png"));
auto decoded2 = decodeImage(util::read_file("test/fixtures/sprites/default_marker.png"));
auto image1 = std::make_unique<SpriteImage>(std::move(decoded1), 1.0);
@@ -393,20 +379,18 @@ TEST(Map, AddImage) {
map.setStyleJSON(util::read_file("test/fixtures/api/icon_style.json"));
map.addImage("test-icon", std::move(image2));
test::checkImage("test/fixtures/map/add_icon", test::render(map));
test::checkImage("test/fixtures/map/add_icon", test::render(map, test.view));
}
TEST(Map, RemoveImage) {
MapTest test;
Map map(test.backend, test.view, test.view.getPixelRatio(), test.fileSource, test.threadPool,
MapMode::Still);
Map map(test.backend, test.view.getSize(), 1, test.fileSource, test.threadPool, MapMode::Still);
auto decoded = decodeImage(util::read_file("test/fixtures/sprites/default_marker.png"));
auto image = std::make_unique<SpriteImage>(std::move(decoded), 1.0);
map.setStyleJSON(util::read_file("test/fixtures/api/icon_style.json"));
map.addImage("test-icon", std::move(image));
map.removeImage("test-icon");
test::checkImage("test/fixtures/map/remove_icon", test::render(map));
test::checkImage("test/fixtures/map/remove_icon", test::render(map, test.view));
}

View File

@@ -1,6 +1,7 @@
#include <mbgl/test/util.hpp>
#include <mbgl/map/map.hpp>
#include <mbgl/platform/default/offscreen_view.hpp>
#include <mbgl/platform/log.hpp>
#include <mbgl/util/image.hpp>
#include <mbgl/util/io.hpp>
@@ -97,10 +98,10 @@ Server::~Server() {
}
}
PremultipliedImage render(Map& map) {
PremultipliedImage render(Map& map, OffscreenView& view) {
PremultipliedImage result;
map.renderStill([&result](std::exception_ptr, PremultipliedImage&& image) {
result = std::move(image);
map.renderStill(view, [&](std::exception_ptr) {
result = view.readStillImage();
});
while (!result.size()) {

View File

@@ -52,6 +52,7 @@
namespace mbgl {
class Map;
class OffscreenView;
namespace test {
@@ -64,7 +65,7 @@ private:
int fd = -1;
};
PremultipliedImage render(Map&);
PremultipliedImage render(Map&, OffscreenView&);
void checkImage(const std::string& base,
const PremultipliedImage& actual,

View File

@@ -3,7 +3,7 @@
#include <mbgl/map/map.hpp>
#include <mbgl/platform/default/headless_backend.hpp>
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/platform/default/offscreen_view.hpp>
#include <mbgl/platform/default/thread_pool.hpp>
#include <mbgl/util/io.hpp>
#include <mbgl/util/run_loop.hpp>
@@ -57,7 +57,7 @@ public:
util::RunLoop runLoop;
HeadlessBackend backend;
HeadlessView view{ 2 };
OffscreenView view{ backend.getContext(), {{ 512, 512 }} };
StubFileSource fileSource;
ThreadPool threadPool { 4 };
@@ -93,22 +93,20 @@ private:
TEST(Memory, Vector) {
MemoryTest test;
Map map(test.backend, test.view, test.view.getPixelRatio(), test.fileSource, test.threadPool,
MapMode::Still);
Map map(test.backend, { { 256, 256 } }, 2, test.fileSource, test.threadPool, MapMode::Still);
map.setZoom(16); // more map features
map.setStyleURL("mapbox://streets");
test::render(map);
test::render(map, test.view);
}
TEST(Memory, Raster) {
MemoryTest test;
Map map(test.backend, test.view, test.view.getPixelRatio(), test.fileSource, test.threadPool,
MapMode::Still);
Map map(test.backend, { { 256, 256 } }, 2, test.fileSource, test.threadPool, MapMode::Still);
map.setStyleURL("mapbox://satellite");
test::render(map);
test::render(map, test.view);
}
// This test will measure the size of a Map object
@@ -123,19 +121,17 @@ TEST(Memory, Footprint) {
MemoryTest test;
auto renderMap = [](Map* map, const char* style){
map->setZoom(16);
map->setStyleURL(style);
test::render(*map);
auto renderMap = [&](Map& map, const char* style){
map.setZoom(16);
map.setStyleURL(style);
test::render(map, test.view);
};
// Warm up buffers and cache.
for (unsigned i = 0; i < 10; ++i) {
Map map(test.backend, test.view, test.view.getPixelRatio(), test.fileSource,
test.threadPool, MapMode::Still);
renderMap(&map, "mapbox://streets");
renderMap(&map, "mapbox://satellite");
Map map(test.backend, {{ 256, 256 }}, 2, test.fileSource, test.threadPool, MapMode::Still);
renderMap(map, "mapbox://streets");
renderMap(map, "mapbox://satellite");
};
// Process close callbacks, mostly needed by
@@ -147,9 +143,9 @@ TEST(Memory, Footprint) {
long vectorInitialRSS = getRSS();
for (unsigned i = 0; i < runs; ++i) {
auto vector = std::make_unique<Map>(test.backend, test.view, test.view.getPixelRatio(),
test.fileSource, test.threadPool, MapMode::Still);
renderMap(vector.get(), "mapbox://streets");
auto vector = std::make_unique<Map>(test.backend, std::array<uint16_t, 2>{ { 256, 256 } },
2, test.fileSource, test.threadPool, MapMode::Still);
renderMap(*vector, "mapbox://streets");
maps.push_back(std::move(vector));
};
@@ -157,9 +153,9 @@ TEST(Memory, Footprint) {
long rasterInitialRSS = getRSS();
for (unsigned i = 0; i < runs; ++i) {
auto raster = std::make_unique<Map>(test.backend, test.view, test.view.getPixelRatio(),
test.fileSource, test.threadPool, MapMode::Still);
renderMap(raster.get(), "mapbox://satellite");
auto raster = std::make_unique<Map>(test.backend, std::array<uint16_t, 2>{ { 256, 256 } },
2, test.fileSource, test.threadPool, MapMode::Still);
renderMap(*raster, "mapbox://satellite");
maps.push_back(std::move(raster));
};

View File

@@ -2,7 +2,7 @@
#include <mbgl/gl/context.hpp>
#include <mbgl/platform/default/headless_backend.hpp>
#include <mbgl/platform/default/headless_view.hpp>
#include <mbgl/platform/default/offscreen_view.hpp>
#include <mbgl/gl/gl.hpp>
#include <mbgl/util/offscreen_texture.hpp>
@@ -11,7 +11,7 @@ using namespace mbgl;
TEST(OffscreenTexture, EmptyRed) {
HeadlessBackend backend;
HeadlessView view(1.0f, 512, 256);
OffscreenView view(backend.getContext(), {{ 512, 256 }});
view.bind();
MBGL_CHECK_ERROR(glClearColor(1.0f, 0.0f, 0.0f, 1.0f));
@@ -68,10 +68,7 @@ struct Buffer {
TEST(OffscreenTexture, RenderToTexture) {
HeadlessBackend backend;
HeadlessView view(1.0f, 512, 256);
view.bind();
gl::Context context;
context.viewport.setDefaultValue(gl::value::Viewport::Get());
auto& context = backend.getContext();
MBGL_CHECK_ERROR(glEnable(GL_BLEND));
MBGL_CHECK_ERROR(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
@@ -109,14 +106,17 @@ void main() {
// Make sure the texture gets destructed before we call context.reset();
{
OffscreenView view(context, {{ 512, 256 }});
// First, draw red to the bound FBO.
context.clearColor = { 1, 0, 0, 1 };
view.bind();
MBGL_CHECK_ERROR(glClear(GL_COLOR_BUFFER_BIT));
// Then, create a texture, bind it, and render yellow to that texture. This should not
// affect the originally bound FBO.
OffscreenTexture texture;
texture.bind(context, {{ 128, 128 }});
OffscreenTexture texture(context, {{ 128, 128 }});
texture.bind();
context.clearColor = { 0, 0, 0, 0 };
MBGL_CHECK_ERROR(glClear(GL_COLOR_BUFFER_BIT));
@@ -128,12 +128,10 @@ void main() {
glVertexAttribPointer(paintShader.a_pos, 2, GL_FLOAT, GL_FALSE, 0, nullptr));
MBGL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, 3));
auto image = view.readStillImage(texture.getSize());
auto image = texture.readStillImage();
test::checkImage("test/fixtures/offscreen_texture/render-to-texture", image, 0, 0);
// Now reset the FBO back to normal and retrieve the original (restored) framebuffer.
context.resetState();
context.bindFramebuffer.setDirty();
view.bind();
image = view.readStillImage();