Add Android Vulkan backend (#2711)
Co-authored-by: Bart Louwers <bart.louwers@gmail.com>
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
package org.maplibre.android.maps.renderer;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.Surface;
|
||||
import android.view.TextureView;
|
||||
|
||||
import androidx.annotation.Keep;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.maplibre.android.maps.renderer.surfaceview.MapLibreVulkanSurfaceView;
|
||||
import org.maplibre.android.maps.renderer.surfaceview.SurfaceViewMapRenderer;
|
||||
import org.maplibre.android.maps.renderer.surfaceview.VulkanSurfaceViewMapRenderer;
|
||||
import org.maplibre.android.maps.renderer.textureview.TextureViewMapRenderer;
|
||||
import org.maplibre.android.maps.renderer.textureview.VulkanTextureViewRenderThread;
|
||||
|
||||
@Keep
|
||||
public class MapRendererFactory {
|
||||
public static TextureViewMapRenderer newTextureViewMapRenderer(@NonNull Context context, TextureView textureView,
|
||||
String localFontFamily, boolean translucentSurface,
|
||||
Runnable initCallback) {
|
||||
|
||||
TextureViewMapRenderer mapRenderer = new TextureViewMapRenderer(context, textureView,
|
||||
localFontFamily, translucentSurface) {
|
||||
@Override
|
||||
protected void onSurfaceCreated(Surface surface) {
|
||||
initCallback.run();
|
||||
super.onSurfaceCreated(surface);
|
||||
}
|
||||
};
|
||||
|
||||
mapRenderer.setRenderThread(new VulkanTextureViewRenderThread(textureView, mapRenderer));
|
||||
return mapRenderer;
|
||||
}
|
||||
|
||||
public static SurfaceViewMapRenderer newSurfaceViewMapRenderer(@NonNull Context context, String localFontFamily,
|
||||
boolean renderSurfaceOnTop, Runnable initCallback) {
|
||||
|
||||
MapLibreVulkanSurfaceView surfaceView = new MapLibreVulkanSurfaceView(context);
|
||||
surfaceView.setZOrderMediaOverlay(renderSurfaceOnTop);
|
||||
|
||||
return new VulkanSurfaceViewMapRenderer(context, surfaceView, localFontFamily) {
|
||||
@Override
|
||||
public void onSurfaceCreated(Surface surface) {
|
||||
initCallback.run();
|
||||
super.onSurfaceCreated(surface);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
package org.maplibre.android.maps.renderer.surfaceview;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
public class MapLibreVulkanSurfaceView extends MapLibreSurfaceView {
|
||||
|
||||
protected final WeakReference<MapLibreVulkanSurfaceView> viewWeakReference = new WeakReference<>(this);
|
||||
|
||||
public MapLibreVulkanSurfaceView(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public MapLibreVulkanSurfaceView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createRenderThread() {
|
||||
renderThread = new VulkanThread(viewWeakReference);
|
||||
}
|
||||
|
||||
static class VulkanThread extends MapLibreSurfaceView.RenderThread {
|
||||
VulkanThread(WeakReference<MapLibreVulkanSurfaceView> surfaceViewWeakRef) {
|
||||
super();
|
||||
mSurfaceViewWeakRef = surfaceViewWeakRef;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void guardedRun() throws InterruptedException {
|
||||
wantRenderNotification = false;
|
||||
|
||||
boolean sizeChanged = false;
|
||||
boolean initSurface = false;
|
||||
boolean destroySurface = false;
|
||||
boolean wantRenderNotification = false;
|
||||
boolean doRenderNotification = false;
|
||||
int w = 0;
|
||||
int h = 0;
|
||||
Runnable event = null;
|
||||
Runnable finishDrawingRunnable = null;
|
||||
|
||||
while (true) {
|
||||
synchronized (renderThreadManager) {
|
||||
while (true) {
|
||||
if (shouldExit) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!eventQueue.isEmpty()) {
|
||||
event = eventQueue.remove(0);
|
||||
break;
|
||||
}
|
||||
|
||||
// Update the pause state.
|
||||
if (paused != requestPaused) {
|
||||
paused = requestPaused;
|
||||
renderThreadManager.notifyAll();
|
||||
}
|
||||
|
||||
if (paused && graphicsSurfaceCreated) {
|
||||
MapLibreVulkanSurfaceView view = mSurfaceViewWeakRef.get();
|
||||
if (view != null) {
|
||||
destroySurface = true;
|
||||
graphicsSurfaceCreated = false;
|
||||
}
|
||||
renderThreadManager.notifyAll();
|
||||
}
|
||||
|
||||
// lost surface
|
||||
if (!hasSurface && !waitingForSurface) {
|
||||
MapLibreVulkanSurfaceView view = mSurfaceViewWeakRef.get();
|
||||
if (view != null) {
|
||||
destroySurface = true;
|
||||
graphicsSurfaceCreated = false;
|
||||
}
|
||||
waitingForSurface = true;
|
||||
renderThreadManager.notifyAll();
|
||||
}
|
||||
|
||||
// acquired surface
|
||||
if (hasSurface && waitingForSurface) {
|
||||
MapLibreVulkanSurfaceView view = mSurfaceViewWeakRef.get();
|
||||
if (view != null) {
|
||||
initSurface = true;
|
||||
graphicsSurfaceCreated = true;
|
||||
}
|
||||
waitingForSurface = false;
|
||||
renderThreadManager.notifyAll();
|
||||
}
|
||||
|
||||
if (doRenderNotification) {
|
||||
this.wantRenderNotification = false;
|
||||
doRenderNotification = false;
|
||||
renderComplete = true;
|
||||
renderThreadManager.notifyAll();
|
||||
}
|
||||
|
||||
if (this.finishDrawingRunnable != null) {
|
||||
finishDrawingRunnable = this.finishDrawingRunnable;
|
||||
this.finishDrawingRunnable = null;
|
||||
}
|
||||
|
||||
// Ready to draw?
|
||||
if (readyToDraw()) {
|
||||
if (this.sizeChanged) {
|
||||
sizeChanged = true;
|
||||
w = width;
|
||||
h = height;
|
||||
this.wantRenderNotification = true;
|
||||
this.sizeChanged = false;
|
||||
}
|
||||
requestRender = false;
|
||||
renderThreadManager.notifyAll();
|
||||
if (this.wantRenderNotification) {
|
||||
wantRenderNotification = true;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
if (finishDrawingRunnable != null) {
|
||||
Log.w(TAG, "Warning, !readyToDraw() but waiting for "
|
||||
+ "draw finished! Early reporting draw finished.");
|
||||
finishDrawingRunnable.run();
|
||||
finishDrawingRunnable = null;
|
||||
}
|
||||
}
|
||||
// By design, this is the only place in a RenderThread thread where we wait().
|
||||
renderThreadManager.wait();
|
||||
}
|
||||
} // end of synchronized(sRenderThreadManager)
|
||||
|
||||
if (event != null) {
|
||||
event.run();
|
||||
event = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
MapLibreVulkanSurfaceView view = mSurfaceViewWeakRef.get();
|
||||
|
||||
if (destroySurface) {
|
||||
if (view != null) {
|
||||
view.renderer.onSurfaceDestroyed();
|
||||
destroySurface = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (initSurface) {
|
||||
if (view != null) {
|
||||
view.renderer.onSurfaceCreated(view.getHolder().getSurface());
|
||||
initSurface = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (sizeChanged) {
|
||||
if (view != null) {
|
||||
view.renderer.onSurfaceChanged(w, h);
|
||||
sizeChanged = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (view != null) {
|
||||
view.renderer.onDrawFrame();
|
||||
if (finishDrawingRunnable != null) {
|
||||
finishDrawingRunnable.run();
|
||||
finishDrawingRunnable = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (wantRenderNotification) {
|
||||
doRenderNotification = true;
|
||||
wantRenderNotification = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean graphicsSurfaceCreated;
|
||||
|
||||
private WeakReference<MapLibreVulkanSurfaceView> mSurfaceViewWeakRef;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package org.maplibre.android.maps.renderer.surfaceview;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
public class VulkanSurfaceViewMapRenderer extends SurfaceViewMapRenderer {
|
||||
|
||||
public VulkanSurfaceViewMapRenderer(Context context,
|
||||
@NonNull MapLibreVulkanSurfaceView surfaceView,
|
||||
String localIdeographFontFamily) {
|
||||
super(context, surfaceView, localIdeographFontFamily);
|
||||
|
||||
this.surfaceView.setRenderer(this);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package org.maplibre.android.maps.renderer.textureview;
|
||||
|
||||
import android.view.Surface;
|
||||
import android.view.TextureView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.UiThread;
|
||||
|
||||
public class VulkanTextureViewRenderThread extends TextureViewRenderThread {
|
||||
|
||||
private Surface surface;
|
||||
|
||||
@UiThread
|
||||
public VulkanTextureViewRenderThread(@NonNull TextureView textureView, @NonNull TextureViewMapRenderer mapRenderer) {
|
||||
super(textureView, mapRenderer);
|
||||
}
|
||||
|
||||
// Thread implementation
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
|
||||
while (true) {
|
||||
Runnable event = null;
|
||||
boolean initialize = false;
|
||||
int w = -1;
|
||||
int h = -1;
|
||||
|
||||
// Guarded block
|
||||
synchronized (lock) {
|
||||
while (true) {
|
||||
|
||||
if (shouldExit) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If any events are scheduled, pop one for processing
|
||||
if (!eventQueue.isEmpty()) {
|
||||
event = eventQueue.remove(0);
|
||||
break;
|
||||
}
|
||||
|
||||
if (destroySurface) {
|
||||
destroySurface = false;
|
||||
mapRenderer.onSurfaceDestroyed();
|
||||
surface = null;
|
||||
break;
|
||||
}
|
||||
|
||||
if (surfaceTexture != null && !paused && requestRender && surface == null) {
|
||||
|
||||
w = width;
|
||||
h = height;
|
||||
surface = new Surface(surfaceTexture);
|
||||
|
||||
initialize = true;
|
||||
|
||||
// Reset the request render flag now, so we can catch new requests
|
||||
// while rendering
|
||||
requestRender = false;
|
||||
|
||||
// Break the guarded loop and continue to process
|
||||
break;
|
||||
}
|
||||
|
||||
if (requestRender && !paused) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Wait until needed
|
||||
lock.wait();
|
||||
|
||||
} // end guarded while loop
|
||||
|
||||
} // end guarded block
|
||||
|
||||
// Run event, if any
|
||||
if (event != null) {
|
||||
event.run();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (initialize) {
|
||||
mapRenderer.onSurfaceCreated(surface);
|
||||
mapRenderer.onSurfaceChanged( w, h);
|
||||
|
||||
initialize = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the surface size has changed inform the map renderer.
|
||||
if (sizeChanged) {
|
||||
mapRenderer.onSurfaceChanged(w, h);
|
||||
sizeChanged = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Time to render a frame
|
||||
mapRenderer.onDrawFrame();
|
||||
}
|
||||
|
||||
} catch (InterruptedException err) {
|
||||
// To be expected
|
||||
} finally {
|
||||
|
||||
// Signal we're done
|
||||
synchronized (lock) {
|
||||
this.exited = true;
|
||||
lock.notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user