fixes + ssbo impl + instanced actor rearch
Some checks failed
studiorailgun/Renderer/pipeline/head There was a failure building this commit

This commit is contained in:
austin 2024-09-12 13:42:36 -04:00
parent dd763b31a9
commit f7d1bbed70
28 changed files with 1353 additions and 410 deletions

View File

@ -730,6 +730,14 @@ DBody key refactor
Audio for grass/leaves
Re-enable UI test
Fix foliage chunk-level radius being too low (causing grass cutoff)
Turn on anti-aliasing for font generation
(09/12/2024)
Alias fonts
Fix audio handling bug
SSBO implementation
Rearch instanced actor
ParticleService implementation
# TODO

View File

@ -13,8 +13,13 @@ import org.lwjgl.openal.AL10;
*/
public class AudioSource {
/**
* An undefined source's id
*/
static final int UNDEFINED_ID = -1;
//The id for the source
int sourceId;
int sourceId = UNDEFINED_ID;
/**
* Creates an audio source object
@ -37,8 +42,10 @@ public class AudioSource {
*/
public void setBuffer(int bufferId) {
stop();
AL10.alSourcei(sourceId, AL10.AL_BUFFER, bufferId);
Globals.audioEngine.checkError();
if(isAllocated()){
AL10.alSourcei(sourceId, AL10.AL_BUFFER, bufferId);
Globals.audioEngine.checkError();
}
}
/**
@ -46,8 +53,10 @@ public class AudioSource {
* @param position the position
*/
public void setPosition(Vector3f position) {
AL10.alSource3f(sourceId, AL10.AL_POSITION, position.x, position.y, position.z);
Globals.audioEngine.checkError();
if(isAllocated()){
AL10.alSource3f(sourceId, AL10.AL_POSITION, position.x, position.y, position.z);
Globals.audioEngine.checkError();
}
}
/**
@ -55,8 +64,10 @@ public class AudioSource {
* @param speed the speed
*/
public void setSpeed(Vector3f speed) {
AL10.alSource3f(sourceId, AL10.AL_VELOCITY, speed.x, speed.y, speed.z);
Globals.audioEngine.checkError();
if(isAllocated()){
AL10.alSource3f(sourceId, AL10.AL_VELOCITY, speed.x, speed.y, speed.z);
Globals.audioEngine.checkError();
}
}
/**
@ -64,8 +75,10 @@ public class AudioSource {
* @param time The time in seconds
*/
public void setOffset(float time){
AL10.alSourcef(sourceId, AL11.AL_SEC_OFFSET, time);
Globals.audioEngine.checkError();
if(isAllocated()){
AL10.alSourcef(sourceId, AL11.AL_SEC_OFFSET, time);
Globals.audioEngine.checkError();
}
}
/**
@ -73,9 +86,11 @@ public class AudioSource {
* @param gain the gain
*/
public void setGain(float gain) {
LoggerInterface.loggerAudio.DEBUG("Set Gain: " + gain);
AL10.alSourcef(sourceId, AL10.AL_GAIN, gain);
Globals.audioEngine.checkError();
if(isAllocated()){
LoggerInterface.loggerAudio.DEBUG("Set Gain: " + gain);
AL10.alSourcef(sourceId, AL10.AL_GAIN, gain);
Globals.audioEngine.checkError();
}
}
/**
@ -84,16 +99,20 @@ public class AudioSource {
* @param value The value to set the param to
*/
public void setProperty(int param, float value) {
AL10.alSourcef(sourceId, param, value);
Globals.audioEngine.checkError();
if(isAllocated()){
AL10.alSourcef(sourceId, param, value);
Globals.audioEngine.checkError();
}
}
/**
* Plays the audio source
*/
public void play() {
AL10.alSourcePlay(sourceId);
Globals.audioEngine.checkError();
if(isAllocated()){
AL10.alSourcePlay(sourceId);
Globals.audioEngine.checkError();
}
}
/**
@ -101,8 +120,11 @@ public class AudioSource {
* @return True if it is playing, false otherwise
*/
public boolean isPlaying() {
boolean isPlaying = AL10.alGetSourcei(sourceId, AL10.AL_SOURCE_STATE) == AL10.AL_PLAYING;
Globals.audioEngine.checkError();
boolean isPlaying = false;
if(isAllocated()){
isPlaying = AL10.alGetSourcei(sourceId, AL10.AL_SOURCE_STATE) == AL10.AL_PLAYING;
Globals.audioEngine.checkError();
}
return isPlaying;
}
@ -110,16 +132,20 @@ public class AudioSource {
* Pauses the audio source
*/
public void pause() {
AL10.alSourcePause(sourceId);
Globals.audioEngine.checkError();
if(isAllocated()){
AL10.alSourcePause(sourceId);
Globals.audioEngine.checkError();
}
}
/**
* Stops the audio source
*/
public void stop() {
AL10.alSourceStop(sourceId);
Globals.audioEngine.checkError();
if(isAllocated()){
AL10.alSourceStop(sourceId);
Globals.audioEngine.checkError();
}
}
/**
@ -127,7 +153,16 @@ public class AudioSource {
*/
public void cleanup() {
stop();
sourceId = UNDEFINED_ID;
AL10.alDeleteSources(sourceId);
Globals.audioEngine.checkError();
}
/**
* Checks if the audio source is allocated or not
* @return true if allocated, false otherwise
*/
private boolean isAllocated(){
return sourceId != UNDEFINED_ID;
}
}

View File

@ -0,0 +1,77 @@
package electrosphere.client.entity.particle;
import java.util.Arrays;
import java.util.List;
import electrosphere.engine.signal.Signal;
import electrosphere.engine.signal.SignalServiceImpl;
import electrosphere.renderer.actor.instance.StridedInstanceData;
import electrosphere.renderer.buffer.HomogenousUniformBuffer.HomogenousBufferTypes;
/**
* The particle service
*/
public class ParticleService extends SignalServiceImpl {
/**
* The maximum number of particles
*/
static final int MAX_PARTICLES = 1000;
/**
* Path to the vertex shader
*/
static final String VERTEX_SHADER_PATH = "";
/**
* Path to the fragment shader
*/
static final String FRAGMENT_SHADER_PATH = "";
/**
* The instance data for the particles
*/
StridedInstanceData particleInstanceData;
/**
* Constructor
*/
public ParticleService() {
super("ParticleService");
}
@Override
public void destroy() {
super.destroy();
if(particleInstanceData != null){
particleInstanceData.destroy();
}
}
@Override
public boolean handle(Signal signal){
boolean rVal = false;
switch(signal.getType()){
case RENDERING_ENGINE_READY: {
List<HomogenousBufferTypes> types = Arrays.asList(new HomogenousBufferTypes[]{
//position
HomogenousBufferTypes.VEC3D,
//rotation
HomogenousBufferTypes.VEC4D,
//scale
HomogenousBufferTypes.VEC3D,
//texture index
HomogenousBufferTypes.INT,
});
this.particleInstanceData = new StridedInstanceData(MAX_PARTICLES,types,VERTEX_SHADER_PATH,FRAGMENT_SHADER_PATH);
rVal = true;
} break;
default: {
} break;
}
return rVal;
}
}

View File

@ -12,6 +12,7 @@ import electrosphere.audio.VirtualAudioSourceManager;
import electrosphere.audio.collision.HitboxAudioService;
import electrosphere.audio.movement.MovementAudioService;
import electrosphere.auth.AuthenticationManager;
import electrosphere.client.entity.particle.ParticleService;
import electrosphere.client.fluid.cells.FluidCellManager;
import electrosphere.client.fluid.manager.ClientFluidManager;
import electrosphere.client.foliagemanager.ClientFoliageManager;
@ -292,6 +293,7 @@ public class Globals {
//
public static String particleBillboardModel;
public static ParticleDefinition particleDefinition;
public static ParticleService particleService;
public static ShaderOptionMap shaderOptionMap;
@ -505,6 +507,7 @@ public class Globals {
//add services here
Globals.signalSystem = (SignalSystem)serviceManager.registerService(new SignalSystem());
Globals.elementService = (ElementService)serviceManager.registerService(new ElementService());
Globals.particleService = (ParticleService)serviceManager.registerService(new ParticleService());
serviceManager.instantiate();
//
//End service manager
@ -515,6 +518,7 @@ public class Globals {
Globals.signalSystem.registerService(SignalType.YOGA_APPLY, Globals.elementService);
Globals.signalSystem.registerService(SignalType.YOGA_DESTROY, Globals.elementService);
Globals.signalSystem.registerService(SignalType.UI_MODIFICATION, Globals.elementService);
Globals.signalSystem.registerService(SignalType.RENDERING_ENGINE_READY, Globals.particleService);
}
@ -668,6 +672,7 @@ public class Globals {
Globals.clientSynchronizationManager = null;
Globals.server = null;
Globals.serverSynchronizationManager = null;
Globals.playerManager = null;
Globals.javaPID = null;
Globals.RENDER_FLAG_RENDER_SHADOW_MAP = true;
Globals.RENDER_FLAG_RENDER_SCREEN_FRAMEBUFFER_CONTENT = false;

View File

@ -17,6 +17,11 @@ public class Signal {
//
ENGINE_SHUTDOWN,
//
//RENDERING
//
RENDERING_ENGINE_READY,
//
//UI
//

View File

@ -5,9 +5,11 @@ import java.util.Map;
import org.joml.Vector2i;
import org.lwjgl.opengl.GL40;
import org.lwjgl.opengl.GL45;
import electrosphere.engine.Globals;
import electrosphere.logger.LoggerInterface;
import electrosphere.renderer.buffer.UniformBlockBinding;
import electrosphere.renderer.shader.ShaderProgram;
/**
@ -54,6 +56,11 @@ public class OpenGLState {
//map of texture units and their corresponding texture pointers
Map<Integer,Integer> unitToPointerMap;
/**
* A map of index -> uniform buffer bound to that index
*/
Map<Integer,UniformBlockBinding> indexBlockMap;
/**
* Initializes the opengl state
@ -69,7 +76,8 @@ public class OpenGLState {
framebufferType = 0;
framebufferPointer = 0;
activeShader = null;
unitToPointerMap = new HashMap<Integer,Integer>();
this.unitToPointerMap = new HashMap<Integer,Integer>();
this.indexBlockMap = new HashMap<Integer,UniformBlockBinding>();
this.storeCurrentEnvironmentContraints();
}
@ -309,4 +317,38 @@ public class OpenGLState {
// }
}
/**
* Binds a buffer to a given uniform buffer block index
* @param index the index
* @param buffer the buffer
*/
public void glBindBufferBase(int index, UniformBlockBinding buffer){
if(this.indexBlockMap.containsKey(index)){
UniformBlockBinding currentBuffer = this.indexBlockMap.get(index);
if(currentBuffer == null || currentBuffer != buffer){
//does not already contain index, should bind
this.indexBlockMap.put(index,buffer);
GL45.glBindBufferBase(index, index, buffer.getId());
Globals.renderingEngine.checkError();
}
} else {
//does not already contain index, should bind
this.indexBlockMap.put(index,buffer);
GL45.glBindBufferBase(index, index, buffer.getId());
Globals.renderingEngine.checkError();
}
}
/**
* Unbinds a buffer from a given uniform buffer block index
* @param index the index
*/
public void glUnbindBufferBase(int index){
if(this.indexBlockMap.containsKey(index)){
this.indexBlockMap.remove(index);
GL45.glBindBufferBase(index, index, UniformBlockBinding.UNBIND_ADDRESS);
Globals.renderingEngine.checkError();
}
}
}

View File

@ -43,6 +43,7 @@ import org.lwjgl.opengl.GLDebugMessageCallback;
import org.lwjgl.system.MemoryStack;
import electrosphere.engine.Globals;
import electrosphere.engine.signal.Signal.SignalType;
import electrosphere.logger.LoggerInterface;
import electrosphere.renderer.debug.DebugRendering;
import electrosphere.renderer.framebuffer.Framebuffer;
@ -462,6 +463,13 @@ public class RenderingEngine {
float nearClip = 0.001f;
Globals.projectionMatrix.setPerspective(verticalFOV, Globals.aspectRatio, nearClip, Globals.userSettings.getGraphicsViewDistance());
Globals.viewMatrix.translation(new Vector3f(0.0f,0.0f,-3.0f));
/**
* Alert everyone that the rendering engine is ready
*/
if(Globals.signalSystem != null){
Globals.signalSystem.post(SignalType.RENDERING_ENGINE_READY);
}
}

View File

@ -0,0 +1,399 @@
package electrosphere.renderer.actor.instance;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.joml.Matrix4d;
import org.joml.Matrix4f;
import org.joml.Vector3d;
import org.joml.Vector3f;
import org.joml.Vector4d;
import org.joml.Vector4f;
import org.lwjgl.BufferUtils;
import electrosphere.engine.Globals;
import electrosphere.renderer.OpenGLState;
import electrosphere.renderer.RenderPipelineState;
import electrosphere.renderer.buffer.HomogenousInstancedArray;
import electrosphere.renderer.buffer.HomogenousUniformBuffer.HomogenousBufferTypes;
import electrosphere.renderer.buffer.ShaderAttribute;
/**
* An instance data that uses multiple homogenous buffers
*/
public class HomogenousInstanceData implements InstanceData {
//the capacity (number of instanced) of this data block. Defaults to 100
int capacity = 1000;
//shader paths
String vertexShaderPath;
String fragmentShaderPath;
//the number of draw calls since the last clear operation
int drawCalls = 0;
//The set of instanced actors to draw
List<InstancedActor> actorQueue = null;
//Map of actor to index in the buffers that are emitted
Map<InstancedActor,Integer> actorIndexMap = new HashMap<InstancedActor,Integer>();
//Map of index -> actor used for buffer evictions
Map<Integer,InstancedActor> indexActorMap = new HashMap<Integer,InstancedActor>();
//list of all attribute indices in use by this instance data
List<ShaderAttribute> attributeIndices = new LinkedList<ShaderAttribute>();
//map of attribute -> buffer of attribute data
Map<ShaderAttribute,Object> attributeCpuBufferMap = new HashMap<ShaderAttribute,Object>();
//map of attribute -> gl HomogenousBuffer
Map<ShaderAttribute,HomogenousInstancedArray> attributeGlBufferMap = new HashMap<ShaderAttribute,HomogenousInstancedArray>();
/**
* Constructor
* @param capacity Capacity of the buffer (number of elements) backing this data
*/
protected HomogenousInstanceData(int capacity, String vertexPath, String fragmentPath){
this.capacity = capacity;
this.vertexShaderPath = vertexPath;
this.fragmentShaderPath = fragmentPath;
actorQueue = new LinkedList<InstancedActor>();
}
/**
* Adds a new type of attribute to this instance data block
* @param shaderAttribute The shader attribute
* @param type The type of the attribute
*/
protected void addDataType(ShaderAttribute shaderAttribute, HomogenousBufferTypes type){
attributeIndices.add(shaderAttribute);
switch(type){
case VEC3F: {
attributeCpuBufferMap.put(shaderAttribute, BufferUtils.createFloatBuffer(capacity * 3));
} break;
case VEC3D: {
attributeCpuBufferMap.put(shaderAttribute, BufferUtils.createDoubleBuffer(capacity * 3));
} break;
case VEC4F: {
attributeCpuBufferMap.put(shaderAttribute, BufferUtils.createFloatBuffer(capacity * 4));
} break;
case VEC4D: {
attributeCpuBufferMap.put(shaderAttribute, BufferUtils.createDoubleBuffer(capacity * 4));
} break;
case DOUBLE: {
attributeCpuBufferMap.put(shaderAttribute, BufferUtils.createDoubleBuffer(capacity));
} break;
case FLOAT: {
attributeCpuBufferMap.put(shaderAttribute, BufferUtils.createFloatBuffer(capacity));
} break;
case INT: {
attributeCpuBufferMap.put(shaderAttribute, BufferUtils.createIntBuffer(capacity));
} break;
case MAT4F: {
attributeCpuBufferMap.put(shaderAttribute, BufferUtils.createFloatBuffer(capacity * 4 * 4));
} break;
case MAT4D: {
attributeCpuBufferMap.put(shaderAttribute, BufferUtils.createFloatBuffer(capacity * 4 * 4));
} break;
}
if(shaderAttribute.isSingleIndex()){
attributeGlBufferMap.put(shaderAttribute,HomogenousInstancedArray.createHomogenousInstancedArray(shaderAttribute.getIndex(), type, capacity));
} else {
attributeGlBufferMap.put(shaderAttribute,HomogenousInstancedArray.createHomogenousInstancedArray(shaderAttribute.getIndices(), type, capacity));
}
}
/**
* Adds an actor to be sorted in the queue
* @param actor The actor to be sorted
*/
public void addInstance(InstancedActor actor){
actorQueue.add(actor);
drawCalls++;
}
/**
* Gets the number of entries that are to be drawn
* @return The number of entries to be drawn
*/
public int getDrawCount(){
return drawCalls;
}
/**
* Clears the queue
*/
public void clearDrawQueue(){
actorQueue.clear();
drawCalls = 0;
}
/**
* Fills the buffers for the upcoming render call. The intention is to make this emberassingly parallel.
*/
public void fillBuffers(){
int i = 0;
//for some reason the limit is not being set correctly. This explicitly forces it for each buffer
for(ShaderAttribute attribute : attributeIndices){
switch(attributeGlBufferMap.get(attribute).getType()){
case VEC3F: {
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
buffer.limit(buffer.capacity());
} break;
case VEC3D: {
DoubleBuffer buffer = ((DoubleBuffer)attributeCpuBufferMap.get(attribute));
buffer.limit(buffer.capacity());
} break;
case VEC4F: {
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
buffer.limit(buffer.capacity());
} break;
case VEC4D: {
DoubleBuffer buffer = ((DoubleBuffer)attributeCpuBufferMap.get(attribute));
buffer.limit(buffer.capacity());
} break;
case MAT4F: {
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
buffer.limit(buffer.capacity());
// System.out.println(buffer.position() + " " + buffer.limit());
} break;
case MAT4D: {
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
buffer.limit(buffer.capacity());
} break;
case DOUBLE: {
DoubleBuffer buffer = ((DoubleBuffer)attributeCpuBufferMap.get(attribute));
buffer.limit(buffer.capacity());
} break;
case FLOAT: {
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
buffer.limit(buffer.capacity());
} break;
case INT: {
IntBuffer buffer = ((IntBuffer)attributeCpuBufferMap.get(attribute));
buffer.limit(buffer.capacity());
} break;
}
}
// actorQueue.sort(Comparator.naturalOrder());
//buffer data
for(InstancedActor actor : actorQueue){
//push values to attribute buffers
for(ShaderAttribute attribute : attributeIndices){
switch(attributeGlBufferMap.get(attribute).getType()){
case VEC3F: {
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
Vector3f vec = (Vector3f)actor.getAttributeValue(attribute);
buffer.put(vec.x);
buffer.put(vec.y);
buffer.put(vec.z);
} break;
case VEC3D: {
DoubleBuffer buffer = ((DoubleBuffer)attributeCpuBufferMap.get(attribute));
Vector3d vec = (Vector3d)actor.getAttributeValue(attribute);
buffer.put(vec.x);
buffer.put(vec.y);
buffer.put(vec.z);
} break;
case VEC4F: {
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
Vector4f vec = (Vector4f)actor.getAttributeValue(attribute);
buffer.put(vec.w);
buffer.put(vec.x);
buffer.put(vec.y);
buffer.put(vec.z);
} break;
case VEC4D: {
DoubleBuffer buffer = ((DoubleBuffer)attributeCpuBufferMap.get(attribute));
Vector4d vec = (Vector4d)actor.getAttributeValue(attribute);
buffer.put(vec.w);
buffer.put(vec.x);
buffer.put(vec.y);
buffer.put(vec.z);
} break;
case MAT4F: {
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
Matrix4f mat = (Matrix4f)actor.getAttributeValue(attribute);
buffer.put(mat.m00());
buffer.put(mat.m01());
buffer.put(mat.m02());
buffer.put(mat.m03());
buffer.put(mat.m10());
buffer.put(mat.m11());
buffer.put(mat.m12());
buffer.put(mat.m13());
buffer.put(mat.m20());
buffer.put(mat.m21());
buffer.put(mat.m22());
buffer.put(mat.m23());
buffer.put(mat.m30());
buffer.put(mat.m31());
buffer.put(mat.m32());
buffer.put(mat.m33());
} break;
case MAT4D: {
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
Matrix4d mat = (Matrix4d)actor.getAttributeValue(attribute);
buffer.put((float)mat.m00());
buffer.put((float)mat.m01());
buffer.put((float)mat.m02());
buffer.put((float)mat.m03());
buffer.put((float)mat.m10());
buffer.put((float)mat.m11());
buffer.put((float)mat.m12());
buffer.put((float)mat.m13());
buffer.put((float)mat.m20());
buffer.put((float)mat.m21());
buffer.put((float)mat.m22());
buffer.put((float)mat.m23());
buffer.put((float)mat.m30());
buffer.put((float)mat.m31());
buffer.put((float)mat.m32());
buffer.put((float)mat.m33());
} break;
case DOUBLE: {
DoubleBuffer buffer = ((DoubleBuffer)attributeCpuBufferMap.get(attribute));
buffer.put((Double)actor.getAttributeValue(attribute));
} break;
case FLOAT: {
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
buffer.put((Float)actor.getAttributeValue(attribute));
} break;
case INT: {
IntBuffer buffer = ((IntBuffer)attributeCpuBufferMap.get(attribute));
buffer.put((Integer)actor.getAttributeValue(attribute));
} break;
}
}
//increment
i++;
if(i >= capacity){
break;
}
}
//reset all buffers
flip();
}
/**
* Flips the data buffer(s)
*/
public void flip(){
//reset all buffers
for(ShaderAttribute attribute : attributeIndices){
switch(attributeGlBufferMap.get(attribute).getType()){
case VEC3F: {
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
if(buffer.position() > 0){
buffer.flip();
}
} break;
case VEC3D: {
DoubleBuffer buffer = ((DoubleBuffer)attributeCpuBufferMap.get(attribute));
if(buffer.position() > 0){
buffer.flip();
}
} break;
case VEC4F: {
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
if(buffer.position() > 0){
buffer.flip();
}
} break;
case VEC4D: {
DoubleBuffer buffer = ((DoubleBuffer)attributeCpuBufferMap.get(attribute));
if(buffer.position() > 0){
buffer.flip();
}
} break;
case MAT4F: {
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
if(buffer.position() > 0){
buffer.flip();
}
} break;
case MAT4D: {
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
if(buffer.position() > 0){
buffer.flip();
}
} break;
case DOUBLE: {
DoubleBuffer buffer = ((DoubleBuffer)attributeCpuBufferMap.get(attribute));
if(buffer.position() > 0){
buffer.flip();
}
} break;
case FLOAT: {
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
if(buffer.position() > 0){
buffer.flip();
}
} break;
case INT: {
IntBuffer buffer = ((IntBuffer)attributeCpuBufferMap.get(attribute));
if(buffer.position() > 0){
buffer.flip();
}
} break;
}
}
}
/**
* Gets a map of all attributes to the buffers of data for that attribute
* @return The data buffers
*/
public Map<ShaderAttribute,Object> getCpuBufferMap(){
return attributeCpuBufferMap;
}
/**
* Gets a map of attribute name to gl homogenous buffer object
* @return The map
*/
public Map<ShaderAttribute,HomogenousInstancedArray> getGlBufferMap(){
return attributeGlBufferMap;
}
@Override
public String getVertexShader(){
return vertexShaderPath;
}
@Override
public String getFragmentShader(){
return fragmentShaderPath;
}
@Override
public void upload(OpenGLState openGLState, RenderPipelineState renderPipelineState){
Map<ShaderAttribute,Object> buffers = this.getCpuBufferMap();
Map<ShaderAttribute,HomogenousInstancedArray> glBufferMap = this.getGlBufferMap();
for(ShaderAttribute attribute : buffers.keySet()){
HomogenousInstancedArray buffer = glBufferMap.get(attribute);
buffer.updateBuffer(buffers.get(attribute), 0);
buffer.bind(renderPipelineState);
}
renderPipelineState.setInstanceCount(this.getDrawCount());
Globals.renderingEngine.checkError();
}
@Override
public void destroy() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'destroy'");
}
}

View File

@ -1,366 +1,61 @@
package electrosphere.renderer.actor.instance;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.joml.Matrix4d;
import org.joml.Matrix4f;
import org.joml.Vector3d;
import org.joml.Vector3f;
import org.joml.Vector4d;
import org.joml.Vector4f;
import org.lwjgl.BufferUtils;
import electrosphere.renderer.buffer.HomogenousInstancedArray;
import electrosphere.renderer.buffer.ShaderAttribute;
import electrosphere.renderer.buffer.HomogenousUniformBuffer.HomogenousBufferTypes;
import electrosphere.renderer.OpenGLState;
import electrosphere.renderer.RenderPipelineState;
/**
* Effectively controls the block of data that is passed to the gpu each time this instance is drawn.
* Does priority management to draw the instanced of higher priority if there happens to be greater than the capacity number of items to draw.
*/
public class InstanceData {
//the capacity (number of instanced) of this data block. Defaults to 100
int capacity = 1000;
//shader paths
String vertexShaderPath;
String fragmentShaderPath;
//the number of draw calls since the last clear operation
int drawCalls = 0;
//The set of instanced actors to draw
List<InstancedActor> actorQueue = null;
//Map of actor to index in the buffers that are emitted
Map<InstancedActor,Integer> actorIndexMap = new HashMap<InstancedActor,Integer>();
//Map of index -> actor used for buffer evictions
Map<Integer,InstancedActor> indexActorMap = new HashMap<Integer,InstancedActor>();
//list of all attribute indices in use by this instance data
List<ShaderAttribute> attributeIndices = new LinkedList<ShaderAttribute>();
//map of attribute -> buffer of attribute data
Map<ShaderAttribute,Object> attributeCpuBufferMap = new HashMap<ShaderAttribute,Object>();
//map of attribute -> gl HomogenousBuffer
Map<ShaderAttribute,HomogenousInstancedArray> attributeGlBufferMap = new HashMap<ShaderAttribute,HomogenousInstancedArray>();
/**
* Constructor
* @param capacity Capacity of the buffer (number of elements) backing this data
*/
protected InstanceData(int capacity, String vertexPath, String fragmentPath){
this.capacity = capacity;
this.vertexShaderPath = vertexPath;
this.fragmentShaderPath = fragmentPath;
actorQueue = new LinkedList<InstancedActor>();
}
/**
* Adds a new type of attribute to this instance data block
* @param shaderAttribute The shader attribute
* @param type The type of the attribute
*/
protected void addDataType(ShaderAttribute shaderAttribute, HomogenousBufferTypes type){
attributeIndices.add(shaderAttribute);
switch(type){
case VEC3F: {
attributeCpuBufferMap.put(shaderAttribute, BufferUtils.createFloatBuffer(capacity * 3));
} break;
case VEC3D: {
attributeCpuBufferMap.put(shaderAttribute, BufferUtils.createDoubleBuffer(capacity * 3));
} break;
case VEC4F: {
attributeCpuBufferMap.put(shaderAttribute, BufferUtils.createFloatBuffer(capacity * 4));
} break;
case VEC4D: {
attributeCpuBufferMap.put(shaderAttribute, BufferUtils.createDoubleBuffer(capacity * 4));
} break;
case DOUBLE: {
attributeCpuBufferMap.put(shaderAttribute, BufferUtils.createDoubleBuffer(capacity));
} break;
case FLOAT: {
attributeCpuBufferMap.put(shaderAttribute, BufferUtils.createFloatBuffer(capacity));
} break;
case INT: {
attributeCpuBufferMap.put(shaderAttribute, BufferUtils.createIntBuffer(capacity));
} break;
case MAT4F: {
attributeCpuBufferMap.put(shaderAttribute, BufferUtils.createFloatBuffer(capacity * 4 * 4));
} break;
case MAT4D: {
attributeCpuBufferMap.put(shaderAttribute, BufferUtils.createFloatBuffer(capacity * 4 * 4));
} break;
}
if(shaderAttribute.isSingleIndex()){
attributeGlBufferMap.put(shaderAttribute,HomogenousInstancedArray.createHomogenousInstancedArray(shaderAttribute.getIndex(), type, capacity));
} else {
attributeGlBufferMap.put(shaderAttribute,HomogenousInstancedArray.createHomogenousInstancedArray(shaderAttribute.getIndices(), type, capacity));
}
}
public interface InstanceData {
/**
* Adds an actor to be sorted in the queue
* @param actor The actor to be sorted
*/
protected void addInstance(InstancedActor actor){
actorQueue.add(actor);
drawCalls++;
}
public void addInstance(InstancedActor actor);
/**
* Gets the number of entries that are to be drawn
* @return The number of entries to be drawn
*/
public int getDrawCount(){
return drawCalls;
}
public int getDrawCount();
/**
* Clears the queue
*/
protected void clearDrawQueue(){
actorQueue.clear();
drawCalls = 0;
}
public void clearDrawQueue();
/**
* Fills the buffers for the upcoming render call. The intention is to make this emberassingly parallel.
*/
protected void fillBuffers(){
int i = 0;
//for some reason the limit is not being set correctly. This explicitly forces it for each buffer
for(ShaderAttribute attribute : attributeIndices){
switch(attributeGlBufferMap.get(attribute).getType()){
case VEC3F: {
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
buffer.limit(buffer.capacity());
} break;
case VEC3D: {
DoubleBuffer buffer = ((DoubleBuffer)attributeCpuBufferMap.get(attribute));
buffer.limit(buffer.capacity());
} break;
case VEC4F: {
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
buffer.limit(buffer.capacity());
} break;
case VEC4D: {
DoubleBuffer buffer = ((DoubleBuffer)attributeCpuBufferMap.get(attribute));
buffer.limit(buffer.capacity());
} break;
case MAT4F: {
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
buffer.limit(buffer.capacity());
// System.out.println(buffer.position() + " " + buffer.limit());
} break;
case MAT4D: {
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
buffer.limit(buffer.capacity());
} break;
case DOUBLE: {
DoubleBuffer buffer = ((DoubleBuffer)attributeCpuBufferMap.get(attribute));
buffer.limit(buffer.capacity());
} break;
case FLOAT: {
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
buffer.limit(buffer.capacity());
} break;
case INT: {
IntBuffer buffer = ((IntBuffer)attributeCpuBufferMap.get(attribute));
buffer.limit(buffer.capacity());
} break;
}
}
actorQueue.sort(Comparator.naturalOrder());
//buffer data
for(InstancedActor actor : actorQueue){
//push values to attribute buffers
for(ShaderAttribute attribute : attributeIndices){
switch(attributeGlBufferMap.get(attribute).getType()){
case VEC3F: {
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
Vector3f vec = (Vector3f)actor.getAttributeValue(attribute);
buffer.put(vec.x);
buffer.put(vec.y);
buffer.put(vec.z);
} break;
case VEC3D: {
DoubleBuffer buffer = ((DoubleBuffer)attributeCpuBufferMap.get(attribute));
Vector3d vec = (Vector3d)actor.getAttributeValue(attribute);
buffer.put(vec.x);
buffer.put(vec.y);
buffer.put(vec.z);
} break;
case VEC4F: {
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
Vector4f vec = (Vector4f)actor.getAttributeValue(attribute);
buffer.put(vec.w);
buffer.put(vec.x);
buffer.put(vec.y);
buffer.put(vec.z);
} break;
case VEC4D: {
DoubleBuffer buffer = ((DoubleBuffer)attributeCpuBufferMap.get(attribute));
Vector4d vec = (Vector4d)actor.getAttributeValue(attribute);
buffer.put(vec.w);
buffer.put(vec.x);
buffer.put(vec.y);
buffer.put(vec.z);
} break;
case MAT4F: {
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
Matrix4f mat = (Matrix4f)actor.getAttributeValue(attribute);
buffer.put(mat.m00());
buffer.put(mat.m01());
buffer.put(mat.m02());
buffer.put(mat.m03());
buffer.put(mat.m10());
buffer.put(mat.m11());
buffer.put(mat.m12());
buffer.put(mat.m13());
buffer.put(mat.m20());
buffer.put(mat.m21());
buffer.put(mat.m22());
buffer.put(mat.m23());
buffer.put(mat.m30());
buffer.put(mat.m31());
buffer.put(mat.m32());
buffer.put(mat.m33());
} break;
case MAT4D: {
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
Matrix4d mat = (Matrix4d)actor.getAttributeValue(attribute);
buffer.put((float)mat.m00());
buffer.put((float)mat.m01());
buffer.put((float)mat.m02());
buffer.put((float)mat.m03());
buffer.put((float)mat.m10());
buffer.put((float)mat.m11());
buffer.put((float)mat.m12());
buffer.put((float)mat.m13());
buffer.put((float)mat.m20());
buffer.put((float)mat.m21());
buffer.put((float)mat.m22());
buffer.put((float)mat.m23());
buffer.put((float)mat.m30());
buffer.put((float)mat.m31());
buffer.put((float)mat.m32());
buffer.put((float)mat.m33());
} break;
case DOUBLE: {
DoubleBuffer buffer = ((DoubleBuffer)attributeCpuBufferMap.get(attribute));
buffer.put((Double)actor.getAttributeValue(attribute));
} break;
case FLOAT: {
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
buffer.put((Float)actor.getAttributeValue(attribute));
} break;
case INT: {
IntBuffer buffer = ((IntBuffer)attributeCpuBufferMap.get(attribute));
buffer.put((Integer)actor.getAttributeValue(attribute));
} break;
}
}
//increment
i++;
if(i >= capacity){
break;
}
}
//reset all buffers
flip();
}
protected void flip(){
//reset all buffers
for(ShaderAttribute attribute : attributeIndices){
switch(attributeGlBufferMap.get(attribute).getType()){
case VEC3F: {
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
if(buffer.position() > 0){
buffer.flip();
}
} break;
case VEC3D: {
DoubleBuffer buffer = ((DoubleBuffer)attributeCpuBufferMap.get(attribute));
if(buffer.position() > 0){
buffer.flip();
}
} break;
case VEC4F: {
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
if(buffer.position() > 0){
buffer.flip();
}
} break;
case VEC4D: {
DoubleBuffer buffer = ((DoubleBuffer)attributeCpuBufferMap.get(attribute));
if(buffer.position() > 0){
buffer.flip();
}
} break;
case MAT4F: {
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
if(buffer.position() > 0){
buffer.flip();
}
} break;
case MAT4D: {
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
if(buffer.position() > 0){
buffer.flip();
}
} break;
case DOUBLE: {
DoubleBuffer buffer = ((DoubleBuffer)attributeCpuBufferMap.get(attribute));
if(buffer.position() > 0){
buffer.flip();
}
} break;
case FLOAT: {
FloatBuffer buffer = ((FloatBuffer)attributeCpuBufferMap.get(attribute));
if(buffer.position() > 0){
buffer.flip();
}
} break;
case INT: {
IntBuffer buffer = ((IntBuffer)attributeCpuBufferMap.get(attribute));
if(buffer.position() > 0){
buffer.flip();
}
} break;
}
}
}
public void fillBuffers();
/**
* Gets a map of all attributes to the buffers of data for that attribute
* @return The data buffers
* Flips the buffer(s)
*/
public Map<ShaderAttribute,Object> getCpuBufferMap(){
return attributeCpuBufferMap;
}
public void flip();
/**
* Gets a map of attribute name to gl homogenous buffer object
* @return The map
* Gets the vertex shader associated with this data
* @return The vertex shader
*/
public Map<ShaderAttribute,HomogenousInstancedArray> getGlBufferMap(){
return attributeGlBufferMap;
}
public String getVertexShader();
/**
* Gets the fragment shader associated with this data
* @return The fragment shader
*/
public String getFragmentShader();
/**
* Uploads the instance data
*/
public void upload(OpenGLState openGLState, RenderPipelineState renderPipelineState);
/**
* Destroys the data
*/
public void destroy();
}

View File

@ -49,7 +49,7 @@ public class InstanceManager {
InstancedActor rVal = new InstancedActor(modelPath);
if(!pathToInstanceData.containsKey(modelPath)){
//create instance data
InstanceData instanceData = new InstanceData(capacity,vertexShaderPath,fragmentShaderPath);
HomogenousInstanceData instanceData = new HomogenousInstanceData(capacity,vertexShaderPath,fragmentShaderPath);
//queue shader
Globals.assetManager.addShaderToQueue(vertexShaderPath, fragmentShaderPath);
//if asset manager doesn't have model, queue model
@ -84,7 +84,7 @@ public class InstanceManager {
data.fillBuffers();
//fetch model/shader and draw if both available
ShaderProgram shader = Globals.assetManager.fetchShader(data.vertexShaderPath, null, data.fragmentShaderPath);
ShaderProgram shader = Globals.assetManager.fetchShader(data.getVertexShader(), null, data.getFragmentShader());
Model model = Globals.assetManager.fetchModel(modelPath);
if(model != null && shader != null){
openGLState.setActiveShader(renderPipelineState, shader);

View File

@ -0,0 +1,237 @@
package electrosphere.renderer.actor.instance;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.joml.Matrix4d;
import org.joml.Matrix4f;
import org.joml.Vector3d;
import org.joml.Vector3f;
import org.joml.Vector4d;
import org.joml.Vector4f;
import electrosphere.renderer.buffer.HomogenousUniformBuffer.HomogenousBufferTypes;
import electrosphere.renderer.buffer.HomogenousUniformBuffer;
import electrosphere.renderer.buffer.ShaderAttribute;
import electrosphere.renderer.buffer.ShaderStorageBuffer;
import electrosphere.renderer.OpenGLState;
import electrosphere.renderer.RenderPipelineState;
import electrosphere.renderer.buffer.BufferEnums.BufferAccess;
import electrosphere.renderer.buffer.BufferEnums.BufferUsage;
/**
* Instance data that uses a single buffer with strided input
*/
public class StridedInstanceData implements InstanceData {
//the capacity (number of instanced) of this data block. Defaults to 100
int capacity = 1000;
//shader paths
String vertexShaderPath;
String fragmentShaderPath;
//the number of draw calls since the last clear operation
int drawCalls = 0;
//The set of instanced actors to draw
List<InstancedActor> actorQueue = null;
//Map of actor to index in the buffers that are emitted
Map<InstancedActor,Integer> actorIndexMap = new HashMap<InstancedActor,Integer>();
//Map of index -> actor used for buffer evictions
Map<Integer,InstancedActor> indexActorMap = new HashMap<Integer,InstancedActor>();
//list of all attribute indices in use by this instance data
List<ShaderAttribute> attributeIndices = new LinkedList<ShaderAttribute>();
//the SSBO
ShaderStorageBuffer buffer;
/**
* Constructor
* @param capacity Capacity of the buffer (number of elements) backing this data
*/
public StridedInstanceData(int capacity, List<HomogenousBufferTypes> types, String vertexPath, String fragmentPath){
int entrySize = 0;
for(HomogenousBufferTypes type : types){
entrySize = entrySize + HomogenousUniformBuffer.calculateTypeSize(type);
}
this.capacity = capacity;
this.vertexShaderPath = vertexPath;
this.fragmentShaderPath = fragmentPath;
actorQueue = new LinkedList<InstancedActor>();
this.buffer = new ShaderStorageBuffer(capacity * entrySize, BufferUsage.STREAM, BufferAccess.DRAW);
}
/**
* Adds a new type of attribute to this instance data block
* @param shaderAttribute The shader attribute
* @param type The type of the attribute
*/
protected void addDataType(ShaderAttribute shaderAttribute, HomogenousBufferTypes type){
attributeIndices.add(shaderAttribute);
}
/**
* Adds an actor to be sorted in the queue
* @param actor The actor to be sorted
*/
public void addInstance(InstancedActor actor){
actorQueue.add(actor);
drawCalls++;
}
/**
* Gets the number of entries that are to be drawn
* @return The number of entries to be drawn
*/
public int getDrawCount(){
return drawCalls;
}
/**
* Clears the queue
*/
public void clearDrawQueue(){
actorQueue.clear();
drawCalls = 0;
}
/**
* Fills the buffers for the upcoming render call. The intention is to make this emberassingly parallel.
*/
public void fillBuffers(){
int i = 0;
ByteBuffer byteBuff = this.buffer.getBuffer();
//buffer data
for(InstancedActor actor : actorQueue){
//push values to attribute buffers
for(ShaderAttribute attribute : attributeIndices){
switch(attribute.getType()){
case VEC3F: {
Vector3f vec = (Vector3f)actor.getAttributeValue(attribute);
byteBuff.putFloat(vec.x);
byteBuff.putFloat(vec.y);
byteBuff.putFloat(vec.z);
} break;
case VEC3D: {
Vector3d vec = (Vector3d)actor.getAttributeValue(attribute);
byteBuff.putDouble(vec.x);
byteBuff.putDouble(vec.y);
byteBuff.putDouble(vec.z);
} break;
case VEC4F: {
Vector4f vec = (Vector4f)actor.getAttributeValue(attribute);
byteBuff.putFloat(vec.w);
byteBuff.putFloat(vec.x);
byteBuff.putFloat(vec.y);
byteBuff.putFloat(vec.z);
} break;
case VEC4D: {
Vector4d vec = (Vector4d)actor.getAttributeValue(attribute);
byteBuff.putDouble(vec.w);
byteBuff.putDouble(vec.x);
byteBuff.putDouble(vec.y);
byteBuff.putDouble(vec.z);
} break;
case MAT4F: {
Matrix4f mat = (Matrix4f)actor.getAttributeValue(attribute);
byteBuff.putFloat(mat.m00());
byteBuff.putFloat(mat.m01());
byteBuff.putFloat(mat.m02());
byteBuff.putFloat(mat.m03());
byteBuff.putFloat(mat.m10());
byteBuff.putFloat(mat.m11());
byteBuff.putFloat(mat.m12());
byteBuff.putFloat(mat.m13());
byteBuff.putFloat(mat.m20());
byteBuff.putFloat(mat.m21());
byteBuff.putFloat(mat.m22());
byteBuff.putFloat(mat.m23());
byteBuff.putFloat(mat.m30());
byteBuff.putFloat(mat.m31());
byteBuff.putFloat(mat.m32());
byteBuff.putFloat(mat.m33());
} break;
case MAT4D: {
Matrix4d mat = (Matrix4d)actor.getAttributeValue(attribute);
byteBuff.putDouble((float)mat.m00());
byteBuff.putDouble((float)mat.m01());
byteBuff.putDouble((float)mat.m02());
byteBuff.putDouble((float)mat.m03());
byteBuff.putDouble((float)mat.m10());
byteBuff.putDouble((float)mat.m11());
byteBuff.putDouble((float)mat.m12());
byteBuff.putDouble((float)mat.m13());
byteBuff.putDouble((float)mat.m20());
byteBuff.putDouble((float)mat.m21());
byteBuff.putDouble((float)mat.m22());
byteBuff.putDouble((float)mat.m23());
byteBuff.putDouble((float)mat.m30());
byteBuff.putDouble((float)mat.m31());
byteBuff.putDouble((float)mat.m32());
byteBuff.putDouble((float)mat.m33());
} break;
case DOUBLE: {
byteBuff.putDouble((Double)actor.getAttributeValue(attribute));
} break;
case FLOAT: {
byteBuff.putFloat((Float)actor.getAttributeValue(attribute));
} break;
case INT: {
byteBuff.putInt((Integer)actor.getAttributeValue(attribute));
} break;
}
}
//increment
i++;
if(i >= capacity){
break;
}
}
//reset all buffers
flip();
}
/**
* Flips the data buffer(s)
*/
public void flip(){
this.buffer.getBuffer().flip();
}
@Override
public String getVertexShader(){
return vertexShaderPath;
}
@Override
public String getFragmentShader(){
return fragmentShaderPath;
}
@Override
public void upload(OpenGLState openGLState, RenderPipelineState renderPipelineState){
this.buffer.upload();
openGLState.glBindBufferBase(0, buffer);
}
@Override
public void destroy() {
if(this.buffer != null){
this.buffer.destroy();
}
}
}

View File

@ -0,0 +1,97 @@
package electrosphere.renderer.buffer;
import org.lwjgl.opengl.GL45;
/**
* Enums for various buffer data
*/
public class BufferEnums {
/**
* Buffer usage enum
*/
public static enum BufferUsage {
/**
* The data store contents will be modified once and used many times.
*/
STATIC,
/**
* The data store contents will be modified once and used at most a few times.
*/
STREAM,
/**
* The data store contents will be modified repeatedly and used many times.
*/
DYNAMIC,
}
/**
* Buffer access enum
*/
public static enum BufferAccess {
/**
* The data store contents are modified by the application, and used as the source for GL drawing and image specification commands.
*/
DRAW,
/**
* The data store contents are modified by reading data from the GL, and used to return that data when queried by the application.
*/
READ,
/**
* The data store contents are modified by reading data from the GL, and used as the source for GL drawing and image specification commands.
*/
COPY,
}
/**
* Gets the constant for the given usage and access combination
* @param usage The usage value
* @param access The access value
* @return The constant
*/
public static int getBufferUsage(BufferUsage usage, BufferAccess access){
if(usage == null || access == null){
throw new IllegalArgumentException("Passed null value into getBufferUsage! " + usage + " " + access);
}
switch(usage){
case STATIC: {
switch(access){
case DRAW:
return GL45.GL_STATIC_DRAW;
case READ:
return GL45.GL_STATIC_READ;
case COPY:
return GL45.GL_STATIC_COPY;
}
} break;
case STREAM: {
switch(access){
case DRAW:
return GL45.GL_STREAM_DRAW;
case READ:
return GL45.GL_STREAM_READ;
case COPY:
return GL45.GL_STREAM_COPY;
}
} break;
case DYNAMIC: {
switch(access){
case DRAW:
return GL45.GL_DYNAMIC_DRAW;
case READ:
return GL45.GL_DYNAMIC_READ;
case COPY:
return GL45.GL_DYNAMIC_COPY;
}
} break;
}
throw new IllegalStateException("Somehow hit unreachable code! " + access + " " + usage);
}
}

View File

@ -119,6 +119,43 @@ public class HomogenousUniformBuffer {
return 0;
}
/**
* Calculates the number of bytes required by this type
* @return The number of bytes
*/
public static int calculateTypeSize(HomogenousBufferTypes type){
switch(type){
case VEC3F: {
return 3 * 4;
}
case VEC3D: {
return 3 * 8;
}
case VEC4F: {
return 4 * 4;
}
case VEC4D: {
return 4 * 8;
}
case DOUBLE: {
return 1 * 8;
}
case FLOAT: {
return 1 * 4;
}
case INT: {
return 1 * 4;
}
case MAT4F: {
return 4 * 4 * 4;
}
case MAT4D: {
return 4 * 4 * 4;
}
}
return 0;
}
/**
* Returns whether this buffer is ready to stream data to or bind
* @return True if ready, false otherwise

View File

@ -0,0 +1,54 @@
package electrosphere.renderer.buffer;
import org.lwjgl.opengl.GL45;
/**
* An opengl buffer
*/
public interface OpenGLBuffer {
/**
* The type of the buffer
*/
public static enum BufferType {
/**
* An array buffer
*/
GL_ARRAY_BUFFER,
/**
* A storage buffer
*/
GL_SHADER_STORAGE_BUFFER,
}
/**
* Gets the id of the buffer on opengl side
* @return The id
*/
public int getId();
/**
* Gets the type of the buffer
* @return The type
*/
public BufferType getType();
/**
* Gets the int representing the buffer type
* @param buffer The buffer
*/
public static int getTypeInt(OpenGLBuffer buffer){
if(buffer == null){
throw new IllegalArgumentException("Passed null buffer into getTypeInt! " + buffer);
}
switch(buffer.getType()){
case GL_ARRAY_BUFFER:
return GL45.GL_ARRAY_BUFFER;
case GL_SHADER_STORAGE_BUFFER:
return GL45.GL_SHADER_STORAGE_BUFFER;
}
throw new IllegalStateException("Somehow reached unreachable code! " + buffer + " " + buffer.getType());
}
}

View File

@ -1,5 +1,7 @@
package electrosphere.renderer.buffer;
import electrosphere.renderer.buffer.HomogenousUniformBuffer.HomogenousBufferTypes;
/**
* Represents an attribute of a shader.
* When working with shader attributes, almost all data types are 1-1 where one vec4 can be stored in 1 attribute.
@ -13,6 +15,11 @@ public class ShaderAttribute {
//for multi-attribute index types (mat4f, mat4d, etc)
int[] attributeIndices;
/**
* The type of the attribute
*/
HomogenousBufferTypes type;
/**
* Constructor for 1-1 attribute
@ -30,6 +37,15 @@ public class ShaderAttribute {
this.attributeIndices = attributeIndices;
}
/**
* Constructor for 1-1 attribute
* @param attributeIndex The attribute index
*/
public ShaderAttribute(int attributeIndex, HomogenousBufferTypes type){
this.attributeIndex = attributeIndex;
this.type = type;
}
/**
* Checks if the attribute is a 1-1 relation
* @return True if 1-1, false otherwise
@ -54,4 +70,12 @@ public class ShaderAttribute {
return attributeIndices;
}
/**
* Gets the type of the attribute
* @return The type
*/
public HomogenousBufferTypes getType(){
return type;
}
}

View File

@ -0,0 +1,105 @@
package electrosphere.renderer.buffer;
import java.nio.ByteBuffer;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL45;
import org.lwjgl.system.MemoryUtil;
import electrosphere.engine.Globals;
import electrosphere.renderer.buffer.BufferEnums.BufferAccess;
import electrosphere.renderer.buffer.BufferEnums.BufferUsage;
/**
* A shader storage buffer
*/
public class ShaderStorageBuffer implements UniformBlockBinding {
/**
* The id for the buffer
*/
int id;
/**
* The java buffer associated with the SSBO
*/
ByteBuffer buffer;
/**
* Constructor
* @param capacity The capacity of the SSBO
* @param usage The usage of the buffer
* @param access The access of the buffer
*/
public ShaderStorageBuffer(int capacity, BufferUsage usage, BufferAccess access){
//create the buffer java-side
buffer = BufferUtils.createByteBuffer(capacity);
//create the buffer opengl-side
id = GL45.glGenBuffers();
Globals.renderingEngine.checkError();
GL45.glBindBuffer(GL45.GL_SHADER_STORAGE_BUFFER, id);
Globals.renderingEngine.checkError();
GL45.glBufferData(GL45.GL_SHADER_STORAGE_BUFFER, MemoryUtil.NULL, BufferEnums.getBufferUsage(usage, access));
Globals.renderingEngine.checkError();
GL45.glBindBuffer(GL45.GL_SHADER_STORAGE_BUFFER,id);
Globals.renderingEngine.checkError();
GL45.glBindBuffer(GL45.GL_SHADER_STORAGE_BUFFER,UNBIND_ADDRESS);
Globals.renderingEngine.checkError();
}
/**
* Uploads the java side buffer to opengl
*/
public void upload(){
long offset = 0;
//bind
GL45.glBindBuffer(GL45.GL_SHADER_STORAGE_BUFFER,id);
Globals.renderingEngine.checkError();
//upload
GL45.glBufferSubData(GL45.GL_SHADER_STORAGE_BUFFER, offset, buffer);
Globals.renderingEngine.checkError();
//unbind
GL45.glBindBuffer(GL45.GL_SHADER_STORAGE_BUFFER,UNBIND_ADDRESS);
Globals.renderingEngine.checkError();
}
/**
* Destroys the buffer
*/
public void destroy(){
if(buffer != null){
//destroy opengl-side buffer
GL45.glDeleteBuffers(id);
//destroy java-side buffer
MemoryUtil.memFree(buffer);
//set the java-side buffer to be null
buffer = null;
}
}
/**
* Gets the java-side buffer
* @return The java-side buffer
*/
public ByteBuffer getBuffer(){
return this.buffer;
}
@Override
public int getId() {
return this.id;
}
@Override
public BufferType getType() {
return BufferType.GL_SHADER_STORAGE_BUFFER;
}
}

View File

@ -0,0 +1,13 @@
package electrosphere.renderer.buffer;
/**
* A buffer that can be bound to a uniform block
*/
public interface UniformBlockBinding extends OpenGLBuffer {
/**
* The address to use to unbind a buffer
*/
static final int UNBIND_ADDRESS = 0;
}

View File

@ -7,8 +7,6 @@ import electrosphere.renderer.RenderPipelineState;
import electrosphere.renderer.RenderingEngine;
import electrosphere.renderer.actor.ActorTextureMask;
import electrosphere.renderer.actor.instance.InstanceData;
import electrosphere.renderer.buffer.HomogenousInstancedArray;
import electrosphere.renderer.buffer.ShaderAttribute;
import electrosphere.renderer.light.LightManager;
import electrosphere.renderer.shader.ShaderProgram;
import electrosphere.renderer.texture.Texture;
@ -18,7 +16,6 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.joml.Matrix4d;
import org.joml.Matrix4f;
@ -322,23 +319,6 @@ public class Mesh {
}
}
}
/**
* Sends a buffer to the gpu
* @param uniformTypeMap The type of the buffer
* @param buffers The buffer
*/
void bufferInstanceData(
RenderPipelineState renderPipelineState,
Map<ShaderAttribute,Object> buffers,
Map<ShaderAttribute,HomogenousInstancedArray> uniformGlBufferMap
){
for(ShaderAttribute attribute : buffers.keySet()){
HomogenousInstancedArray buffer = uniformGlBufferMap.get(attribute);
buffer.updateBuffer(buffers.get(attribute), 0);
buffer.bind(renderPipelineState);
}
}
/**
@ -491,11 +471,7 @@ public class Mesh {
if(renderPipelineState.getInstanced()){
if(renderPipelineState.getInstanceData()!=null){
InstanceData instanceData = renderPipelineState.getInstanceData();
Map<ShaderAttribute,Object> buffers = instanceData.getCpuBufferMap();
Map<ShaderAttribute,HomogenousInstancedArray> glBufferMap = instanceData.getGlBufferMap();
bufferInstanceData(renderPipelineState, buffers, glBufferMap);
renderPipelineState.setInstanceCount(instanceData.getDrawCount());
Globals.renderingEngine.checkError();
instanceData.upload(openGLState, renderPipelineState);
}
}

View File

@ -47,7 +47,7 @@ public class FontManager {
font = new java.awt.Font(java.awt.Font.MONOSPACED, java.awt.Font.PLAIN, 16);
}
if(font!=null){
defaultFont = FontUtils.loadFont(Globals.renderingEngine.getOpenGLState(), font, false);
defaultFont = FontUtils.loadFont(Globals.renderingEngine.getOpenGLState(), font, true);
fontMap.put("default",defaultFont);
}
}

View File

@ -0,0 +1,64 @@
package electrosphere.renderer.buffer;
import static org.junit.jupiter.api.Assertions.*;
import electrosphere.engine.Globals;
import electrosphere.renderer.OpenGLState;
import electrosphere.renderer.buffer.BufferEnums.BufferAccess;
import electrosphere.renderer.buffer.BufferEnums.BufferUsage;
import electrosphere.test.annotations.UnitTest;
import electrosphere.test.template.RenderingTestTemplate;
/**
* Tests for the shader storage buffer
*/
public class ShaderStorageBufferTests extends RenderingTestTemplate {
@UnitTest
public void test_Constructor_NoThrow(){
assertDoesNotThrow(() -> {
new ShaderStorageBuffer(10, BufferUsage.STATIC, BufferAccess.READ);
});
}
@UnitTest
public void test_Constructor_JavaSideCapacity_10(){
ShaderStorageBuffer buffer = new ShaderStorageBuffer(10, BufferUsage.STATIC, BufferAccess.READ);
assertEquals(10, buffer.getBuffer().limit());
}
@UnitTest
public void test_destroy_NoThrow(){
assertDoesNotThrow(() -> {
ShaderStorageBuffer buffer = new ShaderStorageBuffer(10, BufferUsage.STATIC, BufferAccess.READ);
buffer.destroy();
});
}
@UnitTest
public void test_destroyTwice_NoThrow(){
assertDoesNotThrow(() -> {
ShaderStorageBuffer buffer = new ShaderStorageBuffer(10, BufferUsage.STATIC, BufferAccess.READ);
buffer.destroy();
buffer.destroy();
});
}
@UnitTest
public void test_upload_NoThrow(){
assertDoesNotThrow(() -> {
ShaderStorageBuffer buffer = new ShaderStorageBuffer(10, BufferUsage.STATIC, BufferAccess.READ);
buffer.upload();
});
}
@UnitTest
public void test_bind_NoThrow(){
assertDoesNotThrow(() -> {
ShaderStorageBuffer buffer = new ShaderStorageBuffer(10, BufferUsage.STATIC, BufferAccess.READ);
OpenGLState openGLState = Globals.renderingEngine.getOpenGLState();
openGLState.glBindBufferBase(0, buffer);
});
}
}

View File

@ -2,25 +2,19 @@ package electrosphere.renderer.framebuffer;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import electrosphere.engine.Globals;
import electrosphere.renderer.RenderingEngine;
import electrosphere.renderer.texture.Texture;
import electrosphere.test.template.extensions.StateCleanupCheckerExtension;
import electrosphere.test.annotations.UnitTest;
import electrosphere.test.template.RenderingTestTemplate;
/**
* Tests for framebuffer creation utilities
*/
@ExtendWith(StateCleanupCheckerExtension.class)
public class FramebufferUtilsTests {
public class FramebufferUtilsTests extends RenderingTestTemplate {
@Test
@UnitTest
public void testCreateScreenFramebuffer(){
Globals.initGlobals();
Globals.renderingEngine = new RenderingEngine();
Globals.renderingEngine.createOpenglContext();
assertDoesNotThrow(() -> {
Texture screenTextureColor = FramebufferUtils.generateScreenTextureColorAlpha(Globals.renderingEngine.getOpenGLState(), Globals.WINDOW_WIDTH, Globals.WINDOW_HEIGHT);
RenderingEngine.screenTextureColor = screenTextureColor;
@ -29,11 +23,9 @@ public class FramebufferUtilsTests {
Framebuffer screenFramebuffer = FramebufferUtils.generateScreenTextureFramebuffer(Globals.renderingEngine.getOpenGLState(), Globals.WINDOW_WIDTH, Globals.WINDOW_HEIGHT, screenTextureColor, screenTextureDepth);
RenderingEngine.screenFramebuffer = screenFramebuffer;
});
Globals.renderingEngine.destroy();
Globals.resetGlobals();
}
@Test
@UnitTest
public void testCreateScreenFramebufferRepeat(){
assertDoesNotThrow(() -> {
Globals.initGlobals();
@ -51,8 +43,6 @@ public class FramebufferUtilsTests {
screenTextureColor = FramebufferUtils.generateScreenTextureColorAlpha(Globals.renderingEngine.getOpenGLState(), Globals.WINDOW_WIDTH, Globals.WINDOW_HEIGHT);
screenTextureDepth = FramebufferUtils.generateScreenTextureDepth(Globals.renderingEngine.getOpenGLState(), Globals.WINDOW_WIDTH, Globals.WINDOW_HEIGHT);
FramebufferUtils.generateScreenTextureFramebuffer(Globals.renderingEngine.getOpenGLState(), Globals.WINDOW_WIDTH, Globals.WINDOW_HEIGHT, screenTextureColor, screenTextureDepth);
Globals.renderingEngine.destroy();
Globals.resetGlobals();
});
}

View File

@ -3,13 +3,13 @@ package electrosphere.renderer.ui.elements;
import electrosphere.test.annotations.IntegrationTest;
import electrosphere.menu.WindowUtils;
import electrosphere.menu.mainmenu.MenuGeneratorsUITesting;
import electrosphere.test.template.RenderingTestTemplate;
import electrosphere.test.template.UITestTemplate;
import electrosphere.test.testutils.TestEngineUtils;
/**
* Tests for the window class
*/
public class WindowTest extends RenderingTestTemplate {
public class WindowTest extends UITestTemplate {
/**
* Tests creating a window

View File

@ -0,0 +1,43 @@
package electrosphere.test.template;
import java.io.File;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.extension.ExtendWith;
import electrosphere.test.template.extensions.StateCleanupCheckerExtension;
import electrosphere.test.template.extensions.UIExtension;
import static electrosphere.test.testutils.Assertions.*;
import electrosphere.test.testutils.TestRenderingUtils;
/**
* A test class that involves testing renders
*/
@Tag("integration")
@Tag("graphical")
@ExtendWith(StateCleanupCheckerExtension.class)
@ExtendWith(UIExtension.class)
public class UITestTemplate {
/**
* Checks the most recent render versus an existing image
* @param renderName The name associated with the render
* @param existingRenderPath The path to the existing image
*/
public void checkRender(String renderName, String existingRenderPath){
//of the format "electrosphere.renderer.ui.elements.WindowTest"
String canonicalName = this.getClass().getCanonicalName();
//check the render
assertEqualsRender(existingRenderPath, () -> {
//on failure, save the failed render
String failureSavePath = "./.testcache/" + canonicalName + "-" + renderName + ".png";
File saveFile = new File(failureSavePath);
System.err.println("[[ATTACHMENT|" + saveFile.getAbsolutePath() + "]]");
TestRenderingUtils.saveTestRender(failureSavePath);
});
}
}

View File

@ -5,8 +5,7 @@ import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import electrosphere.engine.Globals;
import electrosphere.engine.Main;
import electrosphere.test.testutils.EngineInit;
import electrosphere.renderer.RenderingEngine;
/**
* Spins up an tears down generic rendering environment
@ -15,17 +14,15 @@ public class RenderingExtension implements BeforeEachCallback, AfterEachCallback
@Override
public void beforeEach(ExtensionContext context) throws Exception {
Globals.WINDOW_DECORATED = false;
Globals.WINDOW_FULLSCREEN = true;
Globals.RUN_AUDIO = false;
Globals.WINDOW_WIDTH = 1920;
Globals.WINDOW_HEIGHT = 1080;
EngineInit.initGraphicalEngine();
Globals.initGlobals();
Globals.renderingEngine = new RenderingEngine();
Globals.renderingEngine.createOpenglContext();
}
@Override
public void afterEach(ExtensionContext context) throws Exception {
Main.shutdown();
Globals.renderingEngine.destroy();
Globals.resetGlobals();
}
}

View File

@ -26,6 +26,7 @@ public class StateCleanupCheckerExtension implements AfterEachCallback {
Globals.clientSynchronizationManager,
Globals.server,
Globals.serverSynchronizationManager,
Globals.playerManager,
LoggerInterface.loggerEngine,
RenderingEngine.screenFramebuffer,
};

View File

@ -0,0 +1,31 @@
package electrosphere.test.template.extensions;
import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import electrosphere.engine.Globals;
import electrosphere.engine.Main;
import electrosphere.test.testutils.EngineInit;
/**
* Spins up an tears down generic ui environment
*/
public class UIExtension implements BeforeEachCallback, AfterEachCallback {
@Override
public void beforeEach(ExtensionContext context) throws Exception {
Globals.WINDOW_DECORATED = false;
Globals.WINDOW_FULLSCREEN = true;
Globals.RUN_AUDIO = false;
Globals.WINDOW_WIDTH = 1920;
Globals.WINDOW_HEIGHT = 1080;
EngineInit.initGraphicalEngine();
}
@Override
public void afterEach(ExtensionContext context) throws Exception {
Main.shutdown();
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB