Kaynağa Gözat

beta implementation of SSBO.

JavaSaBr 7 yıl önce
ebeveyn
işleme
cdb42a50c7

+ 19 - 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;
@@ -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());
+                }
             }
         }
 

+ 5 - 1
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

+ 26 - 0
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;
 
     /**
      * <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}
      */
     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);
 }

+ 44 - 18
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<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++) {
-            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.");

+ 5 - 0
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<Attribute> entry : attribs) {
                 entry.getValue().location = ShaderVariable.LOC_UNKNOWN;

+ 33 - 395
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=<not set>");
-        }
-        sb.append("]");
-        return sb.toString();
-    }
-
-    public void setBinding(UniformBinding binding){
-        this.binding = binding;
-    }
-
-    public UniformBinding getBinding(){
-        return binding;
-    }
-
-    public VarType getVarType() {
-        return varType;
-    }
-
-    public Object getValue(){
-        return value;
-    }
-    
-    public FloatBuffer getMultiData() {
-        return multiData;
-    }
-
-    public boolean isSetByCurrentMaterial() {
-        return setByCurrentMaterial;
-    }
-
-    public void clearSetByCurrentMaterial(){
-        setByCurrentMaterial = false;
-    }
-
-    public void clearValue(){
-        updateNeeded = true;
-
-        if (multiData != null){           
-            multiData.clear();
-
-            while (multiData.remaining() > 0){
-                ZERO_BUF.clear();
-                ZERO_BUF.limit( Math.min(multiData.remaining(), 16) );
-                multiData.put(ZERO_BUF);
-            }
-
-            multiData.clear();
-
-            return;
-        }
+    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;
     }
 }