Skip to content

Commit e1cbae8

Browse files
alexey-romanovgithub-actions[bot]
authored andcommitted
support Vulkan backend for Maps SDK Android
GitOrigin-RevId: cef65f0901a91bacc9b5745136d01d852f935307
1 parent 12be16e commit e1cbae8

File tree

10 files changed

+184
-31
lines changed

10 files changed

+184
-31
lines changed

maps-sdk/src/main/java/com/mapbox/maps/MapController.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ internal class MapController : MapPluginProviderDelegate, MapControllable {
300300

301301
@OptIn(MapboxExperimental::class)
302302
override fun removeWidget(widget: Widget): Boolean {
303-
val wasRemoved = renderer.renderThread.removeWidget(widget)
303+
val wasRemoved = renderer.renderThread.removeWidget(widget) ?: false
304304
if (wasRemoved) {
305305
renderer.scheduleRepaint()
306306
}

maps-sdk/src/main/java/com/mapbox/maps/MapSurface.kt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,13 @@ class MapSurface : MapPluginProviderDelegate, MapControllable {
5151
this.context = context
5252
this.surface = surface
5353
this.mapInitOptions = mapInitOptions
54+
if (Map.getSupportedRenderBackend() == RenderBackendType.VULKAN) {
55+
throw RuntimeException("Vulkan is not supported yet on MapSurface")
56+
}
5457
this.renderer = MapboxSurfaceRenderer(
55-
mapInitOptions.antialiasingSampleCount,
56-
mapInitOptions.mapOptions.contextMode ?: ContextMode.UNIQUE,
57-
mapInitOptions.mapName
58+
antialiasingSampleCount = mapInitOptions.antialiasingSampleCount,
59+
contextMode = mapInitOptions.mapOptions.contextMode ?: ContextMode.UNIQUE,
60+
mapName = mapInitOptions.mapName,
5861
)
5962
this.mapController = MapController(renderer, mapInitOptions).apply {
6063
initializePlugins(mapInitOptions)

maps-sdk/src/main/java/com/mapbox/maps/MapView.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,14 +133,14 @@ open class MapView : FrameLayout, MapPluginProviderDelegate, MapControllable {
133133
view.holder,
134134
resolvedMapInitOptions.antialiasingSampleCount,
135135
contextMode,
136-
resolvedMapInitOptions.mapName
136+
resolvedMapInitOptions.mapName,
137137
)
138138

139139
is TextureView -> MapboxTextureViewRenderer(
140140
view,
141141
resolvedMapInitOptions.antialiasingSampleCount,
142142
contextMode,
143-
resolvedMapInitOptions.mapName
143+
resolvedMapInitOptions.mapName,
144144
)
145145

146146
else -> throw IllegalArgumentException("Provided view has to be a texture or a surface.")

maps-sdk/src/main/java/com/mapbox/maps/NativeMapImpl.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ internal class NativeMapImpl(val map: Map) {
7676
map.render()
7777
}
7878

79+
fun getVulkanManager(): IVulkanManager {
80+
return map.createVulkanManager()
81+
}
82+
7983
@OptIn(com.mapbox.annotation.MapboxExperimental::class)
8084
@MapboxExperimental
8185
fun resetThreadServiceType() {

maps-sdk/src/main/java/com/mapbox/maps/renderer/GLMapboxRenderThread.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ internal class GLMapboxRenderThread : MapboxRenderThread {
7373
this.eglSurface = eglCore.eglNoSurface
7474
this.contextMode = ContextMode.UNIQUE
7575
}
76+
init {
77+
logI(TAG, "GLMapboxRenderThread created")
78+
}
7679

7780
override fun detachSurfaceFromRenderer(creatingSurface: Boolean) {
7881
// on Android SDK <= 23 at least on x86 emulators we need to force set EGL14.EGL_NO_CONTEXT

maps-sdk/src/main/java/com/mapbox/maps/renderer/MapboxRenderThread.kt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ internal abstract class MapboxRenderThread : Choreographer.FrameCallback {
8484

8585
internal val renderHandlerThread: RenderHandlerThread
8686
protected val mapboxRenderer: MapboxRenderer
87-
protected val widgetRenderer: MapboxWidgetRenderer
87+
protected val widgetRenderer: MapboxWidgetRenderer?
8888

8989
/**
9090
* [ReentrantLock] to guarantee consistent behaviour of surface create / destroy events.
@@ -214,7 +214,7 @@ internal abstract class MapboxRenderThread : Choreographer.FrameCallback {
214214

215215
constructor(
216216
mapboxRenderer: MapboxRenderer,
217-
widgetRenderer: MapboxWidgetRenderer,
217+
widgetRenderer: MapboxWidgetRenderer?,
218218
mapName: String,
219219
rendererName: String
220220
) {
@@ -232,7 +232,7 @@ internal abstract class MapboxRenderThread : Choreographer.FrameCallback {
232232
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
233233
constructor(
234234
mapboxRenderer: MapboxRenderer,
235-
mapboxWidgetRenderer: MapboxWidgetRenderer,
235+
mapboxWidgetRenderer: MapboxWidgetRenderer?,
236236
handlerThread: RenderHandlerThread,
237237
fpsManager: FpsManager,
238238
surfaceProcessingLock: ReentrantLock,
@@ -348,7 +348,7 @@ internal abstract class MapboxRenderThread : Choreographer.FrameCallback {
348348
@RenderThread
349349
private fun notifyRenderersSizeChanged(width: Int, height: Int) {
350350
mapboxRenderer.onSurfaceChanged(width = width, height = height)
351-
widgetRenderer.onSurfaceChanged(width = width, height = height)
351+
widgetRenderer?.onSurfaceChanged(width = width, height = height)
352352
}
353353

354354
private val presentFrameFunc = Choreographer.FrameCallback { presentFrame() }
@@ -362,7 +362,7 @@ internal abstract class MapboxRenderThread : Choreographer.FrameCallback {
362362
return
363363
}
364364
preRenderWithSharedContext()
365-
if (widgetRenderer.hasWidgets() == true) {
365+
if (widgetRenderer?.hasWidgets() == true) {
366366
renderWithWidgets()
367367
} else {
368368
renderWithoutWidgets()
@@ -509,11 +509,11 @@ internal abstract class MapboxRenderThread : Choreographer.FrameCallback {
509509

510510
@OptIn(MapboxExperimental::class)
511511
fun addWidget(widget: Widget) {
512-
widgetRenderer.addWidget(widget)
512+
widgetRenderer?.addWidget(widget)
513513
}
514514

515515
@OptIn(MapboxExperimental::class)
516-
fun removeWidget(widget: Widget): Boolean = widgetRenderer.removeWidget(widget)
516+
fun removeWidget(widget: Widget): Boolean = widgetRenderer?.removeWidget(widget) == true
517517

518518
@RenderThread
519519
internal fun processAndroidSurface(surface: Surface, width: Int, height: Int) {

maps-sdk/src/main/java/com/mapbox/maps/renderer/MapboxRenderer.kt

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import androidx.annotation.UiThread
99
import androidx.annotation.VisibleForTesting
1010
import com.mapbox.common.Cancelable
1111
import com.mapbox.maps.DelegatingMapClient
12+
import com.mapbox.maps.Map
13+
import com.mapbox.maps.RenderBackendType
1214
import com.mapbox.maps.MapView.OnSnapshotReady
1315
import com.mapbox.maps.MapboxExperimental
1416
import com.mapbox.maps.NativeMapImpl
@@ -22,7 +24,9 @@ import java.util.concurrent.atomic.AtomicBoolean
2224
import java.util.concurrent.locks.ReentrantLock
2325
import kotlin.concurrent.withLock
2426

25-
internal abstract class MapboxRenderer(mapName: String) : DelegatingMapClient {
27+
internal abstract class MapboxRenderer(
28+
mapName: String,
29+
) : DelegatingMapClient {
2630

2731
internal lateinit var renderThread: MapboxRenderThread
2832
internal abstract val widgetRenderer: MapboxWidgetRenderer
@@ -103,7 +107,7 @@ internal abstract class MapboxRenderer(mapName: String) : DelegatingMapClient {
103107
if (width != this.width || height != this.height) {
104108
this.width = width
105109
this.height = height
106-
GLES20.glViewport(0, 0, width, height)
110+
renderThread.resize(width, height)
107111
map?.setSize(Size(width.toFloat(), height.toFloat()))
108112
}
109113
}
@@ -262,5 +266,9 @@ internal abstract class MapboxRenderer(mapName: String) : DelegatingMapClient {
262266
companion object {
263267
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
264268
internal val repaintRenderEvent = RenderEvent(null, true)
269+
270+
internal val supportedRenderBackend: RenderBackendType by lazy {
271+
Map.getSupportedRenderBackend()
272+
}
265273
}
266274
}

maps-sdk/src/main/java/com/mapbox/maps/renderer/MapboxSurfaceRenderer.kt

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.mapbox.maps.renderer
33
import android.view.Surface
44
import androidx.annotation.VisibleForTesting
55
import com.mapbox.maps.ContextMode
6+
import com.mapbox.maps.RenderBackendType
67

78
internal open class MapboxSurfaceRenderer : MapboxRenderer {
89

@@ -19,14 +20,20 @@ internal open class MapboxSurfaceRenderer : MapboxRenderer {
1920
antialiasingSampleCount = antialiasingSampleCount,
2021
mapName = mapName,
2122
)
22-
renderThread = GLMapboxRenderThread(
23-
mapboxRenderer = this,
24-
mapboxWidgetRenderer = widgetRenderer,
25-
translucentSurface = false,
26-
antialiasingSampleCount = antialiasingSampleCount,
27-
contextMode = contextMode,
28-
mapName = mapName,
29-
)
23+
renderThread = when (supportedRenderBackend) {
24+
RenderBackendType.VULKAN -> VulkanMapboxRenderThread(
25+
mapboxRenderer = this,
26+
mapName = mapName,
27+
)
28+
RenderBackendType.OPEN_GL -> GLMapboxRenderThread(
29+
mapboxRenderer = this,
30+
mapboxWidgetRenderer = widgetRenderer,
31+
translucentSurface = false,
32+
antialiasingSampleCount = antialiasingSampleCount,
33+
contextMode = contextMode,
34+
mapName = mapName,
35+
)
36+
}
3037
}
3138

3239
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)

maps-sdk/src/main/java/com/mapbox/maps/renderer/MapboxTextureViewRenderer.kt

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import android.view.TextureView
66
import androidx.annotation.RestrictTo
77
import androidx.annotation.VisibleForTesting
88
import com.mapbox.maps.ContextMode
9+
import com.mapbox.maps.RenderBackendType
910

1011
@RestrictTo(RestrictTo.Scope.LIBRARY)
1112
internal class MapboxTextureViewRenderer : MapboxRenderer, TextureView.SurfaceTextureListener {
@@ -23,14 +24,20 @@ internal class MapboxTextureViewRenderer : MapboxRenderer, TextureView.SurfaceTe
2324
mapName = mapName,
2425
)
2526
this.widgetRenderer = widgetRenderer
26-
renderThread = GLMapboxRenderThread(
27-
mapboxRenderer = this,
28-
mapboxWidgetRenderer = widgetRenderer,
29-
translucentSurface = true,
30-
antialiasingSampleCount = antialiasingSampleCount,
31-
contextMode = contextMode,
32-
mapName = mapName,
33-
)
27+
renderThread = when (supportedRenderBackend) {
28+
RenderBackendType.VULKAN -> VulkanMapboxRenderThread(
29+
mapboxRenderer = this,
30+
mapName = mapName,
31+
)
32+
RenderBackendType.OPEN_GL -> GLMapboxRenderThread(
33+
mapboxRenderer = this,
34+
mapboxWidgetRenderer = widgetRenderer,
35+
translucentSurface = true,
36+
antialiasingSampleCount = antialiasingSampleCount,
37+
contextMode = contextMode,
38+
mapName = mapName,
39+
)
40+
}
3441
textureView.let {
3542
it.isOpaque = false
3643
it.surfaceTextureListener = this
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package com.mapbox.maps.renderer
2+
3+
import android.view.Surface
4+
import com.mapbox.maps.IVulkanManager
5+
import com.mapbox.maps.RenderCallback
6+
import com.mapbox.maps.logI
7+
import com.mapbox.maps.logW
8+
9+
/**
10+
* Vulkan-based implementation of MapboxRenderThread.
11+
*/
12+
internal class VulkanMapboxRenderThread(mapboxRenderer: MapboxRenderer, mapName: String) :
13+
MapboxRenderThread(
14+
mapboxRenderer = mapboxRenderer,
15+
widgetRenderer = null,
16+
mapName = mapName,
17+
rendererName = "Vulkan"
18+
) {
19+
20+
private var nativeVulkanManager: IVulkanManager? = null
21+
private val surfaceWrapper: SurfaceWrapper = SurfaceWrapper()
22+
23+
init {
24+
logI(TAG, "VulkanMapboxRenderThread created")
25+
}
26+
27+
override fun detachSurfaceFromRenderer(creatingSurface: Boolean) {
28+
// no-op for now
29+
}
30+
31+
/**
32+
* For Vulkan renderer there's not much to prepare, just get the [IVulkanManager].
33+
* Preparations will be done in [attachSurfaceToRenderer].
34+
*/
35+
override fun prepareRenderer(): Boolean {
36+
logI(TAG, "prepareRenderer called")
37+
// TODO: How to handle vulkan not supported in the device?
38+
if (nativeVulkanManager == null) {
39+
nativeVulkanManager = mapboxRenderer.map?.getVulkanManager()
40+
}
41+
val result = nativeVulkanManager != null
42+
return result
43+
}
44+
45+
override fun attachSurfaceToRenderer(surface: Surface): Boolean {
46+
logI(TAG, "attachSurfaceToRenderer called")
47+
48+
surfaceWrapper.setSurface(surface)
49+
val nativeWindowPtr = surfaceWrapper.aNativeWindow
50+
51+
if (nativeWindowPtr == 0L) {
52+
logW(TAG, "Failed to get native window pointer")
53+
return false
54+
}
55+
56+
logI(TAG, "Vulkan pre-init")
57+
val result = nativeVulkanManager?.init(nativeWindowPtr) ?: run {
58+
// TODO cache surface until nativeVulkanManager is set via setMap
59+
false
60+
}
61+
62+
logI(TAG, "Vulkan init result: $result")
63+
return result
64+
}
65+
66+
private val renderCallback: RenderCallback = RenderCallback(mapboxRenderer::render)
67+
68+
override fun renderWithoutWidgets() {
69+
nativeVulkanManager?.render(renderCallback)
70+
}
71+
72+
override fun presentFrame() {
73+
// no-op as presentFrame is called from native side as part of `render { }`
74+
}
75+
76+
override fun preRenderWithSharedContext() {
77+
// no-op for now
78+
}
79+
80+
override fun renderWithWidgets() {
81+
TODO("Not yet supported in Vulkan")
82+
}
83+
84+
override fun releaseResources() {
85+
logI(TAG, "releaseResources called")
86+
isRendererReady = false
87+
surfaceWrapper.releaseSurface()
88+
nativeVulkanManager?.release()
89+
}
90+
91+
override fun prepareWidgetRender() {
92+
// no-op for now
93+
}
94+
95+
override fun releaseRenderSurface() {
96+
logI(TAG, "releaseRenderSurface called")
97+
nativeVulkanManager?.releaseSurface()
98+
}
99+
100+
override fun clearRendererStateListeners() {
101+
// no-op for now
102+
}
103+
104+
override fun addRendererStateListener(listener: RendererSetupErrorListener) {
105+
// no-op for now
106+
}
107+
108+
override fun removeRendererStateListener(listener: RendererSetupErrorListener) {
109+
// no-op for now
110+
}
111+
112+
override fun resize(width: Int, height: Int) {
113+
logI(TAG, "resize to ${width}x${height} called")
114+
// TODO cache width/height if NULL
115+
nativeVulkanManager?.resize(width, height)
116+
}
117+
118+
override fun flushCommands() {
119+
// no-op for now
120+
}
121+
}

0 commit comments

Comments
 (0)