Bläddra i källkod

started implementing SSBO

JavaSaBr 7 år sedan
förälder
incheckning
c154b6ae23

+ 15 - 0
jme3-core/src/main/java/com/jme3/renderer/Renderer.java

@@ -37,6 +37,7 @@ import com.jme3.scene.Mesh;
 import com.jme3.scene.VertexBuffer;
 import com.jme3.shader.Shader;
 import com.jme3.shader.Shader.ShaderSource;
+import com.jme3.shader.ShaderStorageBufferObject;
 import com.jme3.system.AppSettings;
 import com.jme3.texture.FrameBuffer;
 import com.jme3.texture.Image;
@@ -267,12 +268,26 @@ public interface Renderer {
      */
     public void updateBufferData(VertexBuffer vb);
 
+    /**
+     * Uploads a shader storage buffer object to the GPU.
+     *
+     * @param ssbo the shader storage buffer object to upload.
+     */
+    public void updateBufferData(ShaderStorageBufferObject ssbo);
+
     /**
      * Deletes a vertex buffer from the GPU.
      * @param vb The vertex buffer to delete
      */
     public void deleteBuffer(VertexBuffer vb);
 
+    /**
+     * Deletes a shader storage buffer object from the GPU.
+     *
+     * @param ssbo the shader storage buffer object to delete.
+     */
+    public void deleteBuffer(ShaderStorageBufferObject ssbo);
+
     /**
      * Renders <code>count</code> meshes, with the geometry data supplied and
      * per-instance data supplied.

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 228 - 228
jme3-core/src/main/java/com/jme3/renderer/opengl/GL.java


+ 65 - 26
jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java

@@ -49,6 +49,7 @@ import com.jme3.shader.Attribute;
 import com.jme3.shader.Shader;
 import com.jme3.shader.Shader.ShaderSource;
 import com.jme3.shader.Shader.ShaderType;
+import com.jme3.shader.ShaderStorageBufferObject;
 import com.jme3.shader.Uniform;
 import com.jme3.texture.FrameBuffer;
 import com.jme3.texture.FrameBuffer.RenderBuffer;
@@ -1050,6 +1051,7 @@ public final class GLRenderer implements Renderer {
         }
     }
 
+    @Override
     public void postFrame() {
         objManager.deleteUnused(this);
         OpenCLObjectManager.getInstance().deleteUnusedObjects();
@@ -1182,38 +1184,17 @@ public final class GLRenderer implements Renderer {
                 Integer i = (Integer) uniform.getValue();
                 gl.glUniform1i(loc, i.intValue());
                 break;
-            case Struct: {
-
-                final ByteBuffer data = (ByteBuffer) uniform.getValue();
-
-                intBuf1.clear();
-                gl3.glGenBuffers(intBuf1);
+            case ShaderStorageBufferObject: {
 
-                final int bufferAddress = intBuf1.get();
-                final int blockIndex = gl3.glGetUniformBlockIndex(shaderId, uniform.getName());
-
-                gl3.glBindBuffer(GL3.GL_UNIFORM_BUFFER, bufferAddress);
-                gl3.glBufferData(GL3.GL_UNIFORM_BUFFER, data, GL.GL_DYNAMIC_DRAW);
-                gl3.glBindBufferBase(GL3.GL_UNIFORM_BUFFER, blockIndex, bufferAddress);
-                break;
-            }
-            case StructArray: {
-
-                //TODO
-                final ByteBuffer data = (ByteBuffer) uniform.getValue();
+                final ShaderStorageBufferObject ssbo = (ShaderStorageBufferObject) uniform.getValue();
+                if (ssbo.getUniqueId() == -1 || ssbo.isUpdateNeeded()) {
+                    updateBufferData(ssbo);
+                }
 
-                intBuf1.clear();
-                gl3.glGenBuffers(intBuf1);
 
-                final int bufferAddress = intBuf1.get();
-                final int blockIndex = gl3.glGetUniformBlockIndex(shaderId, uniform.getName());
 
-                gl3.glBindBuffer(GL3.GL_UNIFORM_BUFFER, bufferAddress);
-                gl3.glBufferData(GL3.GL_UNIFORM_BUFFER, data, GL.GL_DYNAMIC_DRAW);
-                gl3.glBindBufferBase(GL3.GL_UNIFORM_BUFFER, blockIndex, bufferAddress);
                 break;
             }
-
             default:
                 throw new UnsupportedOperationException("Unsupported uniform type: " + uniform.getVarType());
         }
@@ -1229,6 +1210,16 @@ public final class GLRenderer implements Renderer {
         }
     }
 
+    protected void updateShaderStorageBlocks(Shader shader) {
+        ListMap<String, Uniform> uniforms = shader.getUniformMap();
+        for (int i = 0; i < uniforms.size(); i++) {
+            Uniform uniform = uniforms.getValue(i);
+            if (uniform.isUpdateNeeded()) {
+                updateUniform(shader, uniform);
+            }
+        }
+    }
+
     protected void resetUniformLocations(Shader shader) {
         ListMap<String, Uniform> uniforms = shader.getUniformMap();
         for (int i = 0; i < uniforms.size(); i++) {
@@ -2535,6 +2526,37 @@ public final class GLRenderer implements Renderer {
         vb.clearUpdateNeeded();
     }
 
+    @Override
+    public void updateBufferData(final ShaderStorageBufferObject ssbo) {
+
+        final ByteBuffer data = ssbo.getData();
+        if (data == null) {
+            throw new IllegalArgumentException("Can't upload SSBO without data.");
+        }
+
+        int bufferId = ssbo.getId();
+        if (bufferId == -1) {
+
+            // create buffer
+            intBuf1.clear();
+            gl.glGenBuffers(intBuf1);
+            bufferId = intBuf1.get(0);
+
+            ssbo.setId(bufferId);
+
+            objManager.registerObject(ssbo);
+        }
+
+        gl4.glBindBuffer(GL4.GL_SHADER_STORAGE_BUFFER, bufferId);
+
+        data.rewind();
+
+        gl4.glBufferData(GL4.GL_SHADER_STORAGE_BUFFER, data, GL4.GL_DYNAMIC_COPY);
+        gl4.glBindBuffer(GL4.GL_SHADER_STORAGE_BUFFER, 0);
+
+        ssbo.clearUpdateNeeded();
+    }
+
     public void deleteBuffer(VertexBuffer vb) {
         int bufId = vb.getId();
         if (bufId != -1) {
@@ -2548,6 +2570,23 @@ public final class GLRenderer implements Renderer {
         }
     }
 
+    @Override
+    public void deleteBuffer(final ShaderStorageBufferObject ssbo) {
+
+        int bufferId = ssbo.getId();
+        if (bufferId == -1) {
+            return;
+        }
+
+        intBuf1.clear();
+        intBuf1.put(0, bufferId);
+        intBuf1.flip();
+
+        gl.glDeleteBuffers(intBuf1);
+
+        ssbo.resetObject();
+    }
+
     public void clearVertexAttribs() {
         IDList attribList = context.attribIndexList;
         for (int i = 0; i < attribList.oldLen; i++) {

+ 53 - 6
jme3-core/src/main/java/com/jme3/shader/Shader.java

@@ -51,6 +51,11 @@ public final class Shader extends NativeObject {
      * Maps uniform name to the uniform variable.
      */
     private final ListMap<String, Uniform> uniforms;
+
+    /**
+     * Maps storage block name to the storage block variable.
+     */
+    private final ListMap<String, StorageBlock> storageBlocks;
     
     /**
      * Uniforms bound to {@link UniformBinding}s.
@@ -220,10 +225,11 @@ public final class Shader extends NativeObject {
      */
     public Shader(){
         super();
-        shaderSourceList = new ArrayList<ShaderSource>();
-        uniforms = new ListMap<String, Uniform>();
-        attribs = new IntMap<Attribute>();
-        boundUniforms = new ArrayList<Uniform>();
+        shaderSourceList = new ArrayList<>();
+        uniforms = new ListMap<>();
+        storageBlocks = new ListMap<>();
+        attribs = new IntMap<>();
+        boundUniforms = new ArrayList<>();
     }
 
     /**
@@ -240,6 +246,7 @@ public final class Shader extends NativeObject {
         }
         
         uniforms = null;
+        storageBlocks  = null;
         boundUniforms = null;
         attribs = null;
     }
@@ -288,10 +295,40 @@ public final class Shader extends NativeObject {
         return uniform;
     }
 
+    /**
+     * Get or create a storage block by the name.
+     *
+     * @param name the storage block's name.
+     * @return the storage block.
+     */
+    public StorageBlock getStorageBlock(String name) {
+
+        assert name.startsWith("sb_");
+
+        StorageBlock storageBlock = storageBlocks.get(name);
+
+        if (storageBlock == null) {
+            storageBlock = new StorageBlock();
+            storageBlock.name = name;
+            storageBlocks.put(name, storageBlock);
+        }
+
+        return storageBlock;
+    }
+
     public void removeUniform(String name){
         uniforms.remove(name);
     }
 
+    /**
+     * Remove a storage block by the name.
+     *
+     * @param name the storage block's name.
+     */
+    public void removeStorageBlock(String name){
+        storageBlocks.remove(name);
+    }
+
     public Attribute getAttribute(VertexBuffer.Type attribType){
         int ordinal = attribType.ordinal();
         Attribute attrib = attribs.get(ordinal);
@@ -306,7 +343,16 @@ public final class Shader extends NativeObject {
     public ListMap<String, Uniform> getUniformMap(){
         return uniforms;
     }
-    
+
+    /**
+     * Get the storage blocks map.
+     *
+     * @return the storage blocks map.
+     */
+    public ListMap<String, StorageBlock> getStorageBlockMap() {
+        return storageBlocks;
+    }
+
     public ArrayList<Uniform> getBoundUniforms() {
         return boundUniforms;
     }
@@ -320,6 +366,7 @@ public final class Shader extends NativeObject {
         return getClass().getSimpleName() + 
                 "[numSources=" + shaderSourceList.size() +
                 ", numUniforms=" + uniforms.size() +
+                ", numStorageBlocks=" + storageBlocks.size() +
                 ", shaderSources=" + getSources() + "]";
     }
 
@@ -343,7 +390,7 @@ public final class Shader extends NativeObject {
      * Resets all uniforms that do not have the "set-by-current-material" flag
      * to their default value (usually all zeroes or false).
      * When a uniform is modified, that flag is set, to remove the flag,
-     * use {@link #clearUniformsSetByCurrent() }.
+     * use {@link #clearUniformsSetByCurrentFlag() }.
      */
     public void resetUniformsNotSetByCurrent() {
         int size = uniforms.size();

+ 144 - 0
jme3-core/src/main/java/com/jme3/shader/ShaderStorageBufferObject.java

@@ -0,0 +1,144 @@
+package com.jme3.shader;
+
+import com.jme3.renderer.Renderer;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.NativeObject;
+
+import java.nio.ByteBuffer;
+
+/**
+ * The implementation of SSBO.
+ *
+ * @author JavaSaBr
+ */
+public class ShaderStorageBufferObject extends NativeObject {
+
+    /**
+     * The buffer's data.
+     */
+    private ByteBuffer data;
+
+    /**
+     * The binding number.
+     */
+    private int binding;
+
+    public ShaderStorageBufferObject() {
+        this.handleRef = new Object();
+        this.binding = 1;
+    }
+
+    public ShaderStorageBufferObject(final int id) {
+        super(id);
+    }
+
+    /**
+     * Set the binding number.
+     *
+     * @param binding the binding number.
+     */
+    public void setBinding(final int binding) {
+        this.binding = binding;
+    }
+
+    /**
+     * Get the binding number.
+     *
+     * @return the binding number.
+     */
+    public int getBinding() {
+        return binding;
+    }
+
+    /**
+     * Called to initialize the data in the {@link ShaderStorageBufferObject}. Must only
+     * be called once.
+     *
+     * @param data the native byte buffer.
+     */
+    public void setupData(final ByteBuffer data) {
+
+        if (id != -1) {
+            throw new UnsupportedOperationException("Data has already been sent. Cannot setupData again.");
+        } else if (data.isReadOnly()) {
+            throw new IllegalArgumentException("VertexBuffer data cannot be read-only.");
+        }
+
+        this.data = data;
+
+        setUpdateNeeded();
+    }
+
+    /**
+     * Called to update the data in the buffer with new data. Can only
+     * be called after {@link #setupData(java.nio.ByteBuffer) }
+     * has been called. Note that it is fine to call this method on the
+     * data already set, e.g. ssbo.updateData(ssbo.getData()), this will just
+     * set the proper update flag indicating the data should be sent to the GPU
+     * again.
+     * <p>
+     * It is allowed to specify a buffer with different capacity than the
+     * originally set buffer.
+     *
+     * @param data the native data buffer to set.
+     */
+    public void updateData(final ByteBuffer data) {
+
+        if (data == null) {
+            throw new IllegalArgumentException("SSBO can't be without data buffer.");
+        }
+
+        // Check if the data buffer is read-only which is a sign
+        // of a bug on the part of the caller
+        if (data.isReadOnly()) {
+            throw new IllegalArgumentException("SSBO's data cannot be read-only.");
+        }
+
+        this.data = data;
+
+        setUpdateNeeded();
+    }
+
+    /**
+     * Get the buffer's data.
+     *
+     * @return the buffer's data.
+     */
+    public ByteBuffer getData() {
+        return data;
+    }
+
+    @Override
+    public void resetObject() {
+        this.id = -1;
+        setUpdateNeeded();
+    }
+
+    @Override
+    public void deleteObject(final Object rendererObject) {
+
+        if (rendererObject instanceof Renderer) {
+            throw new IllegalArgumentException("This ssbo can't be deleted from " + rendererObject);
+        }
+
+        ((Renderer) rendererObject).deleteBuffer(this);
+    }
+
+    @Override
+    protected void deleteNativeBuffers() {
+        super.deleteNativeBuffers();
+        if (data != null) {
+            BufferUtils.destroyDirectBuffer(data);
+        }
+    }
+
+    @Override
+    public NativeObject createDestructableClone() {
+        return new ShaderStorageBufferObject(id);
+    }
+
+    @Override
+    public long getUniqueId() {
+        return ((long) OBJTYPE_SSBO << 32) | ((long) id);
+    }
+}

+ 455 - 0
jme3-core/src/main/java/com/jme3/shader/StorageBlock.java

@@ -0,0 +1,455 @@
+/*
+ * Copyright (c) 2009-2017 jMonkeyEngine
+ * 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 'jMonkeyEngine' 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 THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS 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.jme3.shader;
+
+import com.jme3.math.*;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.TempVars;
+
+import java.nio.Buffer;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+
+public class StorageBlock extends ShaderVariable {
+
+    private static final Integer ZERO_INT = 0;
+    private static final Float ZERO_FLT = Float.valueOf(0);
+    private static final FloatBuffer ZERO_BUF = BufferUtils.createFloatBuffer(4*4);
+
+    /**
+     * Currently set value of the uniform.
+     */
+    protected Object value = null;
+    
+    /**
+     * For arrays or matrices, efficient format
+     * that can be sent to GL faster.
+     */
+    protected FloatBuffer multiData = null;
+
+    /**
+     * Type of uniform
+     */
+    protected VarType varType;
+
+    /**
+     * Binding to a renderer value, or null if user-defined uniform
+     */
+    protected UniformBinding binding;
+
+    /**
+     * Used to track which uniforms to clear to avoid
+     * values leaking from other materials that use that shader.
+     */
+    protected boolean setByCurrentMaterial = false;
+
+    @Override
+    public int hashCode() {
+        int hash = 5;
+        hash = 31 * hash + (this.value != null ? this.value.hashCode() : 0);
+        hash = 31 * hash + (this.varType != null ? this.varType.hashCode() : 0);
+        hash = 31 * hash + (this.binding != null ? this.binding.hashCode() : 0);
+        return hash;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        final StorageBlock other = (StorageBlock) obj;
+        if (this.value != other.value && (this.value == null || !this.value.equals(other.value))) {
+            return false;
+        }
+        return this.binding == other.binding && this.varType == other.varType;
+    }
+
+    @Override
+    public String toString(){
+        StringBuilder sb = new StringBuilder();
+        sb.append("Uniform[name=");
+        sb.append(name);
+        if (varType != null){
+            sb.append(", type=");
+            sb.append(varType);
+            sb.append(", value=");
+            sb.append(value);
+        }else{
+            sb.append(", value=<not set>");
+        }
+        sb.append("]");
+        return sb.toString();
+    }
+
+    public void setBinding(UniformBinding binding){
+        this.binding = binding;
+    }
+
+    public UniformBinding getBinding(){
+        return binding;
+    }
+
+    public VarType getVarType() {
+        return varType;
+    }
+
+    public Object getValue(){
+        return value;
+    }
+    
+    public FloatBuffer getMultiData() {
+        return multiData;
+    }
+
+    public boolean isSetByCurrentMaterial() {
+        return setByCurrentMaterial;
+    }
+
+    public void clearSetByCurrentMaterial(){
+        setByCurrentMaterial = false;
+    }
+
+    public void clearValue(){
+        updateNeeded = true;
+
+        if (multiData != null){           
+            multiData.clear();
+
+            while (multiData.remaining() > 0){
+                ZERO_BUF.clear();
+                ZERO_BUF.limit( Math.min(multiData.remaining(), 16) );
+                multiData.put(ZERO_BUF);
+            }
+
+            multiData.clear();
+
+            return;
+        }
+
+        if (varType == null) {
+            return;
+        }
+            
+        switch (varType){
+            case Int:
+                this.value = ZERO_INT;
+                break;
+            case Boolean:
+                this.value = Boolean.FALSE;
+                break;
+            case Float:
+                this.value = ZERO_FLT; 
+                break;
+            case Vector2:
+                if (this.value != null) {
+                    ((Vector2f) this.value).set(Vector2f.ZERO);
+                }
+                break;
+            case Vector3:
+                if (this.value != null) {
+                    ((Vector3f) this.value).set(Vector3f.ZERO);
+                }
+                break;
+            case Vector4:
+                if (this.value != null) {
+                    if (this.value instanceof ColorRGBA) {
+                        ((ColorRGBA) this.value).set(ColorRGBA.BlackNoAlpha);
+                    } else if (this.value instanceof Vector4f) {
+                        ((Vector4f) this.value).set(Vector4f.ZERO);
+                    } else {
+                        ((Quaternion) this.value).set(Quaternion.ZERO);
+                    }
+                }
+                break;
+            default:
+                // won't happen because those are either textures
+                // or multidata types
+        }
+    }
+    
+    public void setValue(VarType type, Object value){
+        if (location == LOC_NOT_DEFINED) {
+            return;
+        }
+
+        if (varType != null && varType != type) {
+            throw new IllegalArgumentException("Expected a " + varType.name() + " value!");
+        }
+
+        if (value == null) {
+            throw new IllegalArgumentException("for uniform " + name + ": value cannot be null");
+        }
+
+        setByCurrentMaterial = true;
+
+        switch (type){
+            case Matrix3:
+                if (value.equals(this.value)) {
+                    return;
+                }
+                Matrix3f m3 = (Matrix3f) value;
+                if (multiData == null) {
+                    multiData = BufferUtils.createFloatBuffer(9);
+                }
+                m3.fillFloatBuffer(multiData, true);
+                multiData.clear();
+                if (this.value == null) {
+                    this.value = new Matrix3f(m3);
+                } else {
+                    ((Matrix3f)this.value).set(m3);
+                }
+                break;
+            case Matrix4:
+                if (value.equals(this.value)) {
+                    return;
+                }
+                Matrix4f m4 = (Matrix4f) value;
+                if (multiData == null) {
+                    multiData = BufferUtils.createFloatBuffer(16);
+                }
+                m4.fillFloatBuffer(multiData, true);
+                multiData.clear();
+                if (this.value == null) {
+                    this.value = new Matrix4f(m4);
+                } else {
+                    ((Matrix4f)this.value).copy(m4);
+                }
+                break;
+            case IntArray:
+                int[] ia = (int[]) value;
+                if (this.value == null) {
+                    this.value = BufferUtils.createIntBuffer(ia);
+                } else {
+                    this.value = BufferUtils.ensureLargeEnough((IntBuffer)this.value, ia.length);
+                }
+                ((IntBuffer)this.value).clear();
+                break;
+            case FloatArray:
+                float[] fa = (float[]) value;
+                if (multiData == null) {
+                    multiData = BufferUtils.createFloatBuffer(fa);
+                } else {
+                    multiData = BufferUtils.ensureLargeEnough(multiData, fa.length);
+                }
+                multiData.put(fa);
+                multiData.clear();
+                break;
+            case Vector2Array:
+                Vector2f[] v2a = (Vector2f[]) value;
+                if (multiData == null) {
+                    multiData = BufferUtils.createFloatBuffer(v2a);
+                } else {
+                    multiData = BufferUtils.ensureLargeEnough(multiData, v2a.length * 2);
+                }
+                for (int i = 0; i < v2a.length; i++) {
+                    BufferUtils.setInBuffer(v2a[i], multiData, i);
+                }
+                multiData.clear();
+                break;
+            case Vector3Array:
+                Vector3f[] v3a = (Vector3f[]) value;
+                if (multiData == null) {
+                    multiData = BufferUtils.createFloatBuffer(v3a);
+                } else {
+                    multiData = BufferUtils.ensureLargeEnough(multiData, v3a.length * 3);
+                }
+                for (int i = 0; i < v3a.length; i++) {
+                    BufferUtils.setInBuffer(v3a[i], multiData, i);
+                }
+                multiData.clear();
+                break;
+            case Vector4Array:
+                Vector4f[] v4a = (Vector4f[]) value;
+                if (multiData == null) {
+                    multiData = BufferUtils.createFloatBuffer(v4a);
+                } else {
+                    multiData = BufferUtils.ensureLargeEnough(multiData, v4a.length * 4);
+                }
+                for (int i = 0; i < v4a.length; i++) {
+                    BufferUtils.setInBuffer(v4a[i], multiData, i);
+                }
+                multiData.clear();
+                break;
+            case Matrix3Array:
+                Matrix3f[] m3a = (Matrix3f[]) value;
+                if (multiData == null) {
+                    multiData = BufferUtils.createFloatBuffer(m3a.length * 9);
+                } else {
+                    multiData = BufferUtils.ensureLargeEnough(multiData, m3a.length * 9);
+                }
+                for (int i = 0; i < m3a.length; i++) {
+                    m3a[i].fillFloatBuffer(multiData, true);
+                }
+                multiData.clear();
+                break;
+            case Matrix4Array:
+                Matrix4f[] m4a = (Matrix4f[]) value;
+                if (multiData == null) {
+                    multiData = BufferUtils.createFloatBuffer(m4a.length * 16);
+                } else {
+                    multiData = BufferUtils.ensureLargeEnough(multiData, m4a.length * 16);
+                }
+                for (int i = 0; i < m4a.length; i++) {
+                    m4a[i].fillFloatBuffer(multiData, true);
+                }
+                multiData.clear();
+                break;
+            case Vector2:
+                if (value.equals(this.value)) {
+                    return;
+                }
+                if (this.value == null) {
+                    this.value = new Vector2f((Vector2f) value);
+                } else {
+                    ((Vector2f) this.value).set((Vector2f) value);
+                }
+                break;
+            case Vector3:
+                if (value.equals(this.value)) {
+                    return;
+                }
+                if (this.value == null) {
+                    this.value = new Vector3f((Vector3f) value);
+                } else {
+                    ((Vector3f) this.value).set((Vector3f) value);
+                }
+                break;
+            case Vector4:
+                if (value.equals(this.value)) {
+                    return;
+                }
+
+                TempVars vars = TempVars.get();
+                Vector4f vec4 = vars.vect4f1;
+                //handle the null case
+                if (this.value == null) {
+                    try {
+                        this.value = value.getClass().newInstance();
+                    } catch (InstantiationException | IllegalAccessException e) {
+                        throw new IllegalArgumentException("Cannot instanciate param of class " + value.getClass().getCanonicalName());
+                    }
+                }
+                //feed the pivot vec 4 with the correct value
+                if (value instanceof ColorRGBA) {
+                    ColorRGBA c = (ColorRGBA) value;
+                    vec4.set(c.r, c.g, c.b, c.a);
+                } else if (value instanceof Vector4f) {
+                    vec4.set((Vector4f) value);
+                } else {
+                    Quaternion q = (Quaternion) value;
+                    vec4.set(q.getX(), q.getY(), q.getZ(), q.getW());
+                }
+
+                //feed this.value with the collected values.
+                if (this.value instanceof ColorRGBA) {
+                    ((ColorRGBA) this.value).set(vec4.x, vec4.y, vec4.z, vec4.w);
+                } else if (value instanceof Vector4f) {
+                    ((Vector4f) this.value).set(vec4);
+                } else {
+                    ((Quaternion) this.value).set(vec4.x, vec4.y, vec4.z, vec4.w);
+                }
+                vars.release();
+                break;
+                // Only use check if equals optimization for primitive values
+            case Int:
+            case Float:
+            case Boolean:
+                if (value.equals(this.value)) {
+                    return;
+                }
+                this.value = value;
+                break;
+            default:
+                this.value = value;
+                break;
+        }
+
+//        if (multiData != null) {
+//            this.value = multiData;
+//        }
+
+        varType = type;
+        updateNeeded = true;
+    }
+
+    public void setVector4Length(int length){
+        if (location == -1) {
+            return;
+        }
+        
+        multiData = BufferUtils.ensureLargeEnough(multiData, length * 4);
+        value = multiData;
+        varType = VarType.Vector4Array;
+        updateNeeded = true;
+        setByCurrentMaterial = true;
+    }
+
+    public void setVector4InArray(float x, float y, float z, float w, int index){
+        if (location == -1) {
+            return;
+        }
+
+        if (varType != null && varType != VarType.Vector4Array) {
+            throw new IllegalArgumentException("Expected a " + varType.name() + " value!");
+        }
+
+        multiData.position(index * 4);
+        multiData.put(x).put(y).put(z).put(w);
+        multiData.rewind();
+        updateNeeded = true;
+        setByCurrentMaterial = true;
+    }
+    
+    public boolean isUpdateNeeded(){
+        return updateNeeded;
+    }
+
+    public void clearUpdateNeeded(){
+        updateNeeded = false;
+    }
+
+    public void reset(){
+        setByCurrentMaterial = false;
+        location = -2;
+        updateNeeded = true;
+    }
+
+    public void deleteNativeBuffers() {
+        if (value instanceof Buffer) {
+            BufferUtils.destroyDirectBuffer((Buffer)value);
+            value = null; // ????
+        }
+    }
+}

+ 1 - 2
jme3-core/src/main/java/com/jme3/shader/VarType.java

@@ -58,8 +58,7 @@ public enum VarType {
     TextureArray(false,true,"sampler2DArray|sampler2DArrayShadow"),
     TextureCubeMap(false,true,"samplerCube"),
     Int("int"),
-    Struct(false, false, "dynamic"),
-    StructArray(false, false, "dynamic");
+    ShaderStorageBufferObject(false, false, "dynamic");
 
     private boolean usesMultiData = false;
     private boolean textureType = false;

+ 9 - 0
jme3-core/src/main/java/com/jme3/system/NullRenderer.java

@@ -46,6 +46,7 @@ import com.jme3.scene.Mesh;
 import com.jme3.scene.VertexBuffer;
 import com.jme3.shader.Shader;
 import com.jme3.shader.Shader.ShaderSource;
+import com.jme3.shader.ShaderStorageBufferObject;
 import com.jme3.texture.FrameBuffer;
 import com.jme3.texture.Image;
 import com.jme3.texture.Texture;
@@ -148,9 +149,17 @@ public class NullRenderer implements Renderer {
     public void updateBufferData(VertexBuffer vb) {
     }
 
+    @Override
+    public void updateBufferData(ShaderStorageBufferObject ssbo) {
+    }
+
     public void deleteBuffer(VertexBuffer vb) {
     }
 
+    @Override
+    public void deleteBuffer(ShaderStorageBufferObject ssbo) {
+    }
+
     public void renderMesh(Mesh mesh, int lod, int count, VertexBuffer[] instanceData) {
     }
 

+ 2 - 1
jme3-core/src/main/java/com/jme3/util/NativeObject.java

@@ -52,7 +52,8 @@ public abstract class NativeObject implements Cloneable {
                                OBJTYPE_SHADERSOURCE = 5,
                                OBJTYPE_AUDIOBUFFER  = 6,
                                OBJTYPE_AUDIOSTREAM  = 7,
-                               OBJTYPE_FILTER       = 8;
+                               OBJTYPE_FILTER       = 8,
+                               OBJTYPE_SSBO         = 9;
     
     /**
      * The object manager to which this NativeObject is registered to.

Vissa filer visades inte eftersom för många filer har ändrats