Finally have time off

- Attempt at componentizing ShaderCompiler, starting with Includer
 - Begin refactoring uniform providers
 - Context as an interface
 - Separate ContextSet objects for Pipeline shaders and Culling shaders
 - Inline ProgramAssembler
 - Replace StringUtil#trimEnd with String#stripTrailing
 - Add StringUtil#trimPrefix and #trimSuffix
This commit is contained in:
Jozufozu 2022-12-20 21:20:08 -08:00
parent ef568d22f7
commit 9a746f700d
26 changed files with 437 additions and 221 deletions

View file

@ -0,0 +1,12 @@
package com.jozufozu.flywheel.api.context;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import com.jozufozu.flywheel.core.source.FileResolution;
public interface Context {
void setup(GlProgram program);
FileResolution vertexShader();
FileResolution fragmentShader();
}

View file

@ -3,6 +3,20 @@ package com.jozufozu.flywheel.api.context;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram; import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import com.jozufozu.flywheel.core.source.FileResolution; import com.jozufozu.flywheel.core.source.FileResolution;
public record ContextShader(GlProgram.Factory factory, FileResolution vertexShader, FileResolution fragmentShader) { public record ContextShader(GlProgram.Factory factory, FileResolution vertexShader,
FileResolution fragmentShader) implements Context {
@Override
public void setup(GlProgram program) {
}
@Override
public FileResolution vertexShader() {
return vertexShader;
}
@Override
public FileResolution fragmentShader() {
return fragmentShader;
}
} }

View file

@ -8,15 +8,16 @@ public interface UniformProvider {
FileResolution uniformShader(); FileResolution uniformShader();
ActiveUniformProvider activate(long ptr, Notifier notifier); ActiveUniformProvider activate(long ptr);
interface ActiveUniformProvider { interface ActiveUniformProvider {
void delete(); void delete();
void poll(); /**
} * Poll the provider for changes.
*
interface Notifier { * @return {@code true} if the provider updated its backing store.
void signalChanged(); */
boolean poll();
} }
} }

View file

@ -16,7 +16,7 @@ public class GlProgram extends GlObject {
setHandle(handle); setHandle(handle);
} }
// TODO: Programs bind the uniform buffers they need // TODO: Programs bind the uniform buffers they need, no more GlProgram inheritance
public void bind() { public void bind() {
ProgramManager.glUseProgram(handle()); ProgramManager.glUseProgram(handle());
} }

View file

@ -83,7 +83,8 @@ public class Compilation {
.toString())); .toString()));
} }
fullSource.append(source); fullSource.append(source)
.append('\n');
} }
private String sourceHeader(SourceFile sourceFile) { private String sourceHeader(SourceFile sourceFile) {

View file

@ -1,5 +1,11 @@
package com.jozufozu.flywheel.backend.instancing.compile; package com.jozufozu.flywheel.backend.instancing.compile;
import static org.lwjgl.opengl.GL20.GL_LINK_STATUS;
import static org.lwjgl.opengl.GL20.GL_TRUE;
import static org.lwjgl.opengl.GL20.glGetProgramInfoLog;
import static org.lwjgl.opengl.GL20.glGetProgrami;
import static org.lwjgl.opengl.GL20.glLinkProgram;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -7,6 +13,7 @@ import java.util.stream.Stream;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.gl.GLSLVersion; import com.jozufozu.flywheel.backend.gl.GLSLVersion;
import com.jozufozu.flywheel.backend.gl.shader.ShaderType; import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
import com.jozufozu.flywheel.core.source.SourceFile; import com.jozufozu.flywheel.core.source.SourceFile;
@ -17,9 +24,7 @@ public class CompileUtil {
public static final Pattern matType = Pattern.compile("^mat([234])(?:x([234]))?$"); public static final Pattern matType = Pattern.compile("^mat([234])(?:x([234]))?$");
public static String generateHeader(GLSLVersion version, ShaderType type) { public static String generateHeader(GLSLVersion version, ShaderType type) {
return version.getVersionLine() return version.getVersionLine() + type.getDefineStatement() + '\n';
+ type.getDefineStatement()
+ '\n';
} }
public static int getElementCount(String type) { public static int getElementCount(String type) {
@ -53,10 +58,31 @@ public class CompileUtil {
return 1; return 1;
} }
@NotNull @NotNull
public static String generateDebugName(SourceFile... stages) { public static String generateDebugName(SourceFile... stages) {
return Stream.of(stages) return Stream.of(stages)
.map(SourceFile::toString) .map(SourceFile::toString)
.collect(Collectors.joining(" -> ")); .collect(Collectors.joining(" -> "));
} }
/**
* Check the program info log for errors.
*
* @param handle The handle of the program to check.
*/
public static void checkLinkLog(int handle) {
glLinkProgram(handle);
String log = glGetProgramInfoLog(handle);
if (!log.isEmpty()) {
Backend.LOGGER.debug("Program link log: " + log);
}
int result = glGetProgrami(handle, GL_LINK_STATUS);
if (result != GL_TRUE) {
throw new RuntimeException("Shader program linking failed, see log for details");
}
}
} }

View file

@ -0,0 +1,6 @@
package com.jozufozu.flywheel.backend.instancing.compile;
import com.jozufozu.flywheel.api.struct.StructType;
public record CullingContext(StructType<?> structType) {
}

View file

@ -0,0 +1,38 @@
package com.jozufozu.flywheel.backend.instancing.compile;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.core.ComponentRegistry;
public class CullingContextSet {
static CullingContextSet create() {
var builder = new CullingContextSet();
for (StructType<?> structType : ComponentRegistry.structTypes) {
builder.add(structType);
}
return builder;
}
private final List<CullingContext> contexts = new ArrayList<>();
private final List<CullingContext> contextView = Collections.unmodifiableList(contexts);
CullingContextSet() {
}
public List<CullingContext> all() {
return contextView;
}
public int size() {
return contexts.size();
}
private void add(StructType<?> structType) {
var ctx = new CullingContext(structType);
contexts.add(ctx);
}
}

View file

@ -13,6 +13,7 @@ import com.jozufozu.flywheel.core.source.SourceLines;
import com.jozufozu.flywheel.core.source.error.ErrorBuilder; import com.jozufozu.flywheel.core.source.error.ErrorBuilder;
import com.jozufozu.flywheel.core.source.span.Span; import com.jozufozu.flywheel.core.source.span.Span;
import com.jozufozu.flywheel.util.ConsoleColors; import com.jozufozu.flywheel.util.ConsoleColors;
import com.jozufozu.flywheel.util.StringUtil;
public class FailedCompilation { public class FailedCompilation {
private static final Pattern ERROR_LINE = Pattern.compile("(\\d+)\\((\\d+)\\) : (.*)"); private static final Pattern ERROR_LINE = Pattern.compile("(\\d+)\\((\\d+)\\) : (.*)");
@ -49,7 +50,8 @@ public class FailedCompilation {
if (matcher.find()) { if (matcher.find()) {
int fileId = Integer.parseInt(matcher.group(1)); int fileId = Integer.parseInt(matcher.group(1));
int lineNo = Integer.parseInt(matcher.group(2)); int lineNo = Integer.parseInt(matcher.group(2));
var msg = matcher.group(3); var msg = StringUtil.trimPrefix(matcher.group(3), "error")
.stripLeading();
if (fileId == 0) { if (fileId == 0) {
return interpretGeneratedError(lineNo, msg); return interpretGeneratedError(lineNo, msg);

View file

@ -1,5 +1,9 @@
package com.jozufozu.flywheel.backend.instancing.compile; package com.jozufozu.flywheel.backend.instancing.compile;
import static org.lwjgl.opengl.GL20.glAttachShader;
import static org.lwjgl.opengl.GL20.glCreateProgram;
import static org.lwjgl.opengl.GL20.glLinkProgram;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@ -21,9 +25,7 @@ import com.jozufozu.flywheel.backend.gl.GLSLVersion;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram; import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import com.jozufozu.flywheel.backend.gl.shader.ShaderType; import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
import com.jozufozu.flywheel.backend.instancing.indirect.IndirectComponent; import com.jozufozu.flywheel.backend.instancing.indirect.IndirectComponent;
import com.jozufozu.flywheel.core.BackendTypes;
import com.jozufozu.flywheel.core.ComponentRegistry; import com.jozufozu.flywheel.core.ComponentRegistry;
import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.Pipelines; import com.jozufozu.flywheel.core.Pipelines;
import com.jozufozu.flywheel.core.SourceComponent; import com.jozufozu.flywheel.core.SourceComponent;
import com.jozufozu.flywheel.core.pipeline.SimplePipeline; import com.jozufozu.flywheel.core.pipeline.SimplePipeline;
@ -41,7 +43,9 @@ public class FlwCompiler {
private final ShaderSources sources; private final ShaderSources sources;
private final MaterialAdapterComponent vertexMaterialComponent; private final MaterialAdapterComponent vertexMaterialComponent;
private final MaterialAdapterComponent fragmentMaterialComponent; private final MaterialAdapterComponent fragmentMaterialComponent;
private final List<PipelineContext> pipelineContexts;
private final PipelineContextSet pipelineContexts;
private final CullingContextSet cullingContexts;
final ShaderCompiler shaderCompiler; final ShaderCompiler shaderCompiler;
final Multimap<Set<UniformProvider>, PipelineContext> uniformProviderGroups = ArrayListMultimap.create(); final Multimap<Set<UniformProvider>, PipelineContext> uniformProviderGroups = ArrayListMultimap.create();
@ -50,7 +54,10 @@ public class FlwCompiler {
final List<FailedCompilation> errors = new ArrayList<>(); final List<FailedCompilation> errors = new ArrayList<>();
public FlwCompiler(ShaderSources sources) { public FlwCompiler(ShaderSources sources) {
this.shaderCompiler = new ShaderCompiler(errors::add); this.shaderCompiler = ShaderCompiler.builder()
.errorConsumer(errors::add)
.build();
this.sources = sources; this.sources = sources;
this.vertexMaterialComponent = MaterialAdapterComponent.builder(Flywheel.rl("vertex_material_adapter")) this.vertexMaterialComponent = MaterialAdapterComponent.builder(Flywheel.rl("vertex_material_adapter"))
.materialSources(ComponentRegistry.materials.vertexSources()) .materialSources(ComponentRegistry.materials.vertexSources())
@ -73,21 +80,24 @@ public class FlwCompiler {
.switchOn(GlslExpr.variable("flw_materialFragmentID")) .switchOn(GlslExpr.variable("flw_materialFragmentID"))
.build(sources); .build(sources);
this.pipelineContexts = buildPipelineSet(); this.pipelineContexts = PipelineContextSet.create();
this.cullingContexts = CullingContextSet.create();
// TODO: analyze uniform providers and group them into sets; break up this ctor doCompilation();
for (PipelineContext context : pipelineContexts) {
compilePipelineContext(context);
}
for (StructType<?> type : ComponentRegistry.structTypes) {
compileComputeCuller(type);
}
finish(); finish();
} }
private void doCompilation() {
for (var ctx : pipelineContexts.all()) {
compilePipelineContext(ctx);
}
for (var ctx : cullingContexts.all()) {
compileComputeCuller(ctx);
}
}
private void finish() { private void finish() {
long compileEnd = System.nanoTime(); long compileEnd = System.nanoTime();
int programCount = pipelineContexts.size() + ComponentRegistry.structTypes.size(); int programCount = pipelineContexts.size() + ComponentRegistry.structTypes.size();
@ -132,22 +142,31 @@ public class FlwCompiler {
return; return;
} }
pipelinePrograms.put(ctx, ctx.contextShader() var glProgram = link(vertex.handle(), fragment.handle());
.factory() ctx.contextShader()
.create(new ProgramAssembler().attachShader(vertex) .setup(glProgram);
.attachShader(fragment) pipelinePrograms.put(ctx, glProgram);
.link()));
} }
private void compileComputeCuller(StructType<?> structType) { private void compileComputeCuller(CullingContext ctx) {
var result = shaderCompiler.compile(GLSLVersion.V460, ShaderType.COMPUTE, getComputeComponents(structType)); var computeComponents = getComputeComponents(ctx.structType());
var result = shaderCompiler.compile(GLSLVersion.V460, ShaderType.COMPUTE, computeComponents);
if (result == null) { if (result == null) {
return; return;
} }
cullingPrograms.put(structType, new GlProgram(new ProgramAssembler().attachShader(result) cullingPrograms.put(ctx.structType(), link(result.handle()));
.link())); }
private GlProgram link(int... shaders) {
var handle = glCreateProgram();
for (var shader : shaders) {
glAttachShader(handle, shader);
}
glLinkProgram(handle);
CompileUtil.checkLinkLog(handle);
return new GlProgram(handle);
} }
private ImmutableList<SourceComponent> getVertexComponents(PipelineContext ctx) { private ImmutableList<SourceComponent> getVertexComponents(PipelineContext ctx) {
@ -189,16 +208,4 @@ public class FlwCompiler {
return ImmutableList.of(instanceAssembly, instance, pipeline); return ImmutableList.of(instanceAssembly, instance, pipeline);
} }
private static List<PipelineContext> buildPipelineSet() {
ImmutableList.Builder<PipelineContext> builder = ImmutableList.builder();
for (SimplePipeline pipelineShader : BackendTypes.availablePipelineShaders()) {
for (StructType<?> structType : ComponentRegistry.structTypes) {
for (VertexType vertexType : ComponentRegistry.vertexTypes) {
// TODO: context ubershaders, or not?
builder.add(new PipelineContext(vertexType, structType, Components.WORLD, pipelineShader));
}
}
}
return builder.build();
}
} }

View file

@ -0,0 +1,21 @@
package com.jozufozu.flywheel.backend.instancing.compile;
import java.util.function.Consumer;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.core.SourceComponent;
/**
* A component of a ShaderCompiler, responsible for expanding root sources into the complete set of included sources.
*/
public interface Includer {
/**
* Expand the given root sources into the complete set of included sources.
* <p> Each unique source will be seen exactly once.
*
* @param rootSources The root sources to expand.
* @param out A consumer to which all sources should be passed in the order they should be included.
*/
void expand(ImmutableList<SourceComponent> rootSources, Consumer<SourceComponent> out);
}

View file

@ -0,0 +1,48 @@
package com.jozufozu.flywheel.backend.instancing.compile;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.jozufozu.flywheel.api.context.ContextShader;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.core.BackendTypes;
import com.jozufozu.flywheel.core.ComponentRegistry;
import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.pipeline.SimplePipeline;
public class PipelineContextSet {
static PipelineContextSet create() {
var builder = new PipelineContextSet();
for (SimplePipeline pipelineShader : BackendTypes.availablePipelineShaders()) {
for (StructType<?> structType : ComponentRegistry.structTypes) {
for (VertexType vertexType : ComponentRegistry.vertexTypes) {
builder.add(vertexType, structType, Components.WORLD, pipelineShader);
}
}
}
return builder;
}
private final List<PipelineContext> contexts = new ArrayList<>();
private final List<PipelineContext> contextView = Collections.unmodifiableList(contexts);
PipelineContextSet() {
}
public List<PipelineContext> all() {
return contextView;
}
public int size() {
return contexts.size();
}
private void add(VertexType vertexType, StructType<?> structType, ContextShader world, SimplePipeline pipelineShader) {
var ctx = new PipelineContext(vertexType, structType, world, pipelineShader);
contexts.add(ctx);
}
}

View file

@ -1,47 +0,0 @@
package com.jozufozu.flywheel.backend.instancing.compile;
import static org.lwjgl.opengl.GL11.GL_TRUE;
import static org.lwjgl.opengl.GL20.GL_LINK_STATUS;
import static org.lwjgl.opengl.GL20.glAttachShader;
import static org.lwjgl.opengl.GL20.glCreateProgram;
import static org.lwjgl.opengl.GL20.glGetProgramInfoLog;
import static org.lwjgl.opengl.GL20.glGetProgrami;
import static org.lwjgl.opengl.GL20.glLinkProgram;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.gl.shader.GlShader;
@Deprecated
public class ProgramAssembler {
private final int program;
public ProgramAssembler() {
this.program = glCreateProgram();
}
/**
* Links the attached shaders to this program.
*/
public int link() {
glLinkProgram(this.program);
String log = glGetProgramInfoLog(this.program);
if (!log.isEmpty()) {
Backend.LOGGER.debug("Program link log: " + log);
}
int result = glGetProgrami(this.program, GL_LINK_STATUS);
if (result != GL_TRUE) {
throw new RuntimeException("Shader program linking failed, see log for details");
}
return program;
}
public ProgramAssembler attachShader(GlShader glShader) {
glAttachShader(this.program, glShader.handle());
return this;
}
}

View file

@ -0,0 +1,38 @@
package com.jozufozu.flywheel.backend.instancing.compile;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.function.Consumer;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.core.SourceComponent;
public class RecursiveIncluder implements Includer {
public static final RecursiveIncluder INSTANCE = new RecursiveIncluder();
private RecursiveIncluder() {
}
@Override
public void expand(ImmutableList<SourceComponent> rootSources, Consumer<SourceComponent> out) {
var included = depthFirstInclude(rootSources);
included.forEach(out);
rootSources.forEach(out);
}
private static LinkedHashSet<SourceComponent> depthFirstInclude(ImmutableList<SourceComponent> root) {
var included = new LinkedHashSet<SourceComponent>(); // linked to preserve order
for (var component : root) {
recursiveDepthFirstInclude(included, component);
}
return included;
}
private static void recursiveDepthFirstInclude(Set<SourceComponent> included, SourceComponent component) {
for (var include : component.included()) {
recursiveDepthFirstInclude(included, include);
}
included.addAll(component.included());
}
}

View file

@ -1,10 +1,8 @@
package com.jozufozu.flywheel.backend.instancing.compile; package com.jozufozu.flywheel.backend.instancing.compile;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer; import java.util.function.Consumer;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -15,13 +13,18 @@ import com.jozufozu.flywheel.backend.gl.GLSLVersion;
import com.jozufozu.flywheel.backend.gl.shader.GlShader; import com.jozufozu.flywheel.backend.gl.shader.GlShader;
import com.jozufozu.flywheel.backend.gl.shader.ShaderType; import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
import com.jozufozu.flywheel.core.SourceComponent; import com.jozufozu.flywheel.core.SourceComponent;
import com.jozufozu.flywheel.util.FlwUtil;
public class ShaderCompiler { public class ShaderCompiler {
private final Map<ShaderKey, CompilationResult> shaderCache = new HashMap<>(); private final Map<ShaderKey, CompilationResult> shaderCache = new HashMap<>();
private final Consumer<FailedCompilation> errorConsumer; private final Consumer<FailedCompilation> errorConsumer;
private final CompilationFactory factory;
private final Includer includer;
public ShaderCompiler(Consumer<FailedCompilation> errorConsumer) { public ShaderCompiler(Consumer<FailedCompilation> errorConsumer, CompilationFactory factory, Includer includer) {
this.errorConsumer = errorConsumer; this.errorConsumer = errorConsumer;
this.factory = factory;
this.includer = includer;
} }
public int shaderCount() { public int shaderCount() {
@ -36,7 +39,7 @@ public class ShaderCompiler {
return cached.unwrap(); return cached.unwrap();
} }
CompilationResult out = compileUncached(glslVersion, shaderType, sourceComponents); CompilationResult out = compileUncached(factory.create(glslVersion, shaderType), sourceComponents);
shaderCache.put(key, out); shaderCache.put(key, out);
return unwrapAndReportError(out); return unwrapAndReportError(out);
} }
@ -60,40 +63,51 @@ public class ShaderCompiler {
} }
@NotNull @NotNull
private static CompilationResult compileUncached(GLSLVersion glslVersion, ShaderType shaderType, ImmutableList<SourceComponent> sourceComponents) { private CompilationResult compileUncached(Compilation ctx, ImmutableList<SourceComponent> sourceComponents) {
var ctx = new Compilation(glslVersion, shaderType);
ctx.enableExtension("GL_ARB_explicit_attrib_location"); ctx.enableExtension("GL_ARB_explicit_attrib_location");
ctx.enableExtension("GL_ARB_conservative_depth"); ctx.enableExtension("GL_ARB_conservative_depth");
for (var include : depthFirstInclude(sourceComponents)) { includer.expand(sourceComponents, ctx::appendComponent);
ctx.appendComponent(include);
}
for (var component : sourceComponents) {
ctx.appendComponent(component);
ctx.addComponentName(component.name());
}
return ctx.compile(); return ctx.compile();
} }
private static Set<SourceComponent> depthFirstInclude(ImmutableList<SourceComponent> root) {
var included = new LinkedHashSet<SourceComponent>(); // linked to preserve order
for (var component : root) {
recursiveDepthFirstInclude(included, component);
}
return included;
}
private static void recursiveDepthFirstInclude(Set<SourceComponent> included, SourceComponent component) {
for (var include : component.included()) {
recursiveDepthFirstInclude(included, include);
}
included.addAll(component.included());
}
private record ShaderKey(GLSLVersion glslVersion, ShaderType shaderType, private record ShaderKey(GLSLVersion glslVersion, ShaderType shaderType,
ImmutableList<SourceComponent> sourceComponents) { ImmutableList<SourceComponent> sourceComponents) {
} }
public static Builder builder() {
return new Builder();
}
@FunctionalInterface
public interface CompilationFactory {
Compilation create(GLSLVersion version, ShaderType shaderType);
}
public static class Builder {
private Consumer<FailedCompilation> errorConsumer = FlwUtil::noop;
private CompilationFactory factory = Compilation::new;
private Includer includer = RecursiveIncluder.INSTANCE;
public Builder errorConsumer(Consumer<FailedCompilation> errorConsumer) {
this.errorConsumer = errorConsumer;
return this;
}
public Builder compilationFactory(CompilationFactory factory) {
this.factory = factory;
return this;
}
public Builder includer(Includer includer) {
this.includer = includer;
return this;
}
public ShaderCompiler build() {
return new ShaderCompiler(errorConsumer, factory, includer);
}
}
} }

View file

@ -7,7 +7,6 @@ import org.jetbrains.annotations.NotNull;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.core.source.span.CharPos; import com.jozufozu.flywheel.core.source.span.CharPos;
import com.jozufozu.flywheel.util.StringUtil;
import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList; import it.unimi.dsi.fastutil.ints.IntList;
@ -102,7 +101,8 @@ public class SourceLines implements CharSequence {
int start = lines.getInt(i - 1); int start = lines.getInt(i - 1);
int end = lines.getInt(i); int end = lines.getInt(i);
builder.add(StringUtil.trimEnd(source.substring(start, end))); builder.add(source.substring(start, end)
.stripTrailing());
} }
return builder.build(); return builder.build();

View file

@ -11,6 +11,10 @@ public class GlslBuilder {
add(new Define(name, value)); add(new Define(name, value));
} }
public void undef(String key) {
add(new Undef(key));
}
public GlslStruct struct() { public GlslStruct struct() {
return add(new GlslStruct()); return add(new GlslStruct());
} }
@ -32,6 +36,10 @@ public class GlslBuilder {
elements.add(Separators.BLANK_LINE); elements.add(Separators.BLANK_LINE);
} }
public void _addRaw(String sourceString) {
elements.add(() -> sourceString);
}
public String build() { public String build() {
return elements.stream() return elements.stream()
.map(Declaration::prettyPrint) .map(Declaration::prettyPrint)
@ -65,4 +73,11 @@ public class GlslBuilder {
} }
} }
public record Undef(String name) implements Declaration {
@Override
public String prettyPrint() {
return "#undef " + name;
}
}
} }

View file

@ -22,18 +22,16 @@ public class FogProvider implements UniformProvider {
} }
@Override @Override
public ActiveUniformProvider activate(long ptr, Notifier notifier) { public ActiveUniformProvider activate(long ptr) {
return new Active(ptr, notifier); return new Active(ptr);
} }
public static class Active implements ActiveUniformProvider { public static class Active implements ActiveUniformProvider {
private final long ptr; private final long ptr;
private final Notifier notifier;
public Active(long ptr, Notifier notifier) { public Active(long ptr) {
this.ptr = ptr; this.ptr = ptr;
this.notifier = notifier;
} }
@Override @Override
@ -41,9 +39,9 @@ public class FogProvider implements UniformProvider {
} }
@Override @Override
public void poll() { public boolean poll() {
if (!FOG_UPDATE) { if (!FOG_UPDATE) {
return; return false;
} }
var color = RenderSystem.getShaderFogColor(); var color = RenderSystem.getShaderFogColor();
@ -57,9 +55,9 @@ public class FogProvider implements UniformProvider {
MemoryUtil.memPutInt(ptr + 24, RenderSystem.getShaderFogShape() MemoryUtil.memPutInt(ptr + 24, RenderSystem.getShaderFogShape()
.getIndex()); .getIndex());
notifier.signalChanged();
FOG_UPDATE = false; FOG_UPDATE = false;
return true;
} }
} }
} }

View file

@ -31,18 +31,17 @@ public class FrustumProvider implements UniformProvider {
} }
@Override @Override
public ActiveUniformProvider activate(long ptr, Notifier notifier) { public ActiveUniformProvider activate(long ptr) {
return new Active(ptr, notifier); return new Active(ptr);
} }
static class Active implements ActiveUniformProvider, Consumer<BeginFrameEvent> { static class Active implements ActiveUniformProvider, Consumer<BeginFrameEvent> {
private final long ptr; private final long ptr;
private final Notifier notifier; private boolean dirty = true;
public Active(long ptr, Notifier notifier) { public Active(long ptr) {
this.ptr = ptr; this.ptr = ptr;
this.notifier = notifier;
MinecraftForge.EVENT_BUS.addListener(this); MinecraftForge.EVENT_BUS.addListener(this);
} }
@ -52,8 +51,12 @@ public class FrustumProvider implements UniformProvider {
} }
@Override @Override
public void poll() { public boolean poll() {
if (dirty) {
dirty = false;
return true;
}
return false;
} }
@Override @Override
@ -78,7 +81,7 @@ public class FrustumProvider implements UniformProvider {
shiftedCuller.getJozuPackedPlanes(ptr); shiftedCuller.getJozuPackedPlanes(ptr);
notifier.signalChanged(); dirty = true;
CAPTURE = false; CAPTURE = false;
} }
} }

View file

@ -22,7 +22,7 @@ public class UniformBuffer {
private static final boolean PO2_ALIGNMENT = RenderMath.isPowerOf2(OFFSET_ALIGNMENT); private static final boolean PO2_ALIGNMENT = RenderMath.isPowerOf2(OFFSET_ALIGNMENT);
private static UniformBuffer instance; private static UniformBuffer instance;
private final List<Allocated> allocatedProviders; private final AllocatedProviderSet providerSet;
public static UniformBuffer getInstance() { public static UniformBuffer getInstance() {
if (instance == null) { if (instance == null) {
@ -32,50 +32,19 @@ public class UniformBuffer {
} }
private final GlBuffer buffer; private final GlBuffer buffer;
private final MemoryBlock data;
private final BitSet changedBytes;
private UniformBuffer() { private UniformBuffer() {
buffer = new GlBuffer(GlBufferType.UNIFORM_BUFFER); buffer = new GlBuffer(GlBufferType.UNIFORM_BUFFER);
providerSet = new AllocatedProviderSet(ComponentRegistry.getAllUniformProviders());
Collection<UniformProvider> providers = ComponentRegistry.getAllUniformProviders();
var builder = ImmutableList.<Allocated>builder();
int totalBytes = 0;
int index = 0;
for (UniformProvider provider : providers) {
int size = align16(provider.byteSize());
builder.add(new Allocated(provider, totalBytes, size, index));
totalBytes = alignUniformBuffer(totalBytes + size);
index++;
}
allocatedProviders = builder.build();
data = MemoryBlock.mallocTracked(totalBytes);
changedBytes = new BitSet(totalBytes);
for (Allocated p : allocatedProviders) {
p.updatePtr(data);
}
} }
public void sync() { public void sync() {
allocatedProviders.forEach(Allocated::pollActive); providerSet.sync();
if (changedBytes.isEmpty()) {
return;
}
// TODO: upload only changed bytes buffer.upload(providerSet.data);
changedBytes.clear();
buffer.upload(data);
int handle = buffer.handle(); int handle = buffer.handle();
for (Allocated p : allocatedProviders) { for (Allocated p : providerSet.allocatedProviders) {
GL32.glBindBufferRange(GL32.GL_UNIFORM_BUFFER, p.index, handle, p.offset, p.size); GL32.glBindBufferRange(GL32.GL_UNIFORM_BUFFER, p.index, handle, p.offset, p.size);
} }
} }
@ -93,7 +62,7 @@ public class UniformBuffer {
return (numToRound + 16 - 1) & -16; return (numToRound + 16 - 1) & -16;
} }
private class Allocated implements UniformProvider.Notifier { private static class Allocated {
private final UniformProvider provider; private final UniformProvider provider;
private final int offset; private final int offset;
private final int size; private final int size;
@ -107,20 +76,11 @@ public class UniformBuffer {
this.index = index; this.index = index;
} }
@Override
public void signalChanged() {
changedBytes.set(offset, offset + size);
}
private void updatePtr(MemoryBlock bufferBase) { private void updatePtr(MemoryBlock bufferBase) {
if (activeProvider != null) { if (activeProvider != null) {
activeProvider.delete(); activeProvider.delete();
} }
activeProvider = provider.activate(bufferBase.ptr() + offset, this); activeProvider = provider.activate(bufferBase.ptr() + offset);
}
public UniformProvider provider() {
return provider;
} }
public int offset() { public int offset() {
@ -135,9 +95,46 @@ public class UniformBuffer {
return index; return index;
} }
public void pollActive() { public boolean maybePoll() {
if (activeProvider != null) { return activeProvider != null && activeProvider.poll();
activeProvider.poll(); }
}
private static class AllocatedProviderSet {
private final List<Allocated> allocatedProviders;
private final MemoryBlock data;
private final BitSet changedBytes;
private AllocatedProviderSet(final Collection<UniformProvider> providers) {
var builder = ImmutableList.<Allocated>builder();
int totalBytes = 0;
int index = 0;
for (UniformProvider provider : providers) {
int size = align16(provider.byteSize());
builder.add(new Allocated(provider, totalBytes, size, index));
totalBytes = alignUniformBuffer(totalBytes + size);
index++;
}
allocatedProviders = builder.build();
data = MemoryBlock.mallocTracked(totalBytes);
changedBytes = new BitSet(totalBytes);
for (Allocated p : allocatedProviders) {
p.updatePtr(data);
}
}
public void sync() {
for (Allocated p : allocatedProviders) {
if (p.maybePoll()) {
changedBytes.set(p.offset(), p.offset() + p.size());
}
} }
} }
} }

View file

@ -32,17 +32,16 @@ public class ViewProvider implements UniformProvider {
} }
@Override @Override
public ActiveUniformProvider activate(long ptr, Notifier notifier) { public ActiveUniformProvider activate(long ptr) {
return new Active(ptr, notifier); return new Active(ptr);
} }
public static class Active implements ActiveUniformProvider, Consumer<BeginFrameEvent> { public static class Active implements ActiveUniformProvider, Consumer<BeginFrameEvent> {
private final long ptr; private final long ptr;
private final Notifier notifier; private boolean dirty = true;
public Active(long ptr, Notifier notifier) { public Active(long ptr) {
this.ptr = ptr; this.ptr = ptr;
this.notifier = notifier;
MinecraftForge.EVENT_BUS.addListener(this); MinecraftForge.EVENT_BUS.addListener(this);
} }
@ -52,7 +51,12 @@ public class ViewProvider implements UniformProvider {
} }
@Override @Override
public void poll() { public boolean poll() {
if (dirty) {
dirty = false;
return true;
}
return false;
} }
@Override @Override
@ -85,7 +89,7 @@ public class ViewProvider implements UniformProvider {
MemoryUtil.memPutFloat(ptr + 72, camZ); MemoryUtil.memPutFloat(ptr + 72, camZ);
MemoryUtil.memPutInt(ptr + 76, constantAmbientLight); MemoryUtil.memPutInt(ptr + 76, constantAmbientLight);
notifier.signalChanged(); dirty = true;
} }
} }
} }

View file

@ -70,6 +70,11 @@ public class FlwUtil {
} }
public static <R> Stream<R> mapValues(Map<?, R> map) { public static <R> Stream<R> mapValues(Map<?, R> map) {
return map.values().stream(); return map.values()
.stream();
}
public static <T> void noop(T object) {
// noop
} }
} }

View file

@ -64,13 +64,20 @@ public class StringUtil {
.collect(Collectors.joining(", ")) + ')'; .collect(Collectors.joining(", ")) + ')';
} }
public static String trimEnd(String value) { public static String trimPrefix(String s, String prefix) {
int len = value.length(); if (s.startsWith(prefix)) {
int st = 0; return s.substring(prefix.length());
while ((st < len) && Character.isWhitespace(value.charAt(len - 1))) { } else {
len--; return s;
}
}
public static String trimSuffix(String s, String prefix) {
if (s.endsWith(prefix)) {
return s.substring(0, s.length() - prefix.length());
} else {
return s;
} }
return value.substring(0, len);
} }
/** /**

View file

@ -1,4 +1,6 @@
// TODO: Transform uniform shaders // TODO: inject FLW_UNIFORM_BINDING definitions
vec4 flw_fogColor; layout(std140, binding = FLW_UNIFORM_BINDING) uniform flw_fog {
vec2 flw_fogRange; vec4 flw_fogColor;
int flw_fogShape; vec2 flw_fogRange;
int flw_fogShape;
};

View file

@ -9,4 +9,6 @@ struct FLWPackedPlanes {
vec2 zW; // <nz.w, pz.w> vec2 zW; // <nz.w, pz.w>
}; };
FLWPackedPlanes flw_planes; layout(std140, binding = FLW_UNIFORM_BINDING) uniform FLWFrustum {
FLWPackedPlanes flw_planes;
};

View file

@ -1,3 +1,5 @@
mat4 flw_viewProjection; layout(std140, binding = FLW_UNIFORM_BINDING) uniform flw_view {
vec4 flw_cameraPos; mat4 flw_viewProjection;
int flw_constantAmbientLight; vec4 flw_cameraPos;
int flw_constantAmbientLight;
};