started implementing SSBO
This commit is contained in:
parent
13112b4532
commit
c154b6ae23
@ -37,6 +37,7 @@ import com.jme3.scene.Mesh;
|
|||||||
import com.jme3.scene.VertexBuffer;
|
import com.jme3.scene.VertexBuffer;
|
||||||
import com.jme3.shader.Shader;
|
import com.jme3.shader.Shader;
|
||||||
import com.jme3.shader.Shader.ShaderSource;
|
import com.jme3.shader.Shader.ShaderSource;
|
||||||
|
import com.jme3.shader.ShaderStorageBufferObject;
|
||||||
import com.jme3.system.AppSettings;
|
import com.jme3.system.AppSettings;
|
||||||
import com.jme3.texture.FrameBuffer;
|
import com.jme3.texture.FrameBuffer;
|
||||||
import com.jme3.texture.Image;
|
import com.jme3.texture.Image;
|
||||||
@ -267,12 +268,26 @@ public interface Renderer {
|
|||||||
*/
|
*/
|
||||||
public void updateBufferData(VertexBuffer vb);
|
public void updateBufferData(VertexBuffer vb);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uploads a shader storage buffer object to the GPU.
|
||||||
|
*
|
||||||
|
* @param ssbo the shader storage buffer object to upload.
|
||||||
|
*/
|
||||||
|
public void updateBufferData(ShaderStorageBufferObject ssbo);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes a vertex buffer from the GPU.
|
* Deletes a vertex buffer from the GPU.
|
||||||
* @param vb The vertex buffer to delete
|
* @param vb The vertex buffer to delete
|
||||||
*/
|
*/
|
||||||
public void deleteBuffer(VertexBuffer vb);
|
public void deleteBuffer(VertexBuffer vb);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a shader storage buffer object from the GPU.
|
||||||
|
*
|
||||||
|
* @param ssbo the shader storage buffer object to delete.
|
||||||
|
*/
|
||||||
|
public void deleteBuffer(ShaderStorageBufferObject ssbo);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders <code>count</code> meshes, with the geometry data supplied and
|
* Renders <code>count</code> meshes, with the geometry data supplied and
|
||||||
* per-instance data supplied.
|
* per-instance data supplied.
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -49,6 +49,7 @@ import com.jme3.shader.Attribute;
|
|||||||
import com.jme3.shader.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.shader.Uniform;
|
||||||
import com.jme3.texture.FrameBuffer;
|
import com.jme3.texture.FrameBuffer;
|
||||||
import com.jme3.texture.FrameBuffer.RenderBuffer;
|
import com.jme3.texture.FrameBuffer.RenderBuffer;
|
||||||
@ -1050,6 +1051,7 @@ public final class GLRenderer implements Renderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void postFrame() {
|
public void postFrame() {
|
||||||
objManager.deleteUnused(this);
|
objManager.deleteUnused(this);
|
||||||
OpenCLObjectManager.getInstance().deleteUnusedObjects();
|
OpenCLObjectManager.getInstance().deleteUnusedObjects();
|
||||||
@ -1182,38 +1184,17 @@ 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 Struct: {
|
case ShaderStorageBufferObject: {
|
||||||
|
|
||||||
final ByteBuffer data = (ByteBuffer) uniform.getValue();
|
final ShaderStorageBufferObject ssbo = (ShaderStorageBufferObject) uniform.getValue();
|
||||||
|
if (ssbo.getUniqueId() == -1 || ssbo.isUpdateNeeded()) {
|
||||||
|
updateBufferData(ssbo);
|
||||||
|
}
|
||||||
|
|
||||||
intBuf1.clear();
|
|
||||||
gl3.glGenBuffers(intBuf1);
|
|
||||||
|
|
||||||
final int bufferAddress = intBuf1.get();
|
|
||||||
final int blockIndex = gl3.glGetUniformBlockIndex(shaderId, uniform.getName());
|
|
||||||
|
|
||||||
gl3.glBindBuffer(GL3.GL_UNIFORM_BUFFER, bufferAddress);
|
|
||||||
gl3.glBufferData(GL3.GL_UNIFORM_BUFFER, data, GL.GL_DYNAMIC_DRAW);
|
|
||||||
gl3.glBindBufferBase(GL3.GL_UNIFORM_BUFFER, blockIndex, bufferAddress);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case StructArray: {
|
|
||||||
|
|
||||||
//TODO
|
|
||||||
final ByteBuffer data = (ByteBuffer) uniform.getValue();
|
|
||||||
|
|
||||||
intBuf1.clear();
|
|
||||||
gl3.glGenBuffers(intBuf1);
|
|
||||||
|
|
||||||
final int bufferAddress = intBuf1.get();
|
|
||||||
final int blockIndex = gl3.glGetUniformBlockIndex(shaderId, uniform.getName());
|
|
||||||
|
|
||||||
gl3.glBindBuffer(GL3.GL_UNIFORM_BUFFER, bufferAddress);
|
|
||||||
gl3.glBufferData(GL3.GL_UNIFORM_BUFFER, data, GL.GL_DYNAMIC_DRAW);
|
|
||||||
gl3.glBindBufferBase(GL3.GL_UNIFORM_BUFFER, blockIndex, bufferAddress);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new UnsupportedOperationException("Unsupported uniform type: " + uniform.getVarType());
|
throw new UnsupportedOperationException("Unsupported uniform type: " + uniform.getVarType());
|
||||||
}
|
}
|
||||||
@ -1229,6 +1210,16 @@ public final class GLRenderer implements Renderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void updateShaderStorageBlocks(Shader shader) {
|
||||||
|
ListMap<String, Uniform> uniforms = shader.getUniformMap();
|
||||||
|
for (int i = 0; i < uniforms.size(); i++) {
|
||||||
|
Uniform uniform = uniforms.getValue(i);
|
||||||
|
if (uniform.isUpdateNeeded()) {
|
||||||
|
updateUniform(shader, uniform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected void resetUniformLocations(Shader shader) {
|
protected void resetUniformLocations(Shader shader) {
|
||||||
ListMap<String, Uniform> uniforms = shader.getUniformMap();
|
ListMap<String, Uniform> uniforms = shader.getUniformMap();
|
||||||
for (int i = 0; i < uniforms.size(); i++) {
|
for (int i = 0; i < uniforms.size(); i++) {
|
||||||
@ -2535,6 +2526,37 @@ public final class GLRenderer implements Renderer {
|
|||||||
vb.clearUpdateNeeded();
|
vb.clearUpdateNeeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateBufferData(final ShaderStorageBufferObject ssbo) {
|
||||||
|
|
||||||
|
final ByteBuffer data = ssbo.getData();
|
||||||
|
if (data == null) {
|
||||||
|
throw new IllegalArgumentException("Can't upload SSBO without data.");
|
||||||
|
}
|
||||||
|
|
||||||
|
int bufferId = ssbo.getId();
|
||||||
|
if (bufferId == -1) {
|
||||||
|
|
||||||
|
// create buffer
|
||||||
|
intBuf1.clear();
|
||||||
|
gl.glGenBuffers(intBuf1);
|
||||||
|
bufferId = intBuf1.get(0);
|
||||||
|
|
||||||
|
ssbo.setId(bufferId);
|
||||||
|
|
||||||
|
objManager.registerObject(ssbo);
|
||||||
|
}
|
||||||
|
|
||||||
|
gl4.glBindBuffer(GL4.GL_SHADER_STORAGE_BUFFER, bufferId);
|
||||||
|
|
||||||
|
data.rewind();
|
||||||
|
|
||||||
|
gl4.glBufferData(GL4.GL_SHADER_STORAGE_BUFFER, data, GL4.GL_DYNAMIC_COPY);
|
||||||
|
gl4.glBindBuffer(GL4.GL_SHADER_STORAGE_BUFFER, 0);
|
||||||
|
|
||||||
|
ssbo.clearUpdateNeeded();
|
||||||
|
}
|
||||||
|
|
||||||
public void deleteBuffer(VertexBuffer vb) {
|
public void deleteBuffer(VertexBuffer vb) {
|
||||||
int bufId = vb.getId();
|
int bufId = vb.getId();
|
||||||
if (bufId != -1) {
|
if (bufId != -1) {
|
||||||
@ -2548,6 +2570,23 @@ public final class GLRenderer implements Renderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteBuffer(final ShaderStorageBufferObject ssbo) {
|
||||||
|
|
||||||
|
int bufferId = ssbo.getId();
|
||||||
|
if (bufferId == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
intBuf1.clear();
|
||||||
|
intBuf1.put(0, bufferId);
|
||||||
|
intBuf1.flip();
|
||||||
|
|
||||||
|
gl.glDeleteBuffers(intBuf1);
|
||||||
|
|
||||||
|
ssbo.resetObject();
|
||||||
|
}
|
||||||
|
|
||||||
public void clearVertexAttribs() {
|
public void clearVertexAttribs() {
|
||||||
IDList attribList = context.attribIndexList;
|
IDList attribList = context.attribIndexList;
|
||||||
for (int i = 0; i < attribList.oldLen; i++) {
|
for (int i = 0; i < attribList.oldLen; i++) {
|
||||||
|
@ -51,6 +51,11 @@ public final class Shader extends NativeObject {
|
|||||||
* Maps uniform name to the uniform variable.
|
* Maps uniform name to the uniform variable.
|
||||||
*/
|
*/
|
||||||
private final ListMap<String, Uniform> uniforms;
|
private final ListMap<String, Uniform> uniforms;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps storage block name to the storage block variable.
|
||||||
|
*/
|
||||||
|
private final ListMap<String, StorageBlock> storageBlocks;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Uniforms bound to {@link UniformBinding}s.
|
* Uniforms bound to {@link UniformBinding}s.
|
||||||
@ -220,10 +225,11 @@ public final class Shader extends NativeObject {
|
|||||||
*/
|
*/
|
||||||
public Shader(){
|
public Shader(){
|
||||||
super();
|
super();
|
||||||
shaderSourceList = new ArrayList<ShaderSource>();
|
shaderSourceList = new ArrayList<>();
|
||||||
uniforms = new ListMap<String, Uniform>();
|
uniforms = new ListMap<>();
|
||||||
attribs = new IntMap<Attribute>();
|
storageBlocks = new ListMap<>();
|
||||||
boundUniforms = new ArrayList<Uniform>();
|
attribs = new IntMap<>();
|
||||||
|
boundUniforms = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -240,6 +246,7 @@ public final class Shader extends NativeObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uniforms = null;
|
uniforms = null;
|
||||||
|
storageBlocks = null;
|
||||||
boundUniforms = null;
|
boundUniforms = null;
|
||||||
attribs = null;
|
attribs = null;
|
||||||
}
|
}
|
||||||
@ -288,10 +295,40 @@ public final class Shader extends NativeObject {
|
|||||||
return uniform;
|
return uniform;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get or create a storage block by the name.
|
||||||
|
*
|
||||||
|
* @param name the storage block's name.
|
||||||
|
* @return the storage block.
|
||||||
|
*/
|
||||||
|
public StorageBlock getStorageBlock(String name) {
|
||||||
|
|
||||||
|
assert name.startsWith("sb_");
|
||||||
|
|
||||||
|
StorageBlock storageBlock = storageBlocks.get(name);
|
||||||
|
|
||||||
|
if (storageBlock == null) {
|
||||||
|
storageBlock = new StorageBlock();
|
||||||
|
storageBlock.name = name;
|
||||||
|
storageBlocks.put(name, storageBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
return storageBlock;
|
||||||
|
}
|
||||||
|
|
||||||
public void removeUniform(String name){
|
public void removeUniform(String name){
|
||||||
uniforms.remove(name);
|
uniforms.remove(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a storage block by the name.
|
||||||
|
*
|
||||||
|
* @param name the storage block's name.
|
||||||
|
*/
|
||||||
|
public void removeStorageBlock(String name){
|
||||||
|
storageBlocks.remove(name);
|
||||||
|
}
|
||||||
|
|
||||||
public Attribute getAttribute(VertexBuffer.Type attribType){
|
public Attribute getAttribute(VertexBuffer.Type attribType){
|
||||||
int ordinal = attribType.ordinal();
|
int ordinal = attribType.ordinal();
|
||||||
Attribute attrib = attribs.get(ordinal);
|
Attribute attrib = attribs.get(ordinal);
|
||||||
@ -306,7 +343,16 @@ public final class Shader extends NativeObject {
|
|||||||
public ListMap<String, Uniform> getUniformMap(){
|
public ListMap<String, Uniform> getUniformMap(){
|
||||||
return uniforms;
|
return uniforms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the storage blocks map.
|
||||||
|
*
|
||||||
|
* @return the storage blocks map.
|
||||||
|
*/
|
||||||
|
public ListMap<String, StorageBlock> getStorageBlockMap() {
|
||||||
|
return storageBlocks;
|
||||||
|
}
|
||||||
|
|
||||||
public ArrayList<Uniform> getBoundUniforms() {
|
public ArrayList<Uniform> getBoundUniforms() {
|
||||||
return boundUniforms;
|
return boundUniforms;
|
||||||
}
|
}
|
||||||
@ -320,6 +366,7 @@ public final class Shader extends NativeObject {
|
|||||||
return getClass().getSimpleName() +
|
return getClass().getSimpleName() +
|
||||||
"[numSources=" + shaderSourceList.size() +
|
"[numSources=" + shaderSourceList.size() +
|
||||||
", numUniforms=" + uniforms.size() +
|
", numUniforms=" + uniforms.size() +
|
||||||
|
", numStorageBlocks=" + storageBlocks.size() +
|
||||||
", shaderSources=" + getSources() + "]";
|
", 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
|
* Resets all uniforms that do not have the "set-by-current-material" flag
|
||||||
* to their default value (usually all zeroes or false).
|
* to their default value (usually all zeroes or false).
|
||||||
* When a uniform is modified, that flag is set, to remove the flag,
|
* When a uniform is modified, that flag is set, to remove the flag,
|
||||||
* use {@link #clearUniformsSetByCurrent() }.
|
* use {@link #clearUniformsSetByCurrentFlag() }.
|
||||||
*/
|
*/
|
||||||
public void resetUniformsNotSetByCurrent() {
|
public void resetUniformsNotSetByCurrent() {
|
||||||
int size = uniforms.size();
|
int size = uniforms.size();
|
||||||
|
@ -0,0 +1,144 @@
|
|||||||
|
package com.jme3.shader;
|
||||||
|
|
||||||
|
import com.jme3.renderer.Renderer;
|
||||||
|
import com.jme3.util.BufferUtils;
|
||||||
|
import com.jme3.util.NativeObject;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The implementation of SSBO.
|
||||||
|
*
|
||||||
|
* @author JavaSaBr
|
||||||
|
*/
|
||||||
|
public class ShaderStorageBufferObject extends NativeObject {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The buffer's data.
|
||||||
|
*/
|
||||||
|
private ByteBuffer data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The binding number.
|
||||||
|
*/
|
||||||
|
private int binding;
|
||||||
|
|
||||||
|
public ShaderStorageBufferObject() {
|
||||||
|
this.handleRef = new Object();
|
||||||
|
this.binding = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ShaderStorageBufferObject(final int id) {
|
||||||
|
super(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the binding number.
|
||||||
|
*
|
||||||
|
* @param binding the binding number.
|
||||||
|
*/
|
||||||
|
public void setBinding(final int binding) {
|
||||||
|
this.binding = binding;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the binding number.
|
||||||
|
*
|
||||||
|
* @return the binding number.
|
||||||
|
*/
|
||||||
|
public int getBinding() {
|
||||||
|
return binding;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called to initialize the data in the {@link ShaderStorageBufferObject}. Must only
|
||||||
|
* be called once.
|
||||||
|
*
|
||||||
|
* @param data the native byte buffer.
|
||||||
|
*/
|
||||||
|
public void setupData(final ByteBuffer data) {
|
||||||
|
|
||||||
|
if (id != -1) {
|
||||||
|
throw new UnsupportedOperationException("Data has already been sent. Cannot setupData again.");
|
||||||
|
} else if (data.isReadOnly()) {
|
||||||
|
throw new IllegalArgumentException("VertexBuffer data cannot be read-only.");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.data = data;
|
||||||
|
|
||||||
|
setUpdateNeeded();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called to update the data in the buffer with new data. Can only
|
||||||
|
* be called after {@link #setupData(java.nio.ByteBuffer) }
|
||||||
|
* has been called. Note that it is fine to call this method on the
|
||||||
|
* data already set, e.g. ssbo.updateData(ssbo.getData()), this will just
|
||||||
|
* set the proper update flag indicating the data should be sent to the GPU
|
||||||
|
* again.
|
||||||
|
* <p>
|
||||||
|
* It is allowed to specify a buffer with different capacity than the
|
||||||
|
* originally set buffer.
|
||||||
|
*
|
||||||
|
* @param data the native data buffer to set.
|
||||||
|
*/
|
||||||
|
public void updateData(final ByteBuffer data) {
|
||||||
|
|
||||||
|
if (data == null) {
|
||||||
|
throw new IllegalArgumentException("SSBO can't be without data buffer.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the data buffer is read-only which is a sign
|
||||||
|
// of a bug on the part of the caller
|
||||||
|
if (data.isReadOnly()) {
|
||||||
|
throw new IllegalArgumentException("SSBO's data cannot be read-only.");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.data = data;
|
||||||
|
|
||||||
|
setUpdateNeeded();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the buffer's data.
|
||||||
|
*
|
||||||
|
* @return the buffer's data.
|
||||||
|
*/
|
||||||
|
public ByteBuffer getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resetObject() {
|
||||||
|
this.id = -1;
|
||||||
|
setUpdateNeeded();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteObject(final Object rendererObject) {
|
||||||
|
|
||||||
|
if (rendererObject instanceof Renderer) {
|
||||||
|
throw new IllegalArgumentException("This ssbo can't be deleted from " + rendererObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
((Renderer) rendererObject).deleteBuffer(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void deleteNativeBuffers() {
|
||||||
|
super.deleteNativeBuffers();
|
||||||
|
if (data != null) {
|
||||||
|
BufferUtils.destroyDirectBuffer(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NativeObject createDestructableClone() {
|
||||||
|
return new ShaderStorageBufferObject(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getUniqueId() {
|
||||||
|
return ((long) OBJTYPE_SSBO << 32) | ((long) id);
|
||||||
|
}
|
||||||
|
}
|
455
jme3-core/src/main/java/com/jme3/shader/StorageBlock.java
Normal file
455
jme3-core/src/main/java/com/jme3/shader/StorageBlock.java
Normal file
@ -0,0 +1,455 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2017 jMonkeyEngine
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
package com.jme3.shader;
|
||||||
|
|
||||||
|
import com.jme3.math.*;
|
||||||
|
import com.jme3.util.BufferUtils;
|
||||||
|
import com.jme3.util.TempVars;
|
||||||
|
|
||||||
|
import java.nio.Buffer;
|
||||||
|
import java.nio.FloatBuffer;
|
||||||
|
import java.nio.IntBuffer;
|
||||||
|
|
||||||
|
public class StorageBlock extends ShaderVariable {
|
||||||
|
|
||||||
|
private static final Integer ZERO_INT = 0;
|
||||||
|
private static final Float ZERO_FLT = Float.valueOf(0);
|
||||||
|
private static final FloatBuffer ZERO_BUF = BufferUtils.createFloatBuffer(4*4);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Currently set value of the uniform.
|
||||||
|
*/
|
||||||
|
protected Object value = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For arrays or matrices, efficient format
|
||||||
|
* that can be sent to GL faster.
|
||||||
|
*/
|
||||||
|
protected FloatBuffer multiData = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type of uniform
|
||||||
|
*/
|
||||||
|
protected VarType varType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binding to a renderer value, or null if user-defined uniform
|
||||||
|
*/
|
||||||
|
protected UniformBinding binding;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to track which uniforms to clear to avoid
|
||||||
|
* values leaking from other materials that use that shader.
|
||||||
|
*/
|
||||||
|
protected boolean setByCurrentMaterial = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int hash = 5;
|
||||||
|
hash = 31 * hash + (this.value != null ? this.value.hashCode() : 0);
|
||||||
|
hash = 31 * hash + (this.varType != null ? this.varType.hashCode() : 0);
|
||||||
|
hash = 31 * hash + (this.binding != null ? this.binding.hashCode() : 0);
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final StorageBlock other = (StorageBlock) obj;
|
||||||
|
if (this.value != other.value && (this.value == null || !this.value.equals(other.value))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return this.binding == other.binding && this.varType == other.varType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString(){
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("Uniform[name=");
|
||||||
|
sb.append(name);
|
||||||
|
if (varType != null){
|
||||||
|
sb.append(", type=");
|
||||||
|
sb.append(varType);
|
||||||
|
sb.append(", value=");
|
||||||
|
sb.append(value);
|
||||||
|
}else{
|
||||||
|
sb.append(", value=<not set>");
|
||||||
|
}
|
||||||
|
sb.append("]");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBinding(UniformBinding binding){
|
||||||
|
this.binding = binding;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UniformBinding getBinding(){
|
||||||
|
return binding;
|
||||||
|
}
|
||||||
|
|
||||||
|
public VarType getVarType() {
|
||||||
|
return varType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getValue(){
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FloatBuffer getMultiData() {
|
||||||
|
return multiData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSetByCurrentMaterial() {
|
||||||
|
return setByCurrentMaterial;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearSetByCurrentMaterial(){
|
||||||
|
setByCurrentMaterial = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearValue(){
|
||||||
|
updateNeeded = true;
|
||||||
|
|
||||||
|
if (multiData != null){
|
||||||
|
multiData.clear();
|
||||||
|
|
||||||
|
while (multiData.remaining() > 0){
|
||||||
|
ZERO_BUF.clear();
|
||||||
|
ZERO_BUF.limit( Math.min(multiData.remaining(), 16) );
|
||||||
|
multiData.put(ZERO_BUF);
|
||||||
|
}
|
||||||
|
|
||||||
|
multiData.clear();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (varType == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (varType){
|
||||||
|
case Int:
|
||||||
|
this.value = ZERO_INT;
|
||||||
|
break;
|
||||||
|
case Boolean:
|
||||||
|
this.value = Boolean.FALSE;
|
||||||
|
break;
|
||||||
|
case Float:
|
||||||
|
this.value = ZERO_FLT;
|
||||||
|
break;
|
||||||
|
case Vector2:
|
||||||
|
if (this.value != null) {
|
||||||
|
((Vector2f) this.value).set(Vector2f.ZERO);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Vector3:
|
||||||
|
if (this.value != null) {
|
||||||
|
((Vector3f) this.value).set(Vector3f.ZERO);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Vector4:
|
||||||
|
if (this.value != null) {
|
||||||
|
if (this.value instanceof ColorRGBA) {
|
||||||
|
((ColorRGBA) this.value).set(ColorRGBA.BlackNoAlpha);
|
||||||
|
} else if (this.value instanceof Vector4f) {
|
||||||
|
((Vector4f) this.value).set(Vector4f.ZERO);
|
||||||
|
} else {
|
||||||
|
((Quaternion) this.value).set(Quaternion.ZERO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// won't happen because those are either textures
|
||||||
|
// or multidata types
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(VarType type, Object value){
|
||||||
|
if (location == LOC_NOT_DEFINED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (varType != null && varType != type) {
|
||||||
|
throw new IllegalArgumentException("Expected a " + varType.name() + " value!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value == null) {
|
||||||
|
throw new IllegalArgumentException("for uniform " + name + ": value cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
setByCurrentMaterial = true;
|
||||||
|
|
||||||
|
switch (type){
|
||||||
|
case Matrix3:
|
||||||
|
if (value.equals(this.value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Matrix3f m3 = (Matrix3f) value;
|
||||||
|
if (multiData == null) {
|
||||||
|
multiData = BufferUtils.createFloatBuffer(9);
|
||||||
|
}
|
||||||
|
m3.fillFloatBuffer(multiData, true);
|
||||||
|
multiData.clear();
|
||||||
|
if (this.value == null) {
|
||||||
|
this.value = new Matrix3f(m3);
|
||||||
|
} else {
|
||||||
|
((Matrix3f)this.value).set(m3);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Matrix4:
|
||||||
|
if (value.equals(this.value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Matrix4f m4 = (Matrix4f) value;
|
||||||
|
if (multiData == null) {
|
||||||
|
multiData = BufferUtils.createFloatBuffer(16);
|
||||||
|
}
|
||||||
|
m4.fillFloatBuffer(multiData, true);
|
||||||
|
multiData.clear();
|
||||||
|
if (this.value == null) {
|
||||||
|
this.value = new Matrix4f(m4);
|
||||||
|
} else {
|
||||||
|
((Matrix4f)this.value).copy(m4);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case IntArray:
|
||||||
|
int[] ia = (int[]) value;
|
||||||
|
if (this.value == null) {
|
||||||
|
this.value = BufferUtils.createIntBuffer(ia);
|
||||||
|
} else {
|
||||||
|
this.value = BufferUtils.ensureLargeEnough((IntBuffer)this.value, ia.length);
|
||||||
|
}
|
||||||
|
((IntBuffer)this.value).clear();
|
||||||
|
break;
|
||||||
|
case FloatArray:
|
||||||
|
float[] fa = (float[]) value;
|
||||||
|
if (multiData == null) {
|
||||||
|
multiData = BufferUtils.createFloatBuffer(fa);
|
||||||
|
} else {
|
||||||
|
multiData = BufferUtils.ensureLargeEnough(multiData, fa.length);
|
||||||
|
}
|
||||||
|
multiData.put(fa);
|
||||||
|
multiData.clear();
|
||||||
|
break;
|
||||||
|
case Vector2Array:
|
||||||
|
Vector2f[] v2a = (Vector2f[]) value;
|
||||||
|
if (multiData == null) {
|
||||||
|
multiData = BufferUtils.createFloatBuffer(v2a);
|
||||||
|
} else {
|
||||||
|
multiData = BufferUtils.ensureLargeEnough(multiData, v2a.length * 2);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < v2a.length; i++) {
|
||||||
|
BufferUtils.setInBuffer(v2a[i], multiData, i);
|
||||||
|
}
|
||||||
|
multiData.clear();
|
||||||
|
break;
|
||||||
|
case Vector3Array:
|
||||||
|
Vector3f[] v3a = (Vector3f[]) value;
|
||||||
|
if (multiData == null) {
|
||||||
|
multiData = BufferUtils.createFloatBuffer(v3a);
|
||||||
|
} else {
|
||||||
|
multiData = BufferUtils.ensureLargeEnough(multiData, v3a.length * 3);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < v3a.length; i++) {
|
||||||
|
BufferUtils.setInBuffer(v3a[i], multiData, i);
|
||||||
|
}
|
||||||
|
multiData.clear();
|
||||||
|
break;
|
||||||
|
case Vector4Array:
|
||||||
|
Vector4f[] v4a = (Vector4f[]) value;
|
||||||
|
if (multiData == null) {
|
||||||
|
multiData = BufferUtils.createFloatBuffer(v4a);
|
||||||
|
} else {
|
||||||
|
multiData = BufferUtils.ensureLargeEnough(multiData, v4a.length * 4);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < v4a.length; i++) {
|
||||||
|
BufferUtils.setInBuffer(v4a[i], multiData, i);
|
||||||
|
}
|
||||||
|
multiData.clear();
|
||||||
|
break;
|
||||||
|
case Matrix3Array:
|
||||||
|
Matrix3f[] m3a = (Matrix3f[]) value;
|
||||||
|
if (multiData == null) {
|
||||||
|
multiData = BufferUtils.createFloatBuffer(m3a.length * 9);
|
||||||
|
} else {
|
||||||
|
multiData = BufferUtils.ensureLargeEnough(multiData, m3a.length * 9);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < m3a.length; i++) {
|
||||||
|
m3a[i].fillFloatBuffer(multiData, true);
|
||||||
|
}
|
||||||
|
multiData.clear();
|
||||||
|
break;
|
||||||
|
case Matrix4Array:
|
||||||
|
Matrix4f[] m4a = (Matrix4f[]) value;
|
||||||
|
if (multiData == null) {
|
||||||
|
multiData = BufferUtils.createFloatBuffer(m4a.length * 16);
|
||||||
|
} else {
|
||||||
|
multiData = BufferUtils.ensureLargeEnough(multiData, m4a.length * 16);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < m4a.length; i++) {
|
||||||
|
m4a[i].fillFloatBuffer(multiData, true);
|
||||||
|
}
|
||||||
|
multiData.clear();
|
||||||
|
break;
|
||||||
|
case Vector2:
|
||||||
|
if (value.equals(this.value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.value == null) {
|
||||||
|
this.value = new Vector2f((Vector2f) value);
|
||||||
|
} else {
|
||||||
|
((Vector2f) this.value).set((Vector2f) value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Vector3:
|
||||||
|
if (value.equals(this.value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.value == null) {
|
||||||
|
this.value = new Vector3f((Vector3f) value);
|
||||||
|
} else {
|
||||||
|
((Vector3f) this.value).set((Vector3f) value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Vector4:
|
||||||
|
if (value.equals(this.value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TempVars vars = TempVars.get();
|
||||||
|
Vector4f vec4 = vars.vect4f1;
|
||||||
|
//handle the null case
|
||||||
|
if (this.value == null) {
|
||||||
|
try {
|
||||||
|
this.value = value.getClass().newInstance();
|
||||||
|
} catch (InstantiationException | IllegalAccessException e) {
|
||||||
|
throw new IllegalArgumentException("Cannot instanciate param of class " + value.getClass().getCanonicalName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//feed the pivot vec 4 with the correct value
|
||||||
|
if (value instanceof ColorRGBA) {
|
||||||
|
ColorRGBA c = (ColorRGBA) value;
|
||||||
|
vec4.set(c.r, c.g, c.b, c.a);
|
||||||
|
} else if (value instanceof Vector4f) {
|
||||||
|
vec4.set((Vector4f) value);
|
||||||
|
} else {
|
||||||
|
Quaternion q = (Quaternion) value;
|
||||||
|
vec4.set(q.getX(), q.getY(), q.getZ(), q.getW());
|
||||||
|
}
|
||||||
|
|
||||||
|
//feed this.value with the collected values.
|
||||||
|
if (this.value instanceof ColorRGBA) {
|
||||||
|
((ColorRGBA) this.value).set(vec4.x, vec4.y, vec4.z, vec4.w);
|
||||||
|
} else if (value instanceof Vector4f) {
|
||||||
|
((Vector4f) this.value).set(vec4);
|
||||||
|
} else {
|
||||||
|
((Quaternion) this.value).set(vec4.x, vec4.y, vec4.z, vec4.w);
|
||||||
|
}
|
||||||
|
vars.release();
|
||||||
|
break;
|
||||||
|
// Only use check if equals optimization for primitive values
|
||||||
|
case Int:
|
||||||
|
case Float:
|
||||||
|
case Boolean:
|
||||||
|
if (value.equals(this.value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.value = value;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
this.value = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (multiData != null) {
|
||||||
|
// this.value = multiData;
|
||||||
|
// }
|
||||||
|
|
||||||
|
varType = type;
|
||||||
|
updateNeeded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVector4Length(int length){
|
||||||
|
if (location == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
multiData = BufferUtils.ensureLargeEnough(multiData, length * 4);
|
||||||
|
value = multiData;
|
||||||
|
varType = VarType.Vector4Array;
|
||||||
|
updateNeeded = true;
|
||||||
|
setByCurrentMaterial = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVector4InArray(float x, float y, float z, float w, int index){
|
||||||
|
if (location == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (varType != null && varType != VarType.Vector4Array) {
|
||||||
|
throw new IllegalArgumentException("Expected a " + varType.name() + " value!");
|
||||||
|
}
|
||||||
|
|
||||||
|
multiData.position(index * 4);
|
||||||
|
multiData.put(x).put(y).put(z).put(w);
|
||||||
|
multiData.rewind();
|
||||||
|
updateNeeded = true;
|
||||||
|
setByCurrentMaterial = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUpdateNeeded(){
|
||||||
|
return updateNeeded;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearUpdateNeeded(){
|
||||||
|
updateNeeded = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset(){
|
||||||
|
setByCurrentMaterial = false;
|
||||||
|
location = -2;
|
||||||
|
updateNeeded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteNativeBuffers() {
|
||||||
|
if (value instanceof Buffer) {
|
||||||
|
BufferUtils.destroyDirectBuffer((Buffer)value);
|
||||||
|
value = null; // ????
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -58,8 +58,7 @@ public enum VarType {
|
|||||||
TextureArray(false,true,"sampler2DArray|sampler2DArrayShadow"),
|
TextureArray(false,true,"sampler2DArray|sampler2DArrayShadow"),
|
||||||
TextureCubeMap(false,true,"samplerCube"),
|
TextureCubeMap(false,true,"samplerCube"),
|
||||||
Int("int"),
|
Int("int"),
|
||||||
Struct(false, false, "dynamic"),
|
ShaderStorageBufferObject(false, false, "dynamic");
|
||||||
StructArray(false, false, "dynamic");
|
|
||||||
|
|
||||||
private boolean usesMultiData = false;
|
private boolean usesMultiData = false;
|
||||||
private boolean textureType = false;
|
private boolean textureType = false;
|
||||||
|
@ -46,6 +46,7 @@ import com.jme3.scene.Mesh;
|
|||||||
import com.jme3.scene.VertexBuffer;
|
import com.jme3.scene.VertexBuffer;
|
||||||
import com.jme3.shader.Shader;
|
import com.jme3.shader.Shader;
|
||||||
import com.jme3.shader.Shader.ShaderSource;
|
import com.jme3.shader.Shader.ShaderSource;
|
||||||
|
import com.jme3.shader.ShaderStorageBufferObject;
|
||||||
import com.jme3.texture.FrameBuffer;
|
import com.jme3.texture.FrameBuffer;
|
||||||
import com.jme3.texture.Image;
|
import com.jme3.texture.Image;
|
||||||
import com.jme3.texture.Texture;
|
import com.jme3.texture.Texture;
|
||||||
@ -148,9 +149,17 @@ public class NullRenderer implements Renderer {
|
|||||||
public void updateBufferData(VertexBuffer vb) {
|
public void updateBufferData(VertexBuffer vb) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateBufferData(ShaderStorageBufferObject ssbo) {
|
||||||
|
}
|
||||||
|
|
||||||
public void deleteBuffer(VertexBuffer vb) {
|
public void deleteBuffer(VertexBuffer vb) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteBuffer(ShaderStorageBufferObject ssbo) {
|
||||||
|
}
|
||||||
|
|
||||||
public void renderMesh(Mesh mesh, int lod, int count, VertexBuffer[] instanceData) {
|
public void renderMesh(Mesh mesh, int lod, int count, VertexBuffer[] instanceData) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,7 +52,8 @@ public abstract class NativeObject implements Cloneable {
|
|||||||
OBJTYPE_SHADERSOURCE = 5,
|
OBJTYPE_SHADERSOURCE = 5,
|
||||||
OBJTYPE_AUDIOBUFFER = 6,
|
OBJTYPE_AUDIOBUFFER = 6,
|
||||||
OBJTYPE_AUDIOSTREAM = 7,
|
OBJTYPE_AUDIOSTREAM = 7,
|
||||||
OBJTYPE_FILTER = 8;
|
OBJTYPE_FILTER = 8,
|
||||||
|
OBJTYPE_SSBO = 9;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The object manager to which this NativeObject is registered to.
|
* The object manager to which this NativeObject is registered to.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user