Ver código fonte

point particles: improve performance by x3

 * Use an interleaved VBO to reduce BufferData calls
 * Preload the VBO before rendering
 * Store the results into an intermediate float array to speed up copy
Kirill Vainer 9 anos atrás
pai
commit
d76cb99772

+ 3 - 2
jme3-core/src/main/java/com/jme3/effect/ParticleEmitter.java

@@ -1092,7 +1092,8 @@ public class ParticleEmitter extends Geometry {
 
             inverseRotation = this.getWorldRotation().toRotationMatrix(vars.tempMat3).invertLocal();
         }
-        particleMesh.updateParticleData(particles, cam, inverseRotation);
+        particleMesh.updateParticleData(rm, particles, cam, inverseRotation);
+
         if (!worldSpace) {
             vars.release();
         }
@@ -1100,7 +1101,7 @@ public class ParticleEmitter extends Geometry {
 
     public void preload(RenderManager rm, ViewPort vp) {
         this.updateParticleState(0);
-        particleMesh.updateParticleData(particles, vp.getCamera(), Matrix3f.IDENTITY);
+        particleMesh.updateParticleData(rm, particles, vp.getCamera(), Matrix3f.IDENTITY);
     }
 
     @Override

+ 2 - 1
jme3-core/src/main/java/com/jme3/effect/ParticleMesh.java

@@ -34,6 +34,7 @@ package com.jme3.effect;
 import com.jme3.material.RenderState;
 import com.jme3.math.Matrix3f;
 import com.jme3.renderer.Camera;
+import com.jme3.renderer.RenderManager;
 import com.jme3.scene.Mesh;
 
 /**
@@ -80,6 +81,6 @@ public abstract class ParticleMesh extends Mesh {
     /**
      * Update the particle visual data. Typically called every frame.
      */
-    public abstract void updateParticleData(Particle[] particles, Camera cam, Matrix3f inverseRotation);
+    public abstract void updateParticleData(RenderManager rm, Particle[] particles, Camera cam, Matrix3f inverseRotation);
 
 }

+ 78 - 94
jme3-core/src/main/java/com/jme3/effect/ParticlePointMesh.java

@@ -33,15 +33,22 @@ package com.jme3.effect;
 
 import com.jme3.math.Matrix3f;
 import com.jme3.renderer.Camera;
+import com.jme3.renderer.RenderManager;
 import com.jme3.scene.VertexBuffer;
 import com.jme3.scene.VertexBuffer.Format;
 import com.jme3.scene.VertexBuffer.Usage;
 import com.jme3.util.BufferUtils;
+import com.jme3.util.TempVars;
 import java.nio.ByteBuffer;
-import java.nio.FloatBuffer;
 
 public class ParticlePointMesh extends ParticleMesh {
 
+    private static final int POS_SIZE = 3 * 4;
+    private static final int COLOR_SIZE = 4 * 1;
+    private static final int SIZE_SIZE = 1 * 4;
+    private static final int UV_SIZE = 4 * 4;
+    private static final int TOTAL_SIZE = POS_SIZE + COLOR_SIZE + SIZE_SIZE + UV_SIZE;
+
     private ParticleEmitter emitter;
 
     private int imagesX = 1;
@@ -59,109 +66,86 @@ public class ParticlePointMesh extends ParticleMesh {
 
         this.emitter = emitter;
 
-        // set positions
-        FloatBuffer pb = BufferUtils.createVector3Buffer(numParticles);
-        
-        //if the buffer is already set only update the data
-        VertexBuffer buf = getBuffer(VertexBuffer.Type.Position);
-        if (buf != null) {
-            buf.updateData(pb);
-        } else {
-            VertexBuffer pvb = new VertexBuffer(VertexBuffer.Type.Position);
-            pvb.setupData(Usage.Stream, 3, Format.Float, pb);
-            setBuffer(pvb);
-        }
-
-        // set colors
-        ByteBuffer cb = BufferUtils.createByteBuffer(numParticles * 4);
-        
-        buf = getBuffer(VertexBuffer.Type.Color);
-        if (buf != null) {
-            buf.updateData(cb);
-        } else {
-            VertexBuffer cvb = new VertexBuffer(VertexBuffer.Type.Color);
-            cvb.setupData(Usage.Stream, 4, Format.UnsignedByte, cb);
-            cvb.setNormalized(true);
-            setBuffer(cvb);
-        }
+        ByteBuffer eb = BufferUtils.createByteBuffer(TOTAL_SIZE * numParticles);
+        VertexBuffer vb = new VertexBuffer(VertexBuffer.Type.InterleavedData);
+        vb.setupData(Usage.Stream, 1, Format.Byte, eb);
+        setBuffer(vb);
+
+        VertexBuffer pb = new VertexBuffer(VertexBuffer.Type.Position);
+        pb.setupData(Usage.Stream, 3, Format.Float, eb);
+        pb.updateData(null);
+        pb.setOffset(0);
+        pb.setStride(TOTAL_SIZE);
+        setBuffer(pb);
+
+        VertexBuffer cb = new VertexBuffer(VertexBuffer.Type.Color);
+        cb.setupData(Usage.Stream, 4, Format.UnsignedByte, eb);
+        cb.updateData(null);
+        cb.setNormalized(true);
+        cb.setOffset(POS_SIZE);
+        cb.setStride(TOTAL_SIZE);
+        setBuffer(cb);
+
+        VertexBuffer sb = new VertexBuffer(VertexBuffer.Type.Size);
+        sb.setupData(Usage.Stream, 1, Format.Float, eb);
+        sb.updateData(null);
+        sb.setOffset(POS_SIZE + COLOR_SIZE);
+        sb.setStride(TOTAL_SIZE);
+        setBuffer(sb);
+
+        VertexBuffer tb = new VertexBuffer(VertexBuffer.Type.TexCoord);
+        tb.setupData(Usage.Stream, 4, Format.Float, eb);
+        tb.updateData(null);
+        tb.setOffset(POS_SIZE + COLOR_SIZE + SIZE_SIZE);
+        tb.setStride(TOTAL_SIZE);
+        setBuffer(tb);
 
-        // set sizes
-        FloatBuffer sb = BufferUtils.createFloatBuffer(numParticles);
-        
-        buf = getBuffer(VertexBuffer.Type.Size);
-        if (buf != null) {
-            buf.updateData(sb);
-        } else {
-            VertexBuffer svb = new VertexBuffer(VertexBuffer.Type.Size);
-            svb.setupData(Usage.Stream, 1, Format.Float, sb);
-            setBuffer(svb);
-        }
-
-        // set UV-scale
-        FloatBuffer tb = BufferUtils.createFloatBuffer(numParticles*4);
-        
-        buf = getBuffer(VertexBuffer.Type.TexCoord);
-        if (buf != null) {
-            buf.updateData(tb);
-        } else {
-            VertexBuffer tvb = new VertexBuffer(VertexBuffer.Type.TexCoord);
-            tvb.setupData(Usage.Stream, 4, Format.Float, tb);
-            setBuffer(tvb);
-        }
-        
         updateCounts();
     }
 
     @Override
-    public void updateParticleData(Particle[] particles, Camera cam, Matrix3f inverseRotation) {
-        VertexBuffer pvb = getBuffer(VertexBuffer.Type.Position);
-        FloatBuffer positions = (FloatBuffer) pvb.getData();
+    public void updateParticleData(RenderManager rm, Particle[] particles, Camera cam, Matrix3f inverseRotation) {
+        VertexBuffer eb = getBuffer(VertexBuffer.Type.InterleavedData);
+        ByteBuffer elements = (ByteBuffer) eb.getData();
 
-        VertexBuffer cvb = getBuffer(VertexBuffer.Type.Color);
-        ByteBuffer colors = (ByteBuffer) cvb.getData();
+        float sizeScale = emitter.getWorldScale().x;
 
-        VertexBuffer svb = getBuffer(VertexBuffer.Type.Size);
-        FloatBuffer sizes = (FloatBuffer) svb.getData();
+        TempVars vars = TempVars.get();
+        try {
+            float[] temp = vars.skinTangents;
+            int index = 0;
 
-        VertexBuffer tvb = getBuffer(VertexBuffer.Type.TexCoord);
-        FloatBuffer texcoords = (FloatBuffer) tvb.getData();
+            for (int i = 0; i < particles.length; i++) {
+                Particle p = particles[i];
 
-        float sizeScale = emitter.getWorldScale().x;
+                temp[index++] = p.position.x;
+                temp[index++] = p.position.y;
+                temp[index++] = p.position.z;
+                temp[index++] = Float.intBitsToFloat(p.color.asIntABGR());
+                temp[index++] = p.size * sizeScale;
+
+                int imgX = p.imageIndex % imagesX;
+                int imgY = (p.imageIndex - imgX) / imagesY;
+
+                float startX = ((float) imgX) / imagesX;
+                float startY = ((float) imgY) / imagesY;
+                float endX = startX + (1f / imagesX);
+                float endY = startY + (1f / imagesY);
+
+                temp[index++] = startX;
+                temp[index++] = startY;
+                temp[index++] = endX;
+                temp[index++] = endY;
+            }
+
+            elements.asFloatBuffer().put(temp, 0, (TOTAL_SIZE / 4) * particles.length).flip();
+
+            eb.updateData(elements);
 
-        // update data in vertex buffers
-        positions.rewind();
-        colors.rewind();
-        sizes.rewind();
-        texcoords.rewind();
-        for (int i = 0; i < particles.length; i++){
-            Particle p = particles[i];
-            
-            positions.put(p.position.x)
-                     .put(p.position.y)
-                     .put(p.position.z);
-
-            sizes.put(p.size * sizeScale);
-            colors.putInt(p.color.asIntABGR());
-
-            int imgX = p.imageIndex % imagesX;
-            int imgY = (p.imageIndex - imgX) / imagesY;
-
-            float startX = ((float) imgX) / imagesX;
-            float startY = ((float) imgY) / imagesY;
-            float endX   = startX + (1f / imagesX);
-            float endY   = startY + (1f / imagesY);
-
-            texcoords.put(startX).put(startY).put(endX).put(endY);
+            // cheating!
+            rm.getRenderer().updateBufferData(eb);
+        } finally {
+            vars.release();
         }
-        positions.flip();
-        colors.flip();
-        sizes.flip();
-        texcoords.flip();
-
-        // force renderer to re-send data to GPU
-        pvb.updateData(positions);
-        cvb.updateData(colors);
-        svb.updateData(sizes);
-        tvb.updateData(texcoords);
     }
 }

+ 2 - 1
jme3-core/src/main/java/com/jme3/effect/ParticleTriMesh.java

@@ -35,6 +35,7 @@ import com.jme3.math.FastMath;
 import com.jme3.math.Matrix3f;
 import com.jme3.math.Vector3f;
 import com.jme3.renderer.Camera;
+import com.jme3.renderer.RenderManager;
 import com.jme3.scene.VertexBuffer;
 import com.jme3.scene.VertexBuffer.Format;
 import com.jme3.scene.VertexBuffer.Usage;
@@ -145,7 +146,7 @@ public class ParticleTriMesh extends ParticleMesh {
     }
 
     @Override
-    public void updateParticleData(Particle[] particles, Camera cam, Matrix3f inverseRotation) {
+    public void updateParticleData(RenderManager rm, Particle[] particles, Camera cam, Matrix3f inverseRotation) {
 //        System.arraycopy(particles, 0, particlesCopy, 0, particlesCopy.length);
 //        comparator.setCamera(cam);
 //        Arrays.sort(particlesCopy, comparator);

+ 13 - 3
jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java

@@ -2680,6 +2680,10 @@ public final class GLRenderer implements Renderer {
 
     private void setupVertexBuffers(Mesh mesh, VertexBuffer[] instanceData) {
         VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData);
+        if (interleavedData != null && interleavedData.isUpdateNeeded()) {
+            updateBufferData(interleavedData);
+        }
+
         if (instanceData != null) {
             for (VertexBuffer vb : instanceData) {
                 setVertexAttribVAO(vb, null);
@@ -2707,9 +2711,7 @@ public final class GLRenderer implements Renderer {
 
     private void updateVertexBuffers(Mesh mesh, VertexBuffer[] instanceData) {
         VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData);
-        if (interleavedData != null && interleavedData.isUpdateNeeded()) {
-            updateBufferData(interleavedData);
-        }
+
         if (instanceData != null) {
             for (VertexBuffer vb : instanceData) {
                 if (vb.isUpdateNeeded()) {
@@ -2717,6 +2719,14 @@ public final class GLRenderer implements Renderer {
                 }
             }
         }
+
+        if (interleavedData != null) {
+            if (interleavedData.isUpdateNeeded()) {
+                updateBufferData(interleavedData);
+            }
+            return;
+        }
+
         for (VertexBuffer vb : mesh.getBufferList().getArray()) {
             if (vb.getBufferType() == Type.InterleavedData
                     || vb.getUsage() == Usage.CpuOnly // ignore cpu-only buffers

+ 12 - 4
jme3-core/src/main/java/com/jme3/scene/Mesh.java

@@ -713,13 +713,21 @@ public class Mesh extends NativeObject implements Savable {
      * {@link #setInterleaved() interleaved} format.
      */
     public void updateCounts(){
-        if (getBuffer(Type.InterleavedData) != null)
-            throw new IllegalStateException("Should update counts before interleave");
+//        if (getBuffer(Type.InterleavedData) != null) {
+//            throw new IllegalStateException("Should update counts before interleave");
+//        }
 
         VertexBuffer pb = getBuffer(Type.Position);
         VertexBuffer ib = getBuffer(Type.Index);
-        if (pb != null){
-            vertCount = pb.getData().limit() / pb.getNumComponents();
+        if (pb != null) {
+            VertexBuffer ip = getBuffer(Type.InterleavedData);
+            if (ip != null) {
+                int limitBytes = ip.getData().limit();
+                int elementSizeWithOthers = pb.getStride();
+                vertCount = limitBytes / elementSizeWithOthers;
+            } else {
+                vertCount = pb.getData().limit() / pb.getNumComponents();
+            }
         }
         if (ib != null){
             elementCount = computeNumElements(ib.getData().limit());

+ 1 - 1
jme3-core/src/main/resources/Common/MatDefs/Misc/Particle.vert

@@ -32,7 +32,7 @@ void main(){
     #ifdef POINT_SPRITE
         vec4 worldPos = g_WorldMatrix * pos;
         float d = distance(g_CameraPosition.xyz, worldPos.xyz);
-        float size = (inSize * SIZE_MULTIPLIER * m_Quadratic) / d);
+        float size = (inSize * SIZE_MULTIPLIER * m_Quadratic) / d;
         gl_PointSize = max(1.0, size);
 
         //vec4 worldViewPos = g_WorldViewMatrix * pos;