From cf9f7fc9fb4d76490995f4afcbceb68d17ff8c1f Mon Sep 17 00:00:00 2001 From: Max Weber Date: Mon, 10 Feb 2020 18:33:27 -0700 Subject: [PATCH 1/3] gpu: refactor shader compilation --- .../runelite/client/plugins/gpu/GLUtil.java | 78 +------ .../client/plugins/gpu/GpuPlugin.java | 150 ++++--------- .../runelite/client/plugins/gpu/Shader.java | 123 +++++++++++ .../client/plugins/gpu/template/Template.java | 52 ++++- .../client/plugins/gpu/ShaderTest.java | 199 ------------------ .../plugins/gpu/template/TemplateTest.java | 4 +- 6 files changed, 214 insertions(+), 392 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/gpu/Shader.java delete mode 100644 runelite-client/src/test/java/net/runelite/client/plugins/gpu/ShaderTest.java diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GLUtil.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GLUtil.java index af5d5ea018..65da9b96e9 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GLUtil.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GLUtil.java @@ -25,8 +25,6 @@ package net.runelite.client.plugins.gpu; import com.jogamp.opengl.GL4; -import java.io.InputStream; -import java.util.Scanner; class GLUtil { @@ -58,14 +56,14 @@ class GLUtil { byte[] err = new byte[ERR_LEN]; gl.glGetShaderInfoLog(shader, ERR_LEN, buf, 0, err, 0); - return new String(err); + return new String(err, 0, buf[0]); } static String glGetProgramInfoLog(GL4 gl, int program) { byte[] err = new byte[ERR_LEN]; gl.glGetProgramInfoLog(program, ERR_LEN, buf, 0, err, 0); - return new String(err); + return new String(err, 0, buf[0]); } static int glGenVertexArrays(GL4 gl) @@ -127,76 +125,4 @@ class GLUtil buf[0] = renderBuffer; gl.glDeleteRenderbuffers(1, buf, 0); } - - static void loadShaders(GL4 gl, int glProgram, int glVertexShader, int glGeometryShader, int glFragmentShader, - String vertexShaderStr, String geomShaderStr, String fragShaderStr) throws ShaderException - { - compileAndAttach(gl, glProgram, glVertexShader, vertexShaderStr); - - if (glGeometryShader != -1) - { - compileAndAttach(gl, glProgram, glGeometryShader, geomShaderStr); - } - - compileAndAttach(gl, glProgram, glFragmentShader, fragShaderStr); - - gl.glLinkProgram(glProgram); - - if (glGetProgram(gl, glProgram, gl.GL_LINK_STATUS) == gl.GL_FALSE) - { - String err = glGetProgramInfoLog(gl, glProgram); - throw new ShaderException(err); - } - - gl.glValidateProgram(glProgram); - - if (glGetProgram(gl, glProgram, gl.GL_VALIDATE_STATUS) == gl.GL_FALSE) - { - String err = glGetProgramInfoLog(gl, glProgram); - throw new ShaderException(err); - } - } - - static void loadComputeShader(GL4 gl, int glProgram, int glComputeShader, String str) throws ShaderException - { - compileAndAttach(gl, glProgram, glComputeShader, str); - - gl.glLinkProgram(glProgram); - - if (glGetProgram(gl, glProgram, gl.GL_LINK_STATUS) == gl.GL_FALSE) - { - String err = glGetProgramInfoLog(gl, glProgram); - throw new ShaderException(err); - } - - gl.glValidateProgram(glProgram); - - if (glGetProgram(gl, glProgram, gl.GL_VALIDATE_STATUS) == gl.GL_FALSE) - { - String err = glGetProgramInfoLog(gl, glProgram); - throw new ShaderException(err); - } - } - - private static void compileAndAttach(GL4 gl, int program, int shader, String source) throws ShaderException - { - gl.glShaderSource(shader, 1, new String[]{source}, null); - gl.glCompileShader(shader); - - if (glGetShader(gl, shader, gl.GL_COMPILE_STATUS) == gl.GL_TRUE) - { - gl.glAttachShader(program, shader); - } - else - { - String err = glGetShaderInfoLog(gl, shader); - throw new ShaderException(err); - } - } - - static String inputStreamToString(InputStream in) - { - Scanner scanner = new Scanner(in).useDelimiter("\\A"); - return scanner.next(); - } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java index e4a0233c0f..3dccef8212 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java @@ -46,7 +46,6 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import java.nio.IntBuffer; -import java.util.function.Function; import javax.inject.Inject; import javax.swing.SwingUtilities; import jogamp.nativewindow.SurfaceScaleUtils; @@ -124,28 +123,41 @@ public class GpuPlugin extends Plugin implements DrawCallbacks private GLContext glContext; private GLDrawable glDrawable; + static final String LINUX_VERSION_HEADER = + "#version 420\n" + + "#extension GL_ARB_compute_shader : require\n" + + "#extension GL_ARB_shader_storage_buffer_object : require\n" + + "#extension GL_ARB_explicit_attrib_location : require\n"; + static final String WINDOWS_VERSION_HEADER = "#version 430\n"; + + static final Shader PROGRAM = new Shader() + .add(GL4.GL_VERTEX_SHADER, "vert.glsl") + .add(GL4.GL_GEOMETRY_SHADER, "geom.glsl") + .add(GL4.GL_FRAGMENT_SHADER, "frag.glsl"); + + static final Shader COMPUTE_PROGRAM = new Shader() + .add(GL4.GL_COMPUTE_SHADER, "comp.glsl"); + + static final Shader SMALL_COMPUTE_PROGRAM = new Shader() + .add(GL4.GL_COMPUTE_SHADER, "comp_small.glsl"); + + static final Shader UNORDERED_COMPUTE_PROGRAM = new Shader() + .add(GL4.GL_COMPUTE_SHADER, "comp_unordered.glsl"); + + static final Shader UI_PROGRAM = new Shader() + .add(GL4.GL_VERTEX_SHADER, "vertui.glsl") + .add(GL4.GL_FRAGMENT_SHADER, "fragui.glsl"); + private int glProgram; - private int glVertexShader; - private int glGeomShader; - private int glFragmentShader; - private int glComputeProgram; - private int glComputeShader; - private int glSmallComputeProgram; - private int glSmallComputeShader; - private int glUnorderedComputeProgram; - private int glUnorderedComputeShader; + private int glUiProgram; private int vaoHandle; private int interfaceTexture; - private int glUiProgram; - private int glUiVertexShader; - private int glUiFragmentShader; - private int vaoUiHandle; private int vboUiHandle; @@ -434,79 +446,23 @@ public class GpuPlugin extends Plugin implements DrawCallbacks private void initProgram() throws ShaderException { - glProgram = gl.glCreateProgram(); - glVertexShader = gl.glCreateShader(gl.GL_VERTEX_SHADER); - glGeomShader = gl.glCreateShader(gl.GL_GEOMETRY_SHADER); - glFragmentShader = gl.glCreateShader(gl.GL_FRAGMENT_SHADER); - - final String glVersionHeader; - - if (OSType.getOSType() == OSType.Linux) + String versionHeader = OSType.getOSType() == OSType.Linux ? LINUX_VERSION_HEADER : WINDOWS_VERSION_HEADER; + Template template = new Template(); + template.add(key -> { - glVersionHeader = - "#version 420\n" + - "#extension GL_ARB_compute_shader : require\n" + - "#extension GL_ARB_shader_storage_buffer_object : require\n"; - } - else - { - glVersionHeader = "#version 430\n"; - } - - Function resourceLoader = (s) -> - { - if (s.endsWith(".glsl")) + if ("version_header".equals(key)) { - return inputStreamToString(getClass().getResourceAsStream(s)); + return versionHeader; } + return null; + }); + template.addInclude(GpuPlugin.class); - if (s.equals("version_header")) - { - return glVersionHeader; - } - - return ""; - }; - - Template template = new Template(resourceLoader); - String source = template.process(resourceLoader.apply("geom.glsl")); - - template = new Template(resourceLoader); - String vertSource = template.process(resourceLoader.apply("vert.glsl")); - - template = new Template(resourceLoader); - String fragSource = template.process(resourceLoader.apply("frag.glsl")); - - GLUtil.loadShaders(gl, glProgram, glVertexShader, glGeomShader, glFragmentShader, - vertSource, - source, - fragSource); - - glComputeProgram = gl.glCreateProgram(); - glComputeShader = gl.glCreateShader(gl.GL_COMPUTE_SHADER); - template = new Template(resourceLoader); - source = template.process(resourceLoader.apply("comp.glsl")); - GLUtil.loadComputeShader(gl, glComputeProgram, glComputeShader, source); - - glSmallComputeProgram = gl.glCreateProgram(); - glSmallComputeShader = gl.glCreateShader(gl.GL_COMPUTE_SHADER); - template = new Template(resourceLoader); - source = template.process(resourceLoader.apply("comp_small.glsl")); - GLUtil.loadComputeShader(gl, glSmallComputeProgram, glSmallComputeShader, source); - - glUnorderedComputeProgram = gl.glCreateProgram(); - glUnorderedComputeShader = gl.glCreateShader(gl.GL_COMPUTE_SHADER); - template = new Template(resourceLoader); - source = template.process(resourceLoader.apply("comp_unordered.glsl")); - GLUtil.loadComputeShader(gl, glUnorderedComputeProgram, glUnorderedComputeShader, source); - - glUiProgram = gl.glCreateProgram(); - glUiVertexShader = gl.glCreateShader(gl.GL_VERTEX_SHADER); - glUiFragmentShader = gl.glCreateShader(gl.GL_FRAGMENT_SHADER); - GLUtil.loadShaders(gl, glUiProgram, glUiVertexShader, -1, glUiFragmentShader, - inputStreamToString(getClass().getResourceAsStream("vertui.glsl")), - null, - inputStreamToString(getClass().getResourceAsStream("fragui.glsl"))); + glProgram = PROGRAM.compile(gl, template); + glComputeProgram = COMPUTE_PROGRAM.compile(gl, template); + glSmallComputeProgram = SMALL_COMPUTE_PROGRAM.compile(gl, template); + glUnorderedComputeProgram = UNORDERED_COMPUTE_PROGRAM.compile(gl, template); + glUiProgram = UI_PROGRAM.compile(gl, template); initUniforms(); } @@ -532,46 +488,18 @@ public class GpuPlugin extends Plugin implements DrawCallbacks private void shutdownProgram() { - gl.glDeleteShader(glVertexShader); - glVertexShader = -1; - - gl.glDeleteShader(glGeomShader); - glGeomShader = -1; - - gl.glDeleteShader(glFragmentShader); - glFragmentShader = -1; - gl.glDeleteProgram(glProgram); glProgram = -1; - /// - - gl.glDeleteShader(glComputeShader); - glComputeShader = -1; - gl.glDeleteProgram(glComputeProgram); glComputeProgram = -1; - gl.glDeleteShader(glSmallComputeShader); - glSmallComputeShader = -1; - gl.glDeleteProgram(glSmallComputeProgram); glSmallComputeProgram = -1; - gl.glDeleteShader(glUnorderedComputeShader); - glUnorderedComputeShader = -1; - gl.glDeleteProgram(glUnorderedComputeProgram); glUnorderedComputeProgram = -1; - /// - - gl.glDeleteShader(glUiVertexShader); - glUiVertexShader = -1; - - gl.glDeleteShader(glUiFragmentShader); - glUiFragmentShader = -1; - gl.glDeleteProgram(glUiProgram); glUiProgram = -1; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/Shader.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/Shader.java new file mode 100644 index 0000000000..1146fd3332 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/Shader.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2018, Adam + * Copyright (c) 2020 Abex + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. 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. + * + * 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 net.runelite.client.plugins.gpu; + +import com.google.common.annotations.VisibleForTesting; +import com.jogamp.opengl.GL4; +import java.util.ArrayList; +import java.util.List; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import net.runelite.client.plugins.gpu.template.Template; + +public class Shader +{ + @VisibleForTesting + final List units = new ArrayList<>(); + + @RequiredArgsConstructor + @VisibleForTesting + static class Unit + { + @Getter + private final int type; + + @Getter + private final String filename; + } + + public Shader() + { + } + + public Shader add(int type, String name) + { + units.add(new Unit(type, name)); + return this; + } + + public int compile(GL4 gl, Template template) throws ShaderException + { + int program = gl.glCreateProgram(); + int[] shaders = new int[units.size()]; + int i = 0; + boolean ok = false; + try + { + while (i < shaders.length) + { + Unit unit = units.get(i); + int shader = gl.glCreateShader(unit.type); + String source = template.load(unit.filename); + gl.glShaderSource(shader, 1, new String[]{source}, null); + gl.glCompileShader(shader); + + if (GLUtil.glGetShader(gl, shader, gl.GL_COMPILE_STATUS) != gl.GL_TRUE) + { + String err = GLUtil.glGetShaderInfoLog(gl, shader); + gl.glDeleteShader(shader); + throw new ShaderException(err); + } + gl.glAttachShader(program, shader); + shaders[i++] = shader; + } + + gl.glLinkProgram(program); + + if (GLUtil.glGetProgram(gl, program, gl.GL_LINK_STATUS) == gl.GL_FALSE) + { + String err = GLUtil.glGetProgramInfoLog(gl, program); + throw new ShaderException(err); + } + + gl.glValidateProgram(program); + + if (GLUtil.glGetProgram(gl, program, gl.GL_VALIDATE_STATUS) == gl.GL_FALSE) + { + String err = GLUtil.glGetProgramInfoLog(gl, program); + throw new ShaderException(err); + } + + ok = true; + } + finally + { + while (i > 0) + { + int shader = shaders[--i]; + gl.glDetachShader(program, shader); + gl.glDeleteShader(shader); + } + + if (!ok) + { + gl.glDeleteProgram(program); + } + } + + return program; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/template/Template.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/template/Template.java index 1f2ea73549..3bf7059382 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/template/Template.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/template/Template.java @@ -24,15 +24,18 @@ */ package net.runelite.client.plugins.gpu.template; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Scanner; import java.util.function.Function; public class Template { - private final Function resourceLoader; + private final List> resourceLoaders = new ArrayList<>(); - public Template(Function resourceLoader) + public Template() { - this.resourceLoader = resourceLoader; } public String process(String str) @@ -43,8 +46,8 @@ public class Template if (line.startsWith("#include ")) { String resource = line.substring(9); - String resourceStr = resourceLoader.apply(resource); - sb.append(process(resourceStr)); + String resourceStr = load(resource); + sb.append(resourceStr); } else { @@ -53,4 +56,43 @@ public class Template } return sb.toString(); } + + public String load(String filename) + { + for (Function loader : resourceLoaders) + { + String value = loader.apply(filename); + if (value != null) + { + return process(value); + } + } + + return ""; + } + + public Template add(Function fn) + { + resourceLoaders.add(fn); + return this; + } + + public Template addInclude(Class clazz) + { + return add(f -> + { + InputStream is = clazz.getResourceAsStream(f); + if (is != null) + { + return inputStreamToString(is); + } + return null; + }); + } + + private static String inputStreamToString(InputStream in) + { + Scanner scanner = new Scanner(in).useDelimiter("\\A"); + return scanner.next(); + } } diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/gpu/ShaderTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/gpu/ShaderTest.java deleted file mode 100644 index bc42b37239..0000000000 --- a/runelite-client/src/test/java/net/runelite/client/plugins/gpu/ShaderTest.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright (c) 2018, Adam - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * 2. 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. - * - * 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 net.runelite.client.plugins.gpu; - -import com.jogamp.nativewindow.AbstractGraphicsConfiguration; -import com.jogamp.nativewindow.NativeWindowFactory; -import com.jogamp.nativewindow.awt.AWTGraphicsConfiguration; -import com.jogamp.nativewindow.awt.JAWTWindow; -import com.jogamp.opengl.GL4; -import com.jogamp.opengl.GLCapabilities; -import com.jogamp.opengl.GLContext; -import com.jogamp.opengl.GLDrawable; -import com.jogamp.opengl.GLDrawableFactory; -import com.jogamp.opengl.GLProfile; -import java.awt.Canvas; -import java.util.function.Function; -import javax.swing.JFrame; -import static net.runelite.client.plugins.gpu.GLUtil.inputStreamToString; -import net.runelite.client.plugins.gpu.template.Template; -import static org.junit.Assert.fail; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; - -public class ShaderTest -{ - private static final String VERTEX_SHADER = "" + - "void main() {" + - " gl_Position = vec4(1.0, 1.0, 1.0, 1.0);" + - "}"; - private GL4 gl; - - @Before - public void before() - { - Canvas canvas = new Canvas(); - JFrame frame = new JFrame(); - frame.setSize(100, 100); - frame.add(canvas); - frame.setVisible(true); - - GLProfile glProfile = GLProfile.getMaxFixedFunc(true); - - GLCapabilities glCaps = new GLCapabilities(glProfile); - AbstractGraphicsConfiguration config = AWTGraphicsConfiguration.create(canvas.getGraphicsConfiguration(), - glCaps, glCaps); - - JAWTWindow jawtWindow = (JAWTWindow) NativeWindowFactory.getNativeWindow(canvas, config); - - GLDrawableFactory glDrawableFactory = GLDrawableFactory.getFactory(glProfile); - - GLDrawable glDrawable = glDrawableFactory.createGLDrawable(jawtWindow); - glDrawable.setRealized(true); - - - GLContext glContext = glDrawable.createContext(null); - int res = glContext.makeCurrent(); - if (res == GLContext.CONTEXT_NOT_CURRENT) - { - fail("error making context current"); - } - - gl = glContext.getGL().getGL4(); - } - - @Test - @Ignore - public void testUnordered() throws ShaderException - { - int glComputeProgram = gl.glCreateProgram(); - int glComputeShader = gl.glCreateShader(gl.GL_COMPUTE_SHADER); - try - { - Function func = (s) -> inputStreamToString(getClass().getResourceAsStream(s)); - Template template = new Template(func); - String source = template.process(func.apply("comp_unordered.glsl")); - - int line = 0; - for (String str : source.split("\\n")) - { - System.out.println(++line + " " + str); - } - - GLUtil.loadComputeShader(gl, glComputeProgram, glComputeShader, source); - } - finally - { - gl.glDeleteShader(glComputeShader); - gl.glDeleteProgram(glComputeProgram); - } - } - - @Test - @Ignore - public void testSmall() throws ShaderException - { - int glComputeProgram = gl.glCreateProgram(); - int glComputeShader = gl.glCreateShader(gl.GL_COMPUTE_SHADER); - try - { - Function func = (s) -> inputStreamToString(getClass().getResourceAsStream(s)); - Template template = new Template(func); - String source = template.process(func.apply("comp_small.glsl")); - - int line = 0; - for (String str : source.split("\\n")) - { - System.out.println(++line + " " + str); - } - - GLUtil.loadComputeShader(gl, glComputeProgram, glComputeShader, source); - } - finally - { - gl.glDeleteShader(glComputeShader); - gl.glDeleteProgram(glComputeProgram); - } - } - - @Test - @Ignore - public void testComp() throws ShaderException - { - int glComputeProgram = gl.glCreateProgram(); - int glComputeShader = gl.glCreateShader(gl.GL_COMPUTE_SHADER); - try - { - Function func = (s) -> inputStreamToString(getClass().getResourceAsStream(s)); - Template template = new Template(func); - String source = template.process(func.apply("comp.glsl")); - - int line = 0; - for (String str : source.split("\\n")) - { - System.out.println(++line + " " + str); - } - - GLUtil.loadComputeShader(gl, glComputeProgram, glComputeShader, source); - } - finally - { - gl.glDeleteShader(glComputeShader); - gl.glDeleteProgram(glComputeProgram); - } - } - - @Test - @Ignore - public void testGeom() throws ShaderException - { - int glComputeProgram = gl.glCreateProgram(); - int glVertexShader = gl.glCreateShader(gl.GL_VERTEX_SHADER); - int glGeometryShader = gl.glCreateShader(gl.GL_GEOMETRY_SHADER); - int glFragmentShader = gl.glCreateShader(gl.GL_FRAGMENT_SHADER); - try - { - Function func = (s) -> inputStreamToString(getClass().getResourceAsStream(s)); - Template template = new Template(func); - String source = template.process(func.apply("geom.glsl")); - - int line = 0; - for (String str : source.split("\\n")) - { - System.out.println(++line + " " + str); - } - - GLUtil.loadShaders(gl, glComputeProgram, glVertexShader, glGeometryShader, glFragmentShader, VERTEX_SHADER, source, ""); - } - finally - { - gl.glDeleteShader(glVertexShader); - gl.glDeleteShader(glGeometryShader); - gl.glDeleteShader(glFragmentShader); - gl.glDeleteProgram(glComputeProgram); - } - } -} diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/gpu/template/TemplateTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/gpu/template/TemplateTest.java index 0c35e65d52..2c60d58bd8 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/gpu/template/TemplateTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/gpu/template/TemplateTest.java @@ -58,7 +58,9 @@ public class TemplateTest throw new RuntimeException("unknown resource"); } }; - String out = new Template(func).process(FILE1); + String out = new Template() + .add(func) + .process(FILE1); assertEquals(RESULT, out); } } \ No newline at end of file From 7e8bebc08329b6c9422237eaab9d0e7eba2f4a38 Mon Sep 17 00:00:00 2001 From: Max Weber Date: Mon, 10 Feb 2020 18:35:29 -0700 Subject: [PATCH 2/3] gpu: run glslangValidator on shaders during tests --- pom.xml | 4 + .../client/plugins/gpu/ShaderTest.java | 130 ++++++++++++++++++ travis/build.sh | 8 +- 3 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 runelite-client/src/test/java/net/runelite/client/plugins/gpu/ShaderTest.java diff --git a/pom.xml b/pom.xml index fafd15de44..76ac38187a 100644 --- a/pom.xml +++ b/pom.xml @@ -42,6 +42,7 @@ true true + 188 @@ -171,6 +172,9 @@ true -Xmx512m + + ${glslang.path} + diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/gpu/ShaderTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/gpu/ShaderTest.java new file mode 100644 index 0000000000..7b206cbacd --- /dev/null +++ b/runelite-client/src/test/java/net/runelite/client/plugins/gpu/ShaderTest.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2020 Abex + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. 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. + * + * 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 net.runelite.client.plugins.gpu; + +import com.jogamp.opengl.GL4; +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.List; +import joptsimple.internal.Strings; +import lombok.extern.slf4j.Slf4j; +import net.runelite.client.plugins.gpu.template.Template; +import org.junit.Assert; +import org.junit.Assume; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +@Slf4j +public class ShaderTest +{ + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Test + public void testShaders() throws Exception + { + String verifier = System.getProperty("glslang.path"); + Assume.assumeFalse("glslang.path is not set", Strings.isNullOrEmpty(verifier)); + + Template[] templates = { + new Template() + .addInclude(GpuPlugin.class) + .add(key -> + { + if ("version_header".equals(key)) + { + return GpuPlugin.WINDOWS_VERSION_HEADER; + } + return null; + }), + }; + + Shader[] shaders = { + GpuPlugin.PROGRAM, + GpuPlugin.COMPUTE_PROGRAM, + GpuPlugin.SMALL_COMPUTE_PROGRAM, + GpuPlugin.UNORDERED_COMPUTE_PROGRAM, + GpuPlugin.UI_PROGRAM, + }; + + for (Template t : templates) + { + for (Shader s : shaders) + { + verify(t, s); + } + } + } + + private void verify(Template template, Shader shader) throws Exception + { + File folder = temp.newFolder(); + List args = new ArrayList<>(); + args.add(System.getProperty("glslang.path")); + args.add("-l"); + for (Shader.Unit u : shader.units) + { + String contents = template.load(u.getFilename()); + String ext; + switch (u.getType()) + { + case GL4.GL_VERTEX_SHADER: + ext = "vert"; + break; + case GL4.GL_TESS_CONTROL_SHADER: + ext = "tesc"; + break; + case GL4.GL_TESS_EVALUATION_SHADER: + ext = "tese"; + break; + case GL4.GL_GEOMETRY_SHADER: + ext = "geom"; + break; + case GL4.GL_FRAGMENT_SHADER: + ext = "frag"; + break; + case GL4.GL_COMPUTE_SHADER: + ext = "comp"; + break; + default: + throw new IllegalArgumentException(u.getType() + ""); + } + File file = new File(folder, u.getFilename() + "." + ext); + Files.write(file.toPath(), contents.getBytes(StandardCharsets.UTF_8)); + args.add(file.getAbsolutePath()); + } + + ProcessBuilder pb = new ProcessBuilder(args.toArray(new String[0])); + pb.inheritIO(); + Process proc = pb.start(); + if (proc.waitFor() != 0) + { + Assert.fail(); + } + } +} diff --git a/travis/build.sh b/travis/build.sh index deba275667..d86e56f113 100755 --- a/travis/build.sh +++ b/travis/build.sh @@ -1,3 +1,9 @@ #!/bin/bash -mvn clean install --settings travis/settings.xml +set -e -x + +wget -O/tmp/glslang.zip 'https://github.com/KhronosGroup/glslang/releases/download/8.13.3559/glslang-master-linux-Release.zip' +echo '9adcfdef5b52275e61068aafbb62747936c6c18ab6dc32a6ef707cfc7b0df423 /tmp/glslang.zip' | sha256sum -c +unzip -q /tmp/glslang.zip -d /tmp/glslang + +mvn clean install --settings travis/settings.xml -Dglslang.path=/tmp/glslang/bin/glslangValidator From a330631945855fec65caf86d28fad80ab3a7c8f8 Mon Sep 17 00:00:00 2001 From: Max Weber Date: Mon, 10 Feb 2020 18:38:11 -0700 Subject: [PATCH 3/3] gpu: rename length to size in shaders `.length` has special meaning in glsl, and the verifier got upset that we were shadowing it --- .../net/runelite/client/plugins/gpu/comp.glsl | 2 +- .../client/plugins/gpu/comp_common.glsl | 2 +- .../client/plugins/gpu/comp_unordered.glsl | 4 ++-- .../client/plugins/gpu/priority_render.glsl | 20 +++++++++---------- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp.glsl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp.glsl index f569bceed6..7c7f948ea3 100644 --- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp.glsl +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp.glsl @@ -44,7 +44,7 @@ void main() { uint groupId = gl_WorkGroupID.x; uint localId = gl_LocalInvocationID.x * 4; modelinfo minfo = ol[groupId]; - int length = minfo.length; + int length = minfo.size; if (localId == 0) { min10 = 1600; diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp_common.glsl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp_common.glsl index c527a942c3..934407b94b 100644 --- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp_common.glsl +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp_common.glsl @@ -41,7 +41,7 @@ struct modelinfo { int offset; // offset into buffer int uvOffset; // offset into uv buffer - int length; // length in faces + int size; // length in faces int idx; // write idx in target buffer int flags; // radius, orientation int x; // scene position x diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp_unordered.glsl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp_unordered.glsl index b4b526838b..531eb6c935 100644 --- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp_unordered.glsl +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp_unordered.glsl @@ -37,14 +37,14 @@ void main() { modelinfo minfo = ol[groupId]; int offset = minfo.offset; - int length = minfo.length; + int size = minfo.size; int outOffset = minfo.idx; int uvOffset = minfo.uvOffset; int flags = minfo.flags; int orientation = flags & 0x7ff; ivec4 pos = ivec4(minfo.x, minfo.y, minfo.z, 0); - if (localId >= length) { + if (localId >= size) { return; } diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/priority_render.glsl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/priority_render.glsl index 968f8aec00..5077182e1a 100644 --- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/priority_render.glsl +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/priority_render.glsl @@ -113,7 +113,7 @@ int count_prio_offset(int priority) { void get_face(uint localId, modelinfo minfo, int cameraYaw, int cameraPitch, int centerX, int centerY, int zoom, out int prio, out int dis, out ivec4 o1, out ivec4 o2, out ivec4 o3) { int offset = minfo.offset; - int length = minfo.length; + int size = minfo.size; int flags = minfo.flags; int radius = (flags & 0x7fffffff) >> 12; int orientation = flags & 0x7ff; @@ -121,7 +121,7 @@ void get_face(uint localId, modelinfo minfo, int cameraYaw, int cameraPitch, int uint ssboOffset; - if (localId < length) { + if (localId < size) { ssboOffset = localId; } else { ssboOffset = 0; @@ -148,7 +148,7 @@ void get_face(uint localId, modelinfo minfo, int cameraYaw, int cameraPitch, int int thisPriority, thisDistance; - if (localId < length) { + if (localId < size) { // rotate for model orientation thisrvA = rotate(thisA, orientation); thisrvB = rotate(thisB, orientation); @@ -186,14 +186,14 @@ void get_face(uint localId, modelinfo minfo, int cameraYaw, int cameraPitch, int } int map_face_priority(uint localId, modelinfo minfo, int thisPriority, int thisDistance, out int prio) { - int length = minfo.length; + int size = minfo.size; // Compute average distances for 0/2, 3/4, and 6/8 int adjPrio; int prioIdx; - if (localId < length) { + if (localId < size) { int avg1 = 0; int avg2 = 0; int avg3 = 0; @@ -224,9 +224,9 @@ int map_face_priority(uint localId, modelinfo minfo, int thisPriority, int thisD } void insert_dfs(uint localId, modelinfo minfo, int adjPrio, int distance, int prioIdx) { - int length = minfo.length; + int size = minfo.size; - if (localId < length) { + if (localId < size) { // calculate base offset into dfs based on number of faces with a lower priority int baseOff = count_prio_offset(adjPrio); // store into face array offset array by unique index @@ -236,14 +236,14 @@ void insert_dfs(uint localId, modelinfo minfo, int adjPrio, int distance, int pr void sort_and_insert(uint localId, modelinfo minfo, int thisPriority, int thisDistance, ivec4 thisrvA, ivec4 thisrvB, ivec4 thisrvC) { /* compute face distance */ - int length = minfo.length; + int size = minfo.size; int outOffset = minfo.idx; int uvOffset = minfo.uvOffset; int flags = minfo.flags; ivec4 pos = ivec4(minfo.x, minfo.y, minfo.z, 0); int start, end, myOffset; - if (localId < length) { + if (localId < size) { const int priorityOffset = count_prio_offset(thisPriority); const int numOfPriority = totalMappedNum[thisPriority]; start = priorityOffset; // index of first face with this priority @@ -253,7 +253,7 @@ void sort_and_insert(uint localId, modelinfo minfo, int thisPriority, int thisDi start = end = myOffset = 0; } - if (localId < length) { + if (localId < size) { // we only have to order faces against others of the same priority // calculate position this face will be in for (int i = start; i < end; ++i) {