diff options
Diffstat (limited to 'platform/default/src/mbgl/gl')
-rw-r--r-- | platform/default/src/mbgl/gl/headless_backend.cpp | 85 | ||||
-rw-r--r-- | platform/default/src/mbgl/gl/headless_backend_osmesa.cpp | 47 | ||||
-rw-r--r-- | platform/default/src/mbgl/gl/headless_frontend.cpp | 144 |
3 files changed, 276 insertions, 0 deletions
diff --git a/platform/default/src/mbgl/gl/headless_backend.cpp b/platform/default/src/mbgl/gl/headless_backend.cpp new file mode 100644 index 0000000000..ba08aecab7 --- /dev/null +++ b/platform/default/src/mbgl/gl/headless_backend.cpp @@ -0,0 +1,85 @@ +#include <mbgl/gl/headless_backend.hpp> +#include <mbgl/gl/context.hpp> +#include <mbgl/renderer/backend_scope.hpp> + +#include <cassert> +#include <stdexcept> +#include <type_traits> + +namespace mbgl { + +class HeadlessBackend::View { +public: + View(gl::Context& context, Size size_) + : color(context.createRenderbuffer<gl::RenderbufferType::RGBA>(size_)), + depthStencil(context.createRenderbuffer<gl::RenderbufferType::DepthStencil>(size_)), + framebuffer(context.createFramebuffer(color, depthStencil)) { + } + + gl::Renderbuffer<gl::RenderbufferType::RGBA> color; + gl::Renderbuffer<gl::RenderbufferType::DepthStencil> depthStencil; + gl::Framebuffer framebuffer; +}; + +HeadlessBackend::HeadlessBackend(Size size_) + : size(size_) { +} + +HeadlessBackend::~HeadlessBackend() { + BackendScope guard { *this }; + view.reset(); + context.reset(); +} + +gl::ProcAddress HeadlessBackend::getExtensionFunctionPointer(const char* name) { + assert(impl); + return impl->getExtensionFunctionPointer(name); +} + +void HeadlessBackend::activate() { + active = true; + + if (!impl) { + createImpl(); + } + + assert(impl); + impl->activateContext(); +} + +void HeadlessBackend::deactivate() { + assert(impl); + impl->deactivateContext(); + active = false; +} + +void HeadlessBackend::bind() { + gl::Context& context_ = getContext(); + + if (!view) { + view = std::make_unique<View>(context_, size); + } + + context_.bindFramebuffer = view->framebuffer.framebuffer; + context_.scissorTest = false; + context_.viewport = { 0, 0, size }; +} + +Size HeadlessBackend::getFramebufferSize() const { + return size; +} + +void HeadlessBackend::updateAssumedState() { + // no-op +} + +void HeadlessBackend::setSize(Size size_) { + size = size_; + view.reset(); +} + +PremultipliedImage HeadlessBackend::readStillImage() { + return getContext().readFramebuffer<PremultipliedImage>(size); +} + +} // namespace mbgl diff --git a/platform/default/src/mbgl/gl/headless_backend_osmesa.cpp b/platform/default/src/mbgl/gl/headless_backend_osmesa.cpp new file mode 100644 index 0000000000..0da1caf9af --- /dev/null +++ b/platform/default/src/mbgl/gl/headless_backend_osmesa.cpp @@ -0,0 +1,47 @@ +#include <mbgl/gl/headless_backend.hpp> +#include <mbgl/util/logging.hpp> + +#include <GL/osmesa.h> + +#include <cassert> + +namespace mbgl { + +class OSMesaBackendImpl : public HeadlessBackend::Impl { +public: + OSMesaBackendImpl() { +#if OSMESA_MAJOR_VERSION * 100 + OSMESA_MINOR_VERSION >= 305 + glContext = OSMesaCreateContextExt(OSMESA_RGBA, 16, 0, 0, nullptr); +#else + glContext = OSMesaCreateContext(OSMESA_RGBA, nullptr); +#endif + if (glContext == nullptr) { + throw std::runtime_error("Error creating GL context object."); + } + } + + ~OSMesaBackendImpl() final { + OSMesaDestroyContext(glContext); + } + + gl::ProcAddress getExtensionFunctionPointer(const char* name) final { + return OSMesaGetProcAddress(name); + } + + void activateContext() final { + if (!OSMesaMakeCurrent(glContext, &fakeBuffer, GL_UNSIGNED_BYTE, 1, 1)) { + throw std::runtime_error("Switching OpenGL context failed.\n"); + } + } + +private: + OSMesaContext glContext = nullptr; + GLubyte fakeBuffer = 0; +}; + +void HeadlessBackend::createImpl() { + assert(!impl); + impl = std::make_unique<OSMesaBackendImpl>(); +} + +} // namespace mbgl diff --git a/platform/default/src/mbgl/gl/headless_frontend.cpp b/platform/default/src/mbgl/gl/headless_frontend.cpp new file mode 100644 index 0000000000..37b0f91f32 --- /dev/null +++ b/platform/default/src/mbgl/gl/headless_frontend.cpp @@ -0,0 +1,144 @@ +#include <mbgl/gl/headless_frontend.hpp> +#include <mbgl/renderer/renderer.hpp> +#include <mbgl/renderer/renderer_state.hpp> +#include <mbgl/renderer/update_parameters.hpp> +#include <mbgl/map/map.hpp> +#include <mbgl/map/transform_state.hpp> +#include <mbgl/util/run_loop.hpp> + +namespace mbgl { + +HeadlessFrontend::HeadlessFrontend(float pixelRatio_, FileSource& fileSource, Scheduler& scheduler, const optional<std::string> programCacheDir, GLContextMode mode, const optional<std::string> localFontFamily) + : HeadlessFrontend({ 256, 256 }, pixelRatio_, fileSource, scheduler, programCacheDir, mode, localFontFamily) { +} + +HeadlessFrontend::HeadlessFrontend(Size size_, float pixelRatio_, FileSource& fileSource, Scheduler& scheduler, const optional<std::string> programCacheDir, GLContextMode mode, const optional<std::string> localFontFamily) + : size(size_), + pixelRatio(pixelRatio_), + backend({ static_cast<uint32_t>(size.width * pixelRatio), + static_cast<uint32_t>(size.height * pixelRatio) }), + asyncInvalidate([this] { + if (renderer && updateParameters) { + mbgl::BackendScope guard { backend }; + renderer->render(*updateParameters); + } + }), + renderer(std::make_unique<Renderer>(backend, pixelRatio, fileSource, scheduler, mode, programCacheDir, localFontFamily)) { +} + +HeadlessFrontend::~HeadlessFrontend() = default; + +void HeadlessFrontend::reset() { + assert(renderer); + renderer.reset(); +} + +void HeadlessFrontend::update(std::shared_ptr<UpdateParameters> updateParameters_) { + updateParameters = updateParameters_; + asyncInvalidate.send(); +} + +void HeadlessFrontend::setObserver(RendererObserver& observer_) { + assert(renderer); + renderer->setObserver(&observer_); +} + +Size HeadlessFrontend::getSize() const { + return size; +} + +Renderer* HeadlessFrontend::getRenderer() { + assert(renderer); + return renderer.get(); +} + +RendererBackend* HeadlessFrontend::getBackend() { + return &backend; +} + +CameraOptions HeadlessFrontend::getCameraOptions() { + if (updateParameters) + return RendererState::getCameraOptions(*updateParameters); + + static CameraOptions nullCamera; + return nullCamera; +} + +bool HeadlessFrontend::hasImage(const std::string& id) { + if (updateParameters) { + return RendererState::hasImage(*updateParameters, id); + } + + return false; +} + +bool HeadlessFrontend::hasLayer(const std::string& id) { + if (updateParameters) { + return RendererState::hasLayer(*updateParameters, id); + } + + return false; +} + +bool HeadlessFrontend::hasSource(const std::string& id) { + if (updateParameters) { + return RendererState::hasSource(*updateParameters, id); + } + + return false; +} +ScreenCoordinate HeadlessFrontend::pixelForLatLng(const LatLng& coordinate) { + if (updateParameters) { + return RendererState::pixelForLatLng(*updateParameters, coordinate); + } + + return ScreenCoordinate {}; +} + +LatLng HeadlessFrontend::latLngForPixel(const ScreenCoordinate& point) { + if (updateParameters) { + return RendererState::latLngForPixel(*updateParameters, point); + } + + return LatLng {}; +} + +void HeadlessFrontend::setSize(Size size_) { + if (size != size_) { + size = size_; + backend.setSize({ static_cast<uint32_t>(size_.width * pixelRatio), + static_cast<uint32_t>(size_.height * pixelRatio) }); + } +} + +PremultipliedImage HeadlessFrontend::readStillImage() { + return backend.readStillImage(); +} + +PremultipliedImage HeadlessFrontend::render(Map& map) { + PremultipliedImage result; + + map.renderStill([&](std::exception_ptr error) { + if (error) { + std::rethrow_exception(error); + } else { + result = backend.readStillImage(); + } + }); + + while (!result.valid()) { + util::RunLoop::Get()->runOnce(); + } + + return result; +} + +optional<TransformState> HeadlessFrontend::getTransformState() const { + if (updateParameters) { + return updateParameters->transformState; + } else { + return {}; + } +} + +} // namespace mbgl |