Add Android Vulkan backend (#2711)

Co-authored-by: Bart Louwers <bart.louwers@gmail.com>
This commit is contained in:
Adrian Cojocaru
2024-09-06 17:40:57 +03:00
committed by GitHub
parent 4b5d06d908
commit ea09bdf2b6
79 changed files with 2580 additions and 1400 deletions

View File

@@ -195,6 +195,30 @@ add_library(maplibre SHARED
${PROJECT_SOURCE_DIR}/util/default_style.hpp
${PROJECT_SOURCE_DIR}/util/tile_server_options.hpp)
if(MLN_WITH_OPENGL)
target_sources(
maplibre PRIVATE
${PROJECT_SOURCE_DIR}/android_gl_renderer_backend.cpp
${PROJECT_SOURCE_DIR}/android_gl_renderer_backend.hpp
)
target_compile_definitions(
maplibre
PRIVATE MLN_RENDER_BACKEND_OPENGL=1
)
endif()
if(MLN_WITH_VULKAN)
target_sources(
maplibre PRIVATE
${PROJECT_SOURCE_DIR}/android_vulkan_renderer_backend.cpp
${PROJECT_SOURCE_DIR}/android_vulkan_renderer_backend.hpp
)
target_compile_definitions(
maplibre
PRIVATE MLN_RENDER_BACKEND_VULKAN=1
)
endif()
target_include_directories(
maplibre
PRIVATE ${PROJECT_SOURCE_DIR}

View File

@@ -0,0 +1,84 @@
#include "android_gl_renderer_backend.hpp"
#include <mbgl/gfx/backend_scope.hpp>
#include <mbgl/gl/context.hpp>
#include <mbgl/gl/renderable_resource.hpp>
#include <EGL/egl.h>
#include <cassert>
namespace mbgl {
namespace android {
class AndroidGLRenderableResource final : public mbgl::gl::RenderableResource {
public:
AndroidGLRenderableResource(AndroidGLRendererBackend& backend_)
: backend(backend_) {}
void bind() override {
assert(gfx::BackendScope::exists());
backend.setFramebufferBinding(0);
backend.setViewport(0, 0, backend.getSize());
}
void swap() override {
const auto& swapBehaviour = static_cast<AndroidGLRendererBackend&>(backend).getSwapBehavior();
if (swapBehaviour == gfx::Renderable::SwapBehaviour::Flush) {
static_cast<gl::Context&>(backend.getContext()).finish();
}
}
private:
AndroidGLRendererBackend& backend;
};
AndroidGLRendererBackend::AndroidGLRendererBackend()
: gl::RendererBackend(gfx::ContextMode::Unique),
mbgl::gfx::Renderable({64, 64}, std::make_unique<AndroidGLRenderableResource>(*this)) {}
AndroidGLRendererBackend::~AndroidGLRendererBackend() = default;
gl::ProcAddress AndroidGLRendererBackend::getExtensionFunctionPointer(const char* name) {
assert(gfx::BackendScope::exists());
return eglGetProcAddress(name);
}
void AndroidGLRendererBackend::updateViewPort() {
assert(gfx::BackendScope::exists());
setViewport(0, 0, size);
}
void AndroidGLRendererBackend::resizeFramebuffer(int width, int height) {
size = {static_cast<uint32_t>(width), static_cast<uint32_t>(height)};
}
PremultipliedImage AndroidGLRendererBackend::readFramebuffer() {
assert(gfx::BackendScope::exists());
return gl::RendererBackend::readFramebuffer(size);
}
void AndroidGLRendererBackend::updateAssumedState() {
assumeFramebufferBinding(0);
assumeViewport(0, 0, size);
}
void AndroidGLRendererBackend::markContextLost() {
if (context) {
getContext<gl::Context>().setCleanupOnDestruction(false);
}
}
} // namespace android
} // namespace mbgl
namespace mbgl {
namespace gfx {
template <>
std::unique_ptr<android::AndroidRendererBackend> Backend::Create<mbgl::gfx::Backend::Type::OpenGL>(ANativeWindow*) {
return std::make_unique<android::AndroidGLRendererBackend>();
}
} // namespace gfx
} // namespace mbgl

View File

@@ -0,0 +1,46 @@
#pragma once
#include <mbgl/gfx/renderable.hpp>
#include <mbgl/gl/renderer_backend.hpp>
#include "android_renderer_backend.hpp"
namespace mbgl {
namespace android {
class AndroidGLRendererBackend : public AndroidRendererBackend,
public gl::RendererBackend,
public mbgl::gfx::Renderable {
public:
AndroidGLRendererBackend();
~AndroidGLRendererBackend() override;
mbgl::gfx::RendererBackend& getImpl() override { return *this; }
void updateViewPort() override;
// Ensures the current context is not cleaned up when destroyed
void markContextLost() override;
void resizeFramebuffer(int width, int height) override;
PremultipliedImage readFramebuffer() override;
// mbgl::gfx::RendererBackend implementation
public:
mbgl::gfx::Renderable& getDefaultRenderable() override { return *this; }
protected:
void activate() override {
// no-op
}
void deactivate() override {
// no-op
}
// mbgl::gl::RendererBackend implementation
protected:
mbgl::gl::ProcAddress getExtensionFunctionPointer(const char*) override;
void updateAssumedState() override;
};
} // namespace android
} // namespace mbgl

View File

@@ -1,78 +1,22 @@
#include "android_renderer_backend.hpp"
#include <mbgl/gfx/backend_scope.hpp>
#include <mbgl/gl/context.hpp>
#include <mbgl/gl/renderable_resource.hpp>
#include <EGL/egl.h>
#include <cassert>
namespace mbgl {
namespace android {
class AndroidGLRenderableResource final : public mbgl::gl::RenderableResource {
public:
AndroidGLRenderableResource(AndroidRendererBackend& backend_)
: backend(backend_) {}
void AndroidRendererBackend::updateViewPort() {}
void bind() override {
assert(gfx::BackendScope::exists());
backend.setFramebufferBinding(0);
backend.setViewport(0, 0, backend.getSize());
}
void swap() override { backend.swap(); }
private:
AndroidRendererBackend& backend;
};
AndroidRendererBackend::AndroidRendererBackend(const TaggedScheduler& threadPool)
: gl::RendererBackend(gfx::ContextMode::Unique),
mbgl::gfx::Renderable({64, 64}, std::make_unique<AndroidGLRenderableResource>(*this)) {}
AndroidRendererBackend::~AndroidRendererBackend() = default;
gl::ProcAddress AndroidRendererBackend::getExtensionFunctionPointer(const char* name) {
assert(gfx::BackendScope::exists());
return eglGetProcAddress(name);
}
void AndroidRendererBackend::updateViewPort() {
assert(gfx::BackendScope::exists());
setViewport(0, 0, size);
}
void AndroidRendererBackend::resizeFramebuffer(int width, int height) {
size = {static_cast<uint32_t>(width), static_cast<uint32_t>(height)};
}
void AndroidRendererBackend::resizeFramebuffer(int width, int height) {}
PremultipliedImage AndroidRendererBackend::readFramebuffer() {
assert(gfx::BackendScope::exists());
return gl::RendererBackend::readFramebuffer(size);
return PremultipliedImage();
}
void AndroidRendererBackend::updateAssumedState() {
assumeFramebufferBinding(0);
assumeViewport(0, 0, size);
}
void AndroidRendererBackend::markContextLost() {}
void AndroidRendererBackend::markContextLost() {
if (context) {
getContext<gl::Context>().setCleanupOnDestruction(false);
}
}
void AndroidRendererBackend::setSwapBehavior(SwapBehaviour swapBehaviour_) {
void AndroidRendererBackend::setSwapBehavior(gfx::Renderable::SwapBehaviour swapBehaviour_) {
swapBehaviour = swapBehaviour_;
}
void AndroidRendererBackend::swap() {
if (swapBehaviour == SwapBehaviour::Flush) {
static_cast<gl::Context&>(getContext()).finish();
}
}
} // namespace android
} // namespace mbgl

View File

@@ -1,46 +1,40 @@
#pragma once
#include <mbgl/gfx/backend.hpp>
#include <mbgl/gfx/renderer_backend.hpp>
#include <mbgl/gfx/renderable.hpp>
#include <mbgl/gl/renderer_backend.hpp>
#include <mbgl/util/image.hpp>
#include <android/native_window.h>
namespace mbgl {
namespace android {
class AndroidRendererBackend : public gl::RendererBackend, public mbgl::gfx::Renderable {
class AndroidRendererBackend {
public:
AndroidRendererBackend(const TaggedScheduler& threadPool);
~AndroidRendererBackend() override;
AndroidRendererBackend() = default;
AndroidRendererBackend(const AndroidRendererBackend&) = delete;
AndroidRendererBackend& operator=(const AndroidRendererBackend&) = delete;
virtual ~AndroidRendererBackend() = default;
void updateViewPort();
static std::unique_ptr<AndroidRendererBackend> Create(ANativeWindow* window) {
return mbgl::gfx::Backend::Create<AndroidRendererBackend, ANativeWindow*>(window);
}
virtual mbgl::gfx::RendererBackend& getImpl() = 0;
virtual void updateViewPort();
// Ensures the current context is not cleaned up when destroyed
void markContextLost();
virtual void markContextLost();
void resizeFramebuffer(int width, int height);
PremultipliedImage readFramebuffer();
virtual void resizeFramebuffer(int width, int height);
virtual PremultipliedImage readFramebuffer();
void setSwapBehavior(SwapBehaviour swapBehaviour);
void swap();
private:
SwapBehaviour swapBehaviour = SwapBehaviour::NoFlush;
// mbgl::gfx::RendererBackend implementation
public:
mbgl::gfx::Renderable& getDefaultRenderable() override { return *this; }
gfx::Renderable::SwapBehaviour getSwapBehavior() const { return swapBehaviour; }
virtual void setSwapBehavior(gfx::Renderable::SwapBehaviour swapBehaviour);
protected:
void activate() override {
// no-op
}
void deactivate() override {
// no-op
}
// mbgl::gl::RendererBackend implementation
protected:
mbgl::gl::ProcAddress getExtensionFunctionPointer(const char*) override;
void updateAssumedState() override;
gfx::Renderable::SwapBehaviour swapBehaviour = gfx::Renderable::SwapBehaviour::NoFlush;
};
} // namespace android

View File

@@ -0,0 +1,79 @@
#include "android_vulkan_renderer_backend.hpp"
#include <mbgl/gfx/backend_scope.hpp>
#include <mbgl/vulkan/context.hpp>
#include <mbgl/vulkan/renderable_resource.hpp>
#include <cassert>
#include <vulkan/vulkan_android.h>
namespace mbgl {
namespace android {
class AndroidVulkanRenderableResource final : public mbgl::vulkan::SurfaceRenderableResource {
public:
AndroidVulkanRenderableResource(AndroidVulkanRendererBackend& backend_)
: SurfaceRenderableResource(backend_) {}
std::vector<const char*> getDeviceExtensions() override {
return {
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
};
}
void createPlatformSurface() override {
auto& backendImpl = static_cast<AndroidVulkanRendererBackend&>(backend);
const vk::AndroidSurfaceCreateInfoKHR createInfo({}, backendImpl.getWindow());
surface = backendImpl.getInstance()->createAndroidSurfaceKHRUnique(createInfo);
}
void bind() override {}
void swap() override {
vulkan::SurfaceRenderableResource::swap();
const auto& swapBehaviour = static_cast<AndroidVulkanRendererBackend&>(backend).getSwapBehavior();
if (swapBehaviour == gfx::Renderable::SwapBehaviour::Flush) {
static_cast<vulkan::Context&>(backend.getContext()).waitFrame();
}
}
private:
};
AndroidVulkanRendererBackend::AndroidVulkanRendererBackend(ANativeWindow* window_)
: vulkan::RendererBackend(gfx::ContextMode::Unique),
mbgl::gfx::Renderable({64, 64}, std::make_unique<AndroidVulkanRenderableResource>(*this)),
window(window_) {
init();
}
AndroidVulkanRendererBackend::~AndroidVulkanRendererBackend() = default;
std::vector<const char*> AndroidVulkanRendererBackend::getInstanceExtensions() {
auto extensions = mbgl::vulkan::RendererBackend::getInstanceExtensions();
extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
extensions.push_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME);
return extensions;
}
void AndroidVulkanRendererBackend::resizeFramebuffer(int width, int height) {
size = {static_cast<uint32_t>(width), static_cast<uint32_t>(height)};
if (context) {
static_cast<vulkan::Context&>(*context).requestSurfaceUpdate();
}
}
} // namespace android
} // namespace mbgl
namespace mbgl {
namespace gfx {
template <>
std::unique_ptr<android::AndroidRendererBackend> Backend::Create<mbgl::gfx::Backend::Type::Vulkan>(
ANativeWindow* window) {
return std::make_unique<android::AndroidVulkanRendererBackend>(window);
}
} // namespace gfx
} // namespace mbgl

View File

@@ -0,0 +1,42 @@
#pragma once
#include <mbgl/gfx/renderable.hpp>
#include <mbgl/vulkan/renderer_backend.hpp>
#include "android_renderer_backend.hpp"
#include <android/native_window.h>
namespace mbgl {
namespace android {
class AndroidVulkanRendererBackend : public AndroidRendererBackend,
public vulkan::RendererBackend,
public mbgl::gfx::Renderable {
public:
AndroidVulkanRendererBackend(ANativeWindow*);
~AndroidVulkanRendererBackend() override;
ANativeWindow* getWindow() { return window; }
mbgl::gfx::RendererBackend& getImpl() override { return *this; }
std::vector<const char*> getInstanceExtensions() override;
void resizeFramebuffer(int width, int height) override;
// mbgl::gfx::RendererBackend implementation
public:
mbgl::gfx::Renderable& getDefaultRenderable() override { return *this; }
protected:
void activate() override {
// no-op
}
void deactivate() override {
// no-op
}
protected:
ANativeWindow* window;
};
} // namespace android
} // namespace mbgl

View File

@@ -7,6 +7,7 @@
#include <mbgl/util/run_loop.hpp>
#include <string>
#include <android/native_window_jni.h>
#include "attach_env.hpp"
#include "android_renderer_backend.hpp"
@@ -24,8 +25,7 @@ MapRenderer::MapRenderer(jni::JNIEnv& _env,
localIdeographFontFamily(localIdeographFontFamily_ ? jni::Make<std::string>(_env, localIdeographFontFamily_)
: std::optional<std::string>{}),
threadPool(Scheduler::GetBackground(), {}),
mailboxData(this),
backend(std::make_unique<AndroidRendererBackend>(threadPool)) {}
mailboxData(this) {}
MapRenderer::MailboxData::MailboxData(Scheduler* scheduler_)
: scheduler(scheduler_) {
@@ -163,7 +163,7 @@ void MapRenderer::requestSnapshot(SnapshotCallback callback) {
void MapRenderer::resetRenderer() {
renderer.reset();
backend = std::make_unique<AndroidRendererBackend>(threadPool);
swapBehaviorFlush = false;
}
void MapRenderer::scheduleSnapshot(std::unique_ptr<SnapshotCallback> callback) {
@@ -186,7 +186,7 @@ void MapRenderer::render(JNIEnv&) {
// Activate the backend
assert(backend);
gfx::BackendScope backendGuard{*backend};
gfx::BackendScope backendGuard{backend->getImpl()};
// Ensure that the "current" scheduler on the render thread is
// this scheduler.
@@ -206,14 +206,10 @@ void MapRenderer::render(JNIEnv&) {
}
}
void MapRenderer::onSurfaceCreated(JNIEnv&) {
void MapRenderer::onSurfaceCreated(JNIEnv& env, const jni::Object<AndroidSurface>& surface) {
// Lock as the initialization can come from the main thread or the GL thread first
std::lock_guard<std::mutex> lock(initialisationMutex);
// The GL context is already active if get a new surface.
assert(backend);
gfx::BackendScope backendGuard{*backend, gfx::BackendScope::ScopeType::Implicit};
// The android system will have already destroyed the underlying
// GL resources if this is not the first initialization and an
// attempt to clean them up will fail
@@ -223,12 +219,22 @@ void MapRenderer::onSurfaceCreated(JNIEnv&) {
// Reset in opposite order
renderer.reset();
backend.reset();
window.reset();
if (surface) {
window = std::unique_ptr<ANativeWindow, std::function<void(ANativeWindow*)>>(
ANativeWindow_fromSurface(&env, reinterpret_cast<jobject>(surface.get())),
[](ANativeWindow* window_) { ANativeWindow_release(window_); });
}
// Create the new backend and renderer
backend = std::make_unique<AndroidRendererBackend>(threadPool);
renderer = std::make_unique<Renderer>(*backend, pixelRatio, localIdeographFontFamily);
backend = AndroidRendererBackend::Create(window.get());
renderer = std::make_unique<Renderer>(backend->getImpl(), pixelRatio, localIdeographFontFamily);
rendererRef = std::make_unique<ActorRef<Renderer>>(*renderer, mailboxData.getMailbox());
backend->setSwapBehavior(swapBehaviorFlush ? gfx::Renderable::SwapBehaviour::Flush
: gfx::Renderable::SwapBehaviour::NoFlush);
// Set the observer on the new Renderer implementation
if (rendererObserver) {
renderer->setObserver(rendererObserver.get());
@@ -238,7 +244,8 @@ void MapRenderer::onSurfaceCreated(JNIEnv&) {
void MapRenderer::onSurfaceChanged(JNIEnv& env, jint width, jint height) {
if (!renderer) {
// In case the surface has been destroyed (due to app back-grounding)
onSurfaceCreated(env);
jni::jobject* nullObj = nullptr;
onSurfaceCreated(env, jni::Object<AndroidSurface>(nullObj));
}
backend->resizeFramebuffer(width, height);
@@ -255,10 +262,16 @@ void MapRenderer::onRendererReset(JNIEnv&) {
// needs to be called on GL thread
void MapRenderer::onSurfaceDestroyed(JNIEnv&) {
resetRenderer();
window.reset();
}
void MapRenderer::setSwapBehaviorFlush(JNIEnv&, jboolean flush) {
backend->setSwapBehavior(flush ? gfx::Renderable::SwapBehaviour::Flush : gfx::Renderable::SwapBehaviour::NoFlush);
swapBehaviorFlush = flush;
if (backend) {
backend->setSwapBehavior(flush ? gfx::Renderable::SwapBehaviour::Flush
: gfx::Renderable::SwapBehaviour::NoFlush);
}
}
// Static methods //

View File

@@ -10,6 +10,7 @@
#include <optional>
#include <jni/jni.hpp>
#include <android/native_window.h>
namespace mbgl {
@@ -26,6 +27,11 @@ namespace android {
class AndroidRendererBackend;
class AndroidSurface {
public:
static constexpr auto Name() { return "android/view/Surface"; };
};
/**
* The MapRenderer is a peer class that encapsulates the actions
* performed on the GL Thread.
@@ -105,7 +111,7 @@ private:
// Renders a frame.
void render(JNIEnv&);
void onSurfaceCreated(JNIEnv&);
void onSurfaceCreated(JNIEnv&, const jni::Object<AndroidSurface>& surface);
void onSurfaceChanged(JNIEnv&, jint width, jint height);
@@ -132,12 +138,14 @@ private:
std::unique_ptr<AndroidRendererBackend> backend;
std::unique_ptr<Renderer> renderer;
std::unique_ptr<ActorRef<Renderer>> rendererRef;
std::unique_ptr<ANativeWindow, std::function<void(ANativeWindow*)>> window;
std::shared_ptr<UpdateParameters> updateParameters;
std::mutex updateMutex;
bool framebufferSizeChanged = false;
std::atomic<bool> destroyed{false};
bool swapBehaviorFlush{true};
std::unique_ptr<SnapshotCallback> snapshotCallback;
mapbox::base::WeakPtrFactory<Scheduler> weakFactory{this};