From 1fb49280b8166fbb944dd1d7b6ddb748bfd2a257 Mon Sep 17 00:00:00 2001 From: takdeveloper Date: Wed, 6 Jul 2022 19:47:12 +0000 Subject: [PATCH] ## 4.5.1.13 * Bug Fixes * ATAK performance is slow when using offline imagery stored on an SD card --- VERSION.md | 5 + atak/ATAK/app/build.gradle | 2 +- .../android/layers/GLLayerOutlinesLayer.java | 514 +++++++++++++++--- atak/gradle/versions.gradle | 2 +- depends/LASzip-3.4.3-mod.tar.gz | 4 +- depends/assimp-4.0.1-mod.tar.gz | 4 +- depends/gdal-2.4.4-mod.tar.gz | 4 +- depends/libLAS-1.8.2-mod.tar.gz | 4 +- depends/tinygltf-2.4.1-mod.tar.gz | 4 +- depends/tinygltfloader-0.9.5-mod.tar.gz | 4 +- pluginsdk.zip | Bin 1938057 -> 1938057 bytes takkernel/VERSION.md | 4 + takkernel/build.gradle | 4 +- .../mosaic/opengl/GLMosaicMapLayer.java | 14 + 14 files changed, 468 insertions(+), 101 deletions(-) diff --git a/VERSION.md b/VERSION.md index f9b89d48c..6b5c2a628 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1,5 +1,10 @@ # Version History +## 4.5.1.13 + +* Bug Fixes + * ATAK performance is slow when using offline imagery stored on an SD card + ## 4.5.1.12 * Feature Addition diff --git a/atak/ATAK/app/build.gradle b/atak/ATAK/app/build.gradle index c382bd105..800ab836d 100644 --- a/atak/ATAK/app/build.gradle +++ b/atak/ATAK/app/build.gradle @@ -26,7 +26,7 @@ buildscript { apply from: '../../gradle/versions.gradle', to: project ext.ATAK_VERSION = "4.5.1" - ext.ATAK_VERSION_SUBMINOR = ".12" + ext.ATAK_VERSION_SUBMINOR = ".13" ext.isDevKitEnabled = { -> return getProperty('takRepoMavenUrl', null) != null && diff --git a/atak/ATAK/app/src/main/java/com/atakmap/android/layers/GLLayerOutlinesLayer.java b/atak/ATAK/app/src/main/java/com/atakmap/android/layers/GLLayerOutlinesLayer.java index 53bfb4256..c37a82a7c 100644 --- a/atak/ATAK/app/src/main/java/com/atakmap/android/layers/GLLayerOutlinesLayer.java +++ b/atak/ATAK/app/src/main/java/com/atakmap/android/layers/GLLayerOutlinesLayer.java @@ -10,6 +10,8 @@ import com.atakmap.map.layer.feature.FeatureCursor; import com.atakmap.map.layer.feature.FeatureDataStore; import com.atakmap.map.layer.feature.FeatureLayer; +import com.atakmap.map.layer.feature.style.BasicStrokeStyle; +import com.atakmap.map.layer.feature.style.CompositeStyle; import com.atakmap.map.layer.feature.style.Style; import com.atakmap.map.layer.feature.geometry.Geometry; import com.atakmap.map.layer.feature.geometry.GeometryCollection; @@ -18,7 +20,6 @@ import com.atakmap.map.layer.feature.geometry.Polygon; import com.atakmap.map.layer.feature.geometry.opengl.GLBatchGeometry; import com.atakmap.map.layer.feature.geometry.opengl.GLBatchGeometryRenderer; -import com.atakmap.map.layer.feature.geometry.opengl.GLBatchLineString; import com.atakmap.map.layer.feature.geometry.opengl.GLBatchPoint; import com.atakmap.map.layer.feature.opengl.GLFeatureLayer; import com.atakmap.map.layer.feature.service.FeatureHitTestControl; @@ -28,19 +29,25 @@ import com.atakmap.map.opengl.GLMapRenderable; import com.atakmap.map.opengl.GLMapSurface; import com.atakmap.map.opengl.GLMapView; -import com.atakmap.opengl.Tessellate; +import com.atakmap.map.opengl.GLRenderGlobals; +import com.atakmap.math.PointD; +import com.atakmap.opengl.GLES20FixedPipeline; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; -import java.util.Iterator; import java.util.LinkedList; import java.util.Map; +import gov.tak.platform.commons.opengl.GLES30; +import gov.tak.platform.graphics.Color; + class GLLayerOutlinesLayer extends - GLAsynchronousLayer> + GLAsynchronousLayer implements FeatureDataStore.OnDataStoreContentChangedListener, Layer.OnLayerVisibleChangedListener, @@ -77,6 +84,58 @@ public int compare(Feature lhs, Feature rhs) { } }; + final static int LINES_VERTEX_SIZE = (12+12+4+2+2); // 32 + final static int LINE_PRIMITIVE_SIZE = 6*LINES_VERTEX_SIZE; + + private final static String LINE_VSH = + "#version 300 es\n" + + "precision highp float;\n" + + "const float c_smoothBuffer = 2.0;\n" + + "uniform mat4 u_mvp;\n" + + "uniform mediump vec2 u_viewportSize;\n" + + "in vec3 a_vertexCoord0;\n" + + "in vec3 a_vertexCoord1;\n" + + "in vec2 a_texCoord;\n" + + "in vec4 a_color;\n" + + "in float a_normal;\n" + + "in float a_halfStrokeWidth;\n" + + "out vec4 v_color;\n" + + "flat out float f_dist;\n" + + "out float v_mix;\n" + + "flat out int f_pattern;\n" + + "out vec2 v_normal;\n" + + "flat out float f_halfStrokeWidth;\n" + + "flat out int f_factor;\n" + + "void main(void) {\n" + + " gl_Position = u_mvp * vec4(a_vertexCoord0.xyz, 1.0);\n" + + " vec4 next_gl_Position = u_mvp * vec4(a_vertexCoord1.xyz, 1.0);\n" + + " vec4 p0 = (gl_Position / gl_Position.w)*vec4(u_viewportSize, 1.0, 1.0);\n" + + " vec4 p1 = (next_gl_Position / next_gl_Position.w)*vec4(u_viewportSize, 1.0, 1.0);\n" + + " float dist = distance(p0.xy, p1.xy);\n" + + " float dx = p1.x - p0.x;\n" + + " float dy = p1.y - p0.y;\n" + + " float normalDir = (2.0*a_normal) - 1.0;\n" + + " float adjX = normalDir*(dx/dist)*((a_halfStrokeWidth+c_smoothBuffer)/u_viewportSize.y);\n" + + " float adjY = normalDir*(dy/dist)*((a_halfStrokeWidth+c_smoothBuffer)/u_viewportSize.x);\n" + + " gl_Position.x = gl_Position.x - adjY*gl_Position.w;\n" + + " gl_Position.y = gl_Position.y + adjX*gl_Position.w;\n" + + " v_color = a_color;\n" + + " v_normal = vec2(-normalDir*(dy/dist)*(a_halfStrokeWidth+c_smoothBuffer), normalDir*(dx/dist)*(a_halfStrokeWidth+c_smoothBuffer));\n" + + " f_halfStrokeWidth = a_halfStrokeWidth;\n" + + "}"; + + private final static String LINE_FSH = + "#version 300 es\n" + + "precision mediump float;\n" + + "in vec4 v_color;\n" + + "in vec2 v_normal;\n" + + "flat in float f_halfStrokeWidth;\n" + + "out vec4 v_FragColor;\n" + + "void main(void) {\n" + + " float antiAlias = smoothstep(-1.0, 0.25, f_halfStrokeWidth-length(v_normal));\n" + + " v_FragColor = vec4(v_color.rgb, v_color.a*antiAlias);\n" + + "}"; + private final FeatureLayer subject; private final FeatureDataStore dataStore; @@ -88,6 +147,9 @@ public int compare(Feature lhs, Feature rhs) { private GLBatchGeometryRenderer renderer; + private Collection lineBuffers = new LinkedList<>(); + private LineShader lineShader; + private GLLayerOutlinesLayer(MapRenderer surface, FeatureLayer subject) { super(surface, subject); @@ -101,6 +163,89 @@ private GLLayerOutlinesLayer(MapRenderer surface, FeatureLayer subject) { /**************************************************************************/ // GL Layer + + public synchronized void draw(GLMapView view) { + if(preparedState != null) + drawLineBuffers(view, lineBuffers, new PointD(preparedState.drawLng, preparedState.drawLat, 0d)); + super.draw(view); + } + + void drawLineBuffers(GLMapView view, Collection buf, PointD centroidProj) { + if (this.lineShader == null) { + this.lineShader = new LineShader(); + final int vertShader = GLES20FixedPipeline.loadShader(GLES30.GL_VERTEX_SHADER, LINE_VSH); + final int fragShader = GLES20FixedPipeline.loadShader(GLES30.GL_FRAGMENT_SHADER, LINE_FSH); + + lineShader.handle = GLES20FixedPipeline.createProgram(vertShader, fragShader); + GLES30.glDeleteShader(vertShader); + GLES30.glDeleteShader(fragShader); + + GLES30.glUseProgram(lineShader.handle); + lineShader.u_mvp = GLES30.glGetUniformLocation(lineShader.handle, "u_mvp"); + lineShader.u_viewportSize = GLES30.glGetUniformLocation(lineShader.handle, "u_viewportSize"); + lineShader.a_vertexCoord0 = GLES30.glGetAttribLocation(lineShader.handle, "a_vertexCoord0"); + lineShader.a_vertexCoord1 = GLES30.glGetAttribLocation(lineShader.handle, "a_vertexCoord1"); + lineShader.a_color = GLES30.glGetAttribLocation(lineShader.handle, "a_color"); + lineShader.a_normal = GLES30.glGetAttribLocation(lineShader.handle, "a_normal"); + lineShader.a_halfStrokeWidth = GLES30.glGetAttribLocation(lineShader.handle, "a_halfStrokeWidth"); + } + + GLES30.glUseProgram(lineShader.handle); + + // MVP + { + // projection + GLES20FixedPipeline.glGetFloatv(GLES20FixedPipeline.GL_PROJECTION, view.scratch.matrixF, 0); + for(int i = 0; i < 16; i++) + view.scratch.matrix.set(i%4, i/4, view.scratch.matrixF[i]); + // model-view + view.scratch.matrix.concatenate(view.scene.forward); + view.scratch.matrix.translate(centroidProj.x, centroidProj.y, centroidProj.z); + for (int i = 0; i < 16; i++) { + double v; + v = view.scratch.matrix.get(i % 4, i / 4); + view.scratch.matrixF[i] = (float)v; + } + GLES30.glUniformMatrix4fv(lineShader.u_mvp, 1, false, view.scratch.matrixF, 0); + } + // viewport size + { + int[] viewport = new int[4]; + GLES30.glGetIntegerv(GLES30.GL_VIEWPORT, viewport, 0); + GLES30.glUniform2f(lineShader.u_viewportSize, (float)viewport[2] / 2.0f, (float)viewport[3] / 2.0f); + } + + GLES30.glEnableVertexAttribArray(lineShader.a_vertexCoord0); + GLES30.glEnableVertexAttribArray(lineShader.a_vertexCoord1); + GLES30.glEnableVertexAttribArray(lineShader.a_color); + GLES30.glEnableVertexAttribArray(lineShader.a_normal); + GLES30.glEnableVertexAttribArray(lineShader.a_halfStrokeWidth); + + GLES30.glEnable(GLES30.GL_BLEND); + GLES30.glBlendFunc(GLES30.GL_SRC_ALPHA, GLES30.GL_ONE_MINUS_SRC_ALPHA); + + for (PrimitiveBuffer it : buf) { + GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, it.handle); + GLES30.glVertexAttribPointer(lineShader.a_vertexCoord0, 3, GLES30.GL_FLOAT, false, LINES_VERTEX_SIZE, 0); + GLES30.glVertexAttribPointer(lineShader.a_vertexCoord1, 3, GLES30.GL_FLOAT, false, LINES_VERTEX_SIZE, 12); + GLES30.glVertexAttribPointer(lineShader.a_color, 4, GLES30.GL_UNSIGNED_BYTE, true, LINES_VERTEX_SIZE, 24); + GLES30.glVertexAttribPointer(lineShader.a_normal, 1, GLES30.GL_UNSIGNED_SHORT, true, LINES_VERTEX_SIZE, 28); + GLES30.glVertexAttribPointer(lineShader.a_halfStrokeWidth, 1, GLES30.GL_UNSIGNED_SHORT, false, LINES_VERTEX_SIZE, 30); + + GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, it.count); + } + GLES30.glDisable(GLES30.GL_BLEND); + + GLES30.glDisableVertexAttribArray(lineShader.a_vertexCoord0); + GLES30.glDisableVertexAttribArray(lineShader.a_vertexCoord1); + GLES30.glDisableVertexAttribArray(lineShader.a_color); + GLES30.glDisableVertexAttribArray(lineShader.a_normal); + GLES30.glDisableVertexAttribArray(lineShader.a_halfStrokeWidth); + + GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, GLES30.GL_NONE); + GLES30.glUseProgram(GLES30.GL_NONE); + } + @Override public void start() { super.start(); @@ -152,6 +297,19 @@ protected void releaseImpl() { g.release(); this.features.clear(); + if(!lineBuffers.isEmpty()) { + final int[] deleteVbos = new int[lineBuffers.size()]; + int idx = 0; + for(PrimitiveBuffer pb : lineBuffers) + deleteVbos[idx++] = pb.handle; + GLES30.glDeleteBuffers(deleteVbos.length, deleteVbos, 0); + lineBuffers.clear(); + } + + if(lineShader != null) { + GLES30.glDeleteShader(lineShader.handle); + lineShader = null; + } this.renderList = null; super.releaseImpl(); @@ -165,65 +323,62 @@ protected Collection getRenderList() { } @Override - protected void resetPendingData(Collection pendingData) { - pendingData.clear(); + protected void resetPendingData(Content pendingData) { + releasePendingData(pendingData); } @Override - protected void releasePendingData(Collection pendingData) { - pendingData.clear(); + protected void releasePendingData(Content pendingData) { + pendingData.labels.clear(); + + // delete VBOs + if(!pendingData.lines.isEmpty()) { + final int[] deleteVbos = new int[pendingData.lines.size()]; + int idx = 0; + for(PrimitiveBuffer pb : pendingData.lines) + deleteVbos[idx++] = pb.handle; + pendingData.lines.clear(); + renderContext.queueEvent(new Runnable() { + @Override + public void run() { + GLES30.glDeleteBuffers(deleteVbos.length, deleteVbos, 0); + } + }); + } } @Override - protected Collection createPendingData() { - return new LinkedList<>(); + protected Content createPendingData() { + return new Content(renderContext); } @Override protected boolean updateRenderableReleaseLists( - Collection pendingData) { - Map swap = new HashMap<>(); - - Iterator iter = pendingData.iterator(); - Feature feature; - RendererEntry entry; - while (iter.hasNext()) { - feature = iter.next(); - entry = this.features.remove(feature.getId()); - if (entry == null) - entry = new RendererEntry(); - - if (entry.version != feature.getVersion()) { - entry.renderer.clear(); - entry.renderer.ensureCapacity(recurseGeometry(null, 0L, - feature.getGeometry(), null, null)); - recurseGeometry(this.renderContext, feature.getId(), - feature.getGeometry(), feature.getStyle(), - entry.renderer); - entry.version = feature.getVersion(); - } - swap.put(feature.getId(), entry); - iter.remove(); - } - - if (this.features.size() > 0) { - final Collection releaseList = this.features - .values(); - this.renderContext.queueEvent(new Runnable() { + Content pendingData) { + + // delete VBOs + if(!lineBuffers.isEmpty()) { + final int[] deleteVbos = new int[lineBuffers.size()]; + int idx = 0; + for(PrimitiveBuffer pb : lineBuffers) + deleteVbos[idx++] = pb.handle; + renderContext.queueEvent(new Runnable() { @Override public void run() { - for (RendererEntry f : releaseList) - for (GLBatchGeometry g : f.renderer) - g.release(); + GLES30.glDeleteBuffers(deleteVbos.length, deleteVbos, 0); } }); } + lineBuffers.clear(); + lineBuffers.addAll(pendingData.lines); + pendingData.lines.clear(); - this.features = swap; + // XXX - release old labels LinkedList geoms = new LinkedList<>(); - for (RendererEntry e : this.features.values()) - geoms.addAll(e.renderer); + for (Collection e : pendingData.labels.values()) + geoms.addAll(e); + pendingData.labels.clear(); // set the render content on the batch. both this method and 'draw' are // invoked while holding 'this' @@ -233,13 +388,14 @@ public void run() { } @Override - protected void query(ViewState state, Collection retval) { + protected void query(ViewState state, Content retval) { if (state.crossesIDL) { double east = Math.min(state.westBound, state.eastBound) + 360; double west = Math.max(state.westBound, state.eastBound); this.queryImpl(state.northBound, west, state.southBound, east, state.drawMapResolution, + state.drawLat, state.drawLng, retval); } else { this.queryImpl(state.northBound, @@ -247,6 +403,7 @@ protected void query(ViewState state, Collection retval) { state.southBound, state.eastBound, state.drawMapResolution, + state.drawLat, state.drawLng, retval); } } @@ -256,7 +413,8 @@ private void queryImpl(double northBound, double southBound, double eastBound, double drawMapResolution, - Collection retval) { + double drawLat, double drawLng, + Content retval) { FeatureCursor result = null; try { @@ -275,8 +433,16 @@ private void queryImpl(double northBound, while (result.moveToNext()) { if (this.checkQueryThreadAbort()) break; - retval.add(result.get()); + final Feature f = result.get(); + retval.processFeature(f.getId(), f.getGeometry(), f.getStyle(), drawLat, drawLng); } + + if(retval.sink != null) { + retval.unmapBuffer(retval.sink); + retval.lines.add(retval.sink); + retval.sink = null; + } + //long e = android.os.SystemClock.elapsedRealtime(); //Log.d(TAG, retval.size() + " results in " + (e-s) + "ms"); } finally { @@ -341,45 +507,32 @@ public synchronized void hitTest(Collection fids, /**************************************************************************/ - private static int recurseGeometry(MapRenderer surface, long fid, - Geometry geom, Style style, Collection renderer) { - if (geom instanceof Point) { - if (renderer != null) { - GLBatchPoint r = new GLBatchPoint(surface); - r.init((fid << 20L) | (renderer.size() & 0xFFFFF), null); - r.setGeometry((Point) geom); - r.setStyle(style); - renderer.add(r); - } - return 1; - } else if (geom instanceof LineString) { - if (renderer != null) { - GLBatchLineString r = new GLBatchLineString(surface); - r.init((fid << 20L) | (renderer.size() & 0xFFFFF), null); - r.setGeometry((LineString) geom); - r.setStyle(style); - r.setTessellationEnabled(true); - // we need to use XYZ tessellation mode here - r.setTessellationMode(Tessellate.Mode.XYZ); - renderer.add(r); - } - return 1; - } else if (geom instanceof Polygon) { - if (renderer != null) { - recurseGeometry(surface, fid, - ((Polygon) geom).getExteriorRing(), style, renderer); - for (LineString ring : ((Polygon) geom).getInteriorRings()) - recurseGeometry(surface, fid, ring, style, renderer); + static BasicStrokeStyle getStrokeStyle(Style s) { + if(s instanceof BasicStrokeStyle) { + return (BasicStrokeStyle)s; + } else if(s instanceof CompositeStyle) { + for(int i = 0; i < ((CompositeStyle) s).getNumStyles(); i++) { + BasicStrokeStyle bs = getStrokeStyle(((CompositeStyle) s).getStyle(i)); + if(bs != null) + return bs; } - return 1 + ((Polygon) geom).getInteriorRings().size(); - } else if (geom instanceof GeometryCollection) { - int retval = 0; - for (Geometry child : ((GeometryCollection) geom).getGeometries()) - retval += recurseGeometry(surface, fid, child, style, renderer); - return retval; - } else { - throw new IllegalStateException(); } + return null; + } + + private static void bls3_vertex(ByteBuffer vbuf, byte stroker, byte strokeg, byte strokeb, byte strokea, float halfWidth, PointD v1, PointD v2, int n) { + vbuf.putFloat((float)v1.x); // 0 + vbuf.putFloat((float)v1.y); + vbuf.putFloat((float)v1.z); + vbuf.putFloat((float)v2.x); // 12 + vbuf.putFloat((float)v2.y); + vbuf.putFloat((float)v2.z); + vbuf.put(stroker); // 24 + vbuf.put(strokeg); + vbuf.put(strokeb); + vbuf.put(strokea); + vbuf.putShort((short)n); // 28 + vbuf.putShort((short)halfWidth); // 30 } /**************************************************************************/ @@ -394,4 +547,195 @@ public RendererEntry() { } } + static class PrimitiveBuffer { + ByteBuffer clientArray = null; + int handle = GLES30.GL_NONE; + int count = 0; + } + + static class Content { + Map> labels; + Collection lines; + PrimitiveBuffer sink; + final MapRenderer surface; + + Content(MapRenderer ctx) { + surface = ctx; + labels = new HashMap<>(); + lines = new LinkedList<>(); + sink = null; + } + + void processFeature(long fid, Geometry geom, Style style, double rtcLat, double rtcLng) { + if(geom instanceof Point) { + Collection renderer = labels.get(fid); + if(renderer == null) + labels.put(fid, renderer=new ArrayList<>(1)); + GLBatchPoint r = new GLBatchPoint(surface); + r.init((fid << 20L) | (renderer.size() & 0xFFFFF), null); + r.setGeometry((Point) geom); + r.setStyle(style); + renderer.add(r); + } else if(geom instanceof LineString) { + processFeature((LineString)geom, style, rtcLat, rtcLng); + } else if(geom instanceof Polygon) { + Polygon poly = (Polygon)geom; + processFeature(poly.getExteriorRing(), style, rtcLat, rtcLng); + for(LineString ring : poly.getInteriorRings()) + processFeature(ring, style, rtcLat, rtcLng); + } else if(geom instanceof GeometryCollection) { + for(Geometry child : ((GeometryCollection)geom).getGeometries()) + processFeature(fid, child, style, rtcLat, rtcLng); + } + } + + void processFeature(LineString geom, Style style, double rtcLat, double rtcLng) { + if(sink == null) + sink = mapBuffer(512*1024); + + byte stroke_r = (byte)0x00; + byte stroke_g = (byte)0xFF; + byte stroke_b = (byte)0x00; + byte stroke_a = (byte)0xFF; + + float strokeWidth = 2f; + final BasicStrokeStyle stroke = getStrokeStyle(style); + if(stroke != null) { + final int strokeColor = stroke.getColor(); + stroke_r = (byte) Color.red(strokeColor); + stroke_g = (byte) Color.green(strokeColor); + stroke_b = (byte) Color.blue(strokeColor); + stroke_a = (byte) Color.alpha(strokeColor); + strokeWidth = stroke.getStrokeWidth(); + } + + final float halfWidth = Math.min(strokeWidth*GLRenderGlobals.getRelativeScaling()/2.0f, 255.0f); + final int numPoints = geom.getNumPoints(); + final int numSegments = numPoints-1; + PointD p0 = new PointD(0d, 0d, 0d); + PointD p1 = new PointD(0d, 0d, 0d); + for(int i = 0; i < numSegments; i++) { + if(sink.clientArray.remaining() < LINE_PRIMITIVE_SIZE) { + // unmap the buffer + unmapBuffer(sink); + // push it back + lines.add(sink); + + sink = mapBuffer(512*1024); + } + + p0.x = geom.getX(i)-rtcLng; + p0.y = geom.getY(i)-rtcLat; + p1.x = geom.getX(i+1)-rtcLng; + p1.y = geom.getY(i+1)-rtcLat; + + // emit vertices + bls3_vertex(sink.clientArray, stroke_r, stroke_g, stroke_b, stroke_a, halfWidth, p0, p1, 0xFFFF); + bls3_vertex(sink.clientArray, stroke_r, stroke_g, stroke_b, stroke_a, halfWidth, p1, p0, 0xFFFF); + bls3_vertex(sink.clientArray, stroke_r, stroke_g, stroke_b, stroke_a, halfWidth, p0, p1, 0x00); + + bls3_vertex(sink.clientArray, stroke_r, stroke_g, stroke_b, stroke_a, halfWidth, p0, p1, 0xFFFF); + bls3_vertex(sink.clientArray, stroke_r, stroke_g, stroke_b, stroke_a, halfWidth, p1, p0, 0xFFFF); + bls3_vertex(sink.clientArray, stroke_r, stroke_g, stroke_b, stroke_a, halfWidth, p1, p0, 0x00); + } + } + + PrimitiveBuffer cb_mapBuffer(final int capacity) { + PrimitiveBuffer pb = new PrimitiveBuffer(); + int[] handle = new int[1]; + GLES30.glGenBuffers(1, handle, 0); + pb.handle = handle[0]; + GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, handle[0]); + GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER, capacity, null, GLES30.GL_STATIC_DRAW); + pb.clientArray = (ByteBuffer)GLES30.glMapBufferRange(GLES30.GL_ARRAY_BUFFER, 0, capacity, android.opengl.GLES30.GL_MAP_WRITE_BIT); + pb.clientArray.order(ByteOrder.nativeOrder()); + GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, GLES30.GL_NONE); + + return pb; + } + + PrimitiveBuffer mapBuffer(final int capacity) { + final PrimitiveBuffer[] pb = new PrimitiveBuffer[1]; + if(surface.isRenderThread()) { + pb[0] = cb_mapBuffer(capacity); + } else { + surface.queueEvent(new Runnable() { + @Override + public void run() { + PrimitiveBuffer buf = cb_mapBuffer(capacity); + synchronized(pb) { + pb[0] = buf; + pb.notify(); + } + } + }); + + while(true) { + synchronized(pb) { + if(pb[0] == null) { + try { + pb.wait(); + continue; + } catch (InterruptedException ignored) { + break; + } + } + break; + } + } + } + return pb[0]; + } + + void cb_unmapBuffer(int handle) { + GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, handle); + GLES30.glUnmapBuffer(GLES30.GL_ARRAY_BUFFER); + GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, GLES30.GL_NONE); + } + void unmapBuffer(PrimitiveBuffer buffer) { + final int[] handle = new int[] {buffer.handle}; + buffer.count = buffer.clientArray.position()/LINES_VERTEX_SIZE; + buffer.clientArray = null; + if(surface.isRenderThread()) { + cb_unmapBuffer(handle[0]); + } else { + surface.queueEvent(new Runnable() { + @Override + public void run() { + cb_unmapBuffer(handle[0]); + synchronized(handle) { + handle[0] = GLES30.GL_NONE; + handle.notify(); + } + } + }); + + while(true) { + synchronized(handle) { + if(handle[0] != GLES30.GL_NONE) { + try { + handle.wait(); + continue; + } catch (InterruptedException ignored) { + break; + } + } + break; + } + } + } + } + } + + final static class LineShader { + public int handle; + int u_mvp; + int u_viewportSize; + int a_vertexCoord0; + int a_vertexCoord1; + int a_color; + int a_normal; + int a_halfStrokeWidth; + } + } // GLLayersOutlineLayer diff --git a/atak/gradle/versions.gradle b/atak/gradle/versions.gradle index ad9ab8ad5..0bfa064b0 100644 --- a/atak/gradle/versions.gradle +++ b/atak/gradle/versions.gradle @@ -1,5 +1,5 @@ ext { jacocoVersion = '0.8.5' takDevkitVersion = '3.2.0' - takKernelVersion = '0.38.2' + takKernelVersion = '0.38.3' } diff --git a/depends/LASzip-3.4.3-mod.tar.gz b/depends/LASzip-3.4.3-mod.tar.gz index 70581dbb0..86550510e 100644 --- a/depends/LASzip-3.4.3-mod.tar.gz +++ b/depends/LASzip-3.4.3-mod.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4a1cdea768b5126d5773f0dd4ec21806abf552341bf845e0f2acd5a6998e8f05 -size 241737 +oid sha256:79a2807f6bd95fddb9360088e0baee3f9628197dcd23965a98b3b8776c6570d7 +size 241733 diff --git a/depends/assimp-4.0.1-mod.tar.gz b/depends/assimp-4.0.1-mod.tar.gz index a02fc2904..bf281cf2f 100644 --- a/depends/assimp-4.0.1-mod.tar.gz +++ b/depends/assimp-4.0.1-mod.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:de51ff3a37d5161bf5502dd4e33d73c1e2880f19bce040682804df541ce57093 -size 45178054 +oid sha256:92a0e3b4ea3871e5cdb95e89207e60fb5f50a05ffda28956b9fc000c201d4a81 +size 45177481 diff --git a/depends/gdal-2.4.4-mod.tar.gz b/depends/gdal-2.4.4-mod.tar.gz index 360a84f6d..28c44c329 100644 --- a/depends/gdal-2.4.4-mod.tar.gz +++ b/depends/gdal-2.4.4-mod.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8374170aa5f0e03f1489f3cc450a7563e1173724b6405429cf773eaea92147bb -size 14552597 +oid sha256:b0b0a973fa43d70e6ef67e282c4e196bfd1c7b736512e26dfc8037f75dd89b84 +size 14552248 diff --git a/depends/libLAS-1.8.2-mod.tar.gz b/depends/libLAS-1.8.2-mod.tar.gz index 9b85eb096..4bbc86bff 100644 --- a/depends/libLAS-1.8.2-mod.tar.gz +++ b/depends/libLAS-1.8.2-mod.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:38a167ebf0ceb218dd62f10c8a1ef81522906d40723a613dc9e095569a4b93a7 -size 141035769 +oid sha256:d8c90452bad3f404a05ce6c0f99be0c4593ad9fdc3400f2757e89bdf1158526f +size 141022434 diff --git a/depends/tinygltf-2.4.1-mod.tar.gz b/depends/tinygltf-2.4.1-mod.tar.gz index cb011d987..5b8612cb5 100644 --- a/depends/tinygltf-2.4.1-mod.tar.gz +++ b/depends/tinygltf-2.4.1-mod.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a1e3d07125fe67ddbfed75f6c711e2cd1e93c3588336c4f14da9409ed3c3e5c9 -size 5755899 +oid sha256:59452b3c5ccaa36586f3bdee12e4f0658e09901197428cf482c9cd2fa2e177af +size 5755882 diff --git a/depends/tinygltfloader-0.9.5-mod.tar.gz b/depends/tinygltfloader-0.9.5-mod.tar.gz index 9ddb7f92b..c794e3699 100644 --- a/depends/tinygltfloader-0.9.5-mod.tar.gz +++ b/depends/tinygltfloader-0.9.5-mod.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6f0bcfb13b894a7acb79f11e9ab37611f6ee09523f102cab7d277b05cac78469 -size 708750 +oid sha256:cf6acee01b5a2ace0445752747fb064e7c87a0d3a14848c9240a1732b34de7d0 +size 708593 diff --git a/pluginsdk.zip b/pluginsdk.zip index 3806fed6cd4b727cf44ffb53237edcfc36efadee..964ac74cf1da3be1c157c9f54116211a0c10e81d 100644 GIT binary patch delta 377 zcmeBNTGqL=j3>aGnT3l11d8TP{@Cyf%5*tWRf}hC=DmdM6r<8De zF+UR!GXpUT5VHa?8xXStF$WNH0x=g5a|1CC5c2{t9}x2cu>cSY0DWkLKG r{j!0`{HO`C!N~k`6J$M+`4$spJ&^eeC&~sO^MxkK`hoaKlVsHaxR04p delta 377 zcmeBNTGqL=j3>aGnT3l11d6*R@@TPX9XXn$b!4KAG!u{?=?vv>T$KJ0C@`7(kty$L&dkZ{b$tS1PN~8V6uVeUq3-s7Q}zi rFB^!=kD4GGjLbhbLDmzQZ!uBU1DU^YqHF*%Uucr7ABdkcNmd;IfzgH^ diff --git a/takkernel/VERSION.md b/takkernel/VERSION.md index 029ae0f61..69c10bd19 100644 --- a/takkernel/VERSION.md +++ b/takkernel/VERSION.md @@ -1,5 +1,9 @@ # Version History +## 0.38.3 (cherrypick `master@0.58.2`) + +* Restore preload of mosaic root nodes on worker thread to mitigate lock contention + ## 0.38.2 (cherrypick `master@0.44.3`) * Add Android 12 compatible implementation for the Unsafe allocator diff --git a/takkernel/build.gradle b/takkernel/build.gradle index 6a714f23c..241e4682e 100644 --- a/takkernel/build.gradle +++ b/takkernel/build.gradle @@ -21,10 +21,10 @@ buildscript { scriptHandler -> def majorVersion = 0 def minorVersion = 38 -def patchVersion = 2 +def patchVersion = 3 // Version code for Android builds, you MUST increase this by 1 if you change ANY of the version components above -ext.versionCode = 74 +ext.versionCode = 75 diff --git a/takkernel/engine/src/main/java/com/atakmap/map/layer/raster/mosaic/opengl/GLMosaicMapLayer.java b/takkernel/engine/src/main/java/com/atakmap/map/layer/raster/mosaic/opengl/GLMosaicMapLayer.java index 3ae079850..56761ea36 100644 --- a/takkernel/engine/src/main/java/com/atakmap/map/layer/raster/mosaic/opengl/GLMosaicMapLayer.java +++ b/takkernel/engine/src/main/java/com/atakmap/map/layer/raster/mosaic/opengl/GLMosaicMapLayer.java @@ -604,6 +604,20 @@ private void queryImpl(ViewState localQuery, MosaicPendingData retval) { // dump everything -- we don't need it to persist retval.spatialCalc.endBatch(false); } + + // instantiate (but do not initialize) renderables for all frames that + // are not currently loaded + GLResolvableMapRenderable renderable; + for(MosaicDatabase2.Frame frame : retval.frames) { + if(this.checkQueryThreadAbort()) + break; + + if(retval.loaded.contains(this.resolvePath(frame.path))) + continue; + renderable = this.createRootNode(frame); + if(renderable != null) + retval.renderablePreload.put(frame, renderable); + } } @Override