Created PrintfInjector to use printf inside shaders
This commit is contained in:
@@ -0,0 +1,220 @@
|
|||||||
|
package me.cortex.voxy.client.core.gl.shader;
|
||||||
|
|
||||||
|
import me.cortex.voxy.client.core.gl.GlBuffer;
|
||||||
|
import me.cortex.voxy.client.core.rendering.util.DownloadStream;
|
||||||
|
import org.lwjgl.system.MemoryUtil;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import static org.lwjgl.opengl.ARBDirectStateAccess.nglClearNamedBufferData;
|
||||||
|
import static org.lwjgl.opengl.GL11.GL_UNSIGNED_INT;
|
||||||
|
import static org.lwjgl.opengl.GL30.GL_R32UI;
|
||||||
|
import static org.lwjgl.opengl.GL30.glBindBufferBase;
|
||||||
|
import static org.lwjgl.opengl.GL30C.GL_RED_INTEGER;
|
||||||
|
import static org.lwjgl.opengl.GL43.GL_SHADER_STORAGE_BUFFER;
|
||||||
|
import static org.lwjgl.opengl.GL45.nglClearNamedBufferSubData;
|
||||||
|
|
||||||
|
public class PrintfInjector {
|
||||||
|
private final GlBuffer textBuffer;
|
||||||
|
private final HashMap<String, Integer> printfStringMap = new HashMap<>();
|
||||||
|
private final HashMap<Integer, String> idToPrintfStringMap = new HashMap<>();
|
||||||
|
private final int bindingIndex;
|
||||||
|
private final Consumer<String> callback;
|
||||||
|
public PrintfInjector(int bufferSize, int bufferBindingIndex, Consumer<String> callback) {
|
||||||
|
this.textBuffer = new GlBuffer(bufferSize*4L+4);
|
||||||
|
nglClearNamedBufferData(this.textBuffer.id, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, 0);
|
||||||
|
this.bindingIndex = bufferBindingIndex;
|
||||||
|
this.callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int findNextCall(String src, int after) {
|
||||||
|
while (true) {
|
||||||
|
int idx = src.indexOf("printf", after);
|
||||||
|
if (idx == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean lineComment = false;
|
||||||
|
boolean multiLineComment = false;
|
||||||
|
//Check for comments
|
||||||
|
for (int i = 0; i < idx; i++) {
|
||||||
|
if (src.charAt(i) == '/' && src.charAt(i + 1) == '/') {
|
||||||
|
lineComment = true;
|
||||||
|
}
|
||||||
|
if (src.charAt(i) == '\n') {
|
||||||
|
lineComment = false;
|
||||||
|
}
|
||||||
|
if ((!lineComment) && src.charAt(i) == '/' && src.charAt(i + 1) == '*') {
|
||||||
|
multiLineComment = true;
|
||||||
|
}
|
||||||
|
if ((!lineComment) && src.charAt(i) == '*' && src.charAt(i + 1) == '/') {
|
||||||
|
multiLineComment = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (lineComment || multiLineComment) {
|
||||||
|
after = idx+1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void parsePrintfTypes(String fmtStr, List<Character> types) {
|
||||||
|
for (int i = 0; i < fmtStr.length()-1; i++) {
|
||||||
|
if (fmtStr.charAt(i)=='%' && (i==0||fmtStr.charAt(i-1)!='%')) {
|
||||||
|
types.add(fmtStr.charAt(i+1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String transformInject(String src) {
|
||||||
|
String original = src;
|
||||||
|
//Quick exit
|
||||||
|
if (!src.contains("printf")) {
|
||||||
|
return src;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pos = 0;
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
List<String> argVals = new ArrayList<>();
|
||||||
|
List<Character> types = new ArrayList<>();
|
||||||
|
|
||||||
|
{
|
||||||
|
int bufferInjection = Math.max(src.lastIndexOf("#version"), src.lastIndexOf("#extension"));
|
||||||
|
bufferInjection = src.indexOf("\n", bufferInjection);
|
||||||
|
|
||||||
|
result.append(src, 0, bufferInjection+1);
|
||||||
|
|
||||||
|
result.append(String.format("""
|
||||||
|
layout(binding = %s, std430) restrict buffer PrintfOutputStream {
|
||||||
|
uint index;
|
||||||
|
uint stream[];
|
||||||
|
} printfOutputStruct;
|
||||||
|
""", this.bindingIndex));
|
||||||
|
|
||||||
|
src = src.substring(bufferInjection+1);
|
||||||
|
}
|
||||||
|
boolean usedPrintf = false;
|
||||||
|
while (true) {
|
||||||
|
int nextCall = findNextCall(src, pos);
|
||||||
|
if (nextCall == -1) {
|
||||||
|
result.append(src, pos, src.length());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
result.append(src, pos, nextCall);
|
||||||
|
|
||||||
|
|
||||||
|
//Parse the printf() call
|
||||||
|
String sub = src.substring(nextCall);
|
||||||
|
sub = sub.substring(sub.indexOf('"')+1);
|
||||||
|
sub = sub.substring(0, sub.indexOf(';'));
|
||||||
|
String fmtStr = sub.substring(0, sub.indexOf('"'));
|
||||||
|
String args = sub.substring(sub.indexOf('"'));
|
||||||
|
|
||||||
|
//Parse the commas in the args
|
||||||
|
int prev = 0;
|
||||||
|
int brace = 0;
|
||||||
|
argVals.clear();
|
||||||
|
for (int i = 0; i < args.length(); i++) {
|
||||||
|
if (args.charAt(i) == '(' || args.charAt(i) == '[') brace++;
|
||||||
|
if (args.charAt(i) == ')' || args.charAt(i) == ']') brace--;
|
||||||
|
if ((args.charAt(i) == ',' && brace==0) || brace==-1) {
|
||||||
|
if (prev == 0) {
|
||||||
|
prev = i;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String arg = args.substring(prev+1, i);
|
||||||
|
prev = i;
|
||||||
|
argVals.add(arg);
|
||||||
|
|
||||||
|
if (brace==-1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Parse the format string
|
||||||
|
types.clear();
|
||||||
|
parsePrintfTypes(fmtStr, types);
|
||||||
|
|
||||||
|
if (types.size() != argVals.size()) {
|
||||||
|
throw new IllegalStateException("Printf obj count dont match arg size");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//Inject the printf code
|
||||||
|
StringBuilder subCode = new StringBuilder();
|
||||||
|
subCode.append(String.format("{" +
|
||||||
|
"uint printfWriteIndex = atomicAdd(printfOutputStruct.index,%s);" +
|
||||||
|
"printfOutputStruct.stream[printfWriteIndex]=%s;", types.size()+1,
|
||||||
|
this.printfStringMap.computeIfAbsent(fmtStr, a->{int id = this.printfStringMap.size();
|
||||||
|
this.idToPrintfStringMap.put(id, a);
|
||||||
|
return id;})));
|
||||||
|
|
||||||
|
for (int i = 0; i < types.size(); i++) {
|
||||||
|
subCode.append("printfOutputStruct.stream[printfWriteIndex+").append(i+1).append("]=");
|
||||||
|
if (types.get(i) == 'd' || types.get(i) == 'i' || types.get(i) == 'u') {
|
||||||
|
subCode.append("uint(").append(argVals.get(i)).append(")");
|
||||||
|
}
|
||||||
|
if (types.get(i) == 'f') {
|
||||||
|
subCode.append("floatBitsToUint(").append(argVals.get(i)).append(")");
|
||||||
|
}
|
||||||
|
subCode.append(";");
|
||||||
|
}
|
||||||
|
subCode.append("}");
|
||||||
|
result.append(subCode);
|
||||||
|
usedPrintf = true;
|
||||||
|
pos = src.indexOf(';', nextCall)+1;
|
||||||
|
}
|
||||||
|
if (!usedPrintf) {
|
||||||
|
return original;
|
||||||
|
}
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void bind() {
|
||||||
|
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, this.bindingIndex, this.textBuffer.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processResult(long ptr, long size) {
|
||||||
|
int total = MemoryUtil.memGetInt(ptr); ptr += 4;
|
||||||
|
int cnt = 0;
|
||||||
|
List<Character> types = new ArrayList<>();
|
||||||
|
while (cnt < total) {
|
||||||
|
int id = MemoryUtil.memGetInt(ptr);
|
||||||
|
ptr += 4;
|
||||||
|
cnt++;
|
||||||
|
String fmt = this.idToPrintfStringMap.get(id);
|
||||||
|
types.clear();
|
||||||
|
parsePrintfTypes(fmt, types);
|
||||||
|
Object[] args = new Object[types.size()];
|
||||||
|
for (int i = 0; i < types.size(); i++) {
|
||||||
|
if (types.get(i) == 'd' || types.get(i) == 'i' || types.get(i) == 'u') {
|
||||||
|
args[i] = MemoryUtil.memGetInt(ptr);
|
||||||
|
ptr += 4;
|
||||||
|
cnt++;
|
||||||
|
}
|
||||||
|
if (types.get(i) == 'f') {
|
||||||
|
args[i] = Float.intBitsToFloat(MemoryUtil.memGetInt(ptr));
|
||||||
|
ptr += 4;
|
||||||
|
cnt++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.callback.accept(String.format(fmt, args));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void download() {
|
||||||
|
DownloadStream.INSTANCE.download(this.textBuffer, 0, this.textBuffer.size(), this::processResult);
|
||||||
|
DownloadStream.INSTANCE.commit();
|
||||||
|
nglClearNamedBufferSubData(this.textBuffer.id, GL_R32UI, 0, 4, GL_RED_INTEGER, GL_UNSIGNED_INT, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void free() {
|
||||||
|
this.textBuffer.free();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package me.cortex.voxy.client.core.rendering;
|
package me.cortex.voxy.client.core.rendering;
|
||||||
|
|
||||||
import me.cortex.voxy.client.core.gl.GlBuffer;
|
import me.cortex.voxy.client.core.gl.GlBuffer;
|
||||||
|
import me.cortex.voxy.client.core.gl.shader.PrintfInjector;
|
||||||
import me.cortex.voxy.client.core.gl.shader.Shader;
|
import me.cortex.voxy.client.core.gl.shader.Shader;
|
||||||
import me.cortex.voxy.client.core.gl.shader.ShaderType;
|
import me.cortex.voxy.client.core.gl.shader.ShaderType;
|
||||||
import me.cortex.voxy.client.core.rendering.building.RenderDataFactory;
|
import me.cortex.voxy.client.core.rendering.building.RenderDataFactory;
|
||||||
|
|||||||
@@ -351,7 +351,7 @@ public class NodeManager2 {
|
|||||||
|
|
||||||
private void download() {
|
private void download() {
|
||||||
//Download the request queue then clear the counter (first 4 bytes)
|
//Download the request queue then clear the counter (first 4 bytes)
|
||||||
DownloadStream.INSTANCE.download(this.);
|
//DownloadStream.INSTANCE.download(this.);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user