Skip to content

Commit 120f46a

Browse files
zglueckDavid Collins
authored andcommitted
Reuse Ellipse element buffers to reduce memory use (#208)
* Add element buffer cache * Fix element buffer cache * Store element buffer in render resource cache * Refactor buffer retrieval from cache * Refactor buffer retrieval and reassembly * Rename assembly function * Rename variables * Simplify buffer retrieval logic * Switch to Range object for managing buffer indices * Fix spelling of function assembleElementsToCache function
1 parent faa6eb0 commit 120f46a

File tree

1 file changed

+80
-45
lines changed
  • worldwind/src/main/java/gov/nasa/worldwind/shape

1 file changed

+80
-45
lines changed

worldwind/src/main/java/gov/nasa/worldwind/shape/Ellipse.java

Lines changed: 80 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
package gov.nasa.worldwind.shape;
77

88
import android.opengl.GLES20;
9+
import android.util.SparseArray;
910

1011
import java.nio.ByteBuffer;
1112
import java.nio.ByteOrder;
@@ -15,6 +16,7 @@
1516
import gov.nasa.worldwind.draw.DrawShapeState;
1617
import gov.nasa.worldwind.draw.DrawableSurfaceShape;
1718
import gov.nasa.worldwind.geom.Position;
19+
import gov.nasa.worldwind.geom.Range;
1820
import gov.nasa.worldwind.geom.Vec3;
1921
import gov.nasa.worldwind.render.BasicShaderProgram;
2022
import gov.nasa.worldwind.render.BufferObject;
@@ -102,13 +104,9 @@ public class Ellipse extends AbstractShape {
102104

103105
protected FloatArray vertexArray = new FloatArray();
104106

105-
protected ShortArray interiorElements = new ShortArray();
106-
107-
protected ShortArray outlineElements = new ShortArray();
108-
109107
protected Object vertexBufferKey = nextCacheKey();
110108

111-
protected Object elementBufferKey = nextCacheKey();
109+
protected static SparseArray<ElementBufferAttributes> ELEMENT_BUFFER_ATTRIBUTES = new SparseArray<>();
112110

113111
protected Vec3 vertexOrigin = new Vec3();
114112

@@ -383,9 +381,7 @@ protected void makeDrawable(RenderContext rc) {
383381

384382
if (this.mustAssembleGeometry(rc)) {
385383
this.assembleGeometry(rc);
386-
this.assembleElements(rc);
387384
this.vertexBufferKey = nextCacheKey();
388-
this.elementBufferKey = nextCacheKey();
389385
}
390386

391387
// Obtain a drawable form the render context pool.
@@ -412,19 +408,16 @@ protected void makeDrawable(RenderContext rc) {
412408
rc.putBufferObject(this.vertexBufferKey, drawState.vertexBuffer);
413409
}
414410

415-
// Assemble the drawable's OpenGL element buffer object.
416-
drawState.elementBuffer = rc.getBufferObject(this.elementBufferKey);
411+
// Get the attributes of the element buffer
412+
ElementBufferAttributes elementBufferAttributes = ELEMENT_BUFFER_ATTRIBUTES.get(this.intervals);
413+
drawState.elementBuffer = rc.getBufferObject(elementBufferAttributes);
417414
if (drawState.elementBuffer == null) {
418-
int size = (this.interiorElements.size() * 2) + (this.outlineElements.size() * 2);
419-
ShortBuffer buffer = ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder()).asShortBuffer();
420-
buffer.put(this.interiorElements.array(), 0, this.interiorElements.size());
421-
buffer.put(this.outlineElements.array(), 0, this.outlineElements.size());
422-
drawState.elementBuffer = new BufferObject(GLES20.GL_ELEMENT_ARRAY_BUFFER, size, buffer.rewind());
423-
rc.putBufferObject(this.elementBufferKey, drawState.elementBuffer);
415+
elementBufferAttributes = assembleElementsToCache(rc, this.intervals);
416+
drawState.elementBuffer = rc.getBufferObject(elementBufferAttributes);
424417
}
425418

426-
this.drawInterior(rc, drawState);
427-
this.drawOutline(rc, drawState);
419+
this.drawInterior(rc, drawState, elementBufferAttributes);
420+
this.drawOutline(rc, drawState, elementBufferAttributes);
428421

429422
// Configure the drawable according to the shape's attributes.
430423
drawState.vertexOrigin.set(this.vertexOrigin);
@@ -436,7 +429,7 @@ protected void makeDrawable(RenderContext rc) {
436429
rc.offerSurfaceDrawable(drawable, 0 /*zOrder*/);
437430
}
438431

439-
protected void drawInterior(RenderContext rc, DrawShapeState drawState) {
432+
protected void drawInterior(RenderContext rc, DrawShapeState drawState, ElementBufferAttributes elementBufferAttrs) {
440433
if (!this.activeAttributes.drawInterior) {
441434
return;
442435
}
@@ -446,11 +439,11 @@ protected void drawInterior(RenderContext rc, DrawShapeState drawState) {
446439
// Configure the drawable to display the shape's interior.
447440
drawState.color(rc.pickMode ? this.pickColor : this.activeAttributes.interiorColor);
448441
drawState.texCoordAttrib(2 /*size*/, 12 /*offset in bytes*/);
449-
drawState.drawElements(GLES20.GL_TRIANGLE_STRIP, this.interiorElements.size(),
450-
GLES20.GL_UNSIGNED_SHORT, 0 /*offset*/);
442+
drawState.drawElements(GLES20.GL_TRIANGLE_STRIP, elementBufferAttrs.interiorElements.length(),
443+
GLES20.GL_UNSIGNED_SHORT, elementBufferAttrs.interiorElements.lower * 2 /*offset*/);
451444
}
452445

453-
protected void drawOutline(RenderContext rc, DrawShapeState drawState) {
446+
protected void drawOutline(RenderContext rc, DrawShapeState drawState, ElementBufferAttributes elementBufferAttrs) {
454447
if (!this.activeAttributes.drawOutline) {
455448
return;
456449
}
@@ -461,8 +454,8 @@ protected void drawOutline(RenderContext rc, DrawShapeState drawState) {
461454
drawState.color(rc.pickMode ? this.pickColor : this.activeAttributes.outlineColor);
462455
drawState.lineWidth(this.activeAttributes.outlineWidth);
463456
drawState.texCoordAttrib(1 /*size*/, 20 /*offset in bytes*/);
464-
drawState.drawElements(GLES20.GL_LINE_LOOP, this.outlineElements.size(),
465-
GLES20.GL_UNSIGNED_SHORT, this.interiorElements.size() * 2 /*offset*/);
457+
drawState.drawElements(GLES20.GL_LINE_LOOP, elementBufferAttrs.outlineElements.length(),
458+
GLES20.GL_UNSIGNED_SHORT, elementBufferAttrs.outlineElements.lower * 2 /*offset*/);
466459
}
467460

468461
protected boolean mustAssembleGeometry(RenderContext rc) {
@@ -477,11 +470,8 @@ protected boolean mustAssembleGeometry(RenderContext rc) {
477470
}
478471

479472
protected void assembleGeometry(RenderContext rc) {
480-
// Clear the shape's vertex array and element arrays. These arrays will accumulate values as the shapes's
481-
// geometry is assembled.
473+
// Clear the shape's vertex array. The array will accumulate values as the shapes's geometry is assembled.
482474
this.vertexArray.clear();
483-
this.interiorElements.clear();
484-
this.outlineElements.clear();
485475

486476
// Use the ellipse's center position as the local origin for vertex positions.
487477
this.vertexOrigin.set(this.center.longitude, this.center.latitude, this.center.altitude);
@@ -537,39 +527,61 @@ protected void assembleGeometry(RenderContext rc) {
537527
this.boundingBox.setToUnitBox(); // Surface/geographic shape bounding box is unused
538528
}
539529

540-
protected void assembleElements(RenderContext rc) {
530+
protected static ElementBufferAttributes assembleElementsToCache(RenderContext rc, int intervals) {
531+
// Create temporary storage for elements
532+
ShortArray interiorElements = new ShortArray();
533+
ShortArray outlineElements = new ShortArray();
534+
541535
// Generate the interior element buffer with spine
542-
int interiorIdx = this.intervals;
536+
int interiorIdx = intervals;
543537
// Add the anchor leg
544-
this.interiorElements.add((short) 0);
545-
this.interiorElements.add((short) 1);
538+
interiorElements.add((short) 0);
539+
interiorElements.add((short) 1);
546540
// Tessellate the interior
547-
for (int i = 2; i < this.intervals; i++) {
541+
for (int i = 2; i < intervals; i++) {
548542
// Add the corresponding interior spine point if this isn't the vertex following the last vertex for the
549543
// negative major axis
550-
if (i != (this.intervals / 2 + 1)) {
551-
if (i > this.intervals / 2) {
552-
this.interiorElements.add((short) --interiorIdx);
544+
if (i != (intervals / 2 + 1)) {
545+
if (i > intervals / 2) {
546+
interiorElements.add((short) --interiorIdx);
553547
} else {
554-
this.interiorElements.add((short) interiorIdx++);
548+
interiorElements.add((short) interiorIdx++);
555549
}
556550
}
557551
// Add the degenerate triangle at the negative major axis in order to flip the triangle strip back towards
558552
// the positive axis
559-
if (i == this.intervals / 2) {
560-
this.interiorElements.add((short) i);
553+
if (i == intervals / 2) {
554+
interiorElements.add((short) i);
561555
}
562556
// Add the exterior vertex
563-
this.interiorElements.add((short) i);
557+
interiorElements.add((short) i);
564558
}
565559
// Complete the strip
566-
this.interiorElements.add((short) --interiorIdx);
567-
this.interiorElements.add((short) 0);
560+
interiorElements.add((short) --interiorIdx);
561+
interiorElements.add((short) 0);
568562

569563
// Generate the outline element buffer
570-
for (int i = 0; i < this.intervals; i++) {
571-
this.outlineElements.add((short) i);
564+
for (int i = 0; i < intervals; i++) {
565+
outlineElements.add((short) i);
572566
}
567+
568+
// Generate an attribute bundle for this element buffer
569+
ElementBufferAttributes elementBufferAttributes = new ElementBufferAttributes();
570+
elementBufferAttributes.interiorElements.set(0, interiorElements.size());
571+
elementBufferAttributes.outlineElements.set(interiorElements.size(), interiorElements.size() + outlineElements.size());
572+
573+
// Generate a buffer for the element
574+
int size = (interiorElements.size() * 2) + (outlineElements.size() * 2);
575+
ShortBuffer buffer = ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder()).asShortBuffer();
576+
buffer.put(interiorElements.array(), 0, interiorElements.size());
577+
buffer.put(outlineElements.array(), 0, outlineElements.size());
578+
BufferObject elementBuffer = new BufferObject(GLES20.GL_ELEMENT_ARRAY_BUFFER, size, buffer.rewind());
579+
580+
// Cache the buffer object and attributes in the render resource cache and attribute map respectively
581+
rc.putBufferObject(elementBufferAttributes, elementBuffer);
582+
ELEMENT_BUFFER_ATTRIBUTES.put(intervals, elementBufferAttributes);
583+
584+
return elementBufferAttributes;
573585
}
574586

575587
protected void addVertex(RenderContext rc, double latitude, double longitude, double altitude) {
@@ -625,7 +637,30 @@ protected double computeCircumference() {
625637
@Override
626638
protected void reset() {
627639
this.vertexArray.clear();
628-
this.interiorElements.clear();
629-
this.outlineElements.clear();
640+
}
641+
642+
protected static class ElementBufferAttributes {
643+
644+
protected Range interiorElements = new Range();
645+
646+
protected Range outlineElements = new Range();
647+
648+
@Override
649+
public boolean equals(Object o) {
650+
if (this == o) return true;
651+
if (o == null || getClass() != o.getClass()) return false;
652+
653+
ElementBufferAttributes that = (ElementBufferAttributes) o;
654+
655+
if (!interiorElements.equals(that.interiorElements)) return false;
656+
return outlineElements.equals(that.outlineElements);
657+
}
658+
659+
@Override
660+
public int hashCode() {
661+
int result = interiorElements.hashCode();
662+
result = 31 * result + outlineElements.hashCode();
663+
return result;
664+
}
630665
}
631666
}

0 commit comments

Comments
 (0)