diff --git a/redhorizon-engine/source/nz/net/ultraq/redhorizon/engine/graphics/Shader.groovy b/redhorizon-engine/source/nz/net/ultraq/redhorizon/engine/graphics/Shader.groovy index 3b1b6c32..b6cc2bf8 100644 --- a/redhorizon-engine/source/nz/net/ultraq/redhorizon/engine/graphics/Shader.groovy +++ b/redhorizon-engine/source/nz/net/ultraq/redhorizon/engine/graphics/Shader.groovy @@ -96,7 +96,8 @@ abstract class Shader implements GraphicsResource { } /** - * Enable the use of this shader for the next rendering commands. + * Enable the use of this shader for all of the rendering commands within the + * given closure. */ - abstract void use() + abstract void use(Closure closure) } diff --git a/redhorizon-engine/source/nz/net/ultraq/redhorizon/engine/graphics/opengl/OpenGLRenderer.groovy b/redhorizon-engine/source/nz/net/ultraq/redhorizon/engine/graphics/opengl/OpenGLRenderer.groovy index 3a051567..b5973ca3 100644 --- a/redhorizon-engine/source/nz/net/ultraq/redhorizon/engine/graphics/opengl/OpenGLRenderer.groovy +++ b/redhorizon-engine/source/nz/net/ultraq/redhorizon/engine/graphics/opengl/OpenGLRenderer.groovy @@ -55,7 +55,6 @@ import org.slf4j.Logger import org.slf4j.LoggerFactory import static org.lwjgl.opengl.GL11C.* import static org.lwjgl.opengl.GL20C.GL_MAX_FRAGMENT_UNIFORM_COMPONENTS -import static org.lwjgl.opengl.GL20C.glUseProgram import static org.lwjgl.opengl.GL30C.GL_FRAMEBUFFER import static org.lwjgl.opengl.GL30C.glBindFramebuffer import static org.lwjgl.opengl.GL31C.* @@ -292,37 +291,50 @@ class OpenGLRenderer implements GraphicsRenderer { } } + private static class DrawList { + Shader shader + final List drawCommands = [] + + void flush() { + shader?.use { -> + drawCommands*.call() + } + shader = null + drawCommands.clear() + } + } + + private final DrawList drawList = new DrawList() + @Override void draw(Mesh mesh, Matrix4f transform, Shader shader, Material material = null) { - averageNanos('draw', 1f, logger) { -> - shader.use() - shader.applyUniforms(transform, material, window) - mesh.bind() - if (mesh.indices) { - glDrawElements(mesh.vertexType, mesh.indices.size(), GL_UNSIGNED_INT, 0) - } - else { - glDrawArrays(mesh.vertexType, 0, mesh.vertices.size()) - } + if (shader != drawList.shader) { + drawList.flush() + drawList.shader = shader + } - trigger(new DrawEvent()) - - // For an error w/ nVidia on Windows, where the program state seems to - // linger when creating a shader with a different layout, eg: we last used - // the Sharp Upscaling shader which has all of colour, position, and - // texcoord attributes, then try to create the Primitives shader which has - // only colour and position. This then manifests as an error on the next - // frame when we call glClear() 🤔 - // "Program/shader state performance warning: Vertex shader in program X - // is being recompiled based on GL state" - glUseProgram(0) + drawList.drawCommands << { -> + averageNanos('draw', 1f, logger) { -> + shader.applyUniforms(transform, material, window) + mesh.bind() + if (mesh.indices) { + glDrawElements(mesh.vertexType, mesh.indices.size(), GL_UNSIGNED_INT, 0) + } + else { + glDrawArrays(mesh.vertexType, 0, mesh.vertices.size()) + } + + trigger(new DrawEvent()) + } } } @Override void setRenderTarget(Framebuffer framebuffer) { + drawList.flush() + if (framebuffer) { framebuffer.bind() glViewport(0, 0, framebuffer.texture.width, framebuffer.texture.height) diff --git a/redhorizon-engine/source/nz/net/ultraq/redhorizon/engine/graphics/opengl/OpenGLShader.groovy b/redhorizon-engine/source/nz/net/ultraq/redhorizon/engine/graphics/opengl/OpenGLShader.groovy index a93d74f6..ac97dcaf 100644 --- a/redhorizon-engine/source/nz/net/ultraq/redhorizon/engine/graphics/opengl/OpenGLShader.groovy +++ b/redhorizon-engine/source/nz/net/ultraq/redhorizon/engine/graphics/opengl/OpenGLShader.groovy @@ -156,8 +156,10 @@ class OpenGLShader extends Shader { } @Override - void use() { + void use(Closure closure) { glUseProgram(programId) + closure() + glUseProgram(0) } }