diff --git a/CMakeLists.txt b/CMakeLists.txt index 9a680480d..a2d6fa1be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -358,6 +358,8 @@ list(APPEND INCLUDE_FILES ${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/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.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/style/layers/fill_extrusion_layer.hpp diff --git a/bazel/core.bzl b/bazel/core.bzl index 54812eccd..bbbc4b7a9 100644 --- a/bazel/core.bzl +++ b/bazel/core.bzl @@ -771,6 +771,8 @@ MLN_CORE_HEADERS = [ "include/mbgl/style/layer.hpp", "include/mbgl/style/layer_properties.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/position.hpp", "include/mbgl/style/property_expression.hpp", diff --git a/cmake/vulkan.cmake b/cmake/vulkan.cmake index 36378817c..889a169ca 100644 --- a/cmake/vulkan.cmake +++ b/cmake/vulkan.cmake @@ -12,6 +12,8 @@ target_compile_definitions( list(APPEND 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/command_encoder.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/vulkan/context.hpp @@ -57,6 +59,7 @@ list(APPEND list(APPEND 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/command_encoder.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/vulkan/context.cpp diff --git a/include/mbgl/style/layers/custom_layer.hpp b/include/mbgl/style/layers/custom_layer.hpp index 2ef7aa39c..bc4b8c9a1 100644 --- a/include/mbgl/style/layers/custom_layer.hpp +++ b/include/mbgl/style/layers/custom_layer.hpp @@ -1,59 +1,14 @@ #pragma once #include -#include +#include -#include #include namespace mbgl { 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 { public: CustomLayer(const std::string& id, std::unique_ptr host); diff --git a/include/mbgl/style/layers/custom_layer_host.hpp b/include/mbgl/style/layers/custom_layer_host.hpp new file mode 100644 index 000000000..32e32f5f8 --- /dev/null +++ b/include/mbgl/style/layers/custom_layer_host.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include +#include + +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 diff --git a/include/mbgl/style/layers/custom_layer_init_parameters.hpp b/include/mbgl/style/layers/custom_layer_init_parameters.hpp new file mode 100644 index 000000000..4fc96f71b --- /dev/null +++ b/include/mbgl/style/layers/custom_layer_init_parameters.hpp @@ -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 diff --git a/include/mbgl/style/layers/vulkan/custom_layer_init_parameters.hpp b/include/mbgl/style/layers/vulkan/custom_layer_init_parameters.hpp new file mode 100644 index 000000000..e5a2c42e3 --- /dev/null +++ b/include/mbgl/style/layers/vulkan/custom_layer_init_parameters.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include + +#include + +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 diff --git a/include/mbgl/style/layers/vulkan/custom_layer_render_parameters.hpp b/include/mbgl/style/layers/vulkan/custom_layer_render_parameters.hpp new file mode 100644 index 000000000..8b5b21a79 --- /dev/null +++ b/include/mbgl/style/layers/vulkan/custom_layer_render_parameters.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include +#include + +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 diff --git a/include/mbgl/vulkan/render_pass.hpp b/include/mbgl/vulkan/render_pass.hpp index e1c037701..48805a6aa 100644 --- a/include/mbgl/vulkan/render_pass.hpp +++ b/include/mbgl/vulkan/render_pass.hpp @@ -18,7 +18,7 @@ public: ~RenderPass() override; vulkan::CommandEncoder& getEncoder() { return commandEncoder; } - const gfx::RenderPassDescriptor& getDescriptor() { return descriptor; } + const gfx::RenderPassDescriptor& getDescriptor() const { return descriptor; } void endEncoding(); void clearStencil(uint32_t value = 0) const; diff --git a/include/mbgl/vulkan/renderable_resource.hpp b/include/mbgl/vulkan/renderable_resource.hpp index 303fb1841..fa5772b94 100644 --- a/include/mbgl/vulkan/renderable_resource.hpp +++ b/include/mbgl/vulkan/renderable_resource.hpp @@ -24,6 +24,7 @@ public: const vk::Extent2D& getExtent() const { return extent; } const vk::UniqueRenderPass& getRenderPass() const { return renderPass; } virtual const vk::UniqueFramebuffer& getFramebuffer() const = 0; + virtual float getRotation() const { return 0.0f; } protected: RendererBackend& backend; @@ -70,7 +71,7 @@ public: bool didSurfaceTransformUpdate() const; // rotation needed to align framebuffer contents with device surface - float getRotation(); + float getRotation() const override; void setSurfaceTransformPollingInterval(int32_t value) { surfaceTransformPollingInterval = value; } int32_t getSurfaceTransformPollingInterval() const { return surfaceTransformPollingInterval; } diff --git a/platform/android/.gitignore b/platform/android/.gitignore index 14c4f869c..17dbdfca6 100644 --- a/platform/android/.gitignore +++ b/platform/android/.gitignore @@ -1 +1,2 @@ MapLibreAndroid/src/vulkanDebug +prefab-headers/ diff --git a/platform/android/MapLibreAndroid/build.gradle.kts b/platform/android/MapLibreAndroid/build.gradle.kts index d5cb9b700..95621aa89 100644 --- a/platform/android/MapLibreAndroid/build.gradle.kts +++ b/platform/android/MapLibreAndroid/build.gradle.kts @@ -177,6 +177,14 @@ android { buildFeatures { buildConfig = true + prefabPublishing = project.findProperty("maplibre.abis") != "none" + } + + prefab { + create("maplibre") { + headers = "../prefab-headers" + libraryName = "libmaplibre" + } } compileOptions { @@ -192,6 +200,25 @@ fun getGitRevision(shortRev: Boolean = true): String { 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 { getByName("implementation") { exclude(group = "commons-logging", module = "commons-logging") diff --git a/platform/android/MapLibreAndroidTestApp/build.gradle.kts b/platform/android/MapLibreAndroidTestApp/build.gradle.kts index 52a764e89..f664ec276 100644 --- a/platform/android/MapLibreAndroidTestApp/build.gradle.kts +++ b/platform/android/MapLibreAndroidTestApp/build.gradle.kts @@ -34,7 +34,7 @@ android { manifestPlaceholders["SENTRY_ENV"] = "" } - nativeBuild(listOf("example-custom-layer")) + nativeBuild(emptyList()) packaging { resources.excludes += listOf("META-INF/LICENSE.txt", "META-INF/NOTICE.txt", "LICENSE.txt") @@ -82,7 +82,8 @@ android { dimension = "renderer" externalNativeBuild { cmake { - arguments("-DMLN_WITH_OPENGL=ON") + arguments += "-DMLN_WITH_OPENGL=ON" + targets += "example-custom-layer" } } } @@ -90,7 +91,8 @@ android { dimension = "renderer" externalNativeBuild { cmake { - arguments("-DMLN_WITH_VULKAN=ON") + arguments += "-DMLN_WITH_VULKAN=ON" + targets += "example-vulkan-custom-layer" } } } diff --git a/platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/customlayer/CustomLayerActivity.kt b/platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/customlayer/CustomLayerActivity.kt index 229f96d9b..53131519d 100644 --- a/platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/customlayer/CustomLayerActivity.kt +++ b/platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/customlayer/CustomLayerActivity.kt @@ -13,8 +13,10 @@ import org.maplibre.android.maps.MapView import org.maplibre.android.maps.MapLibreMap import org.maplibre.android.maps.Style import org.maplibre.android.style.layers.CustomLayer +import org.maplibre.android.testapp.BuildConfig import org.maplibre.android.testapp.R import org.maplibre.android.testapp.model.customlayer.ExampleCustomLayer +import org.maplibre.android.testapp.model.customlayer.ExampleVulkanCustomLayer import org.maplibre.android.testapp.styles.TestStyles /** @@ -29,6 +31,9 @@ class CustomLayerActivity : AppCompatActivity() { private lateinit var mapView: MapView private var customLayer: CustomLayer? = null private lateinit var fab: FloatingActionButton + + private var isVulkan = BuildConfig.FLAVOR.contains("vulkan") + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_custom_layer) @@ -65,7 +70,7 @@ class CustomLayerActivity : AppCompatActivity() { } else { customLayer = CustomLayer( "custom", - ExampleCustomLayer.createContext() + if (!isVulkan) ExampleCustomLayer.createContext() else ExampleVulkanCustomLayer.createContext() ) style!!.addLayerBelow(customLayer!!, "building") fab.setImageResource(R.drawable.ic_layers_clear) @@ -124,18 +129,34 @@ class CustomLayerActivity : AppCompatActivity() { updateLayer() true } + 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 } + 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 } + 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 } + else -> super.onOptionsItemSelected(item) } } diff --git a/platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/model/customlayer/ExampleVulkanCustomLayer.kt b/platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/model/customlayer/ExampleVulkanCustomLayer.kt new file mode 100644 index 000000000..02fb32dbc --- /dev/null +++ b/platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/model/customlayer/ExampleVulkanCustomLayer.kt @@ -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 +} diff --git a/platform/android/android.cmake b/platform/android/android.cmake index 4ae596b3a..e23708666 100644 --- a/platform/android/android.cmake +++ b/platform/android/android.cmake @@ -142,6 +142,27 @@ target_link_libraries( 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( mbgl-test-runner SHARED ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c diff --git a/platform/android/src/example_custom_layer.cpp b/platform/android/src/example_custom_layer.cpp index 7e1b3b198..c00f2a2d1 100644 --- a/platform/android/src/example_custom_layer.cpp +++ b/platform/android/src/example_custom_layer.cpp @@ -131,7 +131,7 @@ class ExampleCustomLayer : mbgl::style::CustomLayerHost { public: ~ExampleCustomLayer() {} - void initialize() { + void initialize(const mbgl::style::CustomLayerInitParameters &) override { __android_log_write(ANDROID_LOG_INFO, LOG_TAG, "Initialize"); // Debug info @@ -163,7 +163,7 @@ public: 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"); glUseProgram(program); glBindBuffer(GL_ARRAY_BUFFER, buffer); @@ -183,12 +183,12 @@ public: GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); } - void contextLost() { + void contextLost() override { __android_log_write(ANDROID_LOG_INFO, LOG_TAG, "ContextLost"); program = 0; } - void deinitialize() { + void deinitialize() override { __android_log_write(ANDROID_LOG_INFO, LOG_TAG, "DeInitialize"); if (program) { glDeleteBuffers(1, &buffer); diff --git a/platform/android/src/example_vulkan_custom_layer.cpp b/platform/android/src/example_vulkan_custom_layer.cpp new file mode 100644 index 000000000..86f44c7bb --- /dev/null +++ b/platform/android/src/example_vulkan_custom_layer.cpp @@ -0,0 +1,450 @@ +#include +#include + +#define VULKAN_HPP_NO_STRUCT_CONSTRUCTORS + +#include +#include +#include + +#include +#include + +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 ¶ms = static_cast(baseParams); + + __android_log_print(ANDROID_LOG_INFO, + VK_LOG_TAG, + "initialize: device=%p, physicalDevice=%p", + static_cast(params.device), + static_cast(params.physicalDevice)); + + vulkanDevice = params.device; + vulkanDispatcher = ¶ms.dispatcher; + + createShaders(vulkanDevice, *vulkanDispatcher); + createPipelineLayout(vulkanDevice, *vulkanDispatcher); + } + + void render(const mbgl::style::CustomLayerRenderParameters &baseParams) override { + const auto ¶ms = static_cast(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 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(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(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 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 dynamicStates = { + vk::DynamicState::eViewport, + vk::DynamicState::eScissor, + }; + const vk::PipelineDynamicStateCreateInfo dynamicState{ + .dynamicStateCount = static_cast(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(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 vertexModule; + vk::UniqueHandle fragmentModule; + + vk::UniqueHandle pipeline; + vk::UniqueHandle pipelineLayout; +}; + +std::array 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(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(&env), JNI_VERSION_1_6); + + jclass cls = env->FindClass("org/maplibre/android/testapp/model/customlayer/ExampleVulkanCustomLayer"); + + const JNINativeMethod methods[] = { + {"createContext", "()J", reinterpret_cast(&nativeCreateContext)}, + {"setColor", "(FFFF)V", reinterpret_cast(&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 *) {} diff --git a/platform/darwin/src/MLNCustomStyleLayer.mm b/platform/darwin/src/MLNCustomStyleLayer.mm index 15cbc0e7f..23d955c73 100644 --- a/platform/darwin/src/MLNCustomStyleLayer.mm +++ b/platform/darwin/src/MLNCustomStyleLayer.mm @@ -88,7 +88,7 @@ public: layer = nil; } - void initialize() { + void initialize(const mbgl::style::CustomLayerInitParameters &) override { if (layerRef == nil) return; else if (layer == nil) @@ -99,7 +99,7 @@ public: } } - void render(const mbgl::style::CustomLayerRenderParameters ¶meters) { + void render(const mbgl::style::CustomLayerRenderParameters ¶meters) override { if (!layer) return; #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.mapView) { diff --git a/src/mbgl/gfx/drawable_custom_layer_host_tweaker.cpp b/src/mbgl/gfx/drawable_custom_layer_host_tweaker.cpp index 1caa8eb9e..43874735f 100644 --- a/src/mbgl/gfx/drawable_custom_layer_host_tweaker.cpp +++ b/src/mbgl/gfx/drawable_custom_layer_host_tweaker.cpp @@ -9,6 +9,9 @@ #if MLN_RENDER_BACKEND_METAL #include #include +#elif MLN_RENDER_BACKEND_VULKAN +#include +#include #endif #include @@ -28,6 +31,8 @@ void DrawableCustomLayerHostTweaker::execute([[maybe_unused]] gfx::Drawable& dra mtlRenderPass->resetState(); style::mtl::CustomLayerRenderParameters parameters(paintParameters); +#elif MLN_RENDER_BACKEND_VULKAN + style::vulkan::CustomLayerRenderParameters parameters(paintParameters); #else style::CustomLayerRenderParameters parameters(paintParameters); #endif diff --git a/src/mbgl/renderer/layers/render_custom_layer.cpp b/src/mbgl/renderer/layers/render_custom_layer.cpp index a01b7566e..b9571259b 100644 --- a/src/mbgl/renderer/layers/render_custom_layer.cpp +++ b/src/mbgl/renderer/layers/render_custom_layer.cpp @@ -13,6 +13,12 @@ #include #include +#if MLN_RENDER_BACKEND_VULKAN +#include +#include +#include +#endif + // TODO: platform agnostic error checks #define MBGL_CHECK_ERROR(cmd) (cmd) @@ -27,13 +33,27 @@ inline const CustomLayer::Impl& impl(const Immutable& impl) return static_cast(*impl); } +void initializeHost(const std::shared_ptr& host, [[maybe_unused]] gfx::Context& context) { +#if MLN_RENDER_BACKEND_VULKAN + { + auto& vkBackend = static_cast(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 RenderCustomLayer::RenderCustomLayer(Immutable _impl) : RenderLayer(makeMutable(std::move(_impl))), host(impl(baseImpl).host) { assert(gfx::BackendScope::exists()); - MBGL_CHECK_ERROR(host->initialize()); } RenderCustomLayer::~RenderCustomLayer() { @@ -86,7 +106,12 @@ void RenderCustomLayer::update([[maybe_unused]] gfx::ShaderRegistry& shaders, MBGL_CHECK_ERROR(host->deinitialize()); } host = impl(baseImpl).host; - MBGL_CHECK_ERROR(host->initialize()); + needsInitialize = true; + } + + if (needsInitialize) { + MBGL_CHECK_ERROR(initializeHost(host, context)); + needsInitialize = false; } // create drawable diff --git a/src/mbgl/renderer/layers/render_custom_layer.hpp b/src/mbgl/renderer/layers/render_custom_layer.hpp index 1fb02133e..684572c1b 100644 --- a/src/mbgl/renderer/layers/render_custom_layer.hpp +++ b/src/mbgl/renderer/layers/render_custom_layer.hpp @@ -27,6 +27,7 @@ private: void prepare(const LayerPrepareParameters&) override; bool contextDestroyed = false; + bool needsInitialize = true; std::shared_ptr host; }; diff --git a/src/mbgl/style/layers/vulkan/custom_layer_render_parameters.cpp b/src/mbgl/style/layers/vulkan/custom_layer_render_parameters.cpp new file mode 100644 index 000000000..cdd10e46a --- /dev/null +++ b/src/mbgl/style/layers/vulkan/custom_layer_render_parameters.cpp @@ -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 as it brings the whole new big +#include +#include +#include +#include +#include +#include + +namespace mbgl { +namespace style { +namespace vulkan { + +CustomLayerRenderParameters::CustomLayerRenderParameters(const mbgl::PaintParameters& paintParameters) + : mbgl::style::CustomLayerRenderParameters(paintParameters), + dispatcher(static_cast(paintParameters.backend).getDispatcher()) { + device = static_cast(paintParameters.backend).getDevice().get(); + auto& vulkanRenderPass = static_cast(*paintParameters.renderPass); + commandBuffer = vulkanRenderPass.getEncoder().getCommandBuffer().get(); + + const auto& renderable = paintParameters.backend.getDefaultRenderable(); + const auto& resource = renderable.getResource(); + renderPass = resource.getRenderPass().get(); + screenPreRotationRadiansClockwise = resource.getRotation(); +} + +} // namespace vulkan +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/vulkan/renderable_resource.cpp b/src/mbgl/vulkan/renderable_resource.cpp index 7acccaf64..c219a64b9 100644 --- a/src/mbgl/vulkan/renderable_resource.cpp +++ b/src/mbgl/vulkan/renderable_resource.cpp @@ -389,7 +389,7 @@ bool SurfaceRenderableResource::didSurfaceTransformUpdate() const { return capabilities.currentTransform != updatedCapabilities.currentTransform; } -float SurfaceRenderableResource::getRotation() { +float SurfaceRenderableResource::getRotation() const { switch (capabilities.currentTransform) { default: case vk::SurfaceTransformFlagBitsKHR::eIdentity: diff --git a/test/api/custom_layer.test.cpp b/test/api/custom_layer.test.cpp index 20cdf3a2b..905b34df1 100644 --- a/test/api/custom_layer.test.cpp +++ b/test/api/custom_layer.test.cpp @@ -42,7 +42,7 @@ void main() { class TestLayer : public mbgl::style::CustomLayerHost { public: - void initialize() override { + void initialize(const mbgl::style::CustomLayerInitParameters&) override { program = MBGL_CHECK_ERROR(glCreateProgram()); vertexShader = MBGL_CHECK_ERROR(glCreateShader(GL_VERTEX_SHADER)); fragmentShader = MBGL_CHECK_ERROR(glCreateShader(GL_FRAGMENT_SHADER));