Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shader Line Annotation #2691

Open
wants to merge 2 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import org.apache.logging.log4j.Logger;
import org.lwjgl.opengl.GL20C;

import java.util.Arrays;

/**
* A compiled OpenGL shader object.
*/
Expand All @@ -15,17 +17,18 @@ public class GlShader extends GlObject {

private final ResourceLocation name;

public GlShader(ShaderType type, ResourceLocation name, String src) {
public GlShader(ShaderType type, ResourceLocation name, ShaderParser.ParsedShader parsedShader) {
this.name = name;

int handle = GL20C.glCreateShader(type.id);
ShaderWorkarounds.safeShaderSource(handle, src);
ShaderWorkarounds.safeShaderSource(handle, parsedShader.src());
GL20C.glCompileShader(handle);

String log = GL20C.glGetShaderInfoLog(handle);

if (!log.isEmpty()) {
LOGGER.warn("Shader compilation log for " + this.name + ": " + log);
LOGGER.warn("Shader compilation log for {}: {}", this.name, log);
LOGGER.warn("Include table: {}", Arrays.toString(parsedShader.includeIds()));
}

int result = GlStateManager.glGetShaderi(handle, GL20C.GL_COMPILE_STATUS);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
package net.caffeinemc.mods.sodium.client.gl.shader;

import net.caffeinemc.mods.sodium.client.services.PlatformRuntimeInformation;
import org.apache.commons.io.IOUtils;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import net.minecraft.resources.ResourceLocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ShaderLoader {
private static final Logger LOGGER = LoggerFactory.getLogger("Sodium-ShaderLoader");

/**
* Creates an OpenGL shader from GLSL sources. The GLSL source file should be made available on the classpath at the
* path of `/assets/{namespace}/shaders/{path}`. User defines can be used to declare variables in the shader source
Expand All @@ -19,7 +24,12 @@ public class ShaderLoader {
* @return An OpenGL shader object compiled with the given user defines
*/
public static GlShader loadShader(ShaderType type, ResourceLocation name, ShaderConstants constants) {
return new GlShader(type, name, ShaderParser.parseShader(getShaderSource(name), constants));
var parsedShader = ShaderParser.parseShader(getShaderSource(name), constants);
if (PlatformRuntimeInformation.INSTANCE.isDevelopmentEnvironment()) {
LOGGER.info("Loaded shader {} with constants {}", name, constants);
LOGGER.info(parsedShader.src());
}
return new GlShader(type, name, parsedShader);
}

public static String getShaderSource(ResourceLocation name) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,44 +1,87 @@
package net.caffeinemc.mods.sodium.client.gl.shader;

import it.unimi.dsi.fastutil.objects.Object2IntArrayMap;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import net.minecraft.resources.ResourceLocation;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import net.minecraft.resources.ResourceLocation;

public class ShaderParser {
public static String parseShader(String src, ShaderConstants constants) {
List<String> lines = parseShader(src);
lines.addAll(1, constants.getDefineStrings());
public record ParsedShader(String src, String[] includeIds) {
}

public static ParsedShader parseShader(String src, ShaderConstants constants) {
var parser = new ShaderParser();
parser.parseShader("_root", src);
parser.prependDefineStrings(constants);

return String.join("\n", lines);
return parser.finish();
}

public static List<String> parseShader(String src) {
List<String> builder = new LinkedList<>();
private final Object2IntMap<String> includeIds = new Object2IntArrayMap<>();
private final List<String> lines = new LinkedList<>();

private ShaderParser() {
}

public void parseShader(String name, String src) {
String line;
int lineNumber = 0;

try (BufferedReader reader = new BufferedReader(new StringReader(src))) {
while ((line = reader.readLine()) != null) {
if (line.startsWith("#import")) {
builder.addAll(resolveImport(line));
lineNumber++;
if (line.startsWith("#version")) {
this.lines.add(line);
this.lines.add(lineDirectiveFor(name, lineNumber));
} else if (line.startsWith("#import")) {
// add the original import statement as a comment for reference
this.lines.add("// START " + line);

processImport(line);

// reset the line directive to the current file
this.lines.add("// END " + line);
this.lines.add(lineDirectiveFor(name, lineNumber));
} else {
builder.add(line);
this.lines.add(line);
}
}
} catch (IOException e) {
throw new RuntimeException("Failed to read shader sources", e);
}
}

private String lineDirectiveFor(String name, int line) {
int idNumber;
if (!this.includeIds.containsKey(name)) {
idNumber = this.includeIds.size();
this.includeIds.put(name, idNumber);
} else {
idNumber = this.includeIds.getInt(name);
}
return "#line " + (line + 1) + " " + idNumber;
}

private void processImport(String line) {
ResourceLocation name = parseImport(line);

return builder;
// mark the start of the imported file
var nameString = name.toString();
this.lines.add(lineDirectiveFor(nameString, 0));

parseShader(nameString, ShaderLoader.getShaderSource(name));
}

private static final Pattern IMPORT_PATTERN = Pattern.compile("#import <(?<namespace>.*):(?<path>.*)>");

private static List<String> resolveImport(String line) {
private ResourceLocation parseImport(String line) {
Matcher matcher = IMPORT_PATTERN.matcher(line);

if (!matcher.matches()) {
Expand All @@ -48,9 +91,20 @@ private static List<String> resolveImport(String line) {
String namespace = matcher.group("namespace");
String path = matcher.group("path");

ResourceLocation name = ResourceLocation.fromNamespaceAndPath(namespace, path);
String source = ShaderLoader.getShaderSource(name);
return ResourceLocation.fromNamespaceAndPath(namespace, path);
}

private void prependDefineStrings(ShaderConstants constants) {
this.lines.addAll(1, constants.getDefineStrings());
}

private ParsedShader finish() {
// convert include id map to a list ordered by id
var includeIds = new String[this.includeIds.size()];
this.includeIds.forEach((name, id) -> {
includeIds[id] = name;
});

return ShaderParser.parseShader(source);
return new ParsedShader(String.join("\n", this.lines), includeIds);
}
}