Building shade

- Use builder pattern for defining shader compiler flows
  - If only java had type inference extending to builders :whywheel:
- Support glsl 330 on instancing again
  - Set uniform block binding at link time
  - Remove associated glsl builder code
  - Use explicit uint literals in material adapter switch cases
  - No need to enable GL_ARB_explicit_attrib_location
This commit is contained in:
Jozufozu 2023-05-18 00:03:53 -07:00
parent 6e6779093a
commit 863958fb00
20 changed files with 276 additions and 256 deletions

View file

@ -1,5 +1,7 @@
package com.jozufozu.flywheel.api.context;
import org.jetbrains.annotations.NotNull;
import com.jozufozu.flywheel.api.registry.Registry;
import com.jozufozu.flywheel.gl.shader.GlProgram;
import com.jozufozu.flywheel.impl.RegistryImpl;
@ -9,7 +11,7 @@ import net.minecraft.resources.ResourceLocation;
public interface Context {
static Registry<Context> REGISTRY = RegistryImpl.create();
void onProgramLink(GlProgram program);
void onProgramLink(@NotNull GlProgram program);
ResourceLocation vertexShader();

View file

@ -13,30 +13,29 @@ import com.jozufozu.flywheel.backend.compile.core.ShaderCompiler;
import com.jozufozu.flywheel.gl.shader.GlProgram;
import com.jozufozu.flywheel.glsl.ShaderSources;
public abstract class AbstractCompiler<K> {
protected final SourceLoader sourceLoader;
protected final ShaderCompiler shaderCompiler;
protected final ProgramLinker programLinker;
public class CompilationHarness<K> {
private final KeyCompiler<K> compiler;
private final SourceLoader sourceLoader;
private final ShaderCompiler shaderCompiler;
private final ProgramLinker programLinker;
private final ImmutableList<K> keys;
private final CompilerStats stats = new CompilerStats();
public AbstractCompiler(ShaderSources sources, ImmutableList<K> keys) {
public CompilationHarness(ShaderSources sources, ImmutableList<K> keys, KeyCompiler<K> compiler) {
this.keys = keys;
this.compiler = compiler;
sourceLoader = new SourceLoader(sources, stats);
shaderCompiler = new ShaderCompiler(stats);
programLinker = new ProgramLinker(stats);
}
@Nullable
protected abstract GlProgram compile(K key);
@Nullable
public Map<K, GlProgram> compileAndReportErrors() {
stats.start();
Map<K, GlProgram> out = new HashMap<>();
for (var key : keys) {
GlProgram glProgram = compile(key);
GlProgram glProgram = compiler.compile(key, sourceLoader, shaderCompiler, programLinker);
if (out != null && glProgram != null) {
out.put(key, glProgram);
} else {
@ -56,4 +55,8 @@ public abstract class AbstractCompiler<K> {
public void delete() {
shaderCompiler.delete();
}
public interface KeyCompiler<K> {
@Nullable GlProgram compile(K key, SourceLoader loader, ShaderCompiler shaderCompiler, ProgramLinker programLinker);
}
}

View file

@ -0,0 +1,132 @@
package com.jozufozu.flywheel.backend.compile;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import org.jetbrains.annotations.Nullable;
import com.jozufozu.flywheel.backend.compile.core.ProgramLinker;
import com.jozufozu.flywheel.backend.compile.core.ShaderCompiler;
import com.jozufozu.flywheel.gl.shader.GlProgram;
import com.jozufozu.flywheel.gl.shader.GlShader;
import com.jozufozu.flywheel.gl.shader.ShaderType;
import com.jozufozu.flywheel.glsl.GLSLVersion;
import com.jozufozu.flywheel.glsl.SourceComponent;
import com.jozufozu.flywheel.util.NotNullFunction;
import net.minecraft.resources.ResourceLocation;
public class Compile {
public static <K> ShaderCompilerBuilder<K> shader(GLSLVersion glslVersion, ShaderType shaderType) {
return new ShaderCompilerBuilder<>(glslVersion, shaderType);
}
public static <K> ProgramLinkBuilder<K> program() {
return new ProgramLinkBuilder<>();
}
public static class ProgramLinkBuilder<K> implements CompilationHarness.KeyCompiler<K> {
private final Map<ShaderType, ShaderCompilerBuilder<K>> compilers = new EnumMap<>(ShaderType.class);
private BiConsumer<K, GlProgram> onLink = (k, p) -> {
};
public ProgramLinkBuilder<K> link(ShaderCompilerBuilder<K> compilerBuilder) {
if (compilers.containsKey(compilerBuilder.shaderType)) {
throw new IllegalArgumentException("Duplicate shader type: " + compilerBuilder.shaderType);
}
compilers.put(compilerBuilder.shaderType, compilerBuilder);
return this;
}
public ProgramLinkBuilder<K> then(BiConsumer<K, GlProgram> onLink) {
this.onLink = onLink;
return this;
}
@Override
@Nullable
public GlProgram compile(K key, SourceLoader loader, ShaderCompiler shaderCompiler, ProgramLinker programLinker) {
if (compilers.isEmpty()) {
throw new IllegalStateException("No shader compilers were added!");
}
List<GlShader> shaders = new ArrayList<>();
boolean ok = true;
for (ShaderCompilerBuilder<K> compiler : compilers.values()) {
var shader = compiler.compile(key, shaderCompiler, loader);
if (shader == null) {
ok = false;
}
shaders.add(shader);
}
if (!ok) {
return null;
}
var out = programLinker.link(shaders);
if (out != null) {
onLink.accept(key, out);
}
return out;
}
}
public static class ShaderCompilerBuilder<K> {
private final GLSLVersion glslVersion;
private final ShaderType shaderType;
private final List<BiFunction<K, SourceLoader, SourceComponent>> fetchers = new ArrayList<>();
public ShaderCompilerBuilder(GLSLVersion glslVersion, ShaderType shaderType) {
this.glslVersion = glslVersion;
this.shaderType = shaderType;
}
public ShaderCompilerBuilder<K> with(BiFunction<K, SourceLoader, SourceComponent> fetch) {
fetchers.add(fetch);
return this;
}
public ShaderCompilerBuilder<K> withComponent(SourceComponent component) {
return withComponent($ -> component);
}
public ShaderCompilerBuilder<K> withComponent(NotNullFunction<K, SourceComponent> sourceFetcher) {
return with((key, $) -> sourceFetcher.apply(key));
}
public ShaderCompilerBuilder<K> withResource(NotNullFunction<K, ResourceLocation> sourceFetcher) {
return with((key, loader) -> loader.find(sourceFetcher.apply(key)));
}
public ShaderCompilerBuilder<K> withResource(ResourceLocation resourceLocation) {
return withResource($ -> resourceLocation);
}
@Nullable
private GlShader compile(K key, ShaderCompiler compiler, SourceLoader loader) {
var components = new ArrayList<SourceComponent>();
boolean ok = true;
for (var fetcher : fetchers) {
SourceComponent apply = fetcher.apply(key, loader);
if (apply == null) {
ok = false;
}
components.add(apply);
}
if (!ok) {
return null;
}
return compiler.compile(glslVersion, shaderType, components);
}
}
}

View file

@ -1,59 +0,0 @@
package com.jozufozu.flywheel.backend.compile;
import org.jetbrains.annotations.Nullable;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.api.instance.InstanceType;
import com.jozufozu.flywheel.backend.compile.component.IndirectComponent;
import com.jozufozu.flywheel.backend.compile.component.UniformComponent;
import com.jozufozu.flywheel.gl.shader.GlProgram;
import com.jozufozu.flywheel.gl.shader.ShaderType;
import com.jozufozu.flywheel.glsl.GLSLVersion;
import com.jozufozu.flywheel.glsl.ShaderSources;
import com.jozufozu.flywheel.glsl.SourceFile;
import net.minecraft.resources.ResourceLocation;
public class CullingCompiler extends AbstractCompiler<InstanceType<?>> {
private final UniformComponent uniformComponent;
private final SourceFile pipelineCompute;
public static CullingCompiler create(SourceLoader sourceLoader, ImmutableList<InstanceType<?>> keys, UniformComponent uniformComponent) {
var sourceFile = sourceLoader.find(Files.INDIRECT_CULL);
return new CullingCompiler(sourceLoader.sources, keys, uniformComponent, sourceFile);
}
private CullingCompiler(ShaderSources sources, ImmutableList<InstanceType<?>> keys, UniformComponent uniformComponent, SourceFile pipeline) {
super(sources, keys);
this.uniformComponent = uniformComponent;
this.pipelineCompute = pipeline;
}
@Nullable
@Override
protected GlProgram compile(InstanceType<?> key) {
var instanceAssembly = IndirectComponent.create(key);
ResourceLocation rl = key.instanceShader();
var instance = sourceLoader.find(rl);
if (instance == null || uniformComponent == null || pipelineCompute == null) {
return null;
}
var computeComponents = ImmutableList.of(uniformComponent, instanceAssembly, instance, pipelineCompute);
var compute = shaderCompiler.compile(GLSLVersion.V460, ShaderType.COMPUTE, computeComponents);
if (compute == null) {
return null;
}
return programLinker.link(compute);
}
private static final class Files {
public static final ResourceLocation INDIRECT_CULL = Flywheel.rl("internal/indirect_cull.glsl");
}
}

View file

@ -31,8 +31,8 @@ public class FlwPrograms {
var vertexMaterialComponent = createVertexMaterialComponent(loadChecker);
var fragmentMaterialComponent = createFragmentMaterialComponent(loadChecker);
InstancingPrograms.reload(loadChecker, pipelineKeys, uniformComponent, vertexMaterialComponent, fragmentMaterialComponent);
IndirectPrograms.reload(loadChecker, pipelineKeys, uniformComponent, vertexMaterialComponent, fragmentMaterialComponent);
InstancingPrograms.reload(sources, pipelineKeys, uniformComponent, vertexMaterialComponent, fragmentMaterialComponent);
IndirectPrograms.reload(sources, pipelineKeys, uniformComponent, vertexMaterialComponent, fragmentMaterialComponent);
if (preLoadStats.errored()) {
Flywheel.LOGGER.error(preLoadStats.generateErrorLog());
@ -47,7 +47,7 @@ public class FlwPrograms {
.returnType("bool")
.name("flw_discardPredicate")
.arg("vec4", "color")
.build(), GlslExpr.literal(false))
.build(), GlslExpr.boolLiteral(false))
.adapt(FnSignature.create()
.returnType("vec4")
.name("flw_fogFilter")

View file

@ -9,9 +9,15 @@ import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.api.context.Context;
import com.jozufozu.flywheel.api.instance.InstanceType;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.compile.component.IndirectComponent;
import com.jozufozu.flywheel.backend.compile.component.MaterialAdapterComponent;
import com.jozufozu.flywheel.backend.compile.component.UniformComponent;
import com.jozufozu.flywheel.gl.shader.GlProgram;
import com.jozufozu.flywheel.gl.shader.ShaderType;
import com.jozufozu.flywheel.glsl.GLSLVersion;
import com.jozufozu.flywheel.glsl.ShaderSources;
import net.minecraft.resources.ResourceLocation;
public class IndirectPrograms {
public static IndirectPrograms instance;
@ -23,10 +29,10 @@ public class IndirectPrograms {
this.culling = culling;
}
static void reload(SourceLoader loadChecker, ImmutableList<PipelineProgramKey> pipelineKeys, UniformComponent uniformComponent, MaterialAdapterComponent vertexMaterialComponent, MaterialAdapterComponent fragmentMaterialComponent) {
static void reload(ShaderSources sources, ImmutableList<PipelineProgramKey> pipelineKeys, UniformComponent uniformComponent, MaterialAdapterComponent vertexMaterialComponent, MaterialAdapterComponent fragmentMaterialComponent) {
_delete();
var pipelineCompiler = PipelineCompiler.create(loadChecker, Pipelines.INDIRECT, pipelineKeys, uniformComponent, vertexMaterialComponent, fragmentMaterialComponent);
var cullingCompiler = CullingCompiler.create(loadChecker, createCullingKeys(), uniformComponent);
var pipelineCompiler = PipelineCompiler.create(sources, Pipelines.INDIRECT, pipelineKeys, uniformComponent, vertexMaterialComponent, fragmentMaterialComponent);
var cullingCompiler = createCullingCompiler(uniformComponent, sources);
try {
var pipelineResult = pipelineCompiler.compileAndReportErrors();
@ -66,6 +72,18 @@ public class IndirectPrograms {
}
}
private static CompilationHarness<InstanceType<?>> createCullingCompiler(UniformComponent uniformComponent, ShaderSources sources) {
return new CompilationHarness<>(sources, createCullingKeys(), Compile.<InstanceType<?>>program()
.link(Compile.<InstanceType<?>>shader(GLSLVersion.V460, ShaderType.COMPUTE)
.withComponent(uniformComponent)
.withComponent(IndirectComponent::create)
.withResource(InstanceType::instanceShader)
.withResource(Files.INDIRECT_CULL))
.then((InstanceType<?> key, GlProgram program) -> {
program.setUniformBlockBinding("FLWUniforms", 0);
}));
}
public GlProgram getIndirectProgram(VertexType vertexType, InstanceType<?> instanceType, Context contextShader) {
return pipeline.get(new PipelineProgramKey(vertexType, instanceType, contextShader));
}
@ -80,4 +98,8 @@ public class IndirectPrograms {
culling.values()
.forEach(GlProgram::delete);
}
private static final class Files {
public static final ResourceLocation INDIRECT_CULL = Flywheel.rl("internal/indirect_cull.glsl");
}
}

View file

@ -12,6 +12,7 @@ import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.compile.component.MaterialAdapterComponent;
import com.jozufozu.flywheel.backend.compile.component.UniformComponent;
import com.jozufozu.flywheel.gl.shader.GlProgram;
import com.jozufozu.flywheel.glsl.ShaderSources;
public class InstancingPrograms {
static InstancingPrograms instance;
@ -21,9 +22,9 @@ public class InstancingPrograms {
this.pipeline = pipeline;
}
static void reload(SourceLoader loadChecker, ImmutableList<PipelineProgramKey> pipelineKeys, UniformComponent uniformComponent, MaterialAdapterComponent vertexMaterialComponent, MaterialAdapterComponent fragmentMaterialComponent) {
static void reload(ShaderSources sources, ImmutableList<PipelineProgramKey> pipelineKeys, UniformComponent uniformComponent, MaterialAdapterComponent vertexMaterialComponent, MaterialAdapterComponent fragmentMaterialComponent) {
_delete();
var instancingCompiler = PipelineCompiler.create(loadChecker, Pipelines.INSTANCED_ARRAYS, pipelineKeys, uniformComponent, vertexMaterialComponent, fragmentMaterialComponent);
var instancingCompiler = PipelineCompiler.create(sources, Pipelines.INSTANCED_ARRAYS, pipelineKeys, uniformComponent, vertexMaterialComponent, fragmentMaterialComponent);
try {
var result = instancingCompiler.compileAndReportErrors();

View file

@ -1,158 +1,37 @@
package com.jozufozu.flywheel.backend.compile;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import org.jetbrains.annotations.Nullable;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.backend.compile.component.MaterialAdapterComponent;
import com.jozufozu.flywheel.backend.compile.component.UniformComponent;
import com.jozufozu.flywheel.gl.shader.GlProgram;
import com.jozufozu.flywheel.gl.shader.GlShader;
import com.jozufozu.flywheel.gl.shader.ShaderType;
import com.jozufozu.flywheel.glsl.ShaderSources;
import com.jozufozu.flywheel.glsl.SourceComponent;
import net.minecraft.resources.ResourceLocation;
public class PipelineCompiler extends AbstractCompiler<PipelineProgramKey> {
private final Pipeline pipeline;
private final List<SourceComponent> vertexPrelude = new ArrayList<>();
private final List<SourceComponent> vertexPostlude = new ArrayList<>();
private final List<SourceComponent> fragmentPrelude = new ArrayList<>();
private final List<SourceComponent> fragmentPostlude = new ArrayList<>();
public PipelineCompiler(ShaderSources sources, ImmutableList<PipelineProgramKey> keys, Pipeline pipeline) {
super(sources, keys);
this.pipeline = pipeline;
}
static PipelineCompiler create(SourceLoader sourceLoader, Pipeline pipeline, ImmutableList<PipelineProgramKey> pipelineKeys, UniformComponent uniformComponent, MaterialAdapterComponent vertexMaterialComponent, MaterialAdapterComponent fragmentMaterialComponent) {
var fragmentPipeline = sourceLoader.find(pipeline.fragmentShader());
var vertexPipeline = sourceLoader.find(pipeline.vertexShader());
return new PipelineCompiler(sourceLoader.sources, pipelineKeys, pipeline).addPrelude(uniformComponent)
.addFragmentPrelude(fragmentMaterialComponent)
.addVertexPrelude(vertexMaterialComponent)
.addFragmentPostlude(fragmentPipeline)
.addVertexPostlude(vertexPipeline);
}
public PipelineCompiler addPrelude(SourceComponent component) {
addVertexPrelude(component);
addFragmentPrelude(component);
return this;
}
public PipelineCompiler addVertexPrelude(SourceComponent component) {
vertexPrelude.add(component);
return this;
}
public PipelineCompiler addVertexPostlude(SourceComponent component) {
vertexPostlude.add(component);
return this;
}
public PipelineCompiler addFragmentPrelude(SourceComponent component) {
fragmentPrelude.add(component);
return this;
}
public PipelineCompiler addFragmentPostlude(SourceComponent component) {
fragmentPostlude.add(component);
return this;
}
@Nullable
@Override
protected GlProgram compile(PipelineProgramKey key) {
GlShader vertex = compileVertex(key);
GlShader fragment = compileFragment(key);
if (vertex == null || fragment == null) {
return null;
}
var glProgram = programLinker.link(vertex, fragment);
key.contextShader()
.onProgramLink(glProgram);
return glProgram;
}
@Nullable
private GlShader compileVertex(PipelineProgramKey key) {
var vertexComponents = getVertexComponents(key);
if (vertexComponents == null) {
return null;
}
return shaderCompiler.compile(pipeline.glslVersion(), ShaderType.VERTEX, vertexComponents);
}
@Nullable
private GlShader compileFragment(PipelineProgramKey key) {
var fragmentComponents = getFragmentComponents(key);
if (fragmentComponents == null) {
return null;
}
return shaderCompiler.compile(pipeline.glslVersion(), ShaderType.FRAGMENT, fragmentComponents);
}
@Nullable
private List<SourceComponent> getVertexComponents(PipelineProgramKey key) {
var instanceAssembly = pipeline.assembler()
.assemble(new Pipeline.InstanceAssemblerContext(key.vertexType(), key.instanceType()));
var layout = sourceLoader.find(key.vertexType()
.layoutShader());
var instance = sourceLoader.find(key.instanceType()
.instanceShader());
var context = sourceLoader.find(key.contextShader()
.vertexShader());
if (instanceAssembly == null || layout == null || instance == null || context == null) {
return null;
}
// Check this here to do a full dry-run in case of a preloading error.
if (vertexPrelude.stream()
.anyMatch(Objects::isNull) || vertexPostlude.stream()
.anyMatch(Objects::isNull)) {
return null;
}
return ImmutableList.<SourceComponent>builder()
.addAll(vertexPrelude)
.add(instanceAssembly, layout, instance, context)
.addAll(vertexPostlude)
.build();
}
@Nullable
private List<SourceComponent> getFragmentComponents(PipelineProgramKey key) {
ResourceLocation rl = key.contextShader()
.fragmentShader();
var context = sourceLoader.find(rl);
if (context == null) {
return null;
}
// Check this here to do a full dry-run in case of a preloading error.
if (fragmentPrelude.stream()
.anyMatch(Objects::isNull) || fragmentPostlude.stream()
.anyMatch(Objects::isNull)) {
return null;
}
return ImmutableList.<SourceComponent>builder()
.addAll(fragmentPrelude)
.add(context)
.addAll(fragmentPostlude)
.build();
public class PipelineCompiler {
static CompilationHarness<PipelineProgramKey> create(ShaderSources sources, Pipeline pipeline, ImmutableList<PipelineProgramKey> pipelineKeys, UniformComponent uniformComponent, MaterialAdapterComponent vertexMaterialComponent, MaterialAdapterComponent fragmentMaterialComponent) {
return new CompilationHarness<>(sources, pipelineKeys, Compile.<PipelineProgramKey>program()
.link(Compile.<PipelineProgramKey>shader(pipeline.glslVersion(), ShaderType.VERTEX)
.withComponent(uniformComponent)
.withComponent(vertexMaterialComponent)
.withComponent(key -> pipeline.assembler()
.assemble(new Pipeline.InstanceAssemblerContext(key.vertexType(), key.instanceType())))
.withResource(key -> key.vertexType()
.layoutShader())
.withResource(key -> key.instanceType()
.instanceShader())
.withResource(key -> key.contextShader()
.vertexShader())
.withResource(pipeline.vertexShader()))
.link(Compile.<PipelineProgramKey>shader(pipeline.glslVersion(), ShaderType.FRAGMENT)
.withComponent(uniformComponent)
.withComponent(fragmentMaterialComponent)
.withResource(key -> key.contextShader()
.fragmentShader())
.withResource(pipeline.fragmentShader()))
.then((PipelineProgramKey key, GlProgram program) -> {
key.contextShader()
.onProgramLink(program);
program.setUniformBlockBinding("FLWUniforms", 0);
}));
}
}

View file

@ -9,7 +9,7 @@ import net.minecraft.resources.ResourceLocation;
public final class Pipelines {
public static final Pipeline INSTANCED_ARRAYS = Pipeline.builder()
.glslVersion(GLSLVersion.V420)
.glslVersion(GLSLVersion.V330)
.vertex(Files.INSTANCED_ARRAYS_DRAW)
.fragment(Files.DRAW_FRAGMENT)
.assembler(InstancedArraysComponent::new)

View file

@ -87,7 +87,7 @@ public class MaterialAdapterComponent implements SourceComponent {
block.ret(adaptedCall);
}
sw.intCase(i, block);
sw.uintCase(i, block);
}
if (!isVoid) {

View file

@ -36,7 +36,6 @@ public class UniformComponent implements SourceComponent {
builder.uniformBlock()
.layout("std140")
.binding(0)
.name("FLWUniforms")
.member("flywheel_uniforms", "flywheel");

View file

@ -8,6 +8,8 @@ import static org.lwjgl.opengl.GL20.glGetProgramInfoLog;
import static org.lwjgl.opengl.GL20.glGetProgrami;
import static org.lwjgl.opengl.GL20.glLinkProgram;
import java.util.List;
import org.jetbrains.annotations.Nullable;
import com.jozufozu.flywheel.gl.shader.GlProgram;
@ -21,14 +23,14 @@ public class ProgramLinker {
}
@Nullable
public GlProgram link(GlShader... shaders) {
public GlProgram link(List<GlShader> shaders) {
// this probably doesn't need caching
var linkResult = linkInternal(shaders);
stats.linkResult(linkResult);
return linkResult.unwrap();
}
private LinkResult linkInternal(GlShader... shaders) {
private LinkResult linkInternal(List<GlShader> shaders) {
int handle = glCreateProgram();
for (GlShader shader : shaders) {

View file

@ -32,7 +32,6 @@ public class ShaderCompiler {
}
Compilation ctx = new Compilation(glslVersion, shaderType);
ctx.enableExtension("GL_ARB_explicit_attrib_location");
ctx.enableExtension("GL_ARB_conservative_depth");
expand(sourceComponents, ctx::appendComponent);

View file

@ -0,0 +1,6 @@
@MethodsReturnNonnullByDefault @ParametersAreNonnullByDefault
package com.jozufozu.flywheel.backend.compile;
import javax.annotation.ParametersAreNonnullByDefault;
import net.minecraft.MethodsReturnNonnullByDefault;

View file

@ -1,8 +1,11 @@
package com.jozufozu.flywheel.gl.shader;
import static org.lwjgl.opengl.GL20.glDeleteProgram;
import static org.lwjgl.opengl.GL20.glGetUniformLocation;
import static org.lwjgl.opengl.GL20.glUniform1i;
import static org.lwjgl.opengl.GL31.GL_INVALID_INDEX;
import static org.lwjgl.opengl.GL31.glGetUniformBlockIndex;
import static org.lwjgl.opengl.GL31.glUniformBlockBinding;
import static org.lwjgl.opengl.GL32.glDeleteProgram;
import static org.lwjgl.opengl.GL32.glGetUniformLocation;
import static org.lwjgl.opengl.GL32.glUniform1i;
import org.slf4j.Logger;
@ -55,6 +58,17 @@ public class GlProgram extends GlObject {
}
}
public void setUniformBlockBinding(String name, int binding) {
int index = glGetUniformBlockIndex(handle(), name);
if (index == GL_INVALID_INDEX) {
LOGGER.debug("No active uniform block '{}' exists. Could be unused.", name);
return;
}
glUniformBlockBinding(handle(), index, binding);
}
@Override
protected void deleteInternal(int handle) {
glDeleteProgram(handle);

View file

@ -31,11 +31,15 @@ public interface GlslExpr {
return new FunctionCall0(functionName);
}
static GlslExpr literal(int expr) {
static GlslExpr intLiteral(int expr) {
return new IntLiteral(expr);
}
static GlslExpr literal(boolean expr) {
static GlslExpr uintLiteral(int expr) {
return new UIntLiteral(expr);
}
static GlslExpr boolLiteral(boolean expr) {
return new BoolLiteral(expr);
}
@ -135,6 +139,19 @@ public interface GlslExpr {
}
}
record UIntLiteral(int value) implements GlslExpr {
public UIntLiteral {
if (value < 0) {
throw new IllegalArgumentException("UIntLiteral must be positive");
}
}
@Override
public String prettyPrint() {
return Integer.toString(value) + 'u';
}
}
record BoolLiteral(boolean value) implements GlslExpr {
@Override
public String prettyPrint() {

View file

@ -25,7 +25,11 @@ public class GlslSwitch implements GlslStmt {
}
public void intCase(int expr, GlslBlock block) {
cases.add(Pair.of(GlslExpr.literal(expr), block));
cases.add(Pair.of(GlslExpr.intLiteral(expr), block));
}
public void uintCase(int expr, GlslBlock block) {
cases.add(Pair.of(GlslExpr.uintLiteral(expr), block));
}
public void defaultCase(GlslBlock block) {
@ -35,7 +39,7 @@ public class GlslSwitch implements GlslStmt {
@Override
public String prettyPrint() {
return """
switch (%s) {
switch (%s) {
%s
}""".formatted(on.prettyPrint(), formatCases());
}

View file

@ -8,19 +8,16 @@ import com.jozufozu.flywheel.util.Pair;
import com.jozufozu.flywheel.util.StringUtil;
public class GlslUniformBlock implements GlslBuilder.Declaration {
private String qualifier;
private int binding;
private String name;
private final List<Pair<String, String>> members = new ArrayList<>();
private String name;
private final List<Pair<String, String>> members = new ArrayList<>();
@Override
public String prettyPrint() {
return """
layout(%s, binding = %d) uniform %s {
%s
};""".formatted(qualifier, binding, name, StringUtil.indent(formatMembers(), 4));
return """
layout(%s) uniform %s {
%s
};""".formatted(qualifier, name, StringUtil.indent(formatMembers(), 4));
}
private String formatMembers() {
@ -34,15 +31,10 @@ public class GlslUniformBlock implements GlslBuilder.Declaration {
return this;
}
public GlslUniformBlock binding(int i) {
binding = i;
return this;
}
public GlslUniformBlock name(String name) {
this.name = name;
return this;
}
public GlslUniformBlock name(String name) {
this.name = name;
return this;
}
public GlslUniformBlock member(String typeName, String variableName) {
members.add(Pair.of(typeName, variableName));

View file

@ -4,7 +4,6 @@ import org.jetbrains.annotations.NotNull;
@FunctionalInterface
public interface NonNullSupplier<T> {
@NotNull
T get();
}

View file

@ -0,0 +1,8 @@
package com.jozufozu.flywheel.util;
import org.jetbrains.annotations.NotNull;
@FunctionalInterface
public interface NotNullFunction<T, R> {
@NotNull R apply(T t);
}