mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2025-01-26 21:07:55 +01:00
More organized shader compilation
This commit is contained in:
parent
2d63d8c7db
commit
7d4055f263
13 changed files with 262 additions and 122 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue