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

Commit

Permalink
coalesce buffer deletions and run them in bulk during rendering
Browse files Browse the repository at this point in the history
On iOS, the main thread does framebuffer operations just before it requests a rerender of the view contents. This means that GL operations on the Map thread will interfere with that and cause a crash. While most GL operations happen throughout rendering (which is synchronized with the main thread), deletion of shared_ptrs release in turn their OpenGL objects. This may happen at any time and isn't synced with rendering.

This patch changes this so instead of deleting all objects immediately, we are collecting all abandoned IDs and delete them in bulk once the main thread has given us permission to use OpenGL calls (i.e. during render()).
  • Loading branch information
kkaefer committed Mar 20, 2015
1 parent 8fb1bbc commit 61fa436
Show file tree
Hide file tree
Showing 10 changed files with 127 additions and 36 deletions.
10 changes: 6 additions & 4 deletions include/mbgl/map/map.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class GlyphAtlas;
class SpriteAtlas;
class LineAtlas;
class Environment;
class EnvironmentScope;
class AnnotationManager;

class Map : private util::noncopyable {
Expand Down Expand Up @@ -192,6 +193,7 @@ class Map : private util::noncopyable {
Mode mode = Mode::None;

const std::unique_ptr<Environment> env;
std::unique_ptr<EnvironmentScope> scope;
View &view;

private:
Expand Down Expand Up @@ -223,13 +225,13 @@ class Map : private util::noncopyable {
FileSource& fileSource;

util::ptr<Style> style;
const std::unique_ptr<GlyphAtlas> glyphAtlas;
std::unique_ptr<GlyphAtlas> glyphAtlas;
util::ptr<GlyphStore> glyphStore;
const std::unique_ptr<SpriteAtlas> spriteAtlas;
std::unique_ptr<SpriteAtlas> spriteAtlas;
util::ptr<Sprite> sprite;
const std::unique_ptr<LineAtlas> lineAtlas;
std::unique_ptr<LineAtlas> lineAtlas;
util::ptr<TexturePool> texturePool;
const std::unique_ptr<Painter> painter;
std::unique_ptr<Painter> painter;
util::ptr<AnnotationManager> annotationManager;

std::string styleURL;
Expand Down
3 changes: 2 additions & 1 deletion src/mbgl/geometry/buffer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include <mbgl/platform/gl.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/map/environment.hpp>

#include <cstdlib>
#include <cassert>
Expand All @@ -21,7 +22,7 @@ class Buffer : private util::noncopyable {
~Buffer() {
cleanup();
if (buffer != 0) {
MBGL_CHECK_ERROR(glDeleteBuffers(1, &buffer));
Environment::Get().abandonBuffer(buffer);
buffer = 0;
}
}
Expand Down
6 changes: 4 additions & 2 deletions src/mbgl/geometry/line_atlas.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include <mbgl/map/environment.hpp>
#include <mbgl/geometry/line_atlas.hpp>
#include <mbgl/platform/gl.hpp>
#include <mbgl/platform/log.hpp>
Expand All @@ -18,9 +19,10 @@ LineAtlas::LineAtlas(uint16_t w, uint16_t h)
LineAtlas::~LineAtlas() {
std::lock_guard<std::recursive_mutex> lock(mtx);

MBGL_CHECK_ERROR(glDeleteTextures(1, &texture));
delete[] data;
Environment::Get().abandonTexture(texture);
texture = 0;

delete[] data;
}

LinePatternPos LineAtlas::getDashPosition(const std::vector<float> &dasharray, bool round) {
Expand Down
4 changes: 2 additions & 2 deletions src/mbgl/geometry/sprite_atlas.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include <mbgl/map/environment.hpp>
#include <mbgl/geometry/sprite_atlas.hpp>
#include <mbgl/platform/gl.hpp>
#include <mbgl/platform/log.hpp>
Expand Down Expand Up @@ -293,8 +294,7 @@ void SpriteAtlas::bind(bool linear) {

SpriteAtlas::~SpriteAtlas() {
std::lock_guard<std::recursive_mutex> lock(mtx);

MBGL_CHECK_ERROR(glDeleteTextures(1, &texture));
Environment::Get().abandonTexture(texture);
texture = 0;
::operator delete(data), data = nullptr;
}
3 changes: 2 additions & 1 deletion src/mbgl/geometry/vao.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include <mbgl/geometry/vao.hpp>
#include <mbgl/platform/log.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/map/environment.hpp>

namespace mbgl {

Expand All @@ -11,7 +12,7 @@ VertexArrayObject::~VertexArrayObject() {
if (!gl::DeleteVertexArrays) return;

if (vao) {
MBGL_CHECK_ERROR(gl::DeleteVertexArrays(1, &vao));
Environment::Get().abandonVAO(vao);
}
}

Expand Down
55 changes: 53 additions & 2 deletions src/mbgl/map/environment.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <mbgl/map/environment.hpp>
#include <mbgl/storage/file_source.hpp>
#include <mbgl/platform/gl.hpp>

#include <uv.h>

Expand Down Expand Up @@ -76,12 +77,12 @@ ThreadInfoStore threadInfoStore;

} // namespace

Environment::Scope::Scope(Environment& env, ThreadType type, const std::string& name)
EnvironmentScope::EnvironmentScope(Environment& env, ThreadType type, const std::string& name)
: id(std::this_thread::get_id()) {
threadInfoStore.registerThread(&env, type, name);
}

Environment::Scope::~Scope() {
EnvironmentScope::~EnvironmentScope() {
assert(id == std::this_thread::get_id());
threadInfoStore.unregisterThread();
}
Expand All @@ -90,6 +91,12 @@ Environment::Environment(FileSource& fs)
: id(makeEnvironmentID()), fileSource(fs), loop(uv_loop_new()) {
}

Environment::~Environment() {
assert(abandonedVAOs.empty());
assert(abandonedTextures.empty());
assert(abandonedBuffers.empty());
}

Environment& Environment::Get() {
Environment* env = threadInfoStore.getThreadInfo().env;
assert(env);
Expand Down Expand Up @@ -129,6 +136,50 @@ void Environment::cancelRequest(Request* req) {
fileSource.cancel(req);
}

// #############################################################################################

#pragma mark - OpenGL cleanup

void Environment::abandonVAO(uint32_t vao) {
assert(currentlyOn(ThreadType::Map));
abandonedVAOs.emplace_back(vao);
}

void Environment::abandonBuffer(uint32_t buffer) {
assert(currentlyOn(ThreadType::Map));
abandonedBuffers.emplace_back(buffer);
}

void Environment::abandonTexture(uint32_t texture) {
assert(currentlyOn(ThreadType::Map));
abandonedTextures.emplace_back(texture);
}

// Actually remove the objects we marked as abandoned with the above methods.
void Environment::performCleanup() {
assert(currentlyOn(ThreadType::Map));

if (!abandonedVAOs.empty()) {
MBGL_CHECK_ERROR(gl::DeleteVertexArrays(static_cast<GLsizei>(abandonedVAOs.size()),
abandonedVAOs.data()));
abandonedVAOs.clear();
}

if (!abandonedTextures.empty()) {
MBGL_CHECK_ERROR(glDeleteTextures(static_cast<GLsizei>(abandonedTextures.size()),
abandonedTextures.data()));
abandonedTextures.clear();
}

if (!abandonedBuffers.empty()) {
MBGL_CHECK_ERROR(glDeleteBuffers(static_cast<GLsizei>(abandonedBuffers.size()),
abandonedBuffers.data()));
abandonedBuffers.clear();
}
}

// #############################################################################################

void Environment::terminate() {
fileSource.abort(*this);
}
Expand Down
42 changes: 33 additions & 9 deletions src/mbgl/map/environment.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include <thread>
#include <functional>
#include <vector>

typedef struct uv_loop_s uv_loop_t;

Expand All @@ -25,38 +26,61 @@ enum class ThreadType : uint8_t {

class Environment final : private util::noncopyable {
public:
class Scope final {
public:
Scope(Environment&, ThreadType, const std::string& name);
~Scope();

private:
std::thread::id id;
};

Environment(FileSource&);
~Environment();

static Environment& Get();
static bool inScope();
static bool currentlyOn(ThreadType);
static std::string threadName();

unsigned getID() const;

// #############################################################################################

// File request APIs
void requestAsync(const Resource&, std::function<void(const Response&)>);
Request* request(const Resource&, std::function<void(const Response&)>);
void cancelRequest(Request*);

// #############################################################################################

// Mark OpenGL objects for deletion
void abandonVAO(uint32_t vao);
void abandonBuffer(uint32_t buffer);
void abandonTexture(uint32_t texture);

// Actually remove the objects we marked as abandoned with the above methods.
// Only call this while the OpenGL context is exclusive to this thread.
void performCleanup();

// #############################################################################################

// Request to terminate the environment.
void terminate();

private:
unsigned id;
FileSource& fileSource;

// Stores OpenGL objects that we marked for deletion
std::vector<uint32_t> abandonedVAOs;
std::vector<uint32_t> abandonedBuffers;
std::vector<uint32_t> abandonedTextures;

public:
uv_loop_t* const loop;
};

class EnvironmentScope final {
public:
EnvironmentScope(Environment&, ThreadType, const std::string& name);
~EnvironmentScope();

private:
std::thread::id id;
};

}

#endif
23 changes: 20 additions & 3 deletions src/mbgl/map/map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ using namespace mbgl;

Map::Map(View& view_, FileSource& fileSource_)
: env(util::make_unique<Environment>(fileSource_)),
scope(util::make_unique<EnvironmentScope>(*env, ThreadType::Main, "Main")),
view(view_),
transform(view_),
fileSource(fileSource_),
Expand All @@ -80,15 +81,28 @@ Map::~Map() {
stop();
}

// Extend the scope to include both Main and Map thread types to ease cleanup.
scope.reset();
scope = util::make_unique<EnvironmentScope>(
*env, static_cast<ThreadType>(static_cast<uint8_t>(ThreadType::Main) |
static_cast<uint8_t>(ThreadType::Map)),
"MapandMain");

// Explicitly reset all pointers.
activeSources.clear();
sprite.reset();
glyphStore.reset();
style.reset();
texturePool.reset();
workers.reset();
painter.reset();
annotationManager.reset();
lineAtlas.reset();
spriteAtlas.reset();
glyphAtlas.reset();

uv_run(env->loop, UV_RUN_DEFAULT);

env->performCleanup();
}

uv::worker &Map::getWorker() {
Expand All @@ -113,7 +127,6 @@ void Map::start(bool startPaused) {

// Remove all of these to make sure they are destructed in the correct thread.
style.reset();
workers.reset();
activeSources.clear();

terminating = true;
Expand Down Expand Up @@ -233,7 +246,7 @@ void Map::run() {
threadName += "andMain";
}

Environment::Scope scope(*env, threadType, threadName);
EnvironmentScope mapScope(*env, threadType, threadName);

if (mode == Mode::Continuous) {
checkForPause();
Expand Down Expand Up @@ -749,6 +762,10 @@ void Map::prepare() {

void Map::render() {
assert(Environment::currentlyOn(ThreadType::Map));

// Cleanup OpenGL objects that we abandoned since the last render call.
env->performCleanup();

assert(painter);
painter->render(*style, activeSources,
state, animationTime);
Expand Down
2 changes: 1 addition & 1 deletion src/mbgl/map/tile_data.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ void TileData::reparse(uv::worker& worker, std::function<void()> callback)
new uv::work<util::ptr<TileData>>(
worker,
[this](util::ptr<TileData>& tile) {
Environment::Scope scope(env, ThreadType::TileWorker, "TileWorker_" + tile->name);
EnvironmentScope scope(env, ThreadType::TileWorker, "TileWorker_" + tile->name);
tile->parse();
},
[callback](util::ptr<TileData>&) {
Expand Down
15 changes: 4 additions & 11 deletions src/mbgl/util/texture_pool.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include <mbgl/util/texture_pool.hpp>
#include <mbgl/map/environment.hpp>

#include <vector>

Expand Down Expand Up @@ -42,17 +43,9 @@ void TexturePool::removeTextureID(GLuint texture_id) {
}

void TexturePool::clearTextureIDs() {
std::vector<GLuint> ids_to_remove;
ids_to_remove.reserve(texture_ids.size());

for (std::set<GLuint>::iterator id_iterator = texture_ids.begin();
id_iterator != texture_ids.end(); ++id_iterator) {
ids_to_remove.push_back(*id_iterator);
}

if (!ids_to_remove.empty()) {
MBGL_CHECK_ERROR(glDeleteTextures((GLsizei)ids_to_remove.size(), &ids_to_remove[0]));
auto& env = Environment::Get();
for (auto texture : texture_ids) {
env.abandonTexture(texture);
}

texture_ids.clear();
}

0 comments on commit 61fa436

Please sign in to comment.