Modified so that read-only operations will not

clobber each other if done from other threads
and provided a way to safely access the whole
buffer if only read-only access is required.
Round 1 of a fix for GeometryBatchFactory...


git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9098 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
PSp..om 13 years ago
parent 0f41e1fee1
commit 606b676d69
  1. 108
      engine/src/core/com/jme3/scene/VertexBuffer.java

@ -374,11 +374,61 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
} }
/** /**
* Returns the raw internal data buffer used by this VertexBuffer.
* This buffer is not safe to call from multiple threads since buffers
* have their own internal position state that cannot be shared.
* Call getData().duplicate(), getData().asReadOnlyBuffer(), or
* the more convenient getDataReadOnly() if the buffer may be accessed
* from multiple threads.
*
* @return A native buffer, in the specified {@link Format format}. * @return A native buffer, in the specified {@link Format format}.
*/ */
public Buffer getData(){ public Buffer getData(){
return data; return data;
} }
/**
* Returns a safe read-only version of this VertexBuffer's data. The
* contents of the buffer will reflect whatever changes are made on
* other threads (eventually) but these should not be used in that way.
* This method provides a read-only buffer that is safe to _read_ from
* a separate thread since it has its own book-keeping state (position, limit, etc.)
*
* @return A rewound native buffer in the specified {@link Format format}
* that is safe to read from a separate thread from other readers.
*/
public Buffer getDataReadOnly() {
if (data == null) {
return null;
}
// Create a read-only duplicate(). Note: this does not copy
// the underlying memory, it just creates a new read-only wrapper
// with its own buffer position state.
// Unfortunately, this is not 100% straight forward since Buffer
// does not have an asReadOnlyBuffer() method.
Buffer result;
if( data instanceof ByteBuffer ) {
result = ((ByteBuffer)data).asReadOnlyBuffer();
} else if( data instanceof FloatBuffer ) {
result = ((FloatBuffer)data).asReadOnlyBuffer();
} else if( data instanceof ShortBuffer ) {
result = ((ShortBuffer)data).asReadOnlyBuffer();
} else if( data instanceof IntBuffer ) {
result = ((IntBuffer)data).asReadOnlyBuffer();
} else {
throw new UnsupportedOperationException( "Cannot get read-only view of buffer type:" + data );
}
// Make sure the caller gets a consistent view since we may
// have grabbed this buffer while another thread was reading
// the raw data.
result.rewind();
return result;
}
/** /**
* @return The usage of this buffer. See {@link Usage} for more * @return The usage of this buffer. See {@link Usage} for more
@ -470,6 +520,9 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
if (usage == null || format == null || data == null) if (usage == null || format == null || data == null)
throw new IllegalArgumentException("None of the arguments can be null"); throw new IllegalArgumentException("None of the arguments can be null");
if (data.isReadOnly())
throw new IllegalArgumentException( "VertexBuffer data cannot be read-only." );
if (components < 1 || components > 4) if (components < 1 || components > 4)
throw new IllegalArgumentException("components must be between 1 and 4"); throw new IllegalArgumentException("components must be between 1 and 4");
@ -499,6 +552,12 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
// request to update data is okay // request to update data is okay
} }
// Check if the data buffer is read-only which is a sign
// of a bug on the part of the caller
if (data != null && data.isReadOnly()) {
throw new IllegalArgumentException( "VertexBuffer data cannot be read-only." );
}
// will force renderer to call glBufferData again // will force renderer to call glBufferData again
if (data != null && (this.data.getClass() != data.getClass() || data.limit() != lastLimit)){ if (data != null && (this.data.getClass() != data.getClass() || data.limit() != lastLimit)){
dataSizeChanged = true; dataSizeChanged = true;
@ -669,24 +728,24 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
elementPos *= 2; elementPos *= 2;
} }
data.clear(); Buffer srcData = getDataReadOnly();
switch (format){ switch (format){
case Byte: case Byte:
case UnsignedByte: case UnsignedByte:
case Half: case Half:
ByteBuffer bin = (ByteBuffer) data; ByteBuffer bin = (ByteBuffer) srcData;
return bin.get(inPos + elementPos); return bin.get(inPos + elementPos);
case Short: case Short:
case UnsignedShort: case UnsignedShort:
ShortBuffer sin = (ShortBuffer) data; ShortBuffer sin = (ShortBuffer) srcData;
return sin.get(inPos + elementPos); return sin.get(inPos + elementPos);
case Int: case Int:
case UnsignedInt: case UnsignedInt:
IntBuffer iin = (IntBuffer) data; IntBuffer iin = (IntBuffer) srcData;
return iin.get(inPos + elementPos); return iin.get(inPos + elementPos);
case Float: case Float:
FloatBuffer fin = (FloatBuffer) data; FloatBuffer fin = (FloatBuffer) srcData;
return fin.get(inPos + elementPos); return fin.get(inPos + elementPos);
default: default:
throw new UnsupportedOperationException("Unrecognized buffer format: "+format); throw new UnsupportedOperationException("Unrecognized buffer format: "+format);
@ -718,14 +777,17 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
elementSz *= 2; elementSz *= 2;
} }
data.clear(); // Make sure to grab a read-only copy in case some other
// thread is also accessing the buffer and messing with its
// position()
Buffer srcData = getDataReadOnly();
outVb.data.clear(); outVb.data.clear();
switch (format){ switch (format){
case Byte: case Byte:
case UnsignedByte: case UnsignedByte:
case Half: case Half:
ByteBuffer bin = (ByteBuffer) data; ByteBuffer bin = (ByteBuffer) srcData;
ByteBuffer bout = (ByteBuffer) outVb.data; ByteBuffer bout = (ByteBuffer) outVb.data;
bin.position(inPos).limit(inPos + elementSz); bin.position(inPos).limit(inPos + elementSz);
bout.position(outPos).limit(outPos + elementSz); bout.position(outPos).limit(outPos + elementSz);
@ -733,7 +795,7 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
break; break;
case Short: case Short:
case UnsignedShort: case UnsignedShort:
ShortBuffer sin = (ShortBuffer) data; ShortBuffer sin = (ShortBuffer) srcData;
ShortBuffer sout = (ShortBuffer) outVb.data; ShortBuffer sout = (ShortBuffer) outVb.data;
sin.position(inPos).limit(inPos + elementSz); sin.position(inPos).limit(inPos + elementSz);
sout.position(outPos).limit(outPos + elementSz); sout.position(outPos).limit(outPos + elementSz);
@ -741,14 +803,14 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
break; break;
case Int: case Int:
case UnsignedInt: case UnsignedInt:
IntBuffer iin = (IntBuffer) data; IntBuffer iin = (IntBuffer) srcData;
IntBuffer iout = (IntBuffer) outVb.data; IntBuffer iout = (IntBuffer) outVb.data;
iin.position(inPos).limit(inPos + elementSz); iin.position(inPos).limit(inPos + elementSz);
iout.position(outPos).limit(outPos + elementSz); iout.position(outPos).limit(outPos + elementSz);
iout.put(iin); iout.put(iin);
break; break;
case Float: case Float:
FloatBuffer fin = (FloatBuffer) data; FloatBuffer fin = (FloatBuffer) srcData;
FloatBuffer fout = (FloatBuffer) outVb.data; FloatBuffer fout = (FloatBuffer) outVb.data;
fin.position(inPos).limit(inPos + elementSz); fin.position(inPos).limit(inPos + elementSz);
fout.position(outPos).limit(outPos + elementSz); fout.position(outPos).limit(outPos + elementSz);
@ -758,7 +820,6 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
throw new UnsupportedOperationException("Unrecognized buffer format: "+format); throw new UnsupportedOperationException("Unrecognized buffer format: "+format);
} }
data.clear();
outVb.data.clear(); outVb.data.clear();
} }
@ -807,8 +868,13 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
VertexBuffer vb = (VertexBuffer) super.clone(); VertexBuffer vb = (VertexBuffer) super.clone();
vb.handleRef = new Object(); vb.handleRef = new Object();
vb.id = -1; vb.id = -1;
if (data != null) if (data != null) {
vb.updateData(BufferUtils.clone(data)); // Make sure to pass a read-only buffer to clone so that
// the position information doesn't get clobbered by another
// reading thread during cloning (and vice versa) since this is
// a purely read-only operation.
vb.updateData(BufferUtils.clone(getDataReadOnly()));
}
return vb; return vb;
} }
@ -824,7 +890,12 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
VertexBuffer vb = new VertexBuffer(overrideType); VertexBuffer vb = new VertexBuffer(overrideType);
vb.components = components; vb.components = components;
vb.componentsLength = componentsLength; vb.componentsLength = componentsLength;
vb.data = BufferUtils.clone(data);
// Make sure to pass a read-only buffer to clone so that
// the position information doesn't get clobbered by another
// reading thread during cloning (and vice versa) since this is
// a purely read-only operation.
vb.data = BufferUtils.clone(getDataReadOnly());
vb.format = format; vb.format = format;
vb.handleRef = new Object(); vb.handleRef = new Object();
vb.id = -1; vb.id = -1;
@ -876,22 +947,23 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable {
oc.write(stride, "stride", 0); oc.write(stride, "stride", 0);
String dataName = "data" + format.name(); String dataName = "data" + format.name();
Buffer roData = getDataReadOnly();
switch (format){ switch (format){
case Float: case Float:
oc.write((FloatBuffer) data, dataName, null); oc.write((FloatBuffer) roData, dataName, null);
break; break;
case Short: case Short:
case UnsignedShort: case UnsignedShort:
oc.write((ShortBuffer) data, dataName, null); oc.write((ShortBuffer) roData, dataName, null);
break; break;
case UnsignedByte: case UnsignedByte:
case Byte: case Byte:
case Half: case Half:
oc.write((ByteBuffer) data, dataName, null); oc.write((ByteBuffer) roData, dataName, null);
break; break;
case Int: case Int:
case UnsignedInt: case UnsignedInt:
oc.write((IntBuffer) data, dataName, null); oc.write((IntBuffer) roData, dataName, null);
break; break;
default: default:
throw new IOException("Unsupported export buffer format: "+format); throw new IOException("Unsupported export buffer format: "+format);

Loading…
Cancel
Save