feat(android): Vulkan graphics backend support for Android custom layers (#4348)

Co-authored-by: Vladyslav Odobesku <VladyslavOdobesku@intellias.com>
Co-authored-by: Claude Opus 4 <noreply@anthropic.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
Vladyslav Odobesku
2026-06-26 19:53:00 +03:00
committed by GitHub
parent 9f6f8e3f1b
commit aa90185d42
25 changed files with 791 additions and 67 deletions

View File

@@ -358,6 +358,8 @@ list(APPEND INCLUDE_FILES
${PROJECT_SOURCE_DIR}/include/mbgl/style/layer.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/style/layer.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/style/layers/background_layer.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/style/layers/background_layer.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/style/layers/circle_layer.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/style/layers/circle_layer.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/style/layers/custom_layer_host.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/style/layers/custom_layer_init_parameters.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/style/layers/custom_layer_render_parameters.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/style/layers/custom_layer_render_parameters.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/style/layers/custom_layer.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/style/layers/custom_layer.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/style/layers/fill_extrusion_layer.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/style/layers/fill_extrusion_layer.hpp

View File

@@ -771,6 +771,8 @@ MLN_CORE_HEADERS = [
"include/mbgl/style/layer.hpp", "include/mbgl/style/layer.hpp",
"include/mbgl/style/layer_properties.hpp", "include/mbgl/style/layer_properties.hpp",
"include/mbgl/style/layers/custom_layer.hpp", "include/mbgl/style/layers/custom_layer.hpp",
"include/mbgl/style/layers/custom_layer_host.hpp",
"include/mbgl/style/layers/custom_layer_init_parameters.hpp",
"include/mbgl/style/layers/custom_layer_render_parameters.hpp", "include/mbgl/style/layers/custom_layer_render_parameters.hpp",
"include/mbgl/style/position.hpp", "include/mbgl/style/position.hpp",
"include/mbgl/style/property_expression.hpp", "include/mbgl/style/property_expression.hpp",

View File

@@ -12,6 +12,8 @@ target_compile_definitions(
list(APPEND list(APPEND
INCLUDE_FILES INCLUDE_FILES
${PROJECT_SOURCE_DIR}/include/mbgl/style/layers/vulkan/custom_layer_init_parameters.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/style/layers/vulkan/custom_layer_render_parameters.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/vulkan/buffer_resource.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/vulkan/buffer_resource.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/vulkan/command_encoder.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/vulkan/command_encoder.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/vulkan/context.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/vulkan/context.hpp
@@ -57,6 +59,7 @@ list(APPEND
list(APPEND list(APPEND
SRC_FILES SRC_FILES
${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/vulkan/custom_layer_render_parameters.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/vulkan/buffer_resource.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/vulkan/buffer_resource.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/vulkan/command_encoder.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/vulkan/command_encoder.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/vulkan/context.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/vulkan/context.cpp

View File

@@ -1,59 +1,14 @@
#pragma once #pragma once
#include <mbgl/style/layer.hpp> #include <mbgl/style/layer.hpp>
#include <mbgl/style/layers/custom_layer_render_parameters.hpp> #include <mbgl/style/layers/custom_layer_host.hpp>
#include <array>
#include <memory> #include <memory>
namespace mbgl { namespace mbgl {
namespace style { namespace style {
class CustomLayerHost {
public:
virtual ~CustomLayerHost() = default;
/**
* Initialize any GL/Metal state needed by the custom layer. This method is called
* once, from the main thread, at a point when the GL context is active but
* before rendering for the first time.
*
* Resources that are acquired in this method must be released in the
* `deinitialize` function.
*/
virtual void initialize() = 0;
/**
* Render the layer. This method is called once per frame. The
* implementation should not make any assumptions about the GL/Metal state (other
* than that the correct context is active). It may make changes to the
* state, and is not required to reset values such as the depth mask,
* stencil mask, and corresponding test flags to their original values. Make
* sure that you are drawing your fragments with a z value of 1 to take
* advantage of the opaque fragment culling in case there are opaque layers
* above your custom layer.
*/
virtual void render(const mbgl::style::CustomLayerRenderParameters&) = 0;
/**
* Called when the system has destroyed the underlying GL/Metal context. The
* `deinitialize` function will not be called in this case, however
* `initialize` will be called instead to prepare for a new render.
*
*/
virtual void contextLost() = 0;
/**
* Destroy any GL/Metal state needed by the custom layer, and deallocate context,
* if necessary. This method is called once, from the main thread, at a
* point when the GL/Metal context is active.
*
* Note that it may be called even when the `initialize` function has not
* been called.
*/
virtual void deinitialize() = 0;
};
class CustomLayer final : public Layer { class CustomLayer final : public Layer {
public: public:
CustomLayer(const std::string& id, std::unique_ptr<CustomLayerHost> host); CustomLayer(const std::string& id, std::unique_ptr<CustomLayerHost> host);

View File

@@ -0,0 +1,59 @@
#pragma once
#include <mbgl/style/layers/custom_layer_init_parameters.hpp>
#include <mbgl/style/layers/custom_layer_render_parameters.hpp>
namespace mbgl {
namespace style {
class CustomLayerHost {
public:
virtual ~CustomLayerHost() = default;
/**
* Initialize any GL/Metal/Vulkan state needed by the custom layer. This method
* is called once, from the main thread, at a point when the graphics context
* is active but before rendering for the first time.
*
* The parameters argument provides backend-specific device handles:
* - Vulkan: cast to mbgl::style::vulkan::CustomLayerInitParameters for
* access to vk::Device, vk::PhysicalDevice, and the dispatcher.
* - Metal/GL: base CustomLayerInitParameters (no extra handles currently).
*
* Resources that are acquired in this method must be released in the
* `deinitialize` function.
*/
virtual void initialize(const CustomLayerInitParameters&) = 0;
/**
* Render the layer. This method is called once per frame. The
* implementation should not make any assumptions about the GL/Metal state (other
* than that the correct context is active). It may make changes to the
* state, and is not required to reset values such as the depth mask,
* stencil mask, and corresponding test flags to their original values. Make
* sure that you are drawing your fragments with a z value of 1 to take
* advantage of the opaque fragment culling in case there are opaque layers
* above your custom layer.
*/
virtual void render(const mbgl::style::CustomLayerRenderParameters&) = 0;
/**
* Called when the system has destroyed the underlying GL/Metal context. The
* `deinitialize` function will not be called in this case, however
* `initialize` will be called instead to prepare for a new render.
*
*/
virtual void contextLost() = 0;
/**
* Destroy any GL/Metal state needed by the custom layer, and deallocate context,
* if necessary. This method is called once, from the main thread, at a
* point when the GL/Metal context is active.
*
* Note that it may be called even when the `initialize` function has not
* been called.
*/
virtual void deinitialize() = 0;
};
} // namespace style
} // namespace mbgl

View File

@@ -0,0 +1,16 @@
#pragma once
namespace mbgl {
namespace style {
/**
* Base parameters passed to CustomLayerHost::initialize().
* Backend-specific subclasses provide device handles needed for
* resource creation (pipelines, buffers, etc.).
*/
struct CustomLayerInitParameters {
virtual ~CustomLayerInitParameters() = default;
};
} // namespace style
} // namespace mbgl

View File

@@ -0,0 +1,39 @@
#pragma once
#include <mbgl/style/layers/custom_layer_init_parameters.hpp>
#include <vulkan/vulkan.hpp>
namespace mbgl {
namespace style {
namespace vulkan {
/**
* Vulkan-specific initialization parameters.
* Provides device handles so that custom layers can create pipelines,
* allocate buffers, and compile shaders during initialize().
*
* Note: vk::RenderPass is NOT available here because it depends on the
* active framebuffer which may not exist yet. Use the render pass from
* CustomLayerRenderParameters during the first render() call if needed
* for pipeline creation.
*/
struct CustomLayerInitParameters : mbgl::style::CustomLayerInitParameters {
/// Dynamic dispatcher for Vulkan function calls.
const vk::detail::DispatchLoaderDynamic& dispatcher;
/// Logical device handle.
vk::Device device;
/// Physical device handle — useful for querying memory properties, limits, etc.
vk::PhysicalDevice physicalDevice;
CustomLayerInitParameters(const vk::detail::DispatchLoaderDynamic& dispatcher_,
vk::Device device_,
vk::PhysicalDevice physicalDevice_)
: dispatcher(dispatcher_),
device(device_),
physicalDevice(physicalDevice_) {}
};
} // namespace vulkan
} // namespace style
} // namespace mbgl

View File

@@ -0,0 +1,48 @@
#pragma once
#include <mbgl/style/layers/custom_layer_render_parameters.hpp>
#include <vulkan/vulkan.hpp>
namespace mbgl {
class PaintParameters;
namespace style {
namespace vulkan {
struct CustomLayerRenderParameters : mbgl::style::CustomLayerRenderParameters {
/// Dynamic dispatcher.
const vk::detail::DispatchLoaderDynamic& dispatcher;
/// Vulkan device
vk::Device device;
/// Compatible render pass handle — use when creating VkPipeline.
vk::RenderPass renderPass;
/// Active command buffer for the current render pass — record all draw calls here.
vk::CommandBuffer commandBuffer;
/// @brief Screen pre-rotation related to VkSurfaceTransformFlagBitsKHR.
/// This field is going to be non 0.0 when renderer decides to do the rotation inside the rendering pipeline instead
/// of relying on compositor. Custom layers must use this parameter to do the rotation in clip space.
///
/// @code{.cpp}
/// if(screenPreRotationRadiansClockwise != 0.0)
/// {
/// glm::vec3 rotation_axis = glm::vec3(0.0f, 0.0f, 1.0f);
/// glm::mat4 pre_rotate_mat = glm::rotate(screenPreRotationRadiansClockwise, rotation_axis);
/// MVP = pre_rotate_mat * MVP;
/// }
/// @endcode
///
/// @note For more info see https://developer.android.com/games/optimize/vulkan-prerotation.
/// @attention dFdx, dFdy in shaders also need adjustments if used.
float screenPreRotationRadiansClockwise = 0.0f;
explicit CustomLayerRenderParameters(const PaintParameters&);
};
} // namespace vulkan
} // namespace style
} // namespace mbgl

View File

@@ -18,7 +18,7 @@ public:
~RenderPass() override; ~RenderPass() override;
vulkan::CommandEncoder& getEncoder() { return commandEncoder; } vulkan::CommandEncoder& getEncoder() { return commandEncoder; }
const gfx::RenderPassDescriptor& getDescriptor() { return descriptor; } const gfx::RenderPassDescriptor& getDescriptor() const { return descriptor; }
void endEncoding(); void endEncoding();
void clearStencil(uint32_t value = 0) const; void clearStencil(uint32_t value = 0) const;

View File

@@ -24,6 +24,7 @@ public:
const vk::Extent2D& getExtent() const { return extent; } const vk::Extent2D& getExtent() const { return extent; }
const vk::UniqueRenderPass& getRenderPass() const { return renderPass; } const vk::UniqueRenderPass& getRenderPass() const { return renderPass; }
virtual const vk::UniqueFramebuffer& getFramebuffer() const = 0; virtual const vk::UniqueFramebuffer& getFramebuffer() const = 0;
virtual float getRotation() const { return 0.0f; }
protected: protected:
RendererBackend& backend; RendererBackend& backend;
@@ -70,7 +71,7 @@ public:
bool didSurfaceTransformUpdate() const; bool didSurfaceTransformUpdate() const;
// rotation needed to align framebuffer contents with device surface // rotation needed to align framebuffer contents with device surface
float getRotation(); float getRotation() const override;
void setSurfaceTransformPollingInterval(int32_t value) { surfaceTransformPollingInterval = value; } void setSurfaceTransformPollingInterval(int32_t value) { surfaceTransformPollingInterval = value; }
int32_t getSurfaceTransformPollingInterval() const { return surfaceTransformPollingInterval; } int32_t getSurfaceTransformPollingInterval() const { return surfaceTransformPollingInterval; }

View File

@@ -1 +1,2 @@
MapLibreAndroid/src/vulkanDebug MapLibreAndroid/src/vulkanDebug
prefab-headers/

View File

@@ -177,6 +177,14 @@ android {
buildFeatures { buildFeatures {
buildConfig = true buildConfig = true
prefabPublishing = project.findProperty("maplibre.abis") != "none"
}
prefab {
create("maplibre") {
headers = "../prefab-headers"
libraryName = "libmaplibre"
}
} }
compileOptions { compileOptions {
@@ -192,6 +200,25 @@ fun getGitRevision(shortRev: Boolean = true): String {
return proc.inputStream.bufferedReader().readText().trim() return proc.inputStream.bufferedReader().readText().trim()
} }
val syncPrefabHeaders by tasks.registering(Sync::class) {
val nativeRoot = rootProject.rootDir.resolve("../..")
from(nativeRoot.resolve("include")) {
include("mbgl/style/layers/custom_layer_host.hpp")
include("mbgl/style/layers/custom_layer_init_parameters.hpp")
include("mbgl/style/layers/custom_layer_render_parameters.hpp")
include("mbgl/style/layers/vulkan/custom_layer_init_parameters.hpp")
include("mbgl/style/layers/vulkan/custom_layer_render_parameters.hpp")
}
into(project.rootDir.resolve("prefab-headers"))
}
tasks.configureEach {
if (name == "syncPrefabHeaders") return@configureEach
if (name.contains("Prefab", ignoreCase = true) || name.contains("bundleLibRuntimeTo", ignoreCase = true)) {
dependsOn(syncPrefabHeaders)
}
}
configurations { configurations {
getByName("implementation") { getByName("implementation") {
exclude(group = "commons-logging", module = "commons-logging") exclude(group = "commons-logging", module = "commons-logging")

View File

@@ -34,7 +34,7 @@ android {
manifestPlaceholders["SENTRY_ENV"] = "" manifestPlaceholders["SENTRY_ENV"] = ""
} }
nativeBuild(listOf("example-custom-layer")) nativeBuild(emptyList())
packaging { packaging {
resources.excludes += listOf("META-INF/LICENSE.txt", "META-INF/NOTICE.txt", "LICENSE.txt") resources.excludes += listOf("META-INF/LICENSE.txt", "META-INF/NOTICE.txt", "LICENSE.txt")
@@ -82,7 +82,8 @@ android {
dimension = "renderer" dimension = "renderer"
externalNativeBuild { externalNativeBuild {
cmake { cmake {
arguments("-DMLN_WITH_OPENGL=ON") arguments += "-DMLN_WITH_OPENGL=ON"
targets += "example-custom-layer"
} }
} }
} }
@@ -90,7 +91,8 @@ android {
dimension = "renderer" dimension = "renderer"
externalNativeBuild { externalNativeBuild {
cmake { cmake {
arguments("-DMLN_WITH_VULKAN=ON") arguments += "-DMLN_WITH_VULKAN=ON"
targets += "example-vulkan-custom-layer"
} }
} }
} }

View File

@@ -13,8 +13,10 @@ import org.maplibre.android.maps.MapView
import org.maplibre.android.maps.MapLibreMap import org.maplibre.android.maps.MapLibreMap
import org.maplibre.android.maps.Style import org.maplibre.android.maps.Style
import org.maplibre.android.style.layers.CustomLayer import org.maplibre.android.style.layers.CustomLayer
import org.maplibre.android.testapp.BuildConfig
import org.maplibre.android.testapp.R import org.maplibre.android.testapp.R
import org.maplibre.android.testapp.model.customlayer.ExampleCustomLayer import org.maplibre.android.testapp.model.customlayer.ExampleCustomLayer
import org.maplibre.android.testapp.model.customlayer.ExampleVulkanCustomLayer
import org.maplibre.android.testapp.styles.TestStyles import org.maplibre.android.testapp.styles.TestStyles
/** /**
@@ -29,6 +31,9 @@ class CustomLayerActivity : AppCompatActivity() {
private lateinit var mapView: MapView private lateinit var mapView: MapView
private var customLayer: CustomLayer? = null private var customLayer: CustomLayer? = null
private lateinit var fab: FloatingActionButton private lateinit var fab: FloatingActionButton
private var isVulkan = BuildConfig.FLAVOR.contains("vulkan")
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_custom_layer) setContentView(R.layout.activity_custom_layer)
@@ -65,7 +70,7 @@ class CustomLayerActivity : AppCompatActivity() {
} else { } else {
customLayer = CustomLayer( customLayer = CustomLayer(
"custom", "custom",
ExampleCustomLayer.createContext() if (!isVulkan) ExampleCustomLayer.createContext() else ExampleVulkanCustomLayer.createContext()
) )
style!!.addLayerBelow(customLayer!!, "building") style!!.addLayerBelow(customLayer!!, "building")
fab.setImageResource(R.drawable.ic_layers_clear) fab.setImageResource(R.drawable.ic_layers_clear)
@@ -124,18 +129,34 @@ class CustomLayerActivity : AppCompatActivity() {
updateLayer() updateLayer()
true true
} }
R.id.action_set_color_red -> { R.id.action_set_color_red -> {
ExampleCustomLayer.setColor(1f, 0f, 0f, 1f) if (!isVulkan) {
ExampleCustomLayer.setColor(1f, 0f, 0f, 1f)
} else {
ExampleVulkanCustomLayer.setColor(1f, 0f, 0f, 1f)
}
true true
} }
R.id.action_set_color_green -> { R.id.action_set_color_green -> {
ExampleCustomLayer.setColor(0f, 1f, 0f, 1f) if (!isVulkan) {
ExampleCustomLayer.setColor(0f, 1f, 0f, 1f)
} else {
ExampleVulkanCustomLayer.setColor(0f, 1f, 0f, 1f)
}
true true
} }
R.id.action_set_color_blue -> { R.id.action_set_color_blue -> {
ExampleCustomLayer.setColor(0f, 0f, 1f, 1f) if (!isVulkan) {
ExampleCustomLayer.setColor(0f, 0f, 1f, 1f)
} else {
ExampleVulkanCustomLayer.setColor(0f, 0f, 1f, 1f)
}
true true
} }
else -> super.onOptionsItemSelected(item) else -> super.onOptionsItemSelected(item)
} }
} }

View File

@@ -0,0 +1,17 @@
package org.maplibre.android.testapp.model.customlayer
import androidx.annotation.Keep
@Keep
object ExampleVulkanCustomLayer {
init {
System.loadLibrary("example-vulkan-custom-layer")
}
external fun createContext(): Long
external fun setColor(red: Float, green: Float, blue: Float, alpha: Float)
var InitializeFunction: Long = 0
var RenderFunction: Long = 0
var ContextLostFunction: Long = 0
var DeinitializeFunction: Long = 0
}

View File

@@ -142,6 +142,27 @@ target_link_libraries(
mbgl-compiler-options mbgl-compiler-options
) )
if(MLN_WITH_VULKAN)
add_library(
example-vulkan-custom-layer MODULE
${PROJECT_SOURCE_DIR}/platform/android/src/example_vulkan_custom_layer.cpp
)
target_include_directories(
example-vulkan-custom-layer
PRIVATE ${PROJECT_SOURCE_DIR}/include
)
target_link_libraries(
example-vulkan-custom-layer
PRIVATE
MapLibreNative::Base
log
mbgl-compiler-options
mbgl-vendor-vulkan-headers
)
endif()
add_library( add_library(
mbgl-test-runner SHARED mbgl-test-runner SHARED
${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c

View File

@@ -131,7 +131,7 @@ class ExampleCustomLayer : mbgl::style::CustomLayerHost {
public: public:
~ExampleCustomLayer() {} ~ExampleCustomLayer() {}
void initialize() { void initialize(const mbgl::style::CustomLayerInitParameters &) override {
__android_log_write(ANDROID_LOG_INFO, LOG_TAG, "Initialize"); __android_log_write(ANDROID_LOG_INFO, LOG_TAG, "Initialize");
// Debug info // Debug info
@@ -163,7 +163,7 @@ public:
GL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, 8 * sizeof(GLfloat), background, GL_STATIC_DRAW)); GL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, 8 * sizeof(GLfloat), background, GL_STATIC_DRAW));
} }
void render(const mbgl::style::CustomLayerRenderParameters &) { void render(const mbgl::style::CustomLayerRenderParameters &) override {
__android_log_write(ANDROID_LOG_INFO, LOG_TAG, "Render"); __android_log_write(ANDROID_LOG_INFO, LOG_TAG, "Render");
glUseProgram(program); glUseProgram(program);
glBindBuffer(GL_ARRAY_BUFFER, buffer); glBindBuffer(GL_ARRAY_BUFFER, buffer);
@@ -183,12 +183,12 @@ public:
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4));
} }
void contextLost() { void contextLost() override {
__android_log_write(ANDROID_LOG_INFO, LOG_TAG, "ContextLost"); __android_log_write(ANDROID_LOG_INFO, LOG_TAG, "ContextLost");
program = 0; program = 0;
} }
void deinitialize() { void deinitialize() override {
__android_log_write(ANDROID_LOG_INFO, LOG_TAG, "DeInitialize"); __android_log_write(ANDROID_LOG_INFO, LOG_TAG, "DeInitialize");
if (program) { if (program) {
glDeleteBuffers(1, &buffer); glDeleteBuffers(1, &buffer);

View File

@@ -0,0 +1,450 @@
#include <android/log.h>
#include <jni.h>
#define VULKAN_HPP_NO_STRUCT_CONSTRUCTORS
#include <mbgl/style/layers/custom_layer_host.hpp>
#include <mbgl/style/layers/vulkan/custom_layer_init_parameters.hpp>
#include <mbgl/style/layers/vulkan/custom_layer_render_parameters.hpp>
#include <array>
#include <vector>
const char *VK_LOG_TAG = "VulkanCustomLayer";
// ---------------------------------------------------------------------------
// Minimal SPIR-V shaders
//
// Vertex shader: generates a full-screen quad from gl_VertexIndex (no VBO).
// index 0 → (-1,-1) index 1 → ( 3,-1) index 2 → (-1, 3)
// This covers the NDC clip space with a single triangle.
//
// Fragment shader: outputs a push-constant RGBA color.
//
// Generated from:
// vert: #version 450
// void main() {
// vec2 pos[3] = vec2[](vec2(-1,-1), vec2(3,-1), vec2(-1,3));
// gl_Position = vec4(pos[gl_VertexIndex], 0.0, 1.0);
// }
// frag: #version 450
// layout(push_constant) uniform PC { vec4 color; } pc;
// layout(location = 0) out vec4 outColor;
// void main() { outColor = pc.color; }
// ---------------------------------------------------------------------------
// clang-format off
static const unsigned char kVertSpirv[] = {
0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0b, 0x00, 0x0d, 0x00,
0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00,
0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00,
0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30,
0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00,
0x18, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00,
0x02, 0x00, 0x00, 0x00, 0xc2, 0x01, 0x00, 0x00, 0x04, 0x00, 0x0a, 0x00,
0x47, 0x4c, 0x5f, 0x47, 0x4f, 0x4f, 0x47, 0x4c, 0x45, 0x5f, 0x63, 0x70,
0x70, 0x5f, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x5f, 0x6c, 0x69, 0x6e, 0x65,
0x5f, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x00, 0x00,
0x04, 0x00, 0x08, 0x00, 0x47, 0x4c, 0x5f, 0x47, 0x4f, 0x4f, 0x47, 0x4c,
0x45, 0x5f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x64, 0x69,
0x72, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x00, 0x05, 0x00, 0x04, 0x00,
0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00,
0x05, 0x00, 0x03, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x70, 0x6f, 0x73, 0x00,
0x05, 0x00, 0x06, 0x00, 0x16, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x50,
0x65, 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x00, 0x00, 0x00, 0x00,
0x06, 0x00, 0x06, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x67, 0x6c, 0x5f, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x00,
0x06, 0x00, 0x07, 0x00, 0x16, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x67, 0x6c, 0x5f, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x53, 0x69, 0x7a, 0x65,
0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x07, 0x00, 0x16, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x43, 0x6c, 0x69, 0x70, 0x44,
0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x00, 0x06, 0x00, 0x07, 0x00,
0x16, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x43,
0x75, 0x6c, 0x6c, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x00,
0x05, 0x00, 0x03, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x05, 0x00, 0x06, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x56,
0x65, 0x72, 0x74, 0x65, 0x78, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x00, 0x00,
0x47, 0x00, 0x03, 0x00, 0x16, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x48, 0x00, 0x05, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00,
0x16, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x16, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x48, 0x00, 0x05, 0x00, 0x16, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x0b, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
0x1c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00,
0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00,
0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00,
0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00,
0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x15, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00,
0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x04, 0x00,
0x0a, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
0x0a, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xbf, 0x2c, 0x00, 0x05, 0x00,
0x07, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
0x0d, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x2c, 0x00, 0x05, 0x00,
0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
0x0d, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00,
0x11, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
0x2c, 0x00, 0x06, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
0x17, 0x00, 0x04, 0x00, 0x13, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00,
0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x04, 0x00,
0x15, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
0x1e, 0x00, 0x06, 0x00, 0x16, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
0x06, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x16, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00,
0x18, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00,
0x19, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x2b, 0x00, 0x04, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x1b, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00,
0x1b, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00,
0x06, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f,
0x20, 0x00, 0x04, 0x00, 0x26, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x13, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0xf8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00,
0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
0x3e, 0x00, 0x03, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
0x3d, 0x00, 0x04, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00,
0x1c, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x1e, 0x00, 0x00, 0x00,
0x1f, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00,
0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x1f, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00,
0x23, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00,
0x13, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
0x24, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x41, 0x00, 0x05, 0x00, 0x26, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00,
0x18, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00,
0x27, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x01, 0x00,
0x38, 0x00, 0x01, 0x00
};
static const unsigned char kFragSpirv[] = {
0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0b, 0x00, 0x0d, 0x00,
0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00,
0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00,
0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30,
0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00,
0x09, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00,
0xc2, 0x01, 0x00, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x47, 0x4c, 0x5f, 0x47,
0x4f, 0x4f, 0x47, 0x4c, 0x45, 0x5f, 0x63, 0x70, 0x70, 0x5f, 0x73, 0x74,
0x79, 0x6c, 0x65, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x64, 0x69, 0x72,
0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x00, 0x00, 0x04, 0x00, 0x08, 0x00,
0x47, 0x4c, 0x5f, 0x47, 0x4f, 0x4f, 0x47, 0x4c, 0x45, 0x5f, 0x69, 0x6e,
0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74,
0x69, 0x76, 0x65, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00,
0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00,
0x09, 0x00, 0x00, 0x00, 0x6f, 0x75, 0x74, 0x43, 0x6f, 0x6c, 0x6f, 0x72,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x0a, 0x00, 0x00, 0x00,
0x50, 0x43, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x00, 0x00, 0x00,
0x05, 0x00, 0x03, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x70, 0x63, 0x00, 0x00,
0x47, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x0a, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00,
0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00,
0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00,
0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00,
0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00,
0x0a, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x0b, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
0x3b, 0x00, 0x04, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
0x09, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00,
0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0xf8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00,
0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
0x0e, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00,
0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00,
0x09, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x01, 0x00,
0x38, 0x00, 0x01, 0x00
};
// clang-format on
// ---------------------------------------------------------------------------
class ExampleVulkanCustomLayer : public mbgl::style::CustomLayerHost {
public:
~ExampleVulkanCustomLayer() = default;
void initialize(const mbgl::style::CustomLayerInitParameters &baseParams) override {
const auto &params = static_cast<const mbgl::style::vulkan::CustomLayerInitParameters &>(baseParams);
__android_log_print(ANDROID_LOG_INFO,
VK_LOG_TAG,
"initialize: device=%p, physicalDevice=%p",
static_cast<VkDevice>(params.device),
static_cast<VkPhysicalDevice>(params.physicalDevice));
vulkanDevice = params.device;
vulkanDispatcher = &params.dispatcher;
createShaders(vulkanDevice, *vulkanDispatcher);
createPipelineLayout(vulkanDevice, *vulkanDispatcher);
}
void render(const mbgl::style::CustomLayerRenderParameters &baseParams) override {
const auto &params = static_cast<const mbgl::style::vulkan::CustomLayerRenderParameters &>(baseParams);
const auto &dispatcher = params.dispatcher;
const auto &cmd = params.commandBuffer;
if (!pipeline) {
createPipeline(params.device, dispatcher, params.renderPass);
}
if (!pipeline) return;
cmd.pushConstants(
*pipelineLayout, vk::ShaderStageFlagBits::eFragment, 0, sizeof(color), color.data(), dispatcher);
cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, *pipeline, dispatcher);
cmd.draw(3, 1, 0, 0, dispatcher);
__android_log_write(ANDROID_LOG_VERBOSE, VK_LOG_TAG, "render");
}
void contextLost() override {
__android_log_write(ANDROID_LOG_INFO, VK_LOG_TAG, "contextLost");
pipeline.reset();
pipelineLayout.reset();
vertexModule.reset();
fragmentModule.reset();
}
void deinitialize() override {
__android_log_write(ANDROID_LOG_INFO, VK_LOG_TAG, "deinitialize");
pipeline.reset();
pipelineLayout.reset();
vertexModule.reset();
fragmentModule.reset();
}
static std::array<float, 4> color;
private:
void createShaders(const vk::Device &device, const vk::detail::DispatchLoaderDynamic &dispatcher) {
try {
vertexModule = device.createShaderModuleUnique(
vk::ShaderModuleCreateInfo{.codeSize = sizeof(kVertSpirv),
.pCode = reinterpret_cast<const std::uint32_t *>(kVertSpirv)},
nullptr,
dispatcher);
} catch (const std::exception &) {
__android_log_write(ANDROID_LOG_ERROR, VK_LOG_TAG, "Failed to create vertex shader module.");
return;
}
try {
fragmentModule = device.createShaderModuleUnique(
vk::ShaderModuleCreateInfo{.codeSize = sizeof(kFragSpirv),
.pCode = reinterpret_cast<const std::uint32_t *>(kFragSpirv)},
nullptr,
dispatcher);
} catch (const std::exception &) {
__android_log_write(ANDROID_LOG_ERROR, VK_LOG_TAG, "Failed to create fragment shader module.");
return;
}
__android_log_write(ANDROID_LOG_INFO, VK_LOG_TAG, "Shader modules created");
}
void createPipelineLayout(const vk::Device &device, const vk::detail::DispatchLoaderDynamic &dispatcher) {
const vk::PushConstantRange pushRange{
.stageFlags = vk::ShaderStageFlagBits::eFragment,
.offset = 0,
.size = sizeof(float) * 4,
};
try {
pipelineLayout = device.createPipelineLayoutUnique(
vk::PipelineLayoutCreateInfo{
.pushConstantRangeCount = 1,
.pPushConstantRanges = &pushRange,
},
nullptr,
dispatcher);
} catch (const std::exception &) {
__android_log_write(ANDROID_LOG_ERROR, VK_LOG_TAG, "Failed to create pipeline layout.");
return;
}
__android_log_write(ANDROID_LOG_INFO, VK_LOG_TAG, "Pipeline layout created");
}
void createPipeline(const vk::Device &device,
const vk::detail::DispatchLoaderDynamic &dispatcher,
vk::RenderPass renderPass) {
if (!vertexModule || !fragmentModule || !pipelineLayout) return;
const std::array<vk::PipelineShaderStageCreateInfo, 2> stages = {{
{
.stage = vk::ShaderStageFlagBits::eVertex,
.module = *vertexModule,
.pName = "main",
},
{
.stage = vk::ShaderStageFlagBits::eFragment,
.module = *fragmentModule,
.pName = "main",
},
}};
const vk::PipelineVertexInputStateCreateInfo vertexInput{};
const vk::PipelineInputAssemblyStateCreateInfo inputAssembly{
.topology = vk::PrimitiveTopology::eTriangleList,
};
const std::array<vk::DynamicState, 2> dynamicStates = {
vk::DynamicState::eViewport,
vk::DynamicState::eScissor,
};
const vk::PipelineDynamicStateCreateInfo dynamicState{
.dynamicStateCount = static_cast<uint32_t>(dynamicStates.size()),
.pDynamicStates = dynamicStates.data(),
};
const vk::PipelineViewportStateCreateInfo viewportState{
.viewportCount = 1,
.scissorCount = 1,
};
const vk::PipelineRasterizationStateCreateInfo rasterizer{
.polygonMode = vk::PolygonMode::eFill,
.cullMode = vk::CullModeFlagBits::eNone,
.frontFace = vk::FrontFace::eClockwise,
.lineWidth = 1.0f,
};
const vk::PipelineMultisampleStateCreateInfo multisampling{
.rasterizationSamples = vk::SampleCountFlagBits::e1,
};
const vk::PipelineColorBlendAttachmentState blendAttachment{
.blendEnable = VK_TRUE,
.srcColorBlendFactor = vk::BlendFactor::eSrcAlpha,
.dstColorBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha,
.colorBlendOp = vk::BlendOp::eAdd,
.srcAlphaBlendFactor = vk::BlendFactor::eOne,
.dstAlphaBlendFactor = vk::BlendFactor::eZero,
.alphaBlendOp = vk::BlendOp::eAdd,
.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG |
vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA,
};
const vk::PipelineColorBlendStateCreateInfo colorBlend{
.attachmentCount = 1,
.pAttachments = &blendAttachment,
};
const vk::PipelineDepthStencilStateCreateInfo depthStencil{
.depthTestEnable = VK_FALSE,
.depthWriteEnable = VK_FALSE,
};
const vk::GraphicsPipelineCreateInfo pipelineInfo{
.stageCount = static_cast<uint32_t>(stages.size()),
.pStages = stages.data(),
.pVertexInputState = &vertexInput,
.pInputAssemblyState = &inputAssembly,
.pViewportState = &viewportState,
.pRasterizationState = &rasterizer,
.pMultisampleState = &multisampling,
.pDepthStencilState = &depthStencil,
.pColorBlendState = &colorBlend,
.pDynamicState = &dynamicState,
.layout = *pipelineLayout,
.renderPass = renderPass,
.subpass = 0,
};
auto result = device.createGraphicsPipelineUnique(nullptr, pipelineInfo, nullptr, dispatcher);
if (result.result != vk::Result::eSuccess) {
__android_log_write(ANDROID_LOG_ERROR, VK_LOG_TAG, "Failed to create graphics pipeline");
return;
}
pipeline = std::move(result.value);
__android_log_write(ANDROID_LOG_INFO, VK_LOG_TAG, "Pipeline created");
}
vk::Device vulkanDevice;
const vk::detail::DispatchLoaderDynamic *vulkanDispatcher = nullptr;
vk::UniqueHandle<vk::ShaderModule, vk::detail::DispatchLoaderDynamic> vertexModule;
vk::UniqueHandle<vk::ShaderModule, vk::detail::DispatchLoaderDynamic> fragmentModule;
vk::UniqueHandle<vk::Pipeline, vk::detail::DispatchLoaderDynamic> pipeline;
vk::UniqueHandle<vk::PipelineLayout, vk::detail::DispatchLoaderDynamic> pipelineLayout;
};
std::array<float, 4> ExampleVulkanCustomLayer::color = {0.0f, 1.0f, 0.0f, 0.5f};
// ---------------------------------------------------------------------------
// JNI entry points
// ---------------------------------------------------------------------------
jlong JNICALL nativeCreateContext(JNIEnv *, jobject) {
__android_log_write(ANDROID_LOG_INFO, VK_LOG_TAG, "nativeCreateContext");
return reinterpret_cast<jlong>(new ExampleVulkanCustomLayer());
}
void JNICALL nativeSetColor(JNIEnv *, jobject, jfloat red, jfloat green, jfloat blue, jfloat alpha) {
__android_log_print(ANDROID_LOG_INFO,
VK_LOG_TAG,
"ExampleVulkanCustomLayer.nativeSetColor: %.2f, %.2f, %.2f, %.2f",
red,
green,
blue,
alpha);
ExampleVulkanCustomLayer::color = {red, green, blue, alpha};
}
extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {
__android_log_write(ANDROID_LOG_INFO, VK_LOG_TAG, "JNI_OnLoad");
JNIEnv *env = nullptr;
vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
jclass cls = env->FindClass("org/maplibre/android/testapp/model/customlayer/ExampleVulkanCustomLayer");
const JNINativeMethod methods[] = {
{"createContext", "()J", reinterpret_cast<void *>(&nativeCreateContext)},
{"setColor", "(FFFF)V", reinterpret_cast<void *>(&nativeSetColor)},
};
if (env->RegisterNatives(cls, methods, 2) < 0) {
env->ExceptionDescribe();
return JNI_ERR;
}
return JNI_VERSION_1_6;
}
extern "C" JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *, void *) {}

View File

@@ -88,7 +88,7 @@ public:
layer = nil; layer = nil;
} }
void initialize() { void initialize(const mbgl::style::CustomLayerInitParameters &) override {
if (layerRef == nil) if (layerRef == nil)
return; return;
else if (layer == nil) else if (layer == nil)
@@ -99,7 +99,7 @@ public:
} }
} }
void render(const mbgl::style::CustomLayerRenderParameters &parameters) { void render(const mbgl::style::CustomLayerRenderParameters &parameters) override {
if (!layer) return; if (!layer) return;
#if MLN_RENDER_BACKEND_METAL #if MLN_RENDER_BACKEND_METAL
@@ -124,9 +124,9 @@ public:
} }
} }
void contextLost() {} void contextLost() override {}
void deinitialize() { void deinitialize() override {
if (layer == nil) return; if (layer == nil) return;
if (layer.mapView) { if (layer.mapView) {

View File

@@ -9,6 +9,9 @@
#if MLN_RENDER_BACKEND_METAL #if MLN_RENDER_BACKEND_METAL
#include <mbgl/style/layers/mtl/custom_layer_render_parameters.hpp> #include <mbgl/style/layers/mtl/custom_layer_render_parameters.hpp>
#include <mbgl/mtl/render_pass.hpp> #include <mbgl/mtl/render_pass.hpp>
#elif MLN_RENDER_BACKEND_VULKAN
#include <mbgl/style/layers/vulkan/custom_layer_render_parameters.hpp>
#include <mbgl/vulkan/render_pass.hpp>
#endif #endif
#include <memory> #include <memory>
@@ -28,6 +31,8 @@ void DrawableCustomLayerHostTweaker::execute([[maybe_unused]] gfx::Drawable& dra
mtlRenderPass->resetState(); mtlRenderPass->resetState();
style::mtl::CustomLayerRenderParameters parameters(paintParameters); style::mtl::CustomLayerRenderParameters parameters(paintParameters);
#elif MLN_RENDER_BACKEND_VULKAN
style::vulkan::CustomLayerRenderParameters parameters(paintParameters);
#else #else
style::CustomLayerRenderParameters parameters(paintParameters); style::CustomLayerRenderParameters parameters(paintParameters);
#endif #endif

View File

@@ -13,6 +13,12 @@
#include <mbgl/gfx/drawable_custom_layer_host_tweaker.hpp> #include <mbgl/gfx/drawable_custom_layer_host_tweaker.hpp>
#include <mbgl/gfx/drawable_builder.hpp> #include <mbgl/gfx/drawable_builder.hpp>
#if MLN_RENDER_BACKEND_VULKAN
#include <mbgl/vulkan/context.hpp>
#include <mbgl/vulkan/renderer_backend.hpp>
#include <mbgl/style/layers/vulkan/custom_layer_init_parameters.hpp>
#endif
// TODO: platform agnostic error checks // TODO: platform agnostic error checks
#define MBGL_CHECK_ERROR(cmd) (cmd) #define MBGL_CHECK_ERROR(cmd) (cmd)
@@ -27,13 +33,27 @@ inline const CustomLayer::Impl& impl(const Immutable<style::Layer::Impl>& impl)
return static_cast<const CustomLayer::Impl&>(*impl); return static_cast<const CustomLayer::Impl&>(*impl);
} }
void initializeHost(const std::shared_ptr<style::CustomLayerHost>& host, [[maybe_unused]] gfx::Context& context) {
#if MLN_RENDER_BACKEND_VULKAN
{
auto& vkBackend = static_cast<mbgl::vulkan::Context&>(context).getBackend();
style::vulkan::CustomLayerInitParameters params(
vkBackend.getDispatcher(), vkBackend.getDevice().get(), vkBackend.getPhysicalDevice());
host->initialize(params);
}
#else
{
style::CustomLayerInitParameters params;
host->initialize(params);
}
#endif
}
} // namespace } // namespace
RenderCustomLayer::RenderCustomLayer(Immutable<style::CustomLayer::Impl> _impl) RenderCustomLayer::RenderCustomLayer(Immutable<style::CustomLayer::Impl> _impl)
: RenderLayer(makeMutable<CustomLayerProperties>(std::move(_impl))), : RenderLayer(makeMutable<CustomLayerProperties>(std::move(_impl))),
host(impl(baseImpl).host) { host(impl(baseImpl).host) {
assert(gfx::BackendScope::exists()); assert(gfx::BackendScope::exists());
MBGL_CHECK_ERROR(host->initialize());
} }
RenderCustomLayer::~RenderCustomLayer() { RenderCustomLayer::~RenderCustomLayer() {
@@ -86,7 +106,12 @@ void RenderCustomLayer::update([[maybe_unused]] gfx::ShaderRegistry& shaders,
MBGL_CHECK_ERROR(host->deinitialize()); MBGL_CHECK_ERROR(host->deinitialize());
} }
host = impl(baseImpl).host; host = impl(baseImpl).host;
MBGL_CHECK_ERROR(host->initialize()); needsInitialize = true;
}
if (needsInitialize) {
MBGL_CHECK_ERROR(initializeHost(host, context));
needsInitialize = false;
} }
// create drawable // create drawable

View File

@@ -27,6 +27,7 @@ private:
void prepare(const LayerPrepareParameters&) override; void prepare(const LayerPrepareParameters&) override;
bool contextDestroyed = false; bool contextDestroyed = false;
bool needsInitialize = true;
std::shared_ptr<style::CustomLayerHost> host; std::shared_ptr<style::CustomLayerHost> host;
}; };

View File

@@ -0,0 +1,29 @@
// Include this before including custom_layer_render_parameters as custom_layer_render_parameters needs to be
// "lightweight". It shouldn't touch the full <mbgl/vulkan/renderer_backend.hpp> as it brings the whole new big
#include <mbgl/vulkan/renderer_backend.hpp>
#include <mbgl/style/layers/vulkan/custom_layer_render_parameters.hpp>
#include <mbgl/renderer/paint_parameters.hpp>
#include <mbgl/vulkan/command_encoder.hpp>
#include <mbgl/vulkan/render_pass.hpp>
#include <mbgl/vulkan/renderable_resource.hpp>
namespace mbgl {
namespace style {
namespace vulkan {
CustomLayerRenderParameters::CustomLayerRenderParameters(const mbgl::PaintParameters& paintParameters)
: mbgl::style::CustomLayerRenderParameters(paintParameters),
dispatcher(static_cast<mbgl::vulkan::RendererBackend&>(paintParameters.backend).getDispatcher()) {
device = static_cast<mbgl::vulkan::RendererBackend&>(paintParameters.backend).getDevice().get();
auto& vulkanRenderPass = static_cast<mbgl::vulkan::RenderPass&>(*paintParameters.renderPass);
commandBuffer = vulkanRenderPass.getEncoder().getCommandBuffer().get();
const auto& renderable = paintParameters.backend.getDefaultRenderable();
const auto& resource = renderable.getResource<mbgl::vulkan::RenderableResource>();
renderPass = resource.getRenderPass().get();
screenPreRotationRadiansClockwise = resource.getRotation();
}
} // namespace vulkan
} // namespace style
} // namespace mbgl

View File

@@ -389,7 +389,7 @@ bool SurfaceRenderableResource::didSurfaceTransformUpdate() const {
return capabilities.currentTransform != updatedCapabilities.currentTransform; return capabilities.currentTransform != updatedCapabilities.currentTransform;
} }
float SurfaceRenderableResource::getRotation() { float SurfaceRenderableResource::getRotation() const {
switch (capabilities.currentTransform) { switch (capabilities.currentTransform) {
default: default:
case vk::SurfaceTransformFlagBitsKHR::eIdentity: case vk::SurfaceTransformFlagBitsKHR::eIdentity:

View File

@@ -42,7 +42,7 @@ void main() {
class TestLayer : public mbgl::style::CustomLayerHost { class TestLayer : public mbgl::style::CustomLayerHost {
public: public:
void initialize() override { void initialize(const mbgl::style::CustomLayerInitParameters&) override {
program = MBGL_CHECK_ERROR(glCreateProgram()); program = MBGL_CHECK_ERROR(glCreateProgram());
vertexShader = MBGL_CHECK_ERROR(glCreateShader(GL_VERTEX_SHADER)); vertexShader = MBGL_CHECK_ERROR(glCreateShader(GL_VERTEX_SHADER));
fragmentShader = MBGL_CHECK_ERROR(glCreateShader(GL_FRAGMENT_SHADER)); fragmentShader = MBGL_CHECK_ERROR(glCreateShader(GL_FRAGMENT_SHADER));