/* * Copyright (c) 2016, Metron, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Metron, Inc. nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL METRON, INC. BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.metsci.glimpse.gl; import static com.jogamp.common.nio.Buffers.*; import static com.metsci.glimpse.gl.util.GLUtils.*; import static com.metsci.glimpse.util.buffer.DirectBufferDealloc.*; import static com.metsci.glimpse.util.buffer.DirectBufferUtils.*; import static java.lang.Math.*; import static javax.media.opengl.GL.*; import java.nio.ByteBuffer; import java.nio.FloatBuffer; import javax.media.opengl.GL; import com.metsci.glimpse.util.primitives.rangeset.IntRangeSet; import com.metsci.glimpse.util.primitives.rangeset.IntRangeSetModifiable; import com.metsci.glimpse.util.primitives.sorted.SortedInts; public class GLEditableBuffer { public final int usage; protected ByteBuffer hBuffer; protected int dBuffer; protected long dCapacity; protected final IntRangeSetModifiable dirtyRanges; public GLEditableBuffer( int usage, int capacityBytes ) { this.usage = usage; this.hBuffer = newDirectByteBuffer( capacityBytes ); this.dBuffer = 0; this.dCapacity = 0; this.dirtyRanges = new IntRangeSetModifiable( ); } public int sizeBytes( ) { return this.hBuffer.position( ); } public ByteBuffer hostBytes( ) { return flipped( readonly( this.hBuffer ) ); } public IntRangeSet dirtyByteRanges( ) { return this.dirtyRanges; } public ByteBuffer growBytes( int countBytes ) { this.ensureRemainingBytes( countBytes ); return this.editBytes( this.sizeBytes( ), countBytes ); } public void ensureRemainingBytes( int minRemainingBytes ) { long minCapacity = ( ( long ) this.hBuffer.position( ) ) + minRemainingBytes; this.hEnsureCapacity( minCapacity ); } public void ensureCapacityBytes( int minCapacityBytes ) { this.hEnsureCapacity( minCapacityBytes ); } protected void hEnsureCapacity( long minCapacity ) { if ( minCapacity > Integer.MAX_VALUE ) { throw new RuntimeException( "Cannot create a buffer larger than MAX_INT bytes: requested-capacity = " + minCapacity + " bytes" ); } this.hBuffer = ensureCapacity( this.hBuffer, ( int ) minCapacity, true ); } public ByteBuffer editBytes( int firstByte, int countBytes ) { this.dirtyRanges.add( firstByte, countBytes ); this.hBuffer.position( max( this.hBuffer.position( ), firstByte + countBytes ) ); return sliced( this.hBuffer, firstByte, countBytes ); } public void clear( ) { this.hBuffer.clear( ); this.dirtyRanges.clear( ); } public int deviceBuffer( GL gl ) { if ( this.dBuffer == 0 ) { this.dBuffer = genBuffer( gl ); } gl.glBindBuffer( GL_ARRAY_BUFFER, this.dBuffer ); int hCapacity = this.hBuffer.capacity( ); if ( this.dCapacity != hCapacity ) { gl.glBufferData( GL_ARRAY_BUFFER, hCapacity, null, this.usage ); this.dCapacity = hCapacity; this.dirtyRanges.add( 0, this.hBuffer.position( ) ); } // glBufferSubData should do its own write-combining anyway, but it may // help performance to reduce the number of calls to glBufferSubData this.dirtyRanges.coalesce( 1024 ); SortedInts ranges = this.dirtyRanges.ranges( ); for ( int i = 0; i < ranges.n( ); i += 2 ) { int first = ranges.v( i + 0 ); int count = ranges.v( i + 1 ) - first; ByteBuffer hRange = sliced( this.hBuffer, first, count ); gl.glBufferSubData( GL_ARRAY_BUFFER, first, count, hRange ); } this.dirtyRanges.clear( ); return this.dBuffer; } public void dispose( GL gl ) { deallocateDirectBuffers( this.hBuffer ); this.hBuffer = null; if ( this.dBuffer != 0 ) { deleteBuffers( gl, this.dBuffer ); this.dBuffer = 0; } this.dirtyRanges.clear( ); } // Floats // public int sizeFloats( ) { return this.sizeBytes( ) / SIZEOF_FLOAT; } public FloatBuffer hostFloats( ) { return this.hostBytes( ).asFloatBuffer( ); } public void grow1f( float a ) { this.growFloats( 1 ).put( a ); } public void grow2f( float a, float b ) { this.growFloats( 2 ).put( a ).put( b ); } public void grow3f( float a, float b, float c ) { this.growFloats( 3 ).put( a ).put( b ).put( c ); } public void grow4f( float a, float b, float c, float d ) { this.growFloats( 4 ).put( a ).put( b ).put( c ).put( d ); } public void growNfv( float[] array, int offset, int length ) { this.growFloats( length ).put( array, offset, length ); } public FloatBuffer growFloats( int countFloats ) { return this.growBytes( countFloats * SIZEOF_FLOAT ).asFloatBuffer( ); } public void ensureRemainingFloats( int minRemainingFloats ) { this.ensureRemainingBytes( minRemainingFloats * SIZEOF_FLOAT ); } public void ensureCapacityFloats( int minCapacityFloats ) { this.ensureCapacityBytes( minCapacityFloats * SIZEOF_FLOAT ); } public FloatBuffer editFloats( int firstFloat, int countFloats ) { return this.editBytes( firstFloat * SIZEOF_FLOAT, countFloats * SIZEOF_FLOAT ).asFloatBuffer( ); } // Convenience methods // public void growQuad2f( float left, float bottom, float right, float top ) { FloatBuffer floats = this.growFloats( 12 ); put2f( floats, left, bottom ); put2f( floats, left, top ); put2f( floats, right, bottom ); put2f( floats, right, bottom ); put2f( floats, left, top ); put2f( floats, right, top ); } public void growQuad1f( float leftBottom, float leftTop, float rightBottom, float rightTop ) { FloatBuffer floats = this.growFloats( 6 ); put1f( floats, leftBottom ); put1f( floats, leftTop ); put1f( floats, rightBottom ); put1f( floats, rightBottom ); put1f( floats, leftTop ); put1f( floats, rightTop ); } public void growQuadSolidColor( float[] color ) { FloatBuffer floats = this.growFloats( 24 ); floats.put( color ); floats.put( color ); floats.put( color ); floats.put( color ); floats.put( color ); floats.put( color ); } }