More organized shader compilation

This commit is contained in:
Jozufozu 2022-01-11 13:17:55 -08:00
parent 2d63d8c7db
commit 7d4055f263
13 changed files with 262 additions and 122 deletions

View file

@ -5,9 +5,12 @@ import com.jozufozu.flywheel.core.Contexts;
import com.jozufozu.flywheel.core.Materials;
import com.jozufozu.flywheel.core.PartialModel;
import com.jozufozu.flywheel.core.StitchedSprite;
import com.jozufozu.flywheel.core.compile.ProgramCompiler;
import com.jozufozu.flywheel.event.ReloadRenderersEvent;
import com.jozufozu.flywheel.mixin.PausedPartialTickAccessor;
import com.jozufozu.flywheel.vanilla.VanillaInstances;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.fml.CrashReportCallables;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
@ -29,6 +32,8 @@ public class FlywheelClient {
modEventBus.addListener(StitchedSprite::onTextureStitchPre);
modEventBus.addListener(StitchedSprite::onTextureStitchPost);
MinecraftForge.EVENT_BUS.<ReloadRenderersEvent>addListener(ProgramCompiler::invalidateAll);
VanillaInstances.init();
// https://github.com/Jozufozu/Flywheel/issues/69

View file

@ -38,7 +38,7 @@ public enum RenderLayer {
;
@Nullable
public static RenderLayer fromRenderType(RenderType type) {
public static RenderLayer getPrimaryLayer(RenderType type) {
if (type == RenderType.solid()) {
return SOLID;
} else if (type == RenderType.cutoutMipped()) {
@ -49,4 +49,17 @@ public enum RenderLayer {
return null;
}
@Nullable
public static RenderLayer getLayer(RenderType type) {
if (type == RenderType.solid()) {
return SOLID;
} else if (type == RenderType.cutoutMipped() || type == RenderType.cutout()) {
return CUTOUT;
} else if (type == RenderType.translucent()) {
return TRANSPARENT;
}
return null;
}
}

View file

@ -151,11 +151,17 @@ public class GPUInstancer<D extends InstanceData> extends AbstractInstancer<D> {
final StructWriter<D> writer = instancedType.getWriter(mapped);
boolean sequential = true;
for (int i = 0; i < size; i++) {
final D element = data.get(i);
if (element.checkDirtyAndClear()) {
writer.seek(i);
if (!sequential) {
writer.seek(i);
}
writer.write(element);
sequential = true;
} else {
sequential = false;
}
}
} catch (Exception e) {

View file

@ -33,6 +33,6 @@ public abstract class BufferWriter<S> implements StructWriter<S> {
@Override
public void seek(int pos) {
backingBuffer.position(pos * stride);
backingBuffer.position(pos * stride);
}
}

View file

@ -0,0 +1,16 @@
package com.jozufozu.flywheel.core.compile;
import com.jozufozu.flywheel.backend.gl.GLSLVersion;
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
public class CompileUtil {
protected static String generateHeader(GLSLVersion version, ShaderType type) {
return "#version "
+ version
+ '\n'
+ "#extension GL_ARB_explicit_attrib_location : enable\n"
+ "#extension GL_ARB_conservative_depth : enable\n"
+ type.getDefineStatement();
}
}

View file

@ -0,0 +1,44 @@
package com.jozufozu.flywheel.core.compile;
import com.jozufozu.flywheel.backend.gl.shader.GlShader;
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
import com.jozufozu.flywheel.backend.source.FileResolution;
import com.jozufozu.flywheel.backend.source.SourceFile;
import com.jozufozu.flywheel.core.shader.ProgramSpec;
public class FragmentCompiler extends Memoizer<ProgramContext, GlShader> {
private final FileResolution header;
private final Template<FragmentTemplateData> fragment;
public FragmentCompiler(Template<FragmentTemplateData> fragment, FileResolution header) {
this.header = header;
this.fragment = fragment;
}
@Override
protected GlShader _create(ProgramContext key) {
ProgramSpec spec = key.spec();
SourceFile fragmentFile = spec.getFragmentFile();
FragmentTemplateData appliedTemplate = fragment.apply(fragmentFile);
StringBuilder builder = new StringBuilder();
builder.append(CompileUtil.generateHeader(fragment.getVersion(), ShaderType.FRAGMENT));
key.getShaderConstants().writeInto(builder);
FileIndexImpl index = new FileIndexImpl();
header.getFile().generateFinalSource(index, builder);
fragmentFile.generateFinalSource(index, builder);
builder.append(appliedTemplate.generateFooter());
return new GlShader(spec.name, ShaderType.FRAGMENT, builder.toString());
}
@Override
protected void _destroy(GlShader value) {
value.delete();
}
}

View file

@ -0,0 +1,22 @@
package com.jozufozu.flywheel.core.compile;
import java.util.HashMap;
import java.util.Map;
public abstract class Memoizer<K, V> {
private final Map<K, V> map = new HashMap<>();
public V get(K key) {
return map.computeIfAbsent(key, this::_create);
}
public void invalidate() {
map.values().forEach(this::_destroy);
map.clear();
}
protected abstract V _create(K key);
protected abstract void _destroy(V value);
}

View file

@ -1,13 +1,12 @@
package com.jozufozu.flywheel.core.compile;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import java.util.ArrayList;
import java.util.List;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import com.jozufozu.flywheel.backend.gl.shader.GlShader;
import com.jozufozu.flywheel.backend.source.FileResolution;
import com.jozufozu.flywheel.core.Templates;
import com.jozufozu.flywheel.event.ReloadRenderersEvent;
/**
* A caching compiler.
@ -17,18 +16,20 @@ import com.jozufozu.flywheel.core.Templates;
* compiled programs, and will only compile a program if it is not already in the cache.
* </p>
*/
public class ProgramCompiler<P extends GlProgram> {
public class ProgramCompiler<P extends GlProgram> extends Memoizer<ProgramContext, P> {
protected final Map<ProgramContext, P> cache = new HashMap<>();
private static final List<ProgramCompiler<?>> ALL_COMPILERS = new ArrayList<>();
private final GlProgram.Factory<P> factory;
private final Function<ProgramContext, GlShader> vertexCompiler;
private final Function<ProgramContext, GlShader> fragmentCompiler;
private final VertexCompiler vertexCompiler;
private final FragmentCompiler fragmentCompiler;
public ProgramCompiler(GlProgram.Factory<P> factory, Function<ProgramContext, GlShader> vertexCompiler, Function<ProgramContext, GlShader> fragmentCompiler) {
public ProgramCompiler(GlProgram.Factory<P> factory, VertexCompiler vertexCompiler, FragmentCompiler fragmentCompiler) {
this.factory = factory;
this.vertexCompiler = vertexCompiler;
this.fragmentCompiler = fragmentCompiler;
ALL_COMPILERS.add(this);
}
/**
@ -40,7 +41,7 @@ public class ProgramCompiler<P extends GlProgram> {
* @return A program compiler.
*/
public static <T extends VertexData, P extends GlProgram> ProgramCompiler<P> create(Template<T> template, GlProgram.Factory<P> factory, FileResolution header) {
return new ProgramCompiler<>(factory, ctx -> ShaderCompiler.compileVertex(ctx, template, header), ctx -> ShaderCompiler.compileFragment(ctx, Templates.FRAGMENT, header));
return new ProgramCompiler<>(factory, new VertexCompiler(template, header), new FragmentCompiler(Templates.FRAGMENT, header));
}
/**
@ -50,22 +51,30 @@ public class ProgramCompiler<P extends GlProgram> {
* @return A compiled GlProgram.
*/
public P getProgram(ProgramContext ctx) {
return cache.computeIfAbsent(ctx, this::compile);
return super.get(ctx);
}
public void invalidate() {
cache.values().forEach(P::delete);
cache.clear();
super.invalidate();
vertexCompiler.invalidate();
fragmentCompiler.invalidate();
}
private P compile(ProgramContext ctx) {
@Override
protected P _create(ProgramContext ctx) {
return new ProgramAssembler(ctx.spec().name)
.attachShader(vertexCompiler.apply(ctx))
.attachShader(fragmentCompiler.apply(ctx))
.attachShader(vertexCompiler.get(ctx))
.attachShader(fragmentCompiler.get(ctx))
.link()
.deleteLinkedShaders()
.build(this.factory);
}
@Override
protected void _destroy(P value) {
value.delete();
}
public static void invalidateAll(ReloadRenderersEvent event) {
ALL_COMPILERS.forEach(ProgramCompiler::invalidate);
}
}

View file

@ -1,6 +1,5 @@
package com.jozufozu.flywheel.core.compile;
import java.util.List;
import java.util.Objects;
import javax.annotation.Nullable;
@ -41,18 +40,15 @@ public record ProgramContext(float alphaDiscard, VertexType vertexType, ProgramS
return new ProgramContext(getAlphaDiscard(layer), vertexType, spec, spec.getCurrentStateID());
}
/**
* Gets the alpha discard threshold for the given render layer.
*
* @param layer The render layer to get the alpha discard threshold for.
* @return The alpha discard threshold.
*/
public static float getAlphaDiscard(@Nullable RenderLayer layer) {
return layer == RenderLayer.CUTOUT ? 0.1f : 0f;
}
public ShaderConstants getShaderConstants() {
ShaderConstants shaderConstants = new ShaderConstants();
shaderConstants.defineAll(spec.getDefines(ctx));
public List<String> createDefines() {
return spec().getDefines(ctx());
if (alphaDiscard > 0) {
shaderConstants.define("ALPHA_DISCARD", alphaDiscard);
}
return shaderConstants;
}
@Override
@ -68,4 +64,14 @@ public record ProgramContext(float alphaDiscard, VertexType vertexType, ProgramS
public int hashCode() {
return Objects.hash(alphaDiscard, vertexType, spec, ctx);
}
/**
* Gets the alpha discard threshold for the given render layer.
*
* @param layer The render layer to get the alpha discard threshold for.
* @return The alpha discard threshold.
*/
public static float getAlphaDiscard(@Nullable RenderLayer layer) {
return layer == RenderLayer.CUTOUT ? 0.1f : 0f;
}
}

View file

@ -1,88 +0,0 @@
package com.jozufozu.flywheel.core.compile;
import com.jozufozu.flywheel.backend.gl.GLSLVersion;
import com.jozufozu.flywheel.backend.gl.shader.GlShader;
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
import com.jozufozu.flywheel.backend.source.FileResolution;
import com.jozufozu.flywheel.core.shader.ProgramSpec;
/**
* Compiles a shader program.
*/
public class ShaderCompiler {
public static <T extends VertexData> GlShader compileVertex(ProgramContext context, Template<T> template, FileResolution header) {
StringBuilder finalSource = new StringBuilder();
finalSource.append(generateHeader(template.getVersion(), ShaderType.VERTEX));
for (String def : context.createDefines()) {
finalSource.append("#define ")
.append(def)
.append('\n');
}
finalSource.append("""
struct Vertex {
vec3 pos;
vec4 color;
vec2 texCoords;
vec2 light;
vec3 normal;
};
""");
finalSource.append(context.vertexType()
.getShaderHeader());
FileIndexImpl index = new FileIndexImpl();
header.getFile()
.generateFinalSource(index, finalSource);
ProgramSpec spec = context.spec();
spec.getVertexFile()
.generateFinalSource(index, finalSource);
T appliedTemplate = template.apply(spec.getVertexFile());
finalSource.append(appliedTemplate.generateFooter(index, context.vertexType()));
return new GlShader(spec.name, ShaderType.VERTEX, finalSource.toString());
}
public static <T extends FragmentData> GlShader compileFragment(ProgramContext context, Template<T> template, FileResolution header) {
StringBuilder finalSource = new StringBuilder();
finalSource.append(generateHeader(template.getVersion(), ShaderType.FRAGMENT));
for (String def : context.createDefines()) {
finalSource.append("#define ")
.append(def)
.append('\n');
}
if (context.alphaDiscard() > 0) {
finalSource.append("#define ALPHA_DISCARD 0.1\n");
}
FileIndexImpl index = new FileIndexImpl();
ProgramSpec spec = context.spec();
header.getFile().generateFinalSource(index, finalSource);
spec.getFragmentFile()
.generateFinalSource(index, finalSource);
T appliedTemplate = template.apply(spec.getFragmentFile());
finalSource.append(appliedTemplate.generateFooter());
return new GlShader(spec.name, ShaderType.FRAGMENT, finalSource.toString());
}
protected static String generateHeader(GLSLVersion version, ShaderType type) {
return "#version "
+ version
+ '\n'
+ "#extension GL_ARB_explicit_attrib_location : enable\n"
+ "#extension GL_ARB_conservative_depth : enable\n"
+ type.getDefineStatement();
}
}

View file

@ -0,0 +1,51 @@
package com.jozufozu.flywheel.core.compile;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ShaderConstants {
private final Map<String, String> definitions = new HashMap<>();
public ShaderConstants define(String def) {
definitions.put(def, "");
return this;
}
public ShaderConstants define(String def, String value) {
definitions.put(def, value);
return this;
}
public ShaderConstants define(String def, float value) {
definitions.put(def, Float.toString(value));
return this;
}
public ShaderConstants defineAll(List<String> defines) {
for (String def : defines) {
definitions.put(def, "");
}
return this;
}
public String build() {
final StringBuilder acc = new StringBuilder();
writeInto(acc);
return acc.toString();
}
public void writeInto(final StringBuilder acc) {
for (Map.Entry<String, String> e : definitions.entrySet()) {
acc.append("#define ")
.append(e.getKey());
if (e.getValue().length() > 0) {
acc.append(' ')
.append(e.getValue());
}
acc.append('\n');
}
}
}

View file

@ -0,0 +1,56 @@
package com.jozufozu.flywheel.core.compile;
import com.jozufozu.flywheel.backend.gl.shader.GlShader;
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
import com.jozufozu.flywheel.backend.source.FileResolution;
import com.jozufozu.flywheel.backend.source.SourceFile;
import com.jozufozu.flywheel.core.shader.ProgramSpec;
public class VertexCompiler extends Memoizer<ProgramContext, GlShader> {
private final Template<? extends VertexData> template;
private final FileResolution header;
public VertexCompiler(Template<? extends VertexData> template, FileResolution header) {
this.template = template;
this.header = header;
}
@Override
protected GlShader _create(ProgramContext key) {
StringBuilder finalSource = new StringBuilder();
finalSource.append(CompileUtil.generateHeader(template.getVersion(), ShaderType.VERTEX));
key.getShaderConstants().writeInto(finalSource);
finalSource.append("""
struct Vertex {
vec3 pos;
vec4 color;
vec2 texCoords;
vec2 light;
vec3 normal;
};
""");
finalSource.append(key.vertexType()
.getShaderHeader());
FileIndexImpl index = new FileIndexImpl();
header.getFile().generateFinalSource(index, finalSource);
ProgramSpec spec = key.spec();
SourceFile vertexFile = spec.getVertexFile();
vertexFile.generateFinalSource(index, finalSource);
VertexData appliedTemplate = template.apply(vertexFile);
finalSource.append(appliedTemplate.generateFooter(index, key.vertexType()));
return new GlShader(spec.name, ShaderType.VERTEX, finalSource.toString());
}
@Override
protected void _destroy(GlShader value) {
value.delete();
}
}

View file

@ -38,7 +38,7 @@ public class RenderLayerEvent extends Event {
this.camY = camY;
this.camZ = camZ;
this.layer = RenderLayer.fromRenderType(type);
this.layer = RenderLayer.getPrimaryLayer(type);
}
@Nullable