diff --git a/jme3-core/src/main/java/com/jme3/material/Material.java b/jme3-core/src/main/java/com/jme3/material/Material.java
index e2b030e93..f8cac2b74 100644
--- a/jme3-core/src/main/java/com/jme3/material/Material.java
+++ b/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;
@@ -794,20 +791,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(type == VarType.ShaderStorageBufferObject) {
+
+ final StorageBlock storageBlock = shader.getStorageBlock(name);
+ storageBlock.setValue(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());
+ }
}
}
diff --git a/jme3-core/src/main/java/com/jme3/renderer/Caps.java b/jme3-core/src/main/java/com/jme3/renderer/Caps.java
index d8e73a36e..d381ffd07 100644
--- a/jme3-core/src/main/java/com/jme3/renderer/Caps.java
+++ b/jme3-core/src/main/java/com/jme3/renderer/Caps.java
@@ -394,7 +394,11 @@ public enum Caps {
/**
* GPU can provide and accept binary shaders.
*/
- BinaryShader;
+ BinaryShader,
+ /**
+ * Supporting working with ShaderStorageBufferObjects.
+ */
+ ShaderStorageBufferObject;
/**
* Returns true if given the renderer capabilities, the texture
diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GL4.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GL4.java
index b6d644ba4..ccf3c3a7b 100644
--- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GL4.java
+++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GL4.java
@@ -51,6 +51,8 @@ public interface GL4 extends GL3 {
* 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;
+ public static final int GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS = 0x90DD;
/**
*
Reference Page
@@ -60,4 +62,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);
}
diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java
index 42cfb0b2d..d9b905e17 100644
--- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java
+++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java
@@ -45,12 +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.ShaderStorageBufferObject;
-import com.jme3.shader.Uniform;
import com.jme3.texture.FrameBuffer;
import com.jme3.texture.FrameBuffer.RenderBuffer;
import com.jme3.texture.Image;
@@ -481,6 +478,10 @@ public final class GLRenderer implements Renderer {
}
}
+ if (hasExtension("GL_ARB_shader_storage_buffer_object")) {
+ caps.add(Caps.ShaderStorageBufferObject);
+ }
+
// Print context information
logger.log(Level.INFO, "OpenGL Renderer Information\n" +
" * Vendor: {0}\n" +
@@ -1184,20 +1185,35 @@ public final class GLRenderer implements Renderer {
Integer i = (Integer) uniform.getValue();
gl.glUniform1i(loc, i.intValue());
break;
- case ShaderStorageBufferObject: {
+ default:
+ throw new UnsupportedOperationException("Unsupported uniform type: " + uniform.getVarType());
+ }
+ }
- final ShaderStorageBufferObject ssbo = (ShaderStorageBufferObject) uniform.getValue();
- if (ssbo.getUniqueId() == -1 || ssbo.isUpdateNeeded()) {
- updateBufferData(ssbo);
- }
+ /**
+ * Update the storage block for the shader.
+ *
+ * @param shader the shader.
+ * @param storageBlock the storage block.
+ */
+ protected void updateShaderStorageBlock(final Shader shader, final StorageBlock storageBlock) {
+ int shaderId = shader.getId();
+ assert storageBlock.getName() != null;
+ assert shader.getId() > 0;
- break;
- }
- default:
- throw new UnsupportedOperationException("Unsupported uniform type: " + uniform.getVarType());
+ bindProgram(shader);
+
+ final ShaderStorageBufferObject storageData = (ShaderStorageBufferObject) storageBlock.getStorageData();
+ if (storageData.getUniqueId() == -1 || storageData.isUpdateNeeded()) {
+ updateBufferData(storageData);
}
+
+ final int blockIndex = gl4.glGetProgramResourceIndex(shaderId, GL4.GL_SHADER_STORAGE_BLOCK, storageBlock.getName());
+
+ gl4.glShaderStorageBlockBinding(shaderId, blockIndex, storageData.getBinding());
+ gl4.glBindBufferBase(GL4.GL_SHADER_STORAGE_BUFFER, storageData.getBinding(), storageData.getId());
}
protected void updateShaderUniforms(Shader shader) {
@@ -1210,12 +1226,17 @@ public final class GLRenderer implements Renderer {
}
}
- protected void updateShaderStorageBlocks(Shader shader) {
- ListMap uniforms = shader.getUniformMap();
+ /**
+ * Updates all shader's storage blocks.
+ *
+ * @param shader the shader.
+ */
+ protected void updateShaderStorageBlocks(final Shader shader) {
+ final ListMap uniforms = shader.getStorageBlockMap();
for (int i = 0; i < uniforms.size(); i++) {
- Uniform uniform = uniforms.getValue(i);
- if (uniform.isUpdateNeeded()) {
- updateUniform(shader, uniform);
+ final StorageBlock storageBlock = uniforms.getValue(i);
+ if (storageBlock.isUpdateNeeded()) {
+ updateShaderStorageBlock(shader, storageBlock);
}
}
}
@@ -1438,6 +1459,7 @@ public final class GLRenderer implements Renderer {
assert shader.getId() > 0;
updateShaderUniforms(shader);
+ updateShaderStorageBlocks(shader);
bindProgram(shader);
}
}
@@ -2529,6 +2551,10 @@ public final class GLRenderer implements Renderer {
@Override
public void updateBufferData(final ShaderStorageBufferObject ssbo) {
+ if (!caps.contains(Caps.ShaderStorageBufferObject)) {
+ throw new IllegalArgumentException("The current video hardware doesn't support SSBO.");
+ }
+
final ByteBuffer data = ssbo.getData();
if (data == null) {
throw new IllegalArgumentException("Can't upload SSBO without data.");
diff --git a/jme3-core/src/main/java/com/jme3/shader/Shader.java b/jme3-core/src/main/java/com/jme3/shader/Shader.java
index 79c317093..ce20b0edf 100644
--- a/jme3-core/src/main/java/com/jme3/shader/Shader.java
+++ b/jme3-core/src/main/java/com/jme3/shader/Shader.java
@@ -413,6 +413,11 @@ public final class Shader extends NativeObject {
uniform.reset(); // fixes issue with re-initialization
}
}
+ if (storageBlocks != null) {
+ for (StorageBlock storageBlock : storageBlocks.values()) {
+ storageBlock.reset();
+ }
+ }
if (attribs != null) {
for (Entry entry : attribs) {
entry.getValue().location = ShaderVariable.LOC_UNKNOWN;
diff --git a/jme3-core/src/main/java/com/jme3/shader/StorageBlock.java b/jme3-core/src/main/java/com/jme3/shader/StorageBlock.java
index 6a905cee0..fd386d498 100644
--- a/jme3-core/src/main/java/com/jme3/shader/StorageBlock.java
+++ b/jme3-core/src/main/java/com/jme3/shader/StorageBlock.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2009-2017 jMonkeyEngine
+ * Copyright (c) 2009-2018 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -31,425 +31,63 @@
*/
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;
-
+/**
+ * Implementation of shader's storage block.
+ *
+ * @author JavaSaBr
+ */
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
+ * Current used buffer object.
*/
- protected VarType varType;
+ protected Object storageData;
/**
- * Binding to a renderer value, or null if user-defined uniform
+ * Set the new storage data.
+ *
+ * @param storageData the new storage data
*/
- 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=");
- }
- 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;
- }
+ public void setStorageData(final Object storageData) {
- 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 (storageData == null) {
+ throw new IllegalArgumentException("for storage block " + name + ": storageData cannot be null");
}
- if (varType != null && varType != type) {
- throw new IllegalArgumentException("Expected a " + varType.name() + " value!");
- }
+ this.storageData = storageData;
- 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;
- }
-
+ /**
+ * 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(){
- setByCurrentMaterial = false;
- location = -2;
updateNeeded = true;
}
- public void deleteNativeBuffers() {
- if (value instanceof Buffer) {
- BufferUtils.destroyDirectBuffer((Buffer)value);
- value = null; // ????
- }
+ /**
+ * Get the current storage data.
+ *
+ * @return the current storage data.
+ */
+ public Object getStorageData() {
+ return storageData;
}
}