浏览代码

Implements SSBO (Shader Storage Buffer Object) and UBO (Uniform Buffer Object) support

Rémy Bouquet 7 年之前
父节点
当前提交
266d8b0828

+ 62 - 13
jme3-core/src/main/java/com/jme3/material/Material.java

@@ -46,10 +46,7 @@ import com.jme3.renderer.RenderManager;
 import com.jme3.renderer.Renderer;
 import com.jme3.renderer.queue.RenderQueue.Bucket;
 import com.jme3.scene.Geometry;
-import com.jme3.shader.Shader;
-import com.jme3.shader.Uniform;
-import com.jme3.shader.UniformBindingManager;
-import com.jme3.shader.VarType;
+import com.jme3.shader.*;
 import com.jme3.texture.Image;
 import com.jme3.texture.Texture;
 import com.jme3.texture.image.ColorSpace;
@@ -412,6 +409,17 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
         return paramValues.get(name);
     }
 
+    /**
+     * Returns the current parameter's value.
+     *
+     * @param name the parameter name to look up.
+     * @return current value or null if the parameter wasn't set.
+     */
+    public <T> T getParamValue(final String name) {
+        final MatParam param = paramValues.get(name);
+        return param == null ? null : (T) param.getValue();
+    }
+
     /**
      * Returns the texture parameter set on this material with the given name,
      * returns <code>null</code> if the parameter is not set.
@@ -660,6 +668,28 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
         setParam(name, VarType.Vector4, value);
     }
 
+    /**
+     * Pass an uniform buffer object to the material shader.
+     *
+     * @param name  the name of the buffer object defined in the material definition (j3md).
+     * @param value the buffer object.
+     */
+    public void setUniformBufferObject(final String name, final BufferObject value) {
+        value.setBufferType(BufferObject.BufferType.UniformBufferObject);
+        setParam(name, VarType.BufferObject, value);
+    }
+
+    /**
+     * Pass a shader storage buffer object to the material shader.
+     *
+     * @param name  the name of the buffer object defined in the material definition (j3md).
+     * @param value the buffer object.
+     */
+    public void setShaderStorageBufferObject(final String name, final BufferObject value) {
+        value.setBufferType(BufferObject.BufferType.ShaderStorageBufferObject);
+        setParam(name, VarType.BufferObject, value);
+    }
+
     /**
      * Pass a Vector2f to the material shader.
      *
@@ -794,20 +824,29 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
         }
 
         for (int i = 0; i < paramValues.size(); i++) {
+
             MatParam param = paramValues.getValue(i);
             VarType type = param.getVarType();
-            Uniform uniform = shader.getUniform(param.getPrefixedName());
 
-            if (uniform.isSetByCurrentMaterial()) {
-                continue;
-            }
+            if (isBO(type)) {
+
+                final ShaderBufferBlock bufferBlock = shader.getBufferBlock(param.getPrefixedName());
+                bufferBlock.setBufferObject((BufferObject) param.getValue());
 
-            if (type.isTextureType()) {
-                renderer.setTexture(unit, (Texture) param.getValue());
-                uniform.setValue(VarType.Int, unit);
-                unit++;
             } else {
-                uniform.setValue(type, param.getValue());
+
+                Uniform uniform = shader.getUniform(param.getPrefixedName());
+                if (uniform.isSetByCurrentMaterial()) {
+                    continue;
+                }
+
+                if (type.isTextureType()) {
+                    renderer.setTexture(unit, (Texture) param.getValue());
+                    uniform.setValue(VarType.Int, unit);
+                    unit++;
+                } else {
+                    uniform.setValue(type, param.getValue());
+                }
             }
         }
 
@@ -815,6 +854,16 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
         return unit;
     }
 
+    /**
+     * Returns true if the type is Buffer Object's type.
+     *
+     * @param type the material parameter type.
+     * @return true if the type is Buffer Object's type.
+     */
+    private boolean isBO(final VarType type) {
+        return type == VarType.BufferObject;
+    }
+
     private void updateRenderState(RenderManager renderManager, Renderer renderer, TechniqueDef techniqueDef) {
         if (renderManager.getForcedRenderState() != null) {
             renderer.applyRenderState(renderManager.getForcedRenderState());

+ 9 - 1
jme3-core/src/main/java/com/jme3/renderer/Caps.java

@@ -394,7 +394,15 @@ public enum Caps {
     /**
      * GPU can provide and accept binary shaders.
      */
-    BinaryShader;
+    BinaryShader,
+    /**
+     * Supporting working with UniformBufferObject.
+     */
+    UniformBufferObject,
+    /**
+     * Supporting working with ShaderStorageBufferObjects.
+     */
+    ShaderStorageBufferObject;
 
     /**
      * Returns true if given the renderer capabilities, the texture

+ 16 - 0
jme3-core/src/main/java/com/jme3/renderer/Limits.java

@@ -62,4 +62,20 @@ public enum Limits {
     ColorTextureSamples,
     DepthTextureSamples,
     TextureAnisotropy,
+
+    // UBO
+    UniformBufferObjectMaxVertexBlocks,
+    UniformBufferObjectMaxFragmentBlocks,
+    UniformBufferObjectMaxGeometryBlocks,
+    UniformBufferObjectMaxBlockSize,
+
+    // SSBO
+    ShaderStorageBufferObjectMaxBlockSize,
+    ShaderStorageBufferObjectMaxVertexBlocks,
+    ShaderStorageBufferObjectMaxFragmentBlocks,
+    ShaderStorageBufferObjectMaxGeometryBlocks,
+    ShaderStorageBufferObjectMaxTessControlBlocks,
+    ShaderStorageBufferObjectMaxTessEvaluationBlocks,
+    ShaderStorageBufferObjectMaxComputeBlocks,
+    ShaderStorageBufferObjectMaxCombineBlocks,
 }

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

@@ -35,6 +35,7 @@ import com.jme3.material.RenderState;
 import com.jme3.math.ColorRGBA;
 import com.jme3.scene.Mesh;
 import com.jme3.scene.VertexBuffer;
+import com.jme3.shader.BufferObject;
 import com.jme3.shader.Shader;
 import com.jme3.shader.Shader.ShaderSource;
 import com.jme3.system.AppSettings;
@@ -267,12 +268,26 @@ public interface Renderer {
      */
     public void updateBufferData(VertexBuffer vb);
 
+    /**
+     * Uploads data of the buffer object on the GPU.
+     *
+     * @param bo the buffer object to upload.
+     */
+    public void updateBufferData(BufferObject bo);
+
     /**
      * Deletes a vertex buffer from the GPU.
      * @param vb The vertex buffer to delete
      */
     public void deleteBuffer(VertexBuffer vb);
 
+    /**
+     * Deletes the buffer object from the GPU.
+     *
+     * @param bo the buffer object to delete.
+     */
+    public void deleteBuffer(BufferObject bo);
+
     /**
      * Renders <code>count</code> meshes, with the geometry data supplied and
      * per-instance data supplied.

文件差异内容过多而无法显示
+ 228 - 227
jme3-core/src/main/java/com/jme3/renderer/opengl/GL.java


+ 83 - 0
jme3-core/src/main/java/com/jme3/renderer/opengl/GL3.java

@@ -83,6 +83,46 @@ public interface GL3 extends GL2 {
     public static final int GL_RGB_INTEGER = 36248;
     public static final int GL_RGBA_INTEGER = 36249;
 
+    public static final int GL_UNIFORM_OFFSET = 0x8A3B;
+
+    /**
+     * Accepted by the {@code target} parameters of BindBuffer, BufferData, BufferSubData, MapBuffer, UnmapBuffer, GetBufferSubData, and GetBufferPointerv.
+     */
+    public static final int GL_UNIFORM_BUFFER = 0x8A11;
+
+    /**
+     * Accepted by the {@code pname} parameter of GetActiveUniformBlockiv.
+     */
+    public static final int GL_UNIFORM_BLOCK_BINDING = 0x8A3F;
+    public static final int GL_UNIFORM_BLOCK_DATA_SIZE = 0x8A40;
+    public static final int GL_UNIFORM_BLOCK_NAME_LENGTH = 0x8A41;
+    public static final int GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS = 0x8A42;
+    public static final int GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES = 0x8A43;
+    public static final int GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER = 0x8A44;
+    public static final int GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER = 0x8A45;
+    public static final int GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER = 0x8A46;
+
+    /**
+     *  Accepted by the &lt;pname&gt; parameter of GetBooleanv, GetIntegerv,
+     *  GetFloatv, and GetDoublev:
+     */
+    public static final int GL_MAX_VERTEX_UNIFORM_BLOCKS = 0x8A2B;
+    public static final int GL_MAX_GEOMETRY_UNIFORM_BLOCKS = 0x8A2C;
+    public static final int GL_MAX_FRAGMENT_UNIFORM_BLOCKS = 0x8A2D;
+    public static final int GL_MAX_COMBINED_UNIFORM_BLOCKS = 0x8A2E;
+    public static final int GL_MAX_UNIFORM_BUFFER_BINDINGS = 0x8A2F;
+    public static final int GL_MAX_UNIFORM_BLOCK_SIZE = 0x8A30;
+    public static final int GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS = 0x8A31;
+    public static final int GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS = 0x8A32;
+    public static final int GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS = 0x8A33;
+    public static final int GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT = 0x8A34;
+
+    /**
+     * Accepted by the {@code target} parameters of BindBuffer, BufferData, BufferSubData, MapBuffer, UnmapBuffer, GetBufferSubData, GetBufferPointerv,
+     * BindBufferRange, BindBufferOffset and BindBufferBase.
+     */
+    public static final int GL_TRANSFORM_FEEDBACK_BUFFER = 0x8C8E;
+
     /**
      * <p><a target="_blank" href="http://docs.gl/gl4/glBindFragDataLocation">Reference Page</a></p>
      * <p>
@@ -128,4 +168,47 @@ public interface GL3 extends GL2 {
      * @param index the index of the particular element being queried.
      */
     public String glGetString(int name, int index); /// GL3+
+
+
+    /**
+     * <p><a target="_blank" href="http://docs.gl/gl4/glGetUniformBlockIndex">Reference Page</a></p>
+     *
+     * Retrieves the index of a named uniform block.
+     *
+     * @param program          the name of a program containing the uniform block.
+     * @param uniformBlockName an array of characters to containing the name of the uniform block whose index to retrieve.
+     * @return the block index.
+     */
+    public int glGetUniformBlockIndex(int program, String uniformBlockName);
+
+    /**
+     * <p><a target="_blank" href="http://docs.gl/gl4/glBindBufferBase">Reference Page</a></p>
+     *
+     * Binds a buffer object to an indexed buffer target.
+     *
+     * @param target the target of the bind operation. One of:<br><table><tr><td>{@link #GL_TRANSFORM_FEEDBACK_BUFFER TRANSFORM_FEEDBACK_BUFFER}</td><td>{@link #GL_UNIFORM_BUFFER UNIFORM_BUFFER}</td><td>{@link GL4#GL_ATOMIC_COUNTER_BUFFER ATOMIC_COUNTER_BUFFER}</td><td>{@link GL4#GL_SHADER_STORAGE_BUFFER SHADER_STORAGE_BUFFER}</td></tr></table>
+     * @param index  the index of the binding point within the array specified by {@code target}
+     * @param buffer a buffer object to bind to the specified binding point
+     */
+    public void glBindBufferBase(int target, int index, int buffer);
+
+    /**
+     * Binding points for active uniform blocks are assigned using glUniformBlockBinding. Each of a program's active
+     * uniform blocks has a corresponding uniform buffer binding point. program is the name of a program object for
+     * which the command glLinkProgram has been issued in the past.
+     * <p>
+     * If successful, glUniformBlockBinding specifies that program will use the data store of the buffer object bound
+     * to the binding point uniformBlockBinding to extract the values of the uniforms in the uniform block identified
+     * by uniformBlockIndex.
+     * <p>
+     * When a program object is linked or re-linked, the uniform buffer object binding point assigned to each of its
+     * active uniform blocks is reset to zero.
+     *
+     * @param program             The name of a program object containing the active uniform block whose binding to
+     *                            assign.
+     * @param uniformBlockIndex   The index of the active uniform block within program whose binding to assign.
+     * @param uniformBlockBinding Specifies the binding point to which to bind the uniform block with index
+     *                            uniformBlockIndex within program.
+     */
+    public void glUniformBlockBinding(int program, int uniformBlockIndex, int uniformBlockBinding);
 }

+ 50 - 0
jme3-core/src/main/java/com/jme3/renderer/opengl/GL4.java

@@ -42,6 +42,32 @@ public interface GL4 extends GL3 {
     public static final int GL_TESS_EVALUATION_SHADER = 0x8E87;
     public static final int GL_PATCHES = 0xE;
 
+    /**
+     * Accepted by the {@code target} parameter of BindBufferBase and BindBufferRange.
+     */
+    public static final int GL_ATOMIC_COUNTER_BUFFER = 0x92C0;
+
+    /**
+     * Accepted by the {@code target} parameters of BindBuffer, BufferData, BufferSubData, MapBuffer, UnmapBuffer, GetBufferSubData, and GetBufferPointerv.
+     */
+    public static final int GL_SHADER_STORAGE_BUFFER = 0x90D2;
+    public static final int GL_SHADER_STORAGE_BLOCK = 0x92E6;
+
+    /**
+     *  Accepted by the &lt;pname&gt; parameter of GetIntegerv, GetBooleanv,
+     *  GetInteger64v, GetFloatv, and GetDoublev:
+     */
+    public static final int GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS = 0x90D6;
+    public static final int GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS = 0x90D7;
+    public static final int GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS = 0x90D8;
+    public static final int GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS = 0x90D9;
+    public static final int GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS = 0x90DA;
+    public static final int GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS = 0x90DB;
+    public static final int GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS = 0x90DC;
+    public static final int GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS = 0x90DD;
+    public static final int GL_MAX_SHADER_STORAGE_BLOCK_SIZE = 0x90DE;
+    public static final int GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT = 0x90DF;
+
     /**
      * <p><a target="_blank" href="http://docs.gl/gl4/glPatchParameteri">Reference Page</a></p>
      * <p>
@@ -50,4 +76,28 @@ public interface GL4 extends GL3 {
      * @param count the new value for the parameter given by {@code pname}
      */
     public void glPatchParameter(int count);
+
+    /**
+     * Returns the unsigned integer index assigned to a resource named name in the interface type programInterface of
+     * program object program.
+     *
+     * @param program          the name of a program object whose resources to query.
+     * @param programInterface a token identifying the interface within program containing the resource named name.
+     * @param name             the name of the resource to query the index of.
+     * @return the index of a named resource within a program.
+     */
+    public int glGetProgramResourceIndex(int program, int programInterface, String name);
+
+    /**
+     * Cchanges the active shader storage block with an assigned index of storageBlockIndex in program object program.
+     * storageBlockIndex must be an active shader storage block index in program. storageBlockBinding must be less
+     * than the value of {@code #GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS}. If successful, glShaderStorageBlockBinding specifies
+     * that program will use the data store of the buffer object bound to the binding point storageBlockBinding to
+     * read and write the values of the buffer variables in the shader storage block identified by storageBlockIndex.
+     *
+     * @param program             the name of a program object whose resources to query.
+     * @param storageBlockIndex   The index storage block within the program.
+     * @param storageBlockBinding The index storage block binding to associate with the specified storage block.
+     */
+    public void glShaderStorageBlockBinding(int program, int storageBlockIndex, int storageBlockBinding);
 }

+ 32 - 0
jme3-core/src/main/java/com/jme3/renderer/opengl/GLDebugDesktop.java

@@ -83,6 +83,19 @@ public class GLDebugDesktop extends GLDebugES implements GL2, GL3, GL4 {
         return result;
     }
 
+    @Override
+    public int glGetUniformBlockIndex(final int program, final String uniformBlockName) {
+        final int result = gl3.glGetUniformBlockIndex(program, uniformBlockName);
+        checkError();
+        return result;
+    }
+
+    @Override
+    public void glBindBufferBase(final int target, final int index, final int buffer) {
+        gl3.glBindBufferBase(target, index, buffer);
+        checkError();
+    }
+
     @Override
     public void glDeleteVertexArrays(IntBuffer arrays) {
         gl3.glDeleteVertexArrays(arrays);
@@ -95,8 +108,27 @@ public class GLDebugDesktop extends GLDebugES implements GL2, GL3, GL4 {
         checkError();
     }
 
+    @Override
+    public int glGetProgramResourceIndex(int program, int programInterface, String name) {
+        final int result = gl4.glGetProgramResourceIndex(program, programInterface, name);
+        checkError();
+        return result;
+    }
+
+    @Override
+    public void glShaderStorageBlockBinding(int program, int storageBlockIndex, int storageBlockBinding) {
+        gl4.glShaderStorageBlockBinding(program, storageBlockIndex, storageBlockBinding);
+        checkError();
+    }
+
     public void glBlendEquationSeparate(int colorMode, int alphaMode) {
         gl.glBlendEquationSeparate(colorMode, alphaMode);
         checkError();
     }
+
+    @Override
+    public void glUniformBlockBinding(final int program, final int uniformBlockIndex, final int uniformBlockBinding) {
+        gl3.glUniformBlockBinding(program, uniformBlockIndex, uniformBlockBinding);
+        checkError();
+    }
 }

+ 175 - 22
jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java

@@ -45,11 +45,9 @@ import com.jme3.scene.VertexBuffer;
 import com.jme3.scene.VertexBuffer.Format;
 import com.jme3.scene.VertexBuffer.Type;
 import com.jme3.scene.VertexBuffer.Usage;
-import com.jme3.shader.Attribute;
-import com.jme3.shader.Shader;
+import com.jme3.shader.*;
 import com.jme3.shader.Shader.ShaderSource;
 import com.jme3.shader.Shader.ShaderType;
-import com.jme3.shader.Uniform;
 import com.jme3.texture.FrameBuffer;
 import com.jme3.texture.FrameBuffer.RenderBuffer;
 import com.jme3.texture.Image;
@@ -61,17 +59,17 @@ import com.jme3.util.BufferUtils;
 import com.jme3.util.ListMap;
 import com.jme3.util.MipMapGenerator;
 import com.jme3.util.NativeObjectManager;
-import java.nio.*;
-import java.util.Arrays;
-import java.util.EnumMap;
-import java.util.EnumSet;
-import java.util.HashSet;
-import java.util.List;
+import jme3tools.shader.ShaderDebug;
+
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+import java.nio.ShortBuffer;
+import java.util.*;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
-import jme3tools.shader.ShaderDebug;
 
 public final class GLRenderer implements Renderer {
 
@@ -480,6 +478,26 @@ public final class GLRenderer implements Renderer {
             }
         }
 
+        if (hasExtension("GL_ARB_shader_storage_buffer_object")) {
+            caps.add(Caps.ShaderStorageBufferObject);
+            limits.put(Limits.ShaderStorageBufferObjectMaxBlockSize, getInteger(GL4.GL_MAX_SHADER_STORAGE_BLOCK_SIZE));
+            limits.put(Limits.ShaderStorageBufferObjectMaxComputeBlocks, getInteger(GL4.GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS));
+            limits.put(Limits.ShaderStorageBufferObjectMaxGeometryBlocks, getInteger(GL4.GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS));
+            limits.put(Limits.ShaderStorageBufferObjectMaxFragmentBlocks, getInteger(GL4.GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS));
+            limits.put(Limits.ShaderStorageBufferObjectMaxVertexBlocks, getInteger(GL4.GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS));
+            limits.put(Limits.ShaderStorageBufferObjectMaxTessControlBlocks, getInteger(GL4.GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS));
+            limits.put(Limits.ShaderStorageBufferObjectMaxTessEvaluationBlocks, getInteger(GL4.GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS));
+            limits.put(Limits.ShaderStorageBufferObjectMaxCombineBlocks, getInteger(GL4.GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS));
+        }
+
+        if (hasExtension("GL_ARB_uniform_buffer_object")) {
+            caps.add(Caps.UniformBufferObject);
+            limits.put(Limits.UniformBufferObjectMaxBlockSize, getInteger(GL3.GL_MAX_UNIFORM_BLOCK_SIZE));
+            limits.put(Limits.UniformBufferObjectMaxGeometryBlocks, getInteger(GL3.GL_MAX_GEOMETRY_UNIFORM_BLOCKS));
+            limits.put(Limits.UniformBufferObjectMaxFragmentBlocks, getInteger(GL3.GL_MAX_FRAGMENT_UNIFORM_BLOCKS));
+            limits.put(Limits.UniformBufferObjectMaxVertexBlocks, getInteger(GL3.GL_MAX_VERTEX_UNIFORM_BLOCKS));
+        }
+
         // Print context information
         logger.log(Level.INFO, "OpenGL Renderer Information\n" +
                         " * Vendor: {0}\n" +
@@ -1050,12 +1068,25 @@ public final class GLRenderer implements Renderer {
         }
     }
 
+    @Override
     public void postFrame() {
         objManager.deleteUnused(this);
         OpenCLObjectManager.getInstance().deleteUnusedObjects();
         gl.resetStats();
     }
 
+    protected void bindProgram(Shader shader) {
+        int shaderId = shader.getId();
+        if (context.boundShaderProgram != shaderId) {
+            gl.glUseProgram(shaderId);
+            statistics.onShaderUse(shader, true);
+            context.boundShader = shader;
+            context.boundShaderProgram = shaderId;
+        } else {
+            statistics.onShaderUse(shader, false);
+        }
+    }
+
     /*********************************************************************\
      |* Shaders                                                           *|
      \*********************************************************************/
@@ -1070,18 +1101,6 @@ public final class GLRenderer implements Renderer {
         }
     }
 
-    protected void bindProgram(Shader shader) {
-        int shaderId = shader.getId();
-        if (context.boundShaderProgram != shaderId) {
-            gl.glUseProgram(shaderId);
-            statistics.onShaderUse(shader, true);
-            context.boundShader = shader;
-            context.boundShaderProgram = shaderId;
-        } else {
-            statistics.onShaderUse(shader, false);
-        }
-    }
-
     protected void updateUniform(Shader shader, Uniform uniform) {
         int shaderId = shader.getId();
 
@@ -1187,6 +1206,58 @@ public final class GLRenderer implements Renderer {
         }
     }
 
+    /**
+     * Updates the buffer block for the shader.
+     *
+     * @param shader the shader.
+     * @param bufferBlock the storage block.
+     */
+    protected void updateShaderBufferBlock(final Shader shader, final ShaderBufferBlock bufferBlock) {
+
+        assert bufferBlock.getName() != null;
+        assert shader.getId() > 0;
+
+        final BufferObject bufferObject = bufferBlock.getBufferObject();
+        if (bufferObject.getUniqueId() == -1 || bufferObject.isUpdateNeeded()) {
+            updateBufferData(bufferObject);
+        }
+
+        if (!bufferBlock.isUpdateNeeded()) {
+            return;
+        }
+
+        bindProgram(shader);
+
+        final int shaderId = shader.getId();
+        final BufferObject.BufferType bufferType = bufferObject.getBufferType();
+
+        bindBuffer(bufferBlock, bufferObject, shaderId, bufferType);
+
+        bufferBlock.clearUpdateNeeded();
+    }
+
+    private void bindBuffer(final ShaderBufferBlock bufferBlock, final BufferObject bufferObject, final int shaderId,
+                            final BufferObject.BufferType bufferType) {
+
+        switch (bufferType) {
+            case UniformBufferObject: {
+                final int blockIndex = gl3.glGetUniformBlockIndex(shaderId, bufferBlock.getName());
+                gl3.glBindBufferBase(GL3.GL_UNIFORM_BUFFER, bufferObject.getBinding(), bufferObject.getId());
+                gl3.glUniformBlockBinding(GL3.GL_UNIFORM_BUFFER, blockIndex, bufferObject.getBinding());
+                break;
+            }
+            case ShaderStorageBufferObject: {
+                final int blockIndex = gl4.glGetProgramResourceIndex(shaderId, GL4.GL_SHADER_STORAGE_BLOCK, bufferBlock.getName());
+                gl4.glShaderStorageBlockBinding(shaderId, blockIndex, bufferObject.getBinding());
+                gl4.glBindBufferBase(GL4.GL_SHADER_STORAGE_BUFFER, bufferObject.getBinding(), bufferObject.getId());
+                break;
+            }
+            default: {
+                throw new IllegalArgumentException("Doesn't support binding of " + bufferType);
+            }
+        }
+    }
+
     protected void updateShaderUniforms(Shader shader) {
         ListMap<String, Uniform> uniforms = shader.getUniformMap();
         for (int i = 0; i < uniforms.size(); i++) {
@@ -1197,6 +1268,18 @@ public final class GLRenderer implements Renderer {
         }
     }
 
+    /**
+     * Updates all shader's buffer blocks.
+     *
+     * @param shader the shader.
+     */
+    protected void updateShaderBufferBlocks(final Shader shader) {
+        final ListMap<String, ShaderBufferBlock> bufferBlocks = shader.getBufferBlockMap();
+        for (int i = 0; i < bufferBlocks.size(); i++) {
+            updateShaderBufferBlock(shader, bufferBlocks.getValue(i));
+        }
+    }
+
     protected void resetUniformLocations(Shader shader) {
         ListMap<String, Uniform> uniforms = shader.getUniformMap();
         for (int i = 0; i < uniforms.size(); i++) {
@@ -1415,6 +1498,7 @@ public final class GLRenderer implements Renderer {
             assert shader.getId() > 0;
 
             updateShaderUniforms(shader);
+            updateShaderBufferBlocks(shader);
             bindProgram(shader);
         }
     }
@@ -2503,6 +2587,58 @@ public final class GLRenderer implements Renderer {
         vb.clearUpdateNeeded();
     }
 
+    @Override
+    public void updateBufferData(final BufferObject bo) {
+
+        int maxSize = Integer.MAX_VALUE;
+
+        final BufferObject.BufferType bufferType = bo.getBufferType();
+
+        if (!caps.contains(bufferType.getRequiredCaps())) {
+            throw new IllegalArgumentException("The current video hardware doesn't support " + bufferType);
+        }
+
+        final ByteBuffer data = bo.computeData(maxSize);
+        if (data == null) {
+            throw new IllegalArgumentException("Can't upload BO without data.");
+        }
+
+        int bufferId = bo.getId();
+        if (bufferId == -1) {
+
+            // create buffer
+            intBuf1.clear();
+            gl.glGenBuffers(intBuf1);
+            bufferId = intBuf1.get(0);
+
+            bo.setId(bufferId);
+
+            objManager.registerObject(bo);
+        }
+
+        data.rewind();
+
+        switch (bufferType) {
+            case UniformBufferObject: {
+                gl3.glBindBuffer(GL3.GL_UNIFORM_BUFFER, bufferId);
+                gl3.glBufferData(GL4.GL_UNIFORM_BUFFER, data, GL3.GL_DYNAMIC_DRAW);
+                gl3.glBindBuffer(GL4.GL_UNIFORM_BUFFER, 0);
+                break;
+            }
+            case ShaderStorageBufferObject: {
+                gl4.glBindBuffer(GL4.GL_SHADER_STORAGE_BUFFER, bufferId);
+                gl4.glBufferData(GL4.GL_SHADER_STORAGE_BUFFER, data, GL4.GL_DYNAMIC_COPY);
+                gl4.glBindBuffer(GL4.GL_SHADER_STORAGE_BUFFER, 0);
+                break;
+            }
+            default: {
+                throw new IllegalArgumentException("Doesn't support binding of " + bufferType);
+            }
+        }
+
+        bo.clearUpdateNeeded();
+    }
+
     public void deleteBuffer(VertexBuffer vb) {
         int bufId = vb.getId();
         if (bufId != -1) {
@@ -2516,6 +2652,23 @@ public final class GLRenderer implements Renderer {
         }
     }
 
+    @Override
+    public void deleteBuffer(final BufferObject bo) {
+
+        int bufferId = bo.getId();
+        if (bufferId == -1) {
+            return;
+        }
+
+        intBuf1.clear();
+        intBuf1.put(bufferId);
+        intBuf1.flip();
+
+        gl.glDeleteBuffers(intBuf1);
+
+        bo.resetObject();
+    }
+
     public void clearVertexAttribs() {
         IDList attribList = context.attribIndexList;
         for (int i = 0; i < attribList.oldLen; i++) {

+ 828 - 0
jme3-core/src/main/java/com/jme3/shader/BufferObject.java

@@ -0,0 +1,828 @@
+package com.jme3.shader;
+
+import com.jme3.math.*;
+import com.jme3.renderer.Caps;
+import com.jme3.renderer.Renderer;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.NativeObject;
+import com.jme3.util.SafeArrayList;
+
+import java.nio.ByteBuffer;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * The base implementation of BO.
+ *
+ * @author JavaSaBr
+ */
+public class BufferObject extends NativeObject {
+
+    private static final Map<Class<?>, VarType> CLASS_TO_VAR_TYPE = new HashMap<>();
+
+    static {
+        CLASS_TO_VAR_TYPE.put(Float.class, VarType.Float);
+        CLASS_TO_VAR_TYPE.put(Integer.class, VarType.Int);
+        CLASS_TO_VAR_TYPE.put(Boolean.class, VarType.Boolean);
+        CLASS_TO_VAR_TYPE.put(Vector2f.class, VarType.Vector2);
+        CLASS_TO_VAR_TYPE.put(Vector3f.class, VarType.Vector3);
+        CLASS_TO_VAR_TYPE.put(ColorRGBA.class, VarType.Vector4);
+        CLASS_TO_VAR_TYPE.put(Quaternion.class, VarType.Vector4);
+        CLASS_TO_VAR_TYPE.put(Vector4f.class, VarType.Vector4);
+
+        CLASS_TO_VAR_TYPE.put(Vector2f[].class, VarType.Vector2Array);
+        CLASS_TO_VAR_TYPE.put(Vector3f[].class, VarType.Vector3Array);
+        CLASS_TO_VAR_TYPE.put(Vector4f[].class, VarType.Vector4Array);
+        CLASS_TO_VAR_TYPE.put(ColorRGBA[].class, VarType.Vector4Array);
+        CLASS_TO_VAR_TYPE.put(Quaternion[].class, VarType.Vector4Array);
+
+        CLASS_TO_VAR_TYPE.put(Matrix3f.class, VarType.Matrix3);
+        CLASS_TO_VAR_TYPE.put(Matrix4f.class, VarType.Matrix4);
+        CLASS_TO_VAR_TYPE.put(Matrix3f[].class, VarType.Matrix3Array);
+        CLASS_TO_VAR_TYPE.put(Matrix4f[].class, VarType.Matrix4Array);
+    }
+
+    protected static VarType getVarTypeByValue(final Object value) {
+
+        final VarType varType = CLASS_TO_VAR_TYPE.get(value.getClass());
+        if (varType != null) {
+            return varType;
+        } else if (value instanceof Collection<?> && ((Collection) value).isEmpty()) {
+            throw new IllegalArgumentException("Can't calculate a var type for the empty collection value[" + value + "].");
+        } else if (value instanceof List<?>) {
+            return getVarTypeByValue(((List) value).get(0));
+        } else if (value instanceof Collection<?>) {
+            return getVarTypeByValue(((Collection) value).iterator().next());
+        }
+
+        throw new IllegalArgumentException("Can't calculate a var type for the value " + value);
+    }
+
+    public enum Layout {
+        std140,
+        /** unsupported yet */
+        @Deprecated
+        std430,
+    }
+
+    public enum BufferType {
+        ShaderStorageBufferObject(Caps.ShaderStorageBufferObject),
+        UniformBufferObject(Caps.UniformBufferObject),
+        ;
+
+        private final Caps requiredCaps;
+
+        BufferType(final Caps requiredCaps) {
+            this.requiredCaps = requiredCaps;
+        }
+
+        /**
+         * Get the required caps.
+         *
+         * @return the required caps.
+         */
+        public Caps getRequiredCaps() {
+            return requiredCaps;
+        }
+    }
+
+    /**
+     * The fields of this BO.
+     */
+    private final Map<String, BufferObjectField> fields;
+
+    /**
+     * The field's array.
+     */
+    private final SafeArrayList<BufferObjectField> fieldArray;
+
+    /**
+     * The buffer's data layout.
+     */
+    private final Layout layout;
+
+    /**
+     * The binding number.
+     */
+    private final int binding;
+
+    /**
+     * The buffer's type.
+     */
+    private BufferType bufferType;
+
+    /**
+     * The previous data buffer.
+     */
+    private ByteBuffer previousData;
+
+    public BufferObject(final int binding, final Layout layout, final BufferType bufferType) {
+        this.handleRef = new Object();
+        this.bufferType = bufferType;
+        this.binding = binding;
+        this.layout = layout;
+        this.fields = new HashMap<>();
+        this.fieldArray = new SafeArrayList<>(BufferObjectField.class);
+    }
+
+    public BufferObject(final int binding, final Layout layout) {
+        this(binding, layout, BufferType.UniformBufferObject);
+    }
+
+    public BufferObject(final int binding, final BufferType bufferType) {
+        this(binding, Layout.std140, bufferType);
+    }
+
+    public BufferObject(final BufferType bufferType) {
+        this(1, Layout.std140, bufferType);
+    }
+
+    public BufferObject(final Layout layout) {
+        this(1, layout, BufferType.UniformBufferObject);
+    }
+
+    public BufferObject(final int binding) {
+        this(binding, Layout.std140, BufferType.UniformBufferObject);
+    }
+
+    public BufferObject() {
+        this(1, Layout.std140, BufferType.UniformBufferObject);
+    }
+
+    private BufferObject(final Void unused, final int id) {
+        super(id);
+        this.fieldArray = null;
+        this.fields = null;
+        this.layout = null;
+        this.binding = 0;
+    }
+
+    /**
+     * Declares a filed in this BO.
+     *
+     * @param name    the field's name.
+     * @param varType the field's type.
+     */
+    public void declareField(final String name, final VarType varType) {
+
+        if (fields.containsKey(name)) {
+            throw new IllegalArgumentException("The field " + name + " is already declared.");
+        }
+
+        final BufferObjectField field = new BufferObjectField(name, varType);
+
+        fields.put(name, field);
+        fieldArray.add(field);
+    }
+
+    /**
+     * Gets the buffer's type.
+     *
+     * @return the buffer's type.
+     */
+    public BufferType getBufferType() {
+        return bufferType;
+    }
+
+    /**
+     * Sets the buffer's type.
+     *
+     * @param bufferType the buffer's type.
+     */
+    public void setBufferType(final BufferType bufferType) {
+
+        if (getId() != -1) {
+            throw new IllegalStateException("Can't change buffer's type when this buffer is already initialized.");
+        }
+
+        this.bufferType = bufferType;
+    }
+
+    /**
+     * Sets the value to the filed by the field's name.
+     *
+     * @param name  the field's name.
+     * @param value the value.
+     */
+    public void setFieldValue(final String name, final Object value) {
+
+        BufferObjectField field = fields.get(name);
+
+        if (field == null) {
+            declareField(name, getVarTypeByValue(value));
+            field = fields.get(name);
+        }
+
+        field.setValue(value);
+        setUpdateNeeded();
+    }
+
+    /**
+     * Gets the current value of the field by the name.
+     *
+     * @param name the field name.
+     * @param <T> the value's type.
+     * @return the current value.
+     */
+    public <T> T getFieldValue(final String name) {
+
+        final BufferObjectField field = fields.get(name);
+        if (field == null) {
+            throw new IllegalArgumentException("Unknown a field with the name " + name);
+        }
+
+        return (T) field.getValue();
+    }
+
+    /**
+     * Get the binding number.
+     *
+     * @return the binding number.
+     */
+    public int getBinding() {
+        return binding;
+    }
+
+    @Override
+    public void resetObject() {
+        this.id = -1;
+        setUpdateNeeded();
+    }
+
+    /**
+     * Computes the current binary data of this BO.
+     *
+     * @param maxSize the max data size.
+     * @return the current binary data of this BO.
+     */
+    public ByteBuffer computeData(final int maxSize) {
+
+        int estimateSize = 0;
+
+        for (final BufferObjectField field : fieldArray) {
+            estimateSize += estimateSize(field);
+        }
+
+        if(maxSize < estimateSize) {
+            throw new IllegalStateException("The estimated size(" + estimateSize + ") of this BO is bigger than " +
+                    "maximum available size " + maxSize);
+        }
+
+        if (previousData != null) {
+            if (previousData.capacity() < estimateSize) {
+                BufferUtils.destroyDirectBuffer(previousData);
+                previousData = null;
+            } else {
+                previousData.clear();
+            }
+        }
+
+        final ByteBuffer data = previousData == null ? BufferUtils.createByteBuffer(estimateSize) : previousData;
+
+        for (final BufferObjectField field : fieldArray) {
+            writeField(field, data);
+        }
+
+        data.flip();
+
+        this.previousData = data;
+
+        return data;
+    }
+
+    /**
+     * Estimates size of the field.
+     *
+     * @param field the field.
+     * @return the estimated size.
+     */
+    protected int estimateSize(final BufferObjectField field) {
+
+        switch (field.getType()) {
+            case Float:
+            case Int: {
+                if (layout == Layout.std140) {
+                    return 16;
+                }
+                return 4;
+            }
+            case Boolean: {
+                if (layout == Layout.std140) {
+                    return 16;
+                }
+                return 1;
+            }
+            case Vector2: {
+                return 4 * 2;
+            }
+            case Vector3: {
+                final int multiplier = layout == Layout.std140 ? 4 : 3;
+                return 4 * multiplier;
+            }
+            case Vector4:
+                return 16;
+            case IntArray: {
+                return estimate((int[]) field.getValue());
+            }
+            case FloatArray: {
+                return estimate((float[]) field.getValue());
+            }
+            case Vector2Array: {
+                return estimateArray(field.getValue(), 8);
+            }
+            case Vector3Array: {
+                final int multiplier = layout == Layout.std140 ? 16 : 12;
+                return estimateArray(field.getValue(), multiplier);
+            }
+            case Vector4Array: {
+                return estimateArray(field.getValue(), 16);
+            }
+            case Matrix3: {
+                final int multiplier = layout == Layout.std140 ? 16 : 12;
+                return 3 * 3 * multiplier;
+            }
+            case Matrix4: {
+                return 4 * 4 * 4;
+            }
+            case Matrix3Array: {
+                int multiplier = layout == Layout.std140 ? 16 : 12;
+                multiplier = 3 * 3 * multiplier;
+                return estimateArray(field.getValue(), multiplier);
+            }
+            case Matrix4Array: {
+                final int multiplier = 4 * 4 * 16;
+                return estimateArray(field.getValue(), multiplier);
+            }
+            default: {
+                throw new IllegalArgumentException("The type of BO field " + field.getType() + " doesn't support.");
+            }
+        }
+    }
+
+    /**
+     * Estimates bytes count to present the value on GPU.
+     *
+     * @param value      the value.
+     * @param multiplier the multiplier.
+     * @return the estimated bytes cunt.
+     */
+    protected int estimateArray(final Object value, final int multiplier) {
+
+        if (value instanceof Object[]) {
+            return ((Object[]) value).length * multiplier;
+        } else if (value instanceof Collection) {
+            return ((Collection) value).size() * multiplier;
+        }
+
+        throw new IllegalArgumentException("Unexpected value " + value);
+    }
+
+    /**
+     * Estimates bytes count to present the values on GPU.
+     *
+     * @param values the values.
+     * @return the estimated bytes cunt.
+     */
+    protected int estimate(final float[] values) {
+        return values.length * 4;
+    }
+
+    /**
+     * Estimates bytes count to present the values on GPU.
+     *
+     * @param values the values.
+     * @return the estimated bytes cunt.
+     */
+    protected int estimate(final int[] values) {
+        return values.length * 4;
+    }
+
+    /**
+     * Writes the field to the data buffer.
+     *
+     * @param field the field.
+     * @param data  the data buffer.
+     */
+    protected void writeField(final BufferObjectField field, final ByteBuffer data) {
+
+        final Object value = field.getValue();
+
+        switch (field.getType()) {
+            case Int: {
+                data.putInt(((Number) value).intValue());
+                if (layout == Layout.std140) {
+                    data.putInt(0);
+                    data.putLong(0);
+                }
+                break;
+            }
+            case Float: {
+                data.putFloat(((Number) value).floatValue());
+                if (layout == Layout.std140) {
+                    data.putInt(0);
+                    data.putLong(0);
+                }
+                break;
+            }
+            case Boolean:
+                data.put((byte) (((Boolean) value) ? 1 : 0));
+                if (layout == Layout.std140) {
+                    data.putInt(0);
+                    data.putLong(0);
+                    data.putShort((short) 0);
+                    data.put((byte) 0);
+                }
+                break;
+            case Vector2:
+                write(data, (Vector2f) value);
+                break;
+            case Vector3:
+                write(data, (Vector3f) value);
+                break;
+            case Vector4:
+                writeVec4(data, value);
+                break;
+            case IntArray: {
+                write(data, (int[]) value);
+                break;
+            }
+            case FloatArray: {
+                write(data, (float[]) value);
+                break;
+            }
+            case Vector2Array: {
+                writeVec2Array(data, value);
+                break;
+            }
+            case Vector3Array: {
+                writeVec3Array(data, value);
+                break;
+            }
+            case Vector4Array: {
+                writeVec4Array(data, value);
+                break;
+            }
+            case Matrix3: {
+                write(data, (Matrix3f) value);
+                break;
+            }
+            case Matrix4: {
+                write(data, (Matrix4f) value);
+                break;
+            }
+            case Matrix3Array: {
+                writeMat3Array(data, value);
+                break;
+            }
+            case Matrix4Array: {
+                writeMat4Array(data, value);
+                break;
+            }
+            default: {
+                throw new IllegalArgumentException("The type of BO field " + field.getType() + " doesn't support.");
+            }
+        }
+    }
+
+    /**
+     * Writes the value to the data buffer.
+     *
+     * @param data  the data buffer.
+     * @param value the value.
+     */
+    protected void writeMat3Array(final ByteBuffer data, final Object value) {
+
+        if (value instanceof Matrix3f[]) {
+
+            final Matrix3f[] values = (Matrix3f[]) value;
+            for (final Matrix3f mat : values) {
+                write(data, mat);
+            }
+
+        } else if(value instanceof SafeArrayList) {
+
+            final SafeArrayList<Matrix3f> values = (SafeArrayList<Matrix3f>) value;
+            for (final Matrix3f mat : values.getArray()) {
+                write(data, mat);
+            }
+
+        } else if(value instanceof Collection) {
+
+            final Collection<Matrix3f> values = (Collection<Matrix3f>) value;
+            for (final Matrix3f mat : values) {
+                write(data, mat);
+            }
+        }
+    }
+
+    /**
+     * Writes the value to the data buffer.
+     *
+     * @param data  the data buffer.
+     * @param value the value.
+     */
+    protected void writeMat4Array(final ByteBuffer data, final Object value) {
+
+        if (value instanceof Matrix4f[]) {
+
+            final Matrix4f[] values = (Matrix4f[]) value;
+            for (final Matrix4f mat : values) {
+                write(data, mat);
+            }
+
+        } else if(value instanceof SafeArrayList) {
+
+            final SafeArrayList<Matrix4f> values = (SafeArrayList<Matrix4f>) value;
+            for (final Matrix4f mat : values.getArray()) {
+                write(data, mat);
+            }
+
+        } else if(value instanceof Collection) {
+
+            final Collection<Matrix4f> values = (Collection<Matrix4f>) value;
+            for (final Matrix4f mat : values) {
+                write(data, mat);
+            }
+        }
+    }
+
+
+    /**
+     * Writes the value to the data buffer.
+     *
+     * @param data  the data buffer.
+     * @param value the value.
+     */
+    protected void writeVec4Array(final ByteBuffer data, final Object value) {
+
+        if (value instanceof Object[]) {
+
+            final Object[] values = (Object[]) value;
+            for (final Object vec : values) {
+                writeVec4(data, vec);
+            }
+
+        } else if(value instanceof SafeArrayList) {
+
+            final SafeArrayList<Object> values = (SafeArrayList<Object>) value;
+            for (final Object vec : values.getArray()) {
+                writeVec4(data, vec);
+            }
+
+        } else if(value instanceof Collection) {
+
+            final Collection<Object> values = (Collection<Object>) value;
+            for (final Object vec : values) {
+                writeVec4(data, vec);
+            }
+        }
+    }
+
+    /**
+     * Writes the value to the data buffer.
+     *
+     * @param data  the data buffer.
+     * @param value the value.
+     */
+    protected void writeVec3Array(final ByteBuffer data, final Object value) {
+
+        if (value instanceof Vector3f[]) {
+
+            final Vector3f[] values = (Vector3f[]) value;
+            for (final Vector3f vec : values) {
+                write(data, vec);
+            }
+
+        } else if(value instanceof SafeArrayList) {
+
+            final SafeArrayList<Vector3f> values = (SafeArrayList<Vector3f>) value;
+            for (final Vector3f vec : values.getArray()) {
+                write(data, vec);
+            }
+
+        } else if(value instanceof Collection) {
+
+            final Collection<Vector3f> values = (Collection<Vector3f>) value;
+            for (final Vector3f vec : values) {
+                write(data, vec);
+            }
+        }
+    }
+
+    /**
+     * Writes the value to the data buffer.
+     *
+     * @param data  the data buffer.
+     * @param value the value.
+     */
+    protected void writeVec2Array(final ByteBuffer data, final Object value) {
+
+        if (value instanceof Vector2f[]) {
+
+            final Vector2f[] values = (Vector2f[]) value;
+            for (final Vector2f vec : values) {
+                write(data, vec);
+            }
+
+        } else if(value instanceof SafeArrayList) {
+
+            final SafeArrayList<Vector2f> values = (SafeArrayList<Vector2f>) value;
+            for (final Vector2f vec : values.getArray()) {
+                write(data, vec);
+            }
+
+        } else if(value instanceof Collection) {
+
+            final Collection<Vector2f> values = (Collection<Vector2f>) value;
+            for (final Vector2f vec : values) {
+                write(data, vec);
+            }
+        }
+    }
+
+    /**
+     * Writes the value to the data buffer.
+     *
+     * @param data  the data buffer.
+     * @param value the value.
+     */
+    protected void write(final ByteBuffer data, final float[] value) {
+        for (float val : value) {
+            data.putFloat(val);
+        }
+    }
+
+    /**
+     * Writes the value to the data buffer.
+     *
+     * @param data  the data buffer.
+     * @param value the value.
+     */
+    protected void write(final ByteBuffer data, final int[] value) {
+        for (int val : value) {
+            data.putInt(val);
+        }
+    }
+
+    /**
+     * Writes the value to the data buffer.
+     *
+     * @param data  the data buffer.
+     * @param value the value.
+     */
+    protected void writeVec4(final ByteBuffer data, final Object value) {
+
+        if (value == null) {
+            data.putLong(0).putLong(0);
+        } else if (value instanceof Vector4f) {
+
+            final Vector4f vec4 = (Vector4f) value;
+            data.putFloat(vec4.getX())
+                    .putFloat(vec4.getY())
+                    .putFloat(vec4.getZ())
+                    .putFloat(vec4.getW());
+
+        } else if(value instanceof Quaternion) {
+
+            final Quaternion vec4 = (Quaternion) value;
+            data.putFloat(vec4.getX())
+                    .putFloat(vec4.getY())
+                    .putFloat(vec4.getZ())
+                    .putFloat(vec4.getW());
+
+        } else if(value instanceof ColorRGBA) {
+
+            final ColorRGBA vec4 = (ColorRGBA) value;
+            data.putFloat(vec4.getRed())
+                    .putFloat(vec4.getGreen())
+                    .putFloat(vec4.getBlue())
+                    .putFloat(vec4.getAlpha());
+        }
+    }
+
+    /**
+     * Writes the value to the data buffer.
+     *
+     * @param data  the data buffer.
+     * @param value the value.
+     */
+    protected void write(final ByteBuffer data, final Vector3f value) {
+
+        if (value == null) {
+            data.putLong(0).putInt(0);
+        } else {
+            data.putFloat(value.getX())
+                    .putFloat(value.getY())
+                    .putFloat(value.getZ());
+        }
+
+        if (layout == Layout.std140) {
+            data.putInt(0);
+        }
+    }
+
+    /**
+     * Writes the value to the data buffer.
+     *
+     * @param data  the data buffer.
+     * @param x the x value.
+     * @param y the y value.
+     * @param z the z value.
+     */
+    protected void write(final ByteBuffer data, final float x, final float y, final float z) {
+
+        data.putFloat(x)
+                .putFloat(y)
+                .putFloat(z);
+
+        if (layout == Layout.std140) {
+            data.putInt(0);
+        }
+    }
+
+    /**
+     * Writes the value to the data buffer.
+     *
+     * @param data  the data buffer.
+     * @param x the x value.
+     * @param y the y value.
+     * @param z the z value.
+     * @param w the w value.
+     */
+    protected void write(final ByteBuffer data, final float x, final float y, final float z, final float w) {
+        data.putFloat(x)
+                .putFloat(y)
+                .putFloat(z)
+                .putFloat(w);
+    }
+
+    /**
+     * Writes the value to the data buffer.
+     *
+     * @param data  the data buffer.
+     * @param value the value.
+     */
+    protected void write(final ByteBuffer data, final Vector2f value) {
+        if (value == null) {
+            data.putLong(0);
+        } else {
+            data.putFloat(value.getX()).putFloat(value.getY());
+        }
+    }
+
+    /**
+     * Writes the value to the data buffer.
+     *
+     * @param data  the data buffer.
+     * @param value the value.
+     */
+    protected void write(final ByteBuffer data, final Matrix3f value) {
+        write(data, value.get(0, 0), value.get(1, 0), value.get(2, 0));
+        write(data, value.get(0, 1), value.get(1, 1), value.get(2, 1));
+        write(data, value.get(0, 2), value.get(1, 2), value.get(2, 2));
+    }
+
+    /**
+     * Writes the value to the data buffer.
+     *
+     * @param data  the data buffer.
+     * @param value the value.
+     */
+    protected void write(final ByteBuffer data, final Matrix4f value) {
+        write(data, value.get(0, 0), value.get(1, 0), value.get(2, 0), value.get(3, 0));
+        write(data, value.get(0, 1), value.get(1, 1), value.get(2, 1), value.get(3, 1));
+        write(data, value.get(0, 2), value.get(1, 2), value.get(2, 2), value.get(3, 2));
+        write(data, value.get(0, 3), value.get(1, 3), value.get(2, 3), value.get(3, 3));
+    }
+
+    @Override
+    public void deleteObject(final Object rendererObject) {
+
+        if (!(rendererObject instanceof Renderer)) {
+            throw new IllegalArgumentException("This bo can't be deleted from " + rendererObject);
+        }
+
+        ((Renderer) rendererObject).deleteBuffer(this);
+    }
+
+    @Override
+    public NativeObject createDestructableClone() {
+        return new BufferObject(null, getId());
+    }
+
+    @Override
+    protected void deleteNativeBuffers() {
+        super.deleteNativeBuffers();
+        if (previousData != null) {
+            BufferUtils.destroyDirectBuffer(previousData);
+            previousData = null;
+        }
+    }
+
+    @Override
+    public long getUniqueId() {
+        return ((long) OBJTYPE_BO << 32) | ((long) id);
+    }
+}

+ 77 - 0
jme3-core/src/main/java/com/jme3/shader/BufferObjectField.java

@@ -0,0 +1,77 @@
+package com.jme3.shader;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * The class to describe a filed in BO.
+ *
+ * @author JavaSaBr
+ */
+public class BufferObjectField {
+
+
+    /**
+     * The field name.
+     */
+    private final String name;
+
+    /**
+     * The field type.
+     */
+    private final VarType type;
+
+    /**
+     * The field value.
+     */
+    private Object value;
+
+    public BufferObjectField(final String name, final VarType type) {
+        this.name = name;
+        this.type = type;
+    }
+
+    /**
+     * Get the field name.
+     *
+     * @return the field name.
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Gets the field type.
+     *
+     * @return the field type.
+     */
+    public VarType getType() {
+        return type;
+    }
+
+    /**
+     * Gets the field value.
+     *
+     * @return the field value.
+     */
+    public Object getValue() {
+        return value;
+    }
+
+    /**
+     * Sets the field value.
+     *
+     * @param value the field value.
+     */
+    public void setValue(final Object value) {
+        this.value = requireNonNull(value, "The field's value can't be null.");
+    }
+
+    @Override
+    public String toString() {
+        return "BufferObjectField{" +
+            "name='" + name + '\'' +
+            ", type=" + type +
+            ", value=" + value +
+            '}';
+    }
+}

+ 58 - 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 buffer block variables.
+     */
+    private final ListMap<String, ShaderBufferBlock> bufferBlocks;
     
     /**
      * 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<>();
+        bufferBlocks = new ListMap<>();
+        attribs = new IntMap<>();
+        boundUniforms = new ArrayList<>();
     }
 
     /**
@@ -240,6 +246,7 @@ public final class Shader extends NativeObject {
         }
         
         uniforms = null;
+        bufferBlocks = null;
         boundUniforms = null;
         attribs = null;
     }
@@ -288,10 +295,40 @@ public final class Shader extends NativeObject {
         return uniform;
     }
 
+    /**
+     * Gets or creates a buffer block by the name.
+     *
+     * @param name the buffer block's name.
+     * @return the buffer block.
+     */
+    public ShaderBufferBlock getBufferBlock(final String name) {
+
+        assert name.startsWith("m_");
+
+        ShaderBufferBlock block = bufferBlocks.get(name);
+
+        if (block == null) {
+            block = new ShaderBufferBlock();
+            block.name = name;
+            bufferBlocks.put(name, block);
+        }
+
+        return block;
+    }
+
     public void removeUniform(String name){
         uniforms.remove(name);
     }
 
+    /**
+     * Removes a buffer block by the name.
+     *
+     * @param name the buffer block's name.
+     */
+    public void removeBufferBlock(final String name){
+        bufferBlocks.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 buffer blocks map.
+     *
+     * @return the buffer blocks map.
+     */
+    public ListMap<String, ShaderBufferBlock> getBufferBlockMap() {
+        return bufferBlocks;
+    }
+
     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() +
+                ", numBufferBlocks=" + bufferBlocks.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();
@@ -366,6 +413,11 @@ public final class Shader extends NativeObject {
                 uniform.reset(); // fixes issue with re-initialization
             }
         }
+        if (bufferBlocks != null) {
+            for (ShaderBufferBlock shaderBufferBlock : bufferBlocks.values()) {
+                shaderBufferBlock.reset();
+            }
+        }
         if (attribs != null) {
             for (Entry<Attribute> entry : attribs) {
                 entry.getValue().location = ShaderVariable.LOC_UNKNOWN;

+ 93 - 0
jme3-core/src/main/java/com/jme3/shader/ShaderBufferBlock.java

@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2009-2018 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;
+
+/**
+ * Implementation of shader's buffer block.
+ *
+ * @author JavaSaBr
+ */
+public class ShaderBufferBlock extends ShaderVariable {
+
+    /**
+     * Current used buffer object.
+     */
+    protected BufferObject bufferObject;
+
+    /**
+     * Set the new buffer object.
+     *
+     * @param bufferObject the new buffer object.
+     */
+    public void setBufferObject(final BufferObject bufferObject) {
+
+        if (bufferObject == null) {
+            throw new IllegalArgumentException("for storage block " + name + ": storageData cannot be null");
+        }
+
+        this.bufferObject = bufferObject;
+
+        updateNeeded = true;
+    }
+
+    /**
+     * Return true if need to update this storage block.
+     *
+     * @return true if need to update this storage block.
+     */
+    public boolean isUpdateNeeded(){
+        return updateNeeded;
+    }
+
+    /**
+     * Clear the flag {@link #isUpdateNeeded()}.
+     */
+    public void clearUpdateNeeded(){
+        updateNeeded = false;
+    }
+
+    /**
+     * Reset this storage block.
+     */
+    public void reset(){
+        updateNeeded = true;
+    }
+
+    /**
+     * Get the current storage data.
+     *
+     * @return the current storage data.
+     */
+    public BufferObject getBufferObject() {
+        return bufferObject;
+    }
+}

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

@@ -57,7 +57,8 @@ public enum VarType {
     Texture3D(false,true,"sampler3D"),
     TextureArray(false,true,"sampler2DArray|sampler2DArrayShadow"),
     TextureCubeMap(false,true,"samplerCube"),
-    Int("int");
+    Int("int"),
+    BufferObject(false, false, "custom");
 
     private boolean usesMultiData = false;
     private boolean textureType = false;

+ 12 - 3
jme3-core/src/main/java/com/jme3/system/NullRenderer.java

@@ -31,9 +31,6 @@
  */
 package com.jme3.system;
 
-import java.nio.ByteBuffer;
-import java.util.EnumSet;
-
 import com.jme3.light.LightList;
 import com.jme3.material.RenderState;
 import com.jme3.math.ColorRGBA;
@@ -44,12 +41,16 @@ import com.jme3.renderer.Renderer;
 import com.jme3.renderer.Statistics;
 import com.jme3.scene.Mesh;
 import com.jme3.scene.VertexBuffer;
+import com.jme3.shader.BufferObject;
 import com.jme3.shader.Shader;
 import com.jme3.shader.Shader.ShaderSource;
 import com.jme3.texture.FrameBuffer;
 import com.jme3.texture.Image;
 import com.jme3.texture.Texture;
+
+import java.nio.ByteBuffer;
 import java.util.EnumMap;
+import java.util.EnumSet;
 
 public class NullRenderer implements Renderer {
 
@@ -148,9 +149,17 @@ public class NullRenderer implements Renderer {
     public void updateBufferData(VertexBuffer vb) {
     }
 
+    @Override
+    public void updateBufferData(BufferObject bo) {
+    }
     public void deleteBuffer(VertexBuffer vb) {
     }
 
+    @Override
+    public void deleteBuffer(BufferObject bo) {
+
+    }
+
     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_BO           = 9;
     
     /**
      * The object manager to which this NativeObject is registered to.

+ 29 - 4
jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglGL.java

@@ -4,12 +4,11 @@ import com.jme3.renderer.RendererException;
 import com.jme3.renderer.opengl.GL;
 import com.jme3.renderer.opengl.GL2;
 import com.jme3.renderer.opengl.GL3;
-
-import java.nio.*;
-
 import com.jme3.renderer.opengl.GL4;
 import com.jogamp.opengl.GLContext;
 
+import java.nio.*;
+
 public class JoglGL implements GL, GL2, GL3, GL4 {
     
 	private static int getLimitBytes(ByteBuffer buffer) {
@@ -628,10 +627,36 @@ public class JoglGL implements GL, GL2, GL3, GL4 {
 	public void glPatchParameter(int count) {
         GLContext.getCurrentGL().getGL3().glPatchParameteri(com.jogamp.opengl.GL3.GL_PATCH_VERTICES, count);
     }
-    
+
     @Override
 	public void glDeleteVertexArrays(IntBuffer arrays) {
         checkLimit(arrays);
         GLContext.getCurrentGL().getGL2ES3().glDeleteVertexArrays(arrays.limit(), arrays);
     }
+
+    @Override
+    public int glGetUniformBlockIndex(final int program, final String uniformBlockName) {
+        return GLContext.getCurrentGL().getGL3bc().glGetUniformBlockIndex(program, uniformBlockName);
+    }
+
+    @Override
+    public void glBindBufferBase(final int target, final int index, final int buffer) {
+        GLContext.getCurrentGL().getGL3bc().glBindBufferBase(target, index, buffer);
+    }
+
+    @Override
+    public int glGetProgramResourceIndex(final int program, final int programInterface, final String name) {
+        throw new UnsupportedOperationException();
+        //return GLContext.getCurrentGL().getGL4bc().glGetProgramResourceIndex(program, programInterface, name);
+    }
+
+    @Override
+    public void glShaderStorageBlockBinding(final int program, final int storageBlockIndex, final int storageBlockBinding) {
+        GLContext.getCurrentGL().getGL4bc().glShaderStorageBlockBinding(program, storageBlockIndex, storageBlockBinding);
+    }
+
+    @Override
+    public void glUniformBlockBinding(final int program, final int uniformBlockIndex, final int uniformBlockBinding) {
+        GLContext.getCurrentGL().getGL3bc().glUniformBlockBinding(program, uniformBlockIndex, uniformBlockBinding);
+    }
 }

+ 28 - 7
jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java

@@ -4,16 +4,12 @@ import com.jme3.renderer.RendererException;
 import com.jme3.renderer.opengl.GL;
 import com.jme3.renderer.opengl.GL2;
 import com.jme3.renderer.opengl.GL3;
-import java.nio.Buffer;
-import java.nio.ByteBuffer;
-import java.nio.FloatBuffer;
-import java.nio.IntBuffer;
-import java.nio.ShortBuffer;
-
 import com.jme3.renderer.opengl.GL4;
 import com.jme3.util.BufferUtils;
 import org.lwjgl.opengl.*;
 
+import java.nio.*;
+
 public final class LwjglGL implements GL, GL2, GL3, GL4 {
 
     IntBuffer tmpBuff = BufferUtils.createIntBuffer(1);
@@ -487,10 +483,35 @@ public final class LwjglGL implements GL, GL2, GL3, GL4 {
     public void glPatchParameter(int count) {
         GL40.glPatchParameteri(GL40.GL_PATCH_VERTICES,count);
     }
-    
+
+    @Override
+    public int glGetProgramResourceIndex(final int program, final int programInterface, final String name) {
+        return GL43.glGetProgramResourceIndex(program, programInterface, name);
+    }
+
+    @Override
+    public void glShaderStorageBlockBinding(final int program, final int storageBlockIndex, final int storageBlockBinding) {
+        GL43.glShaderStorageBlockBinding(program, storageBlockIndex, storageBlockBinding);
+    }
+
     @Override
     public void glDeleteVertexArrays(IntBuffer arrays) {
         checkLimit(arrays);
         ARBVertexArrayObject.glDeleteVertexArrays(arrays);
     }
+
+    @Override
+    public int glGetUniformBlockIndex(final int program, final String uniformBlockName) {
+        return GL31.glGetUniformBlockIndex(program, uniformBlockName);
+    }
+
+    @Override
+    public void glBindBufferBase(final int target, final int index, final int buffer) {
+        GL30.glBindBufferBase(target, index, buffer);
+    }
+
+    @Override
+    public void glUniformBlockBinding(final int program, final int uniformBlockIndex, final int uniformBlockBinding) {
+        GL31.glUniformBlockBinding(program, uniformBlockIndex, uniformBlockBinding);
+    }
 }

+ 25 - 0
jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java

@@ -616,9 +616,34 @@ public class LwjglGL extends LwjglRender implements GL, GL2, GL3, GL4 {
         GL40.glPatchParameteri(GL40.GL_PATCH_VERTICES, count);
     }
 
+    @Override
+    public int glGetProgramResourceIndex(final int program, final int programInterface, final String name) {
+        return GL43.glGetProgramResourceIndex(program, programInterface, name);
+    }
+
+    @Override
+    public void glShaderStorageBlockBinding(final int program, final int storageBlockIndex, final int storageBlockBinding) {
+        GL43.glShaderStorageBlockBinding(program, storageBlockIndex, storageBlockBinding);
+    }
+
     @Override
     public void glDeleteVertexArrays(final IntBuffer arrays) {
         checkLimit(arrays);
         ARBVertexArrayObject.glDeleteVertexArrays(arrays);
     }
+
+    @Override
+    public int glGetUniformBlockIndex(final int program, final String uniformBlockName) {
+        return GL31.glGetUniformBlockIndex(program, uniformBlockName);
+    }
+
+    @Override
+    public void glBindBufferBase(final int target, final int index, final int buffer) {
+        GL30.glBindBufferBase(target, index, buffer);
+    }
+
+    @Override
+    public void glUniformBlockBinding(final int program, final int uniformBlockIndex, final int uniformBlockBinding) {
+        GL31.glUniformBlockBinding(program, uniformBlockIndex, uniformBlockBinding);
+    }
 }

部分文件因为文件数量过多而无法显示