beta implementation of SSBO.

JavaSaBr-added_new_var_type
JavaSaBr 7 years ago committed by Nehon
parent c154b6ae23
commit cdb42a50c7
  1. 16
      jme3-core/src/main/java/com/jme3/material/Material.java
  2. 6
      jme3-core/src/main/java/com/jme3/renderer/Caps.java
  3. 26
      jme3-core/src/main/java/com/jme3/renderer/opengl/GL4.java
  4. 62
      jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java
  5. 5
      jme3-core/src/main/java/com/jme3/shader/Shader.java
  6. 426
      jme3-core/src/main/java/com/jme3/shader/StorageBlock.java

@ -46,10 +46,7 @@ import com.jme3.renderer.RenderManager;
import com.jme3.renderer.Renderer; import com.jme3.renderer.Renderer;
import com.jme3.renderer.queue.RenderQueue.Bucket; import com.jme3.renderer.queue.RenderQueue.Bucket;
import com.jme3.scene.Geometry; import com.jme3.scene.Geometry;
import com.jme3.shader.Shader; import com.jme3.shader.*;
import com.jme3.shader.Uniform;
import com.jme3.shader.UniformBindingManager;
import com.jme3.shader.VarType;
import com.jme3.texture.Image; import com.jme3.texture.Image;
import com.jme3.texture.Texture; import com.jme3.texture.Texture;
import com.jme3.texture.image.ColorSpace; import com.jme3.texture.image.ColorSpace;
@ -794,10 +791,18 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
} }
for (int i = 0; i < paramValues.size(); i++) { for (int i = 0; i < paramValues.size(); i++) {
MatParam param = paramValues.getValue(i); MatParam param = paramValues.getValue(i);
VarType type = param.getVarType(); VarType type = param.getVarType();
Uniform uniform = shader.getUniform(param.getPrefixedName());
if(type == VarType.ShaderStorageBufferObject) {
final StorageBlock storageBlock = shader.getStorageBlock(name);
storageBlock.setValue(param.getValue());
} else {
Uniform uniform = shader.getUniform(param.getPrefixedName());
if (uniform.isSetByCurrentMaterial()) { if (uniform.isSetByCurrentMaterial()) {
continue; continue;
} }
@ -810,6 +815,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
uniform.setValue(type, param.getValue()); uniform.setValue(type, param.getValue());
} }
} }
}
//TODO HACKY HACK remove this when texture unit is handled by the uniform. //TODO HACKY HACK remove this when texture unit is handled by the uniform.
return unit; return unit;

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

@ -51,6 +51,8 @@ public interface GL4 extends GL3 {
* Accepted by the {@code target} parameters of BindBuffer, BufferData, BufferSubData, MapBuffer, UnmapBuffer, GetBufferSubData, and GetBufferPointerv. * 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_BUFFER = 0x90D2;
public static final int GL_SHADER_STORAGE_BLOCK = 0x92E6;
public static final int GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS = 0x90DD;
/** /**
* <p><a target="_blank" href="http://docs.gl/gl4/glPatchParameteri">Reference Page</a></p> * <p><a target="_blank" href="http://docs.gl/gl4/glPatchParameteri">Reference Page</a></p>
@ -60,4 +62,28 @@ public interface GL4 extends GL3 {
* @param count the new value for the parameter given by {@code pname} * @param count the new value for the parameter given by {@code pname}
*/ */
public void glPatchParameter(int count); 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);
} }

@ -45,12 +45,9 @@ import com.jme3.scene.VertexBuffer;
import com.jme3.scene.VertexBuffer.Format; import com.jme3.scene.VertexBuffer.Format;
import com.jme3.scene.VertexBuffer.Type; import com.jme3.scene.VertexBuffer.Type;
import com.jme3.scene.VertexBuffer.Usage; import com.jme3.scene.VertexBuffer.Usage;
import com.jme3.shader.Attribute; import com.jme3.shader.*;
import com.jme3.shader.Shader;
import com.jme3.shader.Shader.ShaderSource; import com.jme3.shader.Shader.ShaderSource;
import com.jme3.shader.Shader.ShaderType; 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;
import com.jme3.texture.FrameBuffer.RenderBuffer; import com.jme3.texture.FrameBuffer.RenderBuffer;
import com.jme3.texture.Image; 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 // Print context information
logger.log(Level.INFO, "OpenGL Renderer Information\n" + logger.log(Level.INFO, "OpenGL Renderer Information\n" +
" * Vendor: {0}\n" + " * Vendor: {0}\n" +
@ -1184,20 +1185,35 @@ public final class GLRenderer implements Renderer {
Integer i = (Integer) uniform.getValue(); Integer i = (Integer) uniform.getValue();
gl.glUniform1i(loc, i.intValue()); gl.glUniform1i(loc, i.intValue());
break; 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();
break; assert storageBlock.getName() != null;
} assert shader.getId() > 0;
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) { protected void updateShaderUniforms(Shader shader) {
@ -1210,12 +1226,17 @@ public final class GLRenderer implements Renderer {
} }
} }
protected void updateShaderStorageBlocks(Shader shader) { /**
ListMap<String, Uniform> uniforms = shader.getUniformMap(); * Updates all shader's storage blocks.
*
* @param shader the shader.
*/
protected void updateShaderStorageBlocks(final Shader shader) {
final ListMap<String, StorageBlock> uniforms = shader.getStorageBlockMap();
for (int i = 0; i < uniforms.size(); i++) { for (int i = 0; i < uniforms.size(); i++) {
Uniform uniform = uniforms.getValue(i); final StorageBlock storageBlock = uniforms.getValue(i);
if (uniform.isUpdateNeeded()) { if (storageBlock.isUpdateNeeded()) {
updateUniform(shader, uniform); updateShaderStorageBlock(shader, storageBlock);
} }
} }
} }
@ -1438,6 +1459,7 @@ public final class GLRenderer implements Renderer {
assert shader.getId() > 0; assert shader.getId() > 0;
updateShaderUniforms(shader); updateShaderUniforms(shader);
updateShaderStorageBlocks(shader);
bindProgram(shader); bindProgram(shader);
} }
} }
@ -2529,6 +2551,10 @@ public final class GLRenderer implements Renderer {
@Override @Override
public void updateBufferData(final ShaderStorageBufferObject ssbo) { 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(); final ByteBuffer data = ssbo.getData();
if (data == null) { if (data == null) {
throw new IllegalArgumentException("Can't upload SSBO without data."); throw new IllegalArgumentException("Can't upload SSBO without data.");

@ -413,6 +413,11 @@ public final class Shader extends NativeObject {
uniform.reset(); // fixes issue with re-initialization uniform.reset(); // fixes issue with re-initialization
} }
} }
if (storageBlocks != null) {
for (StorageBlock storageBlock : storageBlocks.values()) {
storageBlock.reset();
}
}
if (attribs != null) { if (attribs != null) {
for (Entry<Attribute> entry : attribs) { for (Entry<Attribute> entry : attribs) {
entry.getValue().location = ShaderVariable.LOC_UNKNOWN; entry.getValue().location = ShaderVariable.LOC_UNKNOWN;

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2009-2017 jMonkeyEngine * Copyright (c) 2009-2018 jMonkeyEngine
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -31,425 +31,63 @@
*/ */
package com.jme3.shader; 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 * Implementation of shader's storage block.
* that can be sent to GL faster. *
*/ * @author JavaSaBr
protected FloatBuffer multiData = null;
/**
* Type of uniform
*/ */
protected VarType varType; public class StorageBlock extends ShaderVariable {
/** /**
* Binding to a renderer value, or null if user-defined uniform * Current used buffer object.
*/ */
protected UniformBinding binding; protected Object storageData;
/** /**
* Used to track which uniforms to clear to avoid * Set the new storage data.
* values leaking from other materials that use that shader. *
* @param storageData the new storage data
*/ */
protected boolean setByCurrentMaterial = false; public void setStorageData(final Object storageData) {
@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() { if (storageData == null) {
return setByCurrentMaterial; throw new IllegalArgumentException("for storage block " + name + ": storageData cannot be null");
} }
public void clearSetByCurrentMaterial(){ this.storageData = storageData;
setByCurrentMaterial = false;
}
public void clearValue(){
updateNeeded = true; 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;
} }
/**
* Return true if need to update this storage block.
*
* @return true if need to update this storage block.
*/
public boolean isUpdateNeeded(){ public boolean isUpdateNeeded(){
return updateNeeded; return updateNeeded;
} }
/**
* Clear the flag {@link #isUpdateNeeded()}.
*/
public void clearUpdateNeeded(){ public void clearUpdateNeeded(){
updateNeeded = false; updateNeeded = false;
} }
/**
* Reset this storage block.
*/
public void reset(){ public void reset(){
setByCurrentMaterial = false;
location = -2;
updateNeeded = true; updateNeeded = true;
} }
public void deleteNativeBuffers() { /**
if (value instanceof Buffer) { * Get the current storage data.
BufferUtils.destroyDirectBuffer((Buffer)value); *
value = null; // ???? * @return the current storage data.
} */
public Object getStorageData() {
return storageData;
} }
} }

Loading…
Cancel
Save