diff --git a/src/main/java/electrosphere/renderer/OpenGLState.java b/src/main/java/electrosphere/renderer/OpenGLState.java index b70a2262..e66837fc 100644 --- a/src/main/java/electrosphere/renderer/OpenGLState.java +++ b/src/main/java/electrosphere/renderer/OpenGLState.java @@ -10,6 +10,7 @@ import org.lwjgl.opengl.GL45; import electrosphere.engine.Globals; import electrosphere.logger.LoggerInterface; import electrosphere.renderer.buffer.UniformBlockBinding; +import electrosphere.renderer.shader.Shader; import electrosphere.renderer.shader.ShaderProgram; /** @@ -20,7 +21,7 @@ import electrosphere.renderer.shader.ShaderProgram; public class OpenGLState { //tracks whether caching should be used or not (to deduplicate opengl calls) - private static final boolean DISABLE_CACHING = false; + public static final boolean DISABLE_CACHING = false; //the max texture allowed by the current environment int MAX_TEXTURE_WIDTH; @@ -51,7 +52,7 @@ public class OpenGLState { int framebufferPointer; //active shader - ShaderProgram activeShader; + Shader activeShader; //map of texture units and their corresponding texture pointers Map unitToPointerMap; @@ -228,16 +229,16 @@ public class OpenGLState { * @param renderPipelineState The render pipeline state object * @param program The shader program to bind */ - public void setActiveShader(RenderPipelineState renderPipelineState, ShaderProgram program){ + public void setActiveShader(RenderPipelineState renderPipelineState, Shader program){ if(DISABLE_CACHING || program != activeShader){ activeShader = program; - GL40.glUseProgram(activeShader.getShaderId()); + GL40.glUseProgram(activeShader.getId()); int glErrorCode = Globals.renderingEngine.getError(); if(glErrorCode != 0){ LoggerInterface.loggerRenderer.DEBUG_LOOP(RenderingEngine.getErrorInEnglish(glErrorCode)); } Globals.renderingEngine.checkError(); - renderPipelineState.setCurrentShaderPointer(activeShader.getShaderId()); + renderPipelineState.setCurrentShaderPointer(activeShader.getId()); } } @@ -245,7 +246,7 @@ public class OpenGLState { * Gets the active shader program * @return The active shader */ - public ShaderProgram getActiveShader(){ + public Shader getActiveShader(){ return activeShader; } diff --git a/src/main/java/electrosphere/renderer/RenderingEngine.java b/src/main/java/electrosphere/renderer/RenderingEngine.java index 40e99f69..c4e14c6a 100644 --- a/src/main/java/electrosphere/renderer/RenderingEngine.java +++ b/src/main/java/electrosphere/renderer/RenderingEngine.java @@ -333,7 +333,7 @@ public class RenderingEngine { //create light depth framebuffer/shader for shadowmapping // lightDepthShaderProgram = ShaderProgram.loadSpecificShader("/Shaders/core/lightDepth/lightDepth.vs", "/Shaders/core/lightDepth/lightDepth.fs"); - Globals.depthMapShaderProgramLoc = lightDepthShaderProgram.getShaderId(); + Globals.depthMapShaderProgramLoc = lightDepthShaderProgram.getId(); try { Framebuffer lightDepthBuffer = FramebufferUtils.generateDepthBuffer(openGLState); RenderingEngine.lightDepthBuffer = lightDepthBuffer; diff --git a/src/main/java/electrosphere/renderer/light/LightManager.java b/src/main/java/electrosphere/renderer/light/LightManager.java index 090fe300..aab9efed 100644 --- a/src/main/java/electrosphere/renderer/light/LightManager.java +++ b/src/main/java/electrosphere/renderer/light/LightManager.java @@ -158,7 +158,7 @@ public class LightManager { */ public void bindBuffer(OpenGLState openGLState){ //get position of lights object in shader - int bufferIndex = GL31.glGetUniformBlockIndex(openGLState.getActiveShader().getShaderId(), "Lights"); + int bufferIndex = GL31.glGetUniformBlockIndex(openGLState.getActiveShader().getId(), "Lights"); int glErrorCode = Globals.renderingEngine.getError(); if(glErrorCode != 0){ LoggerInterface.loggerRenderer.DEBUG_LOOP(RenderingEngine.getErrorInEnglish(glErrorCode)); @@ -167,7 +167,7 @@ public class LightManager { LoggerInterface.loggerRenderer.INFO("Tried to buffer light manager to shader that does not have it active."); } else { //bind that position to the slot '2' - GL31.glUniformBlockBinding(openGLState.getActiveShader().getShaderId(), bufferIndex, BIND_POINT); + GL31.glUniformBlockBinding(openGLState.getActiveShader().getId(), bufferIndex, BIND_POINT); Globals.renderingEngine.checkError(); //bind our buffer to slot '2' as well GL31.glBindBufferBase(GL_UNIFORM_BUFFER, BIND_POINT, uboIndex); diff --git a/src/main/java/electrosphere/renderer/model/Mesh.java b/src/main/java/electrosphere/renderer/model/Mesh.java index 143be93e..3fbb6f30 100644 --- a/src/main/java/electrosphere/renderer/model/Mesh.java +++ b/src/main/java/electrosphere/renderer/model/Mesh.java @@ -304,17 +304,17 @@ public class Mesh { Object currentUniformRaw = uniforms.get(key); if(currentUniformRaw instanceof Matrix4f){ Matrix4f currentUniform = (Matrix4f)currentUniformRaw; - glUniformMatrix4fv(glGetUniformLocation(openGLState.getActiveShader().getShaderId(), key), false, currentUniform.get(new float[16])); + glUniformMatrix4fv(glGetUniformLocation(openGLState.getActiveShader().getId(), key), false, currentUniform.get(new float[16])); Globals.renderingEngine.checkError(); } if(currentUniformRaw instanceof Vector3f){ Vector3f currentUniform = (Vector3f)currentUniformRaw; - glUniform3fv(glGetUniformLocation(openGLState.getActiveShader().getShaderId(), key), currentUniform.get(BufferUtils.createFloatBuffer(3))); + glUniform3fv(glGetUniformLocation(openGLState.getActiveShader().getId(), key), currentUniform.get(BufferUtils.createFloatBuffer(3))); Globals.renderingEngine.checkError(); } if(currentUniformRaw instanceof Integer){ int currentInform = (Integer)currentUniformRaw; - glUniform1i(glGetUniformLocation(openGLState.getActiveShader().getShaderId(), key), currentInform); + glUniform1i(glGetUniformLocation(openGLState.getActiveShader().getId(), key), currentInform); Globals.renderingEngine.checkError(); } } diff --git a/src/main/java/electrosphere/renderer/pipelines/RenderScreenPipeline.java b/src/main/java/electrosphere/renderer/pipelines/RenderScreenPipeline.java index 26107842..967e906f 100644 --- a/src/main/java/electrosphere/renderer/pipelines/RenderScreenPipeline.java +++ b/src/main/java/electrosphere/renderer/pipelines/RenderScreenPipeline.java @@ -75,7 +75,7 @@ public class RenderScreenPipeline implements RenderPipeline { } break; case 8: { openGLState.setActiveShader(renderPipelineState, RenderingEngine.drawChannel); - GL40.glUniform1f(GL40.glGetUniformLocation(openGLState.getActiveShader().getShaderId(), "channel"),4); + GL40.glUniform1f(GL40.glGetUniformLocation(openGLState.getActiveShader().getId(), "channel"),4); openGLState.glBindTexture(GL40.GL_TEXTURE_2D, RenderingEngine.screenTextureDepth.getTexturePointer()); } break; } diff --git a/src/main/java/electrosphere/renderer/pipelines/VolumeBufferPipeline.java b/src/main/java/electrosphere/renderer/pipelines/VolumeBufferPipeline.java index 65da66c5..6dc457c9 100644 --- a/src/main/java/electrosphere/renderer/pipelines/VolumeBufferPipeline.java +++ b/src/main/java/electrosphere/renderer/pipelines/VolumeBufferPipeline.java @@ -42,12 +42,12 @@ public class VolumeBufferPipeline implements RenderPipeline { GL40.glClear(GL40.GL_DEPTH_BUFFER_BIT); openGLState.glActiveTexture(GL40.GL_TEXTURE0); - GL40.glUniformMatrix4fv(GL40.glGetUniformLocation(openGLState.getActiveShader().getShaderId(), "view"), false, Globals.viewMatrix.get(new float[16])); - GL40.glUniformMatrix4fv(GL40.glGetUniformLocation(openGLState.getActiveShader().getShaderId(), "projection"), false, RenderingEngine.nearVolumeProjectionMatrix.get(new float[16])); - GL40.glUniform1f(GL40.glGetUniformLocation(openGLState.getActiveShader().getShaderId(), "linearCoef"), RenderingEngine.volumeDepthLinearCoef); - GL40.glUniform1f(GL40.glGetUniformLocation(openGLState.getActiveShader().getShaderId(), "quadCoef"), RenderingEngine.volumeDepthQuadCoef); - GL40.glUniform1f(GL40.glGetUniformLocation(openGLState.getActiveShader().getShaderId(), "near"), 0.1f); - GL40.glUniform1f(GL40.glGetUniformLocation(openGLState.getActiveShader().getShaderId(), "far"), 100f); + GL40.glUniformMatrix4fv(GL40.glGetUniformLocation(openGLState.getActiveShader().getId(), "view"), false, Globals.viewMatrix.get(new float[16])); + GL40.glUniformMatrix4fv(GL40.glGetUniformLocation(openGLState.getActiveShader().getId(), "projection"), false, RenderingEngine.nearVolumeProjectionMatrix.get(new float[16])); + GL40.glUniform1f(GL40.glGetUniformLocation(openGLState.getActiveShader().getId(), "linearCoef"), RenderingEngine.volumeDepthLinearCoef); + GL40.glUniform1f(GL40.glGetUniformLocation(openGLState.getActiveShader().getId(), "quadCoef"), RenderingEngine.volumeDepthQuadCoef); + GL40.glUniform1f(GL40.glGetUniformLocation(openGLState.getActiveShader().getId(), "near"), 0.1f); + GL40.glUniform1f(GL40.glGetUniformLocation(openGLState.getActiveShader().getId(), "far"), 100f); // // Set render pipeline state diff --git a/src/main/java/electrosphere/renderer/shader/ComputeShader.java b/src/main/java/electrosphere/renderer/shader/ComputeShader.java new file mode 100644 index 00000000..431fa261 --- /dev/null +++ b/src/main/java/electrosphere/renderer/shader/ComputeShader.java @@ -0,0 +1,195 @@ +package electrosphere.renderer.shader; + +import java.util.HashMap; +import java.util.Map; + +import org.joml.Matrix4d; +import org.joml.Matrix4f; +import org.joml.Vector3f; +import org.lwjgl.BufferUtils; +import org.lwjgl.opengl.GL40; +import org.lwjgl.opengl.GL45; +import org.lwjgl.system.MemoryStack; + +import electrosphere.engine.Globals; +import electrosphere.logger.LoggerInterface; +import electrosphere.renderer.OpenGLState; +import electrosphere.renderer.RenderingEngine; + +/** + * A compute shader program (leaving object called "ComputeShader" because that's how most people refer to them) + */ +public class ComputeShader implements Shader { + + /** + * The id for the shader + */ + int programId; + + //Uniforms + public Map uniformMap = new HashMap(); + + //keeps track of programs that have already been compiled and returns them instead of recompiling from scratch + static Map alreadyCompiledMap = new HashMap(); + + /** + * Creates a compute shader + * @param source The source code for the shader + * @return The shader object + */ + public static ComputeShader create(String source){ + ComputeShader rVal = new ComputeShader(); + + //create shader object + int shaderId = GL45.glCreateShader(GL45.GL_COMPUTE_SHADER); + Globals.renderingEngine.checkError(); + + //attach source + GL45.glShaderSource(shaderId, source); + Globals.renderingEngine.checkError(); + + //compile shader + GL45.glCompileShader(shaderId); + int success; + success = GL45.glGetShaderi(shaderId, GL45.GL_COMPILE_STATUS); + if (success != GL45.GL_TRUE) { + LoggerInterface.loggerRenderer.WARNING("Compute Shader failed to compile!"); + LoggerInterface.loggerRenderer.WARNING("Source is: "); + LoggerInterface.loggerRenderer.WARNING(GL45.glGetShaderSource(shaderId)); + LoggerInterface.loggerRenderer.ERROR("Runtime Exception", new RuntimeException(GL45.glGetShaderInfoLog(shaderId))); + } + + //create program + rVal.programId = GL45.glCreateProgram(); + Globals.renderingEngine.checkError(); + + //attach shader to program + GL45.glAttachShader(rVal.programId, shaderId); + Globals.renderingEngine.checkError(); + + //link + GL45.glLinkProgram(rVal.programId); + success = GL45.glGetProgrami(rVal.programId, GL45.GL_LINK_STATUS); + if (success != GL45.GL_TRUE) { + throw new RuntimeException(GL45.glGetProgramInfoLog(rVal.programId)); + } + + + return rVal; + } + + /** + * Dispatches the compute shader for execution + * @param x The X grid size + * @param y The Y grid size + * @param z The Z grid size + */ + public void dispatch(int x, int y, int z){ + GL45.glDispatchCompute(x, y, z); + } + + /** + * Sets the memory barrier value + * @param value The value + */ + public void memoryBarier(int value){ + GL45.glMemoryBarrier(value); + } + + /** + * Tries to set a uniform + * @param uniformName The name of the uniform + * @param value The value to set the uniform to + */ + public void setUniform(OpenGLState openGLState, String uniformName, Object value){ + // + //Error checking + if(uniformName == null || uniformName.equals("")){ + throw new IllegalArgumentException("Trying to set invalid uniform name"); + } + if(this.getId() != openGLState.getActiveShader().getId()){ + throw new IllegalStateException("Trying to set uniform on shader that is not active"); + } + + // + //get uniform location + int uniformLocation = GL40.glGetUniformLocation(this.getId(), uniformName); + int glErrorCode = Globals.renderingEngine.getError(); + if(glErrorCode != 0){ + LoggerInterface.loggerRenderer.DEBUG_LOOP(RenderingEngine.getErrorInEnglish(glErrorCode)); + LoggerInterface.loggerRenderer.DEBUG_LOOP("Shader id: " + this.getId()); + } + + // + //set the uniform + if(uniformLocation == INVALID_UNIFORM_NAME){ + LoggerInterface.loggerRenderer.DEBUG_LOOP("Searched for uniform in a shader that does not contain it. Uniform name: \"" + uniformName + "\""); + } else { + this.setUniform(openGLState, uniformLocation, value); + } + } + + /** + * Sets the value of a uniform on this shader + * @param uniformLocation the uniform location + * @param value the value + */ + public void setUniform(OpenGLState openGLState, int uniformLocation, Object value){ + if( + OpenGLState.DISABLE_CACHING || + !uniformMap.containsKey(uniformLocation) || + !uniformMap.get(uniformLocation).equals(value) + ){ + try(MemoryStack stack = MemoryStack.stackPush()){ + + // + //matrix4f + if(value instanceof Matrix4f){ + Matrix4f currentUniform = (Matrix4f)value; + GL40.glUniformMatrix4fv(uniformLocation, false, currentUniform.get(new float[16])); + Globals.renderingEngine.checkError(); + uniformMap.put(uniformLocation,new Matrix4f(currentUniform)); //create new matrix4f to break pointer-matching with equals on cache check + + // + //matrix4d + } else if(value instanceof Matrix4d){ + Matrix4d currentUniform = (Matrix4d)value; + GL40.glUniformMatrix4fv(uniformLocation, false, currentUniform.get(new float[16])); + Globals.renderingEngine.checkError(); + uniformMap.put(uniformLocation,new Matrix4d(currentUniform)); //create new matrix4f to break pointer-matching with equals on cache check + + // + //vector3f + } else if(value instanceof Vector3f){ + Vector3f currentUniform = (Vector3f)value; + GL40.glUniform3fv(uniformLocation, currentUniform.get(BufferUtils.createFloatBuffer(3))); + Globals.renderingEngine.checkError(); + uniformMap.put(uniformLocation,new Vector3f(currentUniform)); //create new matrix4f to break pointer-matching with equals on cache check + + // + //integer + } else if(value instanceof Integer){ + GL40.glUniform1i(uniformLocation, (Integer)value); + Globals.renderingEngine.checkError(); + uniformMap.put(uniformLocation,(Integer)value); + + // + //float + } else if(value instanceof Float){ + GL40.glUniform1f(uniformLocation, (Float)value); + Globals.renderingEngine.checkError(); + uniformMap.put(uniformLocation,(Float)value); + + } else { + throw new UnsupportedOperationException("Tried to set uniform with unsupported type!"); + } + } + } + } + + @Override + public int getId() { + return programId; + } + +} diff --git a/src/main/java/electrosphere/renderer/shader/MemoryBarrier.java b/src/main/java/electrosphere/renderer/shader/MemoryBarrier.java new file mode 100644 index 00000000..730f2cc1 --- /dev/null +++ b/src/main/java/electrosphere/renderer/shader/MemoryBarrier.java @@ -0,0 +1,210 @@ +package electrosphere.renderer.shader; + +import org.lwjgl.opengl.GL45; + +/** + * Memory barrier operations + */ +public class MemoryBarrier { + + /** + * Types of memory barriers + */ + public static enum Barrier { + + /** + *

+ * Vertex data sources from buffer objects after the barrier will reflect data written by shaders prior to the barrier. + *

+ *

+ * The set of buffer objects affected by this bit is derived from the buffer object bindings used for generic vertex attributes derived from the GL_VERTEX_ATTRIB_ARRAY_BUFFER bindings. + *

+ */ + GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT, + + /** + *

+ * Vertex array indices sourced from buffer objects after the barrier will reflect data written by shaders prior to the barrier. + *

+ *

+ * The buffer objects affected by this bit are derived from the GL_ELEMENT_ARRAY_BUFFER binding. + *

+ */ + GL_ELEMENT_ARRAY_BARRIER_BIT, + + /** + *

+ * Shader uniforms sourced from buffer objects after the barrier will reflect data written by shaders prior to the barrier. + *

+ */ + GL_UNIFORM_BARRIER_BIT, + + /** + *

+ * Texture fetches from shaders, including fetches from buffer object memory via buffer textures, after the barrier will reflect data written by shaders prior to the barrier. + *

+ */ + GL_TEXTURE_FETCH_BARRIER_BIT, + + /** + *

+ * Memory accesses using shader image load, store, and atomic built-in functions issued after the barrier will reflect data written by shaders prior to the barrier. + *

+ *

+ * Additionally, image stores and atomics issued after the barrier will not execute until all memory accesses (e.g., loads, stores, texture fetches, vertex fetches) initiated prior to the barrier complete. + *

+ */ + GL_SHADER_IMAGE_ACCESS_BARRIER_BIT, + + /** + *

+ * Command data sourced from buffer objects by Draw*Indirect commands after the barrier will reflect data written by shaders prior to the barrier. + *

+ *

+ * The buffer objects affected by this bit are derived from the GL_DRAW_INDIRECT_BUFFER binding. + *

+ */ + GL_COMMAND_BARRIER_BIT, + + /** + *

+ * Reads and writes of buffer objects via the GL_PIXEL_PACK_BUFFER and GL_PIXEL_UNPACK_BUFFER bindings (via glReadPixels, glTexSubImage1D, etc.) after the barrier will reflect data written by shaders prior to the barrier. + *

+ *

+ * Additionally, buffer object writes issued after the barrier will wait on the completion of all shader writes initiated prior to the barrier. + *

+ */ + GL_PIXEL_BUFFER_BARRIER_BIT, + + /** + *

+ * Writes to a texture via glTex(Sub)Image*, glCopyTex(Sub)Image*, glCompressedTex(Sub)Image*, and reads via glGetTexImage after the barrier will reflect data written by shaders prior to the barrier. + *

+ *

+ * Additionally, texture writes from these commands issued after the barrier will not execute until all shader writes initiated prior to the barrier complete. + *

+ */ + GL_TEXTURE_UPDATE_BARRIER_BIT, + + /** + *

+ * Reads or writes via glBufferSubData, glCopyBufferSubData, or glGetBufferSubData, or to buffer object memory mapped by glMapBuffer or glMapBufferRange after the barrier will reflect data written by shaders prior to the barrier. + *

+ *

+ * Additionally, writes via these commands issued after the barrier will wait on the completion of any shader writes to the same memory initiated prior to the barrier. + *

+ */ + GL_BUFFER_UPDATE_BARRIER_BIT, + + /** + *

+ * Access by the client to persistent mapped regions of buffer objects will reflect data written by shaders prior to the barrier. + *

+ *

+ * Note that this may cause additional synchronization operations. + *

+ */ + GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT, + + /** + *

+ * Reads and writes via framebuffer object attachments after the barrier will reflect data written by shaders prior to the barrier. + *

+ *

+ * Additionally, framebuffer writes issued after the barrier will wait on the completion of all shader writes issued prior to the barrier. + *

+ */ + GL_FRAMEBUFFER_BARRIER_BIT, + + /** + *

+ * Writes via transform feedback bindings after the barrier will reflect data written by shaders prior to the barrier. + *

+ *

+ * Additionally, transform feedback writes issued after the barrier will wait on the completion of all shader writes issued prior to the barrier. + *

+ */ + GL_TRANSFORM_FEEDBACK_BARRIER_BIT, + + /** + *

+ * Accesses to atomic counters after the barrier will reflect writes prior to the barrier. + *

+ */ + GL_ATOMIC_COUNTER_BARRIER_BIT, + + /** + *

+ * Accesses to shader storage blocks after the barrier will reflect writes prior to the barrier. + *

+ */ + GL_SHADER_STORAGE_BARRIER_BIT, + + /** + *

+ * Writes of buffer objects via the GL_QUERY_BUFFER binding after the barrier will reflect data written by shaders prior to the barrier. + *

+ *

+ * Additionally, buffer object writes issued after the barrier will wait on the completion of all shader writes initiated prior to the barrier. + *

+ */ + GL_QUERY_BUFFER_BARRIER_BIT, + } + + /** + * Sets the memory barrier + * @param barrier The barrier to set + */ + public static void glMemoryBarrier(Barrier barrier){ + int barrierVal = 0; + switch(barrier){ + case GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT: { + barrierVal = GL45.GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT; + } break; + case GL_ELEMENT_ARRAY_BARRIER_BIT: { + barrierVal = GL45.GL_ELEMENT_ARRAY_BARRIER_BIT; + } break; + case GL_UNIFORM_BARRIER_BIT: { + barrierVal = GL45.GL_UNIFORM_BARRIER_BIT; + } break; + case GL_TEXTURE_FETCH_BARRIER_BIT: { + barrierVal = GL45.GL_TEXTURE_FETCH_BARRIER_BIT; + } break; + case GL_SHADER_IMAGE_ACCESS_BARRIER_BIT: { + barrierVal = GL45.GL_SHADER_IMAGE_ACCESS_BARRIER_BIT; + } break; + case GL_COMMAND_BARRIER_BIT: { + barrierVal = GL45.GL_COMMAND_BARRIER_BIT; + } break; + case GL_PIXEL_BUFFER_BARRIER_BIT: { + barrierVal = GL45.GL_PIXEL_BUFFER_BARRIER_BIT; + } break; + case GL_TEXTURE_UPDATE_BARRIER_BIT: { + barrierVal = GL45.GL_TEXTURE_UPDATE_BARRIER_BIT; + } break; + case GL_BUFFER_UPDATE_BARRIER_BIT: { + barrierVal = GL45.GL_BUFFER_UPDATE_BARRIER_BIT; + } break; + case GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT: { + barrierVal = GL45.GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT; + } break; + case GL_FRAMEBUFFER_BARRIER_BIT: { + barrierVal = GL45.GL_FRAMEBUFFER_BARRIER_BIT; + } break; + case GL_TRANSFORM_FEEDBACK_BARRIER_BIT: { + barrierVal = GL45.GL_TRANSFORM_FEEDBACK_BARRIER_BIT; + } break; + case GL_ATOMIC_COUNTER_BARRIER_BIT: { + barrierVal = GL45.GL_ATOMIC_COUNTER_BARRIER_BIT; + } break; + case GL_SHADER_STORAGE_BARRIER_BIT: { + barrierVal = GL45.GL_SHADER_STORAGE_BARRIER_BIT; + } break; + case GL_QUERY_BUFFER_BARRIER_BIT: { + barrierVal = GL45.GL_QUERY_BUFFER_BARRIER_BIT; + } break; + } + GL45.glMemoryBarrier(barrierVal); + } + +} diff --git a/src/main/java/electrosphere/renderer/shader/Shader.java b/src/main/java/electrosphere/renderer/shader/Shader.java new file mode 100644 index 00000000..13839f14 --- /dev/null +++ b/src/main/java/electrosphere/renderer/shader/Shader.java @@ -0,0 +1,29 @@ +package electrosphere.renderer.shader; + +import electrosphere.renderer.OpenGLState; + +/** + * Interface for all shader types + */ +public interface Shader { + + /** + * Returned if the uniform isn't found + */ + public static final int INVALID_UNIFORM_NAME = -1; + + /** + * Gets the id for the shader program + * @return The id + */ + public int getId(); + + /** + * Sets the value of a uniform on the shader + * @param openGLState The opengl state object + * @param uniformName The uniform's name + * @param value The value of the uniform + */ + public void setUniform(OpenGLState openGLState, String uniformName, Object value); + +} diff --git a/src/main/java/electrosphere/renderer/shader/ShaderProgram.java b/src/main/java/electrosphere/renderer/shader/ShaderProgram.java index 5c910cfa..85ff2c2b 100644 --- a/src/main/java/electrosphere/renderer/shader/ShaderProgram.java +++ b/src/main/java/electrosphere/renderer/shader/ShaderProgram.java @@ -44,10 +44,7 @@ import electrosphere.util.FileUtils; /** * A shader program */ -public class ShaderProgram { - - //tracks whether caching should be used or not (to deduplicate opengl calls) - private static final boolean DISABLE_CACHING = false; +public class ShaderProgram implements Shader { // //Program stuff @@ -677,7 +674,7 @@ public class ShaderProgram { */ public void setUniform(OpenGLState openGLState, int uniformLocation, Object value){ if( - DISABLE_CACHING || + OpenGLState.DISABLE_CACHING || !uniformMap.containsKey(uniformLocation) || !uniformMap.get(uniformLocation).equals(value) ){ @@ -728,9 +725,6 @@ public class ShaderProgram { } } - //returned if the uniform isn't found - public static final int INVALID_UNIFORM_NAME = -1; - /** * Tries to set a uniform * @param uniformName The name of the uniform @@ -742,17 +736,17 @@ public class ShaderProgram { if(uniformName == null || uniformName.equals("")){ throw new IllegalArgumentException("Trying to set invalid uniform name"); } - if(this.getShaderId() != openGLState.getActiveShader().getShaderId()){ + if(this.getId() != openGLState.getActiveShader().getId()){ throw new IllegalStateException("Trying to set uniform on shader that is not active"); } // //get uniform location - int uniformLocation = GL40.glGetUniformLocation(this.getShaderId(), uniformName); + int uniformLocation = GL40.glGetUniformLocation(this.getId(), uniformName); int glErrorCode = Globals.renderingEngine.getError(); if(glErrorCode != 0){ LoggerInterface.loggerRenderer.DEBUG_LOOP(RenderingEngine.getErrorInEnglish(glErrorCode)); - LoggerInterface.loggerRenderer.DEBUG_LOOP("Shader id: " + this.getShaderId()); + LoggerInterface.loggerRenderer.DEBUG_LOOP("Shader id: " + this.getId()); } // @@ -770,16 +764,13 @@ public class ShaderProgram { * @return The location of the uniform */ public int getUniformLocation(String uniformName){ - return GL40.glGetUniformLocation(this.getShaderId(), uniformName); + return GL40.glGetUniformLocation(this.getId(), uniformName); } - /** - * Gets the id of the shader - * @return The shader id - */ - public int getShaderId(){ - return shaderId; + @Override + public int getId() { + return this.shaderId; } } diff --git a/src/test/java/electrosphere/renderer/shader/ComputeShaderTests.java b/src/test/java/electrosphere/renderer/shader/ComputeShaderTests.java new file mode 100644 index 00000000..94080389 --- /dev/null +++ b/src/test/java/electrosphere/renderer/shader/ComputeShaderTests.java @@ -0,0 +1,57 @@ +package electrosphere.renderer.shader; + +import static org.junit.jupiter.api.Assertions.*; + +import electrosphere.test.annotations.IntegrationTest; +import electrosphere.test.template.RenderingTestTemplate; + +/** + * Tests for compute shaders + */ +public class ComputeShaderTests extends RenderingTestTemplate { + + /** + * A simple compute shader + */ + String simpleComputeShader = """ +#version 430 core + +layout (local_size_x = 10, local_size_y = 10, local_size_z = 1) in; + +// ---------------------------------------------------------------------------- +// +// uniforms +// +// ---------------------------------------------------------------------------- + +layout(rgba32f, binding = 0) uniform image2D imgOutput; + +layout (location = 0) uniform float t; /** Time */ + +// ---------------------------------------------------------------------------- +// +// functions +// +// ---------------------------------------------------------------------------- + +void main() { + vec4 value = vec4(0.0, 0.0, 0.0, 1.0); + ivec2 texelCoord = ivec2(gl_GlobalInvocationID.xy); + float speed = 100; + // the width of the texture + float width = 1000; + + value.x = mod(float(texelCoord.x) + t * speed, width) / (gl_NumWorkGroups.x * gl_WorkGroupSize.x); + value.y = float(texelCoord.y)/(gl_NumWorkGroups.y*gl_WorkGroupSize.y); + imageStore(imgOutput, texelCoord, value); +} + """; + + @IntegrationTest + public void testCreation(){ + assertDoesNotThrow(() -> { + ComputeShader.create(simpleComputeShader); + }); + } + +} diff --git a/src/test/java/electrosphere/renderer/shader/MemoryBarrierTests.java b/src/test/java/electrosphere/renderer/shader/MemoryBarrierTests.java new file mode 100644 index 00000000..7488da8a --- /dev/null +++ b/src/test/java/electrosphere/renderer/shader/MemoryBarrierTests.java @@ -0,0 +1,17 @@ +package electrosphere.renderer.shader; + +import electrosphere.renderer.shader.MemoryBarrier.Barrier; +import electrosphere.test.annotations.IntegrationTest; +import electrosphere.test.template.RenderingTestTemplate; + +/** + * Tests for memory barrier operations + */ +public class MemoryBarrierTests extends RenderingTestTemplate { + + @IntegrationTest + public void testGLMemoryBarrier(){ + MemoryBarrier.glMemoryBarrier(Barrier.GL_COMMAND_BARRIER_BIT); + } + +}