mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2025-01-08 13:26:39 +01:00
Pre-processing post-pre-refactor-refactor
- Separate pipeline compiler from compute compiler - Remove Pipeline field from PipelineProgramKey - Share one UniformComponent across compilers - Use AbstractCompiler to deduplicate some shared code - Separate indirect programs from instancing programs - Move compilation tracking information to CompilerStats - Improve compilation result/link result reporting
This commit is contained in:
parent
b30d686785
commit
4c8e174712
18 changed files with 523 additions and 377 deletions
|
@ -0,0 +1,47 @@
|
||||||
|
package com.jozufozu.flywheel.backend.compile;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.jozufozu.flywheel.gl.shader.GlProgram;
|
||||||
|
import com.jozufozu.flywheel.glsl.ShaderSources;
|
||||||
|
|
||||||
|
public abstract class AbstractCompiler<K> {
|
||||||
|
protected final ShaderSources sources;
|
||||||
|
protected final ShaderCompiler shaderCompiler;
|
||||||
|
protected final ProgramLinker programLinker;
|
||||||
|
private final ImmutableList<K> keys;
|
||||||
|
private final CompilerStats stats = new CompilerStats();
|
||||||
|
|
||||||
|
public AbstractCompiler(ShaderSources sources, ImmutableList<K> keys) {
|
||||||
|
this.sources = sources;
|
||||||
|
this.keys = keys;
|
||||||
|
|
||||||
|
shaderCompiler = ShaderCompiler.builder()
|
||||||
|
.build(stats);
|
||||||
|
programLinker = new ProgramLinker(stats);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
protected abstract GlProgram compile(K key);
|
||||||
|
|
||||||
|
public Map<K, GlProgram> compile() {
|
||||||
|
stats.start();
|
||||||
|
Map<K, GlProgram> out = new HashMap<>();
|
||||||
|
for (var key : keys) {
|
||||||
|
GlProgram glProgram = compile(key);
|
||||||
|
if (glProgram != null) {
|
||||||
|
out.put(key, glProgram);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stats.finish();
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void delete() {
|
||||||
|
shaderCompiler.delete();
|
||||||
|
}
|
||||||
|
}
|
|
@ -46,7 +46,7 @@ public class Compilation {
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public CompilationResult compile() {
|
public ShaderResult compile() {
|
||||||
int handle = GL20.glCreateShader(shaderType.glEnum);
|
int handle = GL20.glCreateShader(shaderType.glEnum);
|
||||||
var source = fullSource.toString();
|
var source = fullSource.toString();
|
||||||
|
|
||||||
|
@ -56,13 +56,14 @@ public class Compilation {
|
||||||
var shaderName = buildShaderName();
|
var shaderName = buildShaderName();
|
||||||
dumpSource(source, shaderType.getFileName(shaderName));
|
dumpSource(source, shaderType.getFileName(shaderName));
|
||||||
|
|
||||||
|
var infoLog = GL20.glGetShaderInfoLog(handle);
|
||||||
|
|
||||||
if (compiledSuccessfully(handle)) {
|
if (compiledSuccessfully(handle)) {
|
||||||
return CompilationResult.success(new GlShader(handle, shaderType, shaderName));
|
return ShaderResult.success(new GlShader(handle, shaderType, shaderName), infoLog);
|
||||||
}
|
}
|
||||||
|
|
||||||
var errorLog = GL20.glGetShaderInfoLog(handle);
|
|
||||||
GL20.glDeleteShader(handle);
|
GL20.glDeleteShader(handle);
|
||||||
return CompilationResult.failure(new FailedCompilation(shaderName, files, generatedSource.toString(), errorLog));
|
return ShaderResult.failure(new FailedCompilation(shaderName, files, generatedSource.toString(), infoLog));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void enableExtension(String ext) {
|
public void enableExtension(String ext) {
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.compile;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.gl.shader.GlShader;
|
|
||||||
|
|
||||||
public sealed interface CompilationResult {
|
|
||||||
@Nullable
|
|
||||||
default GlShader unwrap() {
|
|
||||||
if (this instanceof Success s) {
|
|
||||||
return s.shader();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
record Success(GlShader shader) implements CompilationResult {
|
|
||||||
}
|
|
||||||
|
|
||||||
record Failure(FailedCompilation failure) implements CompilationResult {
|
|
||||||
}
|
|
||||||
|
|
||||||
static CompilationResult success(GlShader program) {
|
|
||||||
return new Success(program);
|
|
||||||
}
|
|
||||||
|
|
||||||
static CompilationResult failure(FailedCompilation failure) {
|
|
||||||
return new Failure(failure);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +1,5 @@
|
||||||
package com.jozufozu.flywheel.backend.compile;
|
package com.jozufozu.flywheel.backend.compile;
|
||||||
|
|
||||||
import static org.lwjgl.opengl.GL11.GL_TRUE;
|
|
||||||
import static org.lwjgl.opengl.GL20.GL_LINK_STATUS;
|
|
||||||
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;
|
||||||
|
@ -13,7 +7,6 @@ import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.Flywheel;
|
|
||||||
import com.jozufozu.flywheel.gl.GLSLVersion;
|
import com.jozufozu.flywheel.gl.GLSLVersion;
|
||||||
import com.jozufozu.flywheel.gl.shader.ShaderType;
|
import com.jozufozu.flywheel.gl.shader.ShaderType;
|
||||||
import com.jozufozu.flywheel.glsl.SourceFile;
|
import com.jozufozu.flywheel.glsl.SourceFile;
|
||||||
|
@ -64,25 +57,4 @@ public class CompileUtil {
|
||||||
.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()) {
|
|
||||||
Flywheel.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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
package com.jozufozu.flywheel.backend.compile;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.Flywheel;
|
||||||
|
import com.jozufozu.flywheel.util.StringUtil;
|
||||||
|
|
||||||
|
public class CompilerStats {
|
||||||
|
private long compileStart;
|
||||||
|
|
||||||
|
private final List<FailedCompilation> shaderErrors = new ArrayList<>();
|
||||||
|
private final List<String> programErrors = new ArrayList<>();
|
||||||
|
private boolean errored = false;
|
||||||
|
private int shaderCount = 0;
|
||||||
|
private int programCount = 0;
|
||||||
|
|
||||||
|
public void start() {
|
||||||
|
compileStart = System.nanoTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void finish() {
|
||||||
|
long compileEnd = System.nanoTime();
|
||||||
|
var elapsed = StringUtil.formatTime(compileEnd - compileStart);
|
||||||
|
|
||||||
|
Flywheel.LOGGER.info("Compiled " + shaderCount + " shaders (with " + shaderErrors.size() + " compile errors) " + "and " + programCount + " programs (with " + programErrors.size() + " link errors) in " + elapsed);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: use this to turn off backends
|
||||||
|
public boolean errored() {
|
||||||
|
return errored;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String generateLog() {
|
||||||
|
return String.join("\n", programErrors) + '\n' + shaderErrors.stream()
|
||||||
|
.map(FailedCompilation::getMessage)
|
||||||
|
.collect(Collectors.joining("\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void shaderResult(ShaderResult result) {
|
||||||
|
if (result instanceof ShaderResult.Failure f) {
|
||||||
|
shaderErrors.add(f.failure());
|
||||||
|
errored = true;
|
||||||
|
}
|
||||||
|
shaderCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void linkResult(LinkResult linkResult) {
|
||||||
|
if (linkResult instanceof LinkResult.Failure f) {
|
||||||
|
programErrors.add(f.failure());
|
||||||
|
errored = true;
|
||||||
|
}
|
||||||
|
programCount++;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
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.engine.indirect.IndirectComponent;
|
||||||
|
import com.jozufozu.flywheel.gl.GLSLVersion;
|
||||||
|
import com.jozufozu.flywheel.gl.shader.GlProgram;
|
||||||
|
import com.jozufozu.flywheel.gl.shader.ShaderType;
|
||||||
|
import com.jozufozu.flywheel.glsl.ShaderSources;
|
||||||
|
import com.jozufozu.flywheel.glsl.SourceComponent;
|
||||||
|
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 CullingCompiler(ShaderSources sources, ImmutableList<InstanceType<?>> keys, UniformComponent uniformComponent) {
|
||||||
|
super(sources, keys);
|
||||||
|
|
||||||
|
this.uniformComponent = uniformComponent;
|
||||||
|
pipelineCompute = sources.find(Files.INDIRECT_CULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
protected GlProgram compile(InstanceType<?> key) {
|
||||||
|
var computeComponents = getComputeComponents(key);
|
||||||
|
var compute = shaderCompiler.compile(GLSLVersion.V460, ShaderType.COMPUTE, computeComponents);
|
||||||
|
|
||||||
|
if (compute == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return programLinker.link(compute);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ImmutableList<SourceComponent> getComputeComponents(InstanceType<?> instanceType) {
|
||||||
|
var instanceAssembly = new IndirectComponent(sources, instanceType);
|
||||||
|
var instance = sources.find(instanceType.instanceShader());
|
||||||
|
|
||||||
|
return ImmutableList.of(uniformComponent, instanceAssembly, instance, pipelineCompute);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class Files {
|
||||||
|
public static final ResourceLocation INDIRECT_CULL = Flywheel.rl("internal/indirect_cull.glsl");
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,213 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.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.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
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.api.uniform.ShaderUniforms;
|
|
||||||
import com.jozufozu.flywheel.backend.compile.FlwPrograms.PipelineProgramKey;
|
|
||||||
import com.jozufozu.flywheel.backend.compile.pipeline.Pipeline;
|
|
||||||
import com.jozufozu.flywheel.backend.engine.indirect.IndirectComponent;
|
|
||||||
import com.jozufozu.flywheel.gl.GLSLVersion;
|
|
||||||
import com.jozufozu.flywheel.gl.shader.GlProgram;
|
|
||||||
import com.jozufozu.flywheel.gl.shader.ShaderType;
|
|
||||||
import com.jozufozu.flywheel.glsl.ShaderLoadingException;
|
|
||||||
import com.jozufozu.flywheel.glsl.ShaderSources;
|
|
||||||
import com.jozufozu.flywheel.glsl.SourceComponent;
|
|
||||||
import com.jozufozu.flywheel.glsl.generate.FnSignature;
|
|
||||||
import com.jozufozu.flywheel.glsl.generate.GlslExpr;
|
|
||||||
import com.jozufozu.flywheel.lib.material.MaterialIndices;
|
|
||||||
import com.jozufozu.flywheel.util.StringUtil;
|
|
||||||
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
|
|
||||||
public class FlwCompiler {
|
|
||||||
private final long compileStart = System.nanoTime();
|
|
||||||
|
|
||||||
private final ShaderSources sources;
|
|
||||||
|
|
||||||
private final ImmutableList<PipelineProgramKey> pipelineKeys;
|
|
||||||
private final ImmutableList<InstanceType<?>> cullingKeys;
|
|
||||||
|
|
||||||
private final ShaderCompiler shaderCompiler;
|
|
||||||
private final List<FailedCompilation> errors = new ArrayList<>();
|
|
||||||
|
|
||||||
private final MaterialAdapterComponent vertexMaterialComponent;
|
|
||||||
private final MaterialAdapterComponent fragmentMaterialComponent;
|
|
||||||
private final UniformComponent uniformComponent;
|
|
||||||
|
|
||||||
private final Map<PipelineProgramKey, GlProgram> pipelinePrograms = new HashMap<>();
|
|
||||||
private final Map<InstanceType<?>, GlProgram> cullingPrograms = new HashMap<>();
|
|
||||||
|
|
||||||
public FlwCompiler(ShaderSources sources, ImmutableList<PipelineProgramKey> pipelineKeys, ImmutableList<InstanceType<?>> cullingKeys) {
|
|
||||||
this.sources = sources;
|
|
||||||
|
|
||||||
this.pipelineKeys = pipelineKeys;
|
|
||||||
this.cullingKeys = cullingKeys;
|
|
||||||
|
|
||||||
shaderCompiler = ShaderCompiler.builder()
|
|
||||||
.errorConsumer(errors::add)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
vertexMaterialComponent = MaterialAdapterComponent.builder(Flywheel.rl("vertex_material_adapter"))
|
|
||||||
.materialSources(MaterialIndices.getAllVertexShaders())
|
|
||||||
.adapt(FnSignature.ofVoid("flw_materialVertex"))
|
|
||||||
.switchOn(GlslExpr.variable("_flw_materialVertexID"))
|
|
||||||
.build(sources);
|
|
||||||
fragmentMaterialComponent = MaterialAdapterComponent.builder(Flywheel.rl("fragment_material_adapter"))
|
|
||||||
.materialSources(MaterialIndices.getAllFragmentShaders())
|
|
||||||
.adapt(FnSignature.ofVoid("flw_materialFragment"))
|
|
||||||
.adapt(FnSignature.create()
|
|
||||||
.returnType("bool")
|
|
||||||
.name("flw_discardPredicate")
|
|
||||||
.arg("vec4", "color")
|
|
||||||
.build(), GlslExpr.literal(false))
|
|
||||||
.adapt(FnSignature.create()
|
|
||||||
.returnType("vec4")
|
|
||||||
.name("flw_fogFilter")
|
|
||||||
.arg("vec4", "color")
|
|
||||||
.build(), GlslExpr.variable("color"))
|
|
||||||
.switchOn(GlslExpr.variable("_flw_materialFragmentID"))
|
|
||||||
.build(sources);
|
|
||||||
uniformComponent = UniformComponent.builder(Flywheel.rl("uniforms"))
|
|
||||||
.sources(ShaderUniforms.REGISTRY.getAll()
|
|
||||||
.stream()
|
|
||||||
.map(ShaderUniforms::uniformShader)
|
|
||||||
.toList())
|
|
||||||
.build(sources);
|
|
||||||
}
|
|
||||||
|
|
||||||
public FlwPrograms compile() {
|
|
||||||
doCompilation();
|
|
||||||
|
|
||||||
finish();
|
|
||||||
|
|
||||||
return new FlwPrograms(pipelinePrograms, cullingPrograms);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void doCompilation() {
|
|
||||||
for (var key : pipelineKeys) {
|
|
||||||
GlProgram glProgram = compilePipelineProgram(key);
|
|
||||||
if (glProgram != null) {
|
|
||||||
pipelinePrograms.put(key, glProgram);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var key : cullingKeys) {
|
|
||||||
GlProgram glProgram = compileCullingProgram(key);
|
|
||||||
if (glProgram != null) {
|
|
||||||
cullingPrograms.put(key, glProgram);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static GlProgram link(int... shaders) {
|
|
||||||
var handle = glCreateProgram();
|
|
||||||
for (var shader : shaders) {
|
|
||||||
glAttachShader(handle, shader);
|
|
||||||
}
|
|
||||||
glLinkProgram(handle);
|
|
||||||
CompileUtil.checkLinkLog(handle);
|
|
||||||
return new GlProgram(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private GlProgram compilePipelineProgram(PipelineProgramKey key) {
|
|
||||||
var glslVersion = key.pipelineShader()
|
|
||||||
.glslVersion();
|
|
||||||
|
|
||||||
var vertex = shaderCompiler.compile(glslVersion, ShaderType.VERTEX, getVertexComponents(key));
|
|
||||||
var fragment = shaderCompiler.compile(glslVersion, ShaderType.FRAGMENT, getFragmentComponents(key));
|
|
||||||
|
|
||||||
if (vertex == null || fragment == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var glProgram = link(vertex.handle(), fragment.handle());
|
|
||||||
key.contextShader()
|
|
||||||
.onProgramLink(glProgram);
|
|
||||||
return glProgram;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ImmutableList<SourceComponent> getVertexComponents(PipelineProgramKey key) {
|
|
||||||
var instanceAssembly = key.pipelineShader()
|
|
||||||
.assembler()
|
|
||||||
.assemble(new Pipeline.InstanceAssemblerContext(sources, key.vertexType(), key.instanceType()));
|
|
||||||
|
|
||||||
var layout = sources.find(key.vertexType()
|
|
||||||
.layoutShader());
|
|
||||||
var instance = sources.find(key.instanceType()
|
|
||||||
.instanceShader());
|
|
||||||
var context = sources.find(key.contextShader()
|
|
||||||
.vertexShader());
|
|
||||||
var pipeline = sources.find(key.pipelineShader()
|
|
||||||
.vertexShader());
|
|
||||||
|
|
||||||
return ImmutableList.of(uniformComponent, vertexMaterialComponent, instanceAssembly, layout, instance, context, pipeline);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ImmutableList<SourceComponent> getFragmentComponents(PipelineProgramKey key) {
|
|
||||||
var context = sources.find(key.contextShader()
|
|
||||||
.fragmentShader());
|
|
||||||
var pipeline = sources.find(key.pipelineShader()
|
|
||||||
.fragmentShader());
|
|
||||||
return ImmutableList.of(uniformComponent, fragmentMaterialComponent, context, pipeline);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private GlProgram compileCullingProgram(InstanceType<?> key) {
|
|
||||||
var computeComponents = getComputeComponents(key);
|
|
||||||
var result = shaderCompiler.compile(GLSLVersion.V460, ShaderType.COMPUTE, computeComponents);
|
|
||||||
|
|
||||||
if (result == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return link(result.handle());
|
|
||||||
}
|
|
||||||
|
|
||||||
private ImmutableList<SourceComponent> getComputeComponents(InstanceType<?> instanceType) {
|
|
||||||
var instanceAssembly = new IndirectComponent(sources, instanceType);
|
|
||||||
var instance = sources.find(instanceType.instanceShader());
|
|
||||||
var pipeline = sources.find(Files.INDIRECT_CULL);
|
|
||||||
|
|
||||||
return ImmutableList.of(uniformComponent, instanceAssembly, instance, pipeline);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void finish() {
|
|
||||||
long compileEnd = System.nanoTime();
|
|
||||||
int programCount = pipelineKeys.size() + cullingKeys.size();
|
|
||||||
int shaderCount = shaderCompiler.shaderCount();
|
|
||||||
int errorCount = errors.size();
|
|
||||||
var elapsed = StringUtil.formatTime(compileEnd - compileStart);
|
|
||||||
|
|
||||||
Flywheel.LOGGER.info("Compiled " + programCount + " programs and " + shaderCount + " shaders in " + elapsed + " with " + errorCount + " errors.");
|
|
||||||
|
|
||||||
if (errorCount > 0) {
|
|
||||||
var details = errors.stream()
|
|
||||||
.map(FailedCompilation::getMessage)
|
|
||||||
.collect(Collectors.joining("\n"));
|
|
||||||
// TODO: disable backend instead of crashing if compilation fails
|
|
||||||
throw new ShaderLoadingException("Compilation failed.\n" + details);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void delete() {
|
|
||||||
shaderCompiler.delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final class Files {
|
|
||||||
public static final ResourceLocation INDIRECT_CULL = Flywheel.rl("internal/indirect_cull.glsl");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +1,10 @@
|
||||||
package com.jozufozu.flywheel.backend.compile;
|
package com.jozufozu.flywheel.backend.compile;
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.jozufozu.flywheel.api.context.Context;
|
import com.jozufozu.flywheel.Flywheel;
|
||||||
import com.jozufozu.flywheel.api.instance.InstanceType;
|
import com.jozufozu.flywheel.api.instance.InstanceType;
|
||||||
|
import com.jozufozu.flywheel.api.uniform.ShaderUniforms;
|
||||||
import com.jozufozu.flywheel.api.vertex.VertexType;
|
import com.jozufozu.flywheel.api.vertex.VertexType;
|
||||||
import com.jozufozu.flywheel.backend.compile.pipeline.Pipeline;
|
|
||||||
import com.jozufozu.flywheel.backend.compile.pipeline.Pipelines;
|
|
||||||
import com.jozufozu.flywheel.gl.shader.GlProgram;
|
|
||||||
import com.jozufozu.flywheel.glsl.ShaderSources;
|
import com.jozufozu.flywheel.glsl.ShaderSources;
|
||||||
import com.jozufozu.flywheel.glsl.error.ErrorReporter;
|
import com.jozufozu.flywheel.glsl.error.ErrorReporter;
|
||||||
import com.jozufozu.flywheel.lib.context.Contexts;
|
import com.jozufozu.flywheel.lib.context.Contexts;
|
||||||
|
@ -18,77 +12,32 @@ import com.jozufozu.flywheel.lib.context.Contexts;
|
||||||
import net.minecraft.server.packs.resources.ResourceManager;
|
import net.minecraft.server.packs.resources.ResourceManager;
|
||||||
|
|
||||||
public class FlwPrograms {
|
public class FlwPrograms {
|
||||||
private static FlwPrograms instance;
|
private FlwPrograms() {
|
||||||
|
|
||||||
private final Map<PipelineProgramKey, GlProgram> pipelinePrograms;
|
|
||||||
private final Map<InstanceType<?>, GlProgram> cullingPrograms;
|
|
||||||
|
|
||||||
public FlwPrograms(Map<PipelineProgramKey, GlProgram> pipelinePrograms, Map<InstanceType<?>, GlProgram> cullingPrograms) {
|
|
||||||
this.pipelinePrograms = pipelinePrograms;
|
|
||||||
this.cullingPrograms = cullingPrograms;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void reload(ResourceManager resourceManager) {
|
public static void reload(ResourceManager resourceManager) {
|
||||||
if (instance != null) {
|
var errorReporter = new ErrorReporter();
|
||||||
instance.delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorReporter errorReporter = new ErrorReporter();
|
var sources = new ShaderSources(errorReporter, resourceManager);
|
||||||
ShaderSources sources = new ShaderSources(errorReporter, resourceManager);
|
var pipelineKeys = createPipelineKeys();
|
||||||
FlwCompiler compiler = new FlwCompiler(sources, createPipelineKeys(), createCullingKeys());
|
var uniformComponent = UniformComponent.builder(Flywheel.rl("uniforms"))
|
||||||
instance = compiler.compile();
|
.sources(ShaderUniforms.REGISTRY.getAll()
|
||||||
compiler.delete();
|
.stream()
|
||||||
|
.map(ShaderUniforms::uniformShader)
|
||||||
|
.toList())
|
||||||
|
.build(sources);
|
||||||
|
|
||||||
|
InstancingPrograms.reload(sources, pipelineKeys, uniformComponent);
|
||||||
|
IndirectPrograms.reload(sources, pipelineKeys, uniformComponent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ImmutableList<PipelineProgramKey> createPipelineKeys() {
|
private static ImmutableList<PipelineProgramKey> createPipelineKeys() {
|
||||||
ImmutableList.Builder<PipelineProgramKey> builder = ImmutableList.builder();
|
ImmutableList.Builder<PipelineProgramKey> builder = ImmutableList.builder();
|
||||||
for (Pipeline pipelineShader : Pipelines.ALL) {
|
for (InstanceType<?> instanceType : InstanceType.REGISTRY) {
|
||||||
for (InstanceType<?> instanceType : InstanceType.REGISTRY) {
|
for (VertexType vertexType : VertexType.REGISTRY) {
|
||||||
for (VertexType vertexType : VertexType.REGISTRY) {
|
builder.add(new PipelineProgramKey(vertexType, instanceType, Contexts.WORLD));
|
||||||
builder.add(new PipelineProgramKey(vertexType, instanceType, Contexts.WORLD, pipelineShader));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ImmutableList<InstanceType<?>> createCullingKeys() {
|
|
||||||
ImmutableList.Builder<InstanceType<?>> builder = ImmutableList.builder();
|
|
||||||
for (InstanceType<?> instanceType : InstanceType.REGISTRY) {
|
|
||||||
builder.add(instanceType);
|
|
||||||
}
|
|
||||||
return builder.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public static FlwPrograms get() {
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
public GlProgram getPipelineProgram(VertexType vertexType, InstanceType<?> instanceType, Context contextShader, Pipeline pipelineShader) {
|
|
||||||
return pipelinePrograms.get(new PipelineProgramKey(vertexType, instanceType, contextShader, pipelineShader));
|
|
||||||
}
|
|
||||||
|
|
||||||
public GlProgram getCullingProgram(InstanceType<?> instanceType) {
|
|
||||||
return cullingPrograms.get(instanceType);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void delete() {
|
|
||||||
pipelinePrograms.values()
|
|
||||||
.forEach(GlProgram::delete);
|
|
||||||
cullingPrograms.values()
|
|
||||||
.forEach(GlProgram::delete);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents the entire context of a program's usage.
|
|
||||||
*
|
|
||||||
* @param vertexType The vertex type the program should be adapted for.
|
|
||||||
* @param instanceType The instance shader to use.
|
|
||||||
* @param contextShader The context shader to use.
|
|
||||||
* @param pipelineShader The pipeline shader to use.
|
|
||||||
*/
|
|
||||||
public record PipelineProgramKey(VertexType vertexType, InstanceType<?> instanceType, Context contextShader,
|
|
||||||
Pipeline pipelineShader) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
package com.jozufozu.flywheel.backend.compile;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
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.pipeline.Pipelines;
|
||||||
|
import com.jozufozu.flywheel.gl.shader.GlProgram;
|
||||||
|
import com.jozufozu.flywheel.glsl.ShaderSources;
|
||||||
|
|
||||||
|
public class IndirectPrograms {
|
||||||
|
private static IndirectPrograms instance;
|
||||||
|
private final Map<PipelineProgramKey, GlProgram> pipeline;
|
||||||
|
private final Map<InstanceType<?>, GlProgram> culling;
|
||||||
|
|
||||||
|
public IndirectPrograms(Map<PipelineProgramKey, GlProgram> pipeline, Map<InstanceType<?>, GlProgram> culling) {
|
||||||
|
this.pipeline = pipeline;
|
||||||
|
this.culling = culling;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void reload(ShaderSources sources, ImmutableList<PipelineProgramKey> pipelineKeys, UniformComponent uniformComponent) {
|
||||||
|
if (instance != null) {
|
||||||
|
instance.delete();
|
||||||
|
}
|
||||||
|
var indirectCompiler = new PipelineCompiler(sources, pipelineKeys, Pipelines.INDIRECT, uniformComponent);
|
||||||
|
var cullingCompiler = new CullingCompiler(sources, createCullingKeys(), uniformComponent);
|
||||||
|
instance = new IndirectPrograms(indirectCompiler.compile(), cullingCompiler.compile());
|
||||||
|
indirectCompiler.delete();
|
||||||
|
cullingCompiler.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ImmutableList<InstanceType<?>> createCullingKeys() {
|
||||||
|
ImmutableList.Builder<InstanceType<?>> builder = ImmutableList.builder();
|
||||||
|
for (InstanceType<?> instanceType : InstanceType.REGISTRY) {
|
||||||
|
builder.add(instanceType);
|
||||||
|
}
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static IndirectPrograms get() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GlProgram getIndirectProgram(VertexType vertexType, InstanceType<?> instanceType, Context contextShader) {
|
||||||
|
return pipeline.get(new PipelineProgramKey(vertexType, instanceType, contextShader));
|
||||||
|
}
|
||||||
|
|
||||||
|
public GlProgram getCullingProgram(InstanceType<?> instanceType) {
|
||||||
|
return culling.get(instanceType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void delete() {
|
||||||
|
pipeline.values()
|
||||||
|
.forEach(GlProgram::delete);
|
||||||
|
culling.values()
|
||||||
|
.forEach(GlProgram::delete);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
package com.jozufozu.flywheel.backend.compile;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
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.pipeline.Pipelines;
|
||||||
|
import com.jozufozu.flywheel.gl.shader.GlProgram;
|
||||||
|
import com.jozufozu.flywheel.glsl.ShaderSources;
|
||||||
|
|
||||||
|
public class InstancingPrograms {
|
||||||
|
private static InstancingPrograms instance;
|
||||||
|
private final Map<PipelineProgramKey, GlProgram> pipeline;
|
||||||
|
|
||||||
|
public InstancingPrograms(Map<PipelineProgramKey, GlProgram> pipeline) {
|
||||||
|
this.pipeline = pipeline;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void reload(ShaderSources sources, ImmutableList<PipelineProgramKey> pipelineKeys, UniformComponent uniformComponent) {
|
||||||
|
if (instance != null) {
|
||||||
|
instance.delete();
|
||||||
|
}
|
||||||
|
var instancingCompiler = new PipelineCompiler(sources, pipelineKeys, Pipelines.INSTANCED_ARRAYS, uniformComponent);
|
||||||
|
instance = new InstancingPrograms(instancingCompiler.compile());
|
||||||
|
instancingCompiler.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static InstancingPrograms get() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GlProgram get(VertexType vertexType, InstanceType<?> instanceType, Context contextShader) {
|
||||||
|
return pipeline.get(new PipelineProgramKey(vertexType, instanceType, contextShader));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void delete() {
|
||||||
|
pipeline.values()
|
||||||
|
.forEach(GlProgram::delete);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package com.jozufozu.flywheel.backend.compile;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.gl.shader.GlProgram;
|
||||||
|
|
||||||
|
public sealed interface LinkResult {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
default GlProgram unwrap() {
|
||||||
|
if (this instanceof Success s) {
|
||||||
|
return s.program();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
record Success(GlProgram program, String log) implements LinkResult {
|
||||||
|
}
|
||||||
|
|
||||||
|
record Failure(String failure) implements LinkResult {
|
||||||
|
}
|
||||||
|
|
||||||
|
static LinkResult success(GlProgram program, String log) {
|
||||||
|
return new Success(program, log);
|
||||||
|
}
|
||||||
|
|
||||||
|
static LinkResult failure(String failure) {
|
||||||
|
return new Failure(failure);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,92 @@
|
||||||
|
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.backend.compile.pipeline.Pipeline;
|
||||||
|
import com.jozufozu.flywheel.gl.shader.GlProgram;
|
||||||
|
import com.jozufozu.flywheel.gl.shader.ShaderType;
|
||||||
|
import com.jozufozu.flywheel.glsl.ShaderSources;
|
||||||
|
import com.jozufozu.flywheel.glsl.SourceComponent;
|
||||||
|
import com.jozufozu.flywheel.glsl.SourceFile;
|
||||||
|
import com.jozufozu.flywheel.glsl.generate.FnSignature;
|
||||||
|
import com.jozufozu.flywheel.glsl.generate.GlslExpr;
|
||||||
|
import com.jozufozu.flywheel.lib.material.MaterialIndices;
|
||||||
|
|
||||||
|
public class PipelineCompiler extends AbstractCompiler<PipelineProgramKey> {
|
||||||
|
private final Pipeline pipeline;
|
||||||
|
private final MaterialAdapterComponent vertexMaterialComponent;
|
||||||
|
private final MaterialAdapterComponent fragmentMaterialComponent;
|
||||||
|
private final UniformComponent uniformComponent;
|
||||||
|
private final SourceFile pipelineFragment;
|
||||||
|
private final SourceFile pipelineVertex;
|
||||||
|
|
||||||
|
public PipelineCompiler(ShaderSources sources, ImmutableList<PipelineProgramKey> keys, Pipeline pipeline, UniformComponent uniformComponent) {
|
||||||
|
super(sources, keys);
|
||||||
|
this.pipeline = pipeline;
|
||||||
|
this.uniformComponent = uniformComponent;
|
||||||
|
|
||||||
|
vertexMaterialComponent = MaterialAdapterComponent.builder(Flywheel.rl("vertex_material_adapter"))
|
||||||
|
.materialSources(MaterialIndices.getAllVertexShaders())
|
||||||
|
.adapt(FnSignature.ofVoid("flw_materialVertex"))
|
||||||
|
.switchOn(GlslExpr.variable("_flw_materialVertexID"))
|
||||||
|
.build(sources);
|
||||||
|
fragmentMaterialComponent = MaterialAdapterComponent.builder(Flywheel.rl("fragment_material_adapter"))
|
||||||
|
.materialSources(MaterialIndices.getAllFragmentShaders())
|
||||||
|
.adapt(FnSignature.ofVoid("flw_materialFragment"))
|
||||||
|
.adapt(FnSignature.create()
|
||||||
|
.returnType("bool")
|
||||||
|
.name("flw_discardPredicate")
|
||||||
|
.arg("vec4", "color")
|
||||||
|
.build(), GlslExpr.literal(false))
|
||||||
|
.adapt(FnSignature.create()
|
||||||
|
.returnType("vec4")
|
||||||
|
.name("flw_fogFilter")
|
||||||
|
.arg("vec4", "color")
|
||||||
|
.build(), GlslExpr.variable("color"))
|
||||||
|
.switchOn(GlslExpr.variable("_flw_materialFragmentID"))
|
||||||
|
.build(sources);
|
||||||
|
|
||||||
|
pipelineFragment = sources.find(pipeline.fragmentShader());
|
||||||
|
pipelineVertex = sources.find(pipeline.vertexShader());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
protected GlProgram compile(PipelineProgramKey key) {
|
||||||
|
var glslVersion = pipeline.glslVersion();
|
||||||
|
|
||||||
|
var vertex = shaderCompiler.compile(glslVersion, ShaderType.VERTEX, getVertexComponents(key));
|
||||||
|
var fragment = shaderCompiler.compile(glslVersion, ShaderType.FRAGMENT, getFragmentComponents(key));
|
||||||
|
|
||||||
|
if (vertex == null || fragment == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var glProgram = programLinker.link(vertex, fragment);
|
||||||
|
key.contextShader()
|
||||||
|
.onProgramLink(glProgram);
|
||||||
|
return glProgram;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ImmutableList<SourceComponent> getVertexComponents(PipelineProgramKey key) {
|
||||||
|
var instanceAssembly = pipeline.assembler()
|
||||||
|
.assemble(new Pipeline.InstanceAssemblerContext(sources, key.vertexType(), key.instanceType()));
|
||||||
|
|
||||||
|
var layout = sources.find(key.vertexType()
|
||||||
|
.layoutShader());
|
||||||
|
var instance = sources.find(key.instanceType()
|
||||||
|
.instanceShader());
|
||||||
|
var context = sources.find(key.contextShader()
|
||||||
|
.vertexShader());
|
||||||
|
|
||||||
|
return ImmutableList.of(uniformComponent, vertexMaterialComponent, instanceAssembly, layout, instance, context, pipelineVertex);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ImmutableList<SourceComponent> getFragmentComponents(PipelineProgramKey key) {
|
||||||
|
var context = sources.find(key.contextShader()
|
||||||
|
.fragmentShader());
|
||||||
|
return ImmutableList.of(uniformComponent, fragmentMaterialComponent, context, pipelineFragment);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package com.jozufozu.flywheel.backend.compile;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.api.context.Context;
|
||||||
|
import com.jozufozu.flywheel.api.instance.InstanceType;
|
||||||
|
import com.jozufozu.flywheel.api.vertex.VertexType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the entire context of a program's usage.
|
||||||
|
*
|
||||||
|
* @param vertexType The vertex type the program should be adapted for.
|
||||||
|
* @param instanceType The instance shader to use.
|
||||||
|
* @param contextShader The context shader to use.
|
||||||
|
*/
|
||||||
|
public record PipelineProgramKey(VertexType vertexType, InstanceType<?> instanceType, Context contextShader) {
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
package com.jozufozu.flywheel.backend.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 org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.gl.shader.GlProgram;
|
||||||
|
import com.jozufozu.flywheel.gl.shader.GlShader;
|
||||||
|
|
||||||
|
public class ProgramLinker {
|
||||||
|
private final CompilerStats stats;
|
||||||
|
|
||||||
|
public ProgramLinker(CompilerStats stats) {
|
||||||
|
this.stats = stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public GlProgram link(GlShader... shaders) {
|
||||||
|
// this probably doesn't need caching
|
||||||
|
var linkResult = linkInternal(shaders);
|
||||||
|
stats.linkResult(linkResult);
|
||||||
|
return linkResult.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
private LinkResult linkInternal(GlShader... shaders) {
|
||||||
|
int handle = glCreateProgram();
|
||||||
|
|
||||||
|
for (GlShader shader : shaders) {
|
||||||
|
glAttachShader(handle, shader.handle());
|
||||||
|
}
|
||||||
|
|
||||||
|
glLinkProgram(handle);
|
||||||
|
String log = glGetProgramInfoLog(handle);
|
||||||
|
|
||||||
|
if (linkSuccessful(handle)) {
|
||||||
|
return LinkResult.success(new GlProgram(handle), log);
|
||||||
|
} else {
|
||||||
|
return LinkResult.failure(log);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean linkSuccessful(int handle) {
|
||||||
|
return glGetProgrami(handle, GL_LINK_STATUS) == GL_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -3,7 +3,6 @@ package com.jozufozu.flywheel.backend.compile;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
@ -15,13 +14,13 @@ import com.jozufozu.flywheel.gl.shader.ShaderType;
|
||||||
import com.jozufozu.flywheel.glsl.SourceComponent;
|
import com.jozufozu.flywheel.glsl.SourceComponent;
|
||||||
|
|
||||||
public class ShaderCompiler {
|
public class ShaderCompiler {
|
||||||
private final Map<ShaderKey, CompilationResult> shaderCache = new HashMap<>();
|
private final Map<ShaderKey, ShaderResult> shaderCache = new HashMap<>();
|
||||||
private final Consumer<FailedCompilation> errorConsumer;
|
private final CompilerStats stats;
|
||||||
private final CompilationFactory factory;
|
private final CompilationFactory factory;
|
||||||
private final Includer includer;
|
private final Includer includer;
|
||||||
|
|
||||||
public ShaderCompiler(Consumer<FailedCompilation> errorConsumer, CompilationFactory factory, Includer includer) {
|
public ShaderCompiler(CompilerStats stats, CompilationFactory factory, Includer includer) {
|
||||||
this.errorConsumer = errorConsumer;
|
this.stats = stats;
|
||||||
this.factory = factory;
|
this.factory = factory;
|
||||||
this.includer = includer;
|
this.includer = includer;
|
||||||
}
|
}
|
||||||
|
@ -38,31 +37,22 @@ public class ShaderCompiler {
|
||||||
return cached.unwrap();
|
return cached.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
CompilationResult out = compileUncached(factory.create(glslVersion, shaderType), sourceComponents);
|
ShaderResult out = compileUncached(factory.create(glslVersion, shaderType), sourceComponents);
|
||||||
shaderCache.put(key, out);
|
shaderCache.put(key, out);
|
||||||
return unwrapAndReportError(out);
|
stats.shaderResult(out);
|
||||||
|
return out.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void delete() {
|
public void delete() {
|
||||||
shaderCache.values()
|
shaderCache.values()
|
||||||
.stream()
|
.stream()
|
||||||
.map(CompilationResult::unwrap)
|
.map(ShaderResult::unwrap)
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.forEach(GlShader::delete);
|
.forEach(GlShader::delete);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private GlShader unwrapAndReportError(CompilationResult result) {
|
|
||||||
if (result instanceof CompilationResult.Success s) {
|
|
||||||
return s.shader();
|
|
||||||
} else if (result instanceof CompilationResult.Failure f) {
|
|
||||||
errorConsumer.accept(f.failure());
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private CompilationResult compileUncached(Compilation ctx, ImmutableList<SourceComponent> sourceComponents) {
|
private ShaderResult compileUncached(Compilation ctx, ImmutableList<SourceComponent> sourceComponents) {
|
||||||
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");
|
||||||
|
|
||||||
|
@ -86,15 +76,9 @@ public class ShaderCompiler {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
private Consumer<FailedCompilation> errorConsumer = error -> {};
|
|
||||||
private CompilationFactory factory = Compilation::new;
|
private CompilationFactory factory = Compilation::new;
|
||||||
private Includer includer = RecursiveIncluder.INSTANCE;
|
private Includer includer = RecursiveIncluder.INSTANCE;
|
||||||
|
|
||||||
public Builder errorConsumer(Consumer<FailedCompilation> errorConsumer) {
|
|
||||||
this.errorConsumer = errorConsumer;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder compilationFactory(CompilationFactory factory) {
|
public Builder compilationFactory(CompilationFactory factory) {
|
||||||
this.factory = factory;
|
this.factory = factory;
|
||||||
return this;
|
return this;
|
||||||
|
@ -105,8 +89,8 @@ public class ShaderCompiler {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ShaderCompiler build() {
|
public ShaderCompiler build(CompilerStats stats) {
|
||||||
return new ShaderCompiler(errorConsumer, factory, includer);
|
return new ShaderCompiler(stats, factory, includer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
package com.jozufozu.flywheel.backend.compile;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.gl.shader.GlShader;
|
||||||
|
|
||||||
|
public sealed interface ShaderResult {
|
||||||
|
@Nullable
|
||||||
|
default GlShader unwrap() {
|
||||||
|
if (this instanceof Success s) {
|
||||||
|
return s.shader();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
record Success(GlShader shader, String infoLog) implements ShaderResult {
|
||||||
|
}
|
||||||
|
|
||||||
|
record Failure(FailedCompilation failure) implements ShaderResult {
|
||||||
|
}
|
||||||
|
|
||||||
|
static ShaderResult success(GlShader program, String infoLog) {
|
||||||
|
return new Success(program, infoLog);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ShaderResult failure(FailedCompilation failure) {
|
||||||
|
return new Failure(failure);
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,8 +15,7 @@ import com.jozufozu.flywheel.api.event.RenderStage;
|
||||||
import com.jozufozu.flywheel.api.instance.Instance;
|
import com.jozufozu.flywheel.api.instance.Instance;
|
||||||
import com.jozufozu.flywheel.api.instance.InstanceType;
|
import com.jozufozu.flywheel.api.instance.InstanceType;
|
||||||
import com.jozufozu.flywheel.api.vertex.VertexType;
|
import com.jozufozu.flywheel.api.vertex.VertexType;
|
||||||
import com.jozufozu.flywheel.backend.compile.FlwPrograms;
|
import com.jozufozu.flywheel.backend.compile.IndirectPrograms;
|
||||||
import com.jozufozu.flywheel.backend.compile.pipeline.Pipelines;
|
|
||||||
import com.jozufozu.flywheel.backend.engine.UniformBuffer;
|
import com.jozufozu.flywheel.backend.engine.UniformBuffer;
|
||||||
import com.jozufozu.flywheel.gl.shader.GlProgram;
|
import com.jozufozu.flywheel.gl.shader.GlProgram;
|
||||||
import com.jozufozu.flywheel.lib.context.Contexts;
|
import com.jozufozu.flywheel.lib.context.Contexts;
|
||||||
|
@ -62,8 +61,9 @@ public class IndirectCullingGroup<I extends Instance> {
|
||||||
.quads2Tris(2048).glBuffer;
|
.quads2Tris(2048).glBuffer;
|
||||||
setupVertexArray();
|
setupVertexArray();
|
||||||
|
|
||||||
compute = FlwPrograms.get().getCullingProgram(instanceType);
|
var indirectPrograms = IndirectPrograms.get();
|
||||||
draw = FlwPrograms.get().getPipelineProgram(vertexType, instanceType, Contexts.WORLD, Pipelines.INDIRECT);
|
compute = indirectPrograms.getCullingProgram(instanceType);
|
||||||
|
draw = indirectPrograms.getIndirectProgram(vertexType, instanceType, Contexts.WORLD);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupVertexArray() {
|
private void setupVertexArray() {
|
||||||
|
|
|
@ -13,8 +13,7 @@ import com.jozufozu.flywheel.api.instance.Instancer;
|
||||||
import com.jozufozu.flywheel.api.model.Model;
|
import com.jozufozu.flywheel.api.model.Model;
|
||||||
import com.jozufozu.flywheel.api.task.Plan;
|
import com.jozufozu.flywheel.api.task.Plan;
|
||||||
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
||||||
import com.jozufozu.flywheel.backend.compile.FlwPrograms;
|
import com.jozufozu.flywheel.backend.compile.InstancingPrograms;
|
||||||
import com.jozufozu.flywheel.backend.compile.pipeline.Pipelines;
|
|
||||||
import com.jozufozu.flywheel.backend.engine.AbstractEngine;
|
import com.jozufozu.flywheel.backend.engine.AbstractEngine;
|
||||||
import com.jozufozu.flywheel.backend.engine.UniformBuffer;
|
import com.jozufozu.flywheel.backend.engine.UniformBuffer;
|
||||||
import com.jozufozu.flywheel.gl.GlStateTracker;
|
import com.jozufozu.flywheel.gl.GlStateTracker;
|
||||||
|
@ -106,7 +105,8 @@ public class InstancingEngine extends AbstractEngine {
|
||||||
var vertexType = desc.vertexType();
|
var vertexType = desc.vertexType();
|
||||||
var instanceType = desc.instanceType();
|
var instanceType = desc.instanceType();
|
||||||
|
|
||||||
var program = FlwPrograms.get().getPipelineProgram(vertexType, instanceType, context, Pipelines.INSTANCED_ARRAYS);
|
var program = InstancingPrograms.get()
|
||||||
|
.get(vertexType, instanceType, context);
|
||||||
UniformBuffer.syncAndBind(program);
|
UniformBuffer.syncAndBind(program);
|
||||||
|
|
||||||
var uniformLocation = program.getUniformLocation("_flw_materialID_instancing");
|
var uniformLocation = program.getUniformLocation("_flw_materialID_instancing");
|
||||||
|
|
Loading…
Reference in a new issue