A sample of what's to come

- Clean up texture binder stuff that's no longer needed
- Move all sampler binding to program link time
- Centralize sampler bindings/names to Samplers
- Move diffuse, light, and overlay textures into the api
- Add builder for context shader
- Fix generated_indirect -> generated_instancing
This commit is contained in:
Jozufozu 2024-02-26 15:38:11 -08:00
parent 2c3b4c54ca
commit 4d216b6672
18 changed files with 142 additions and 108 deletions

View File

@ -27,3 +27,7 @@ bool flw_discardPredicate(vec4 finalColor);
// To be implemented by the context shader.
void flw_beginFragment();
void flw_endFragment();
sampler2D flw_diffuseTex;
sampler2D flw_overlayTex;
sampler2D flw_lightTex;

View File

@ -1,6 +1,48 @@
package com.jozufozu.flywheel.backend.compile;
import java.util.Objects;
import java.util.function.Consumer;
import org.jetbrains.annotations.Nullable;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import net.minecraft.resources.ResourceLocation;
public record ContextShader(ResourceLocation vertexShader, ResourceLocation fragmentShader) {
public record ContextShader(ResourceLocation vertexShader, ResourceLocation fragmentShader,
Consumer<GlProgram> onLink) {
public static Builder builder() {
return new Builder();
}
public static class Builder {
@Nullable
private ResourceLocation vertexShader;
@Nullable
private ResourceLocation fragmentShader;
@Nullable
private Consumer<GlProgram> onLink;
public Builder vertexShader(ResourceLocation shader) {
this.vertexShader = shader;
return this;
}
public Builder fragmentShader(ResourceLocation shader) {
this.fragmentShader = shader;
return this;
}
public Builder onLink(Consumer<GlProgram> onLink) {
this.onLink = onLink;
return this;
}
public ContextShader build() {
Objects.requireNonNull(vertexShader);
Objects.requireNonNull(fragmentShader);
Objects.requireNonNull(onLink);
return new ContextShader(vertexShader, fragmentShader, onLink);
}
}
}

View File

@ -6,12 +6,27 @@ import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.api.internal.InternalFlywheelApi;
import com.jozufozu.flywheel.api.registry.Registry;
import com.jozufozu.flywheel.api.visualization.VisualEmbedding;
import com.jozufozu.flywheel.backend.engine.Samplers;
public class ContextShaders {
public static final Registry<ContextShader> REGISTRY = InternalFlywheelApi.INSTANCE.createRegistry();
public static final ContextShader DEFAULT = REGISTRY.registerAndGet(new ContextShader(Flywheel.rl("internal/context/default.vert"), Flywheel.rl("internal/context/default.frag")));
public static final ContextShader CRUMBLING = REGISTRY.registerAndGet(new ContextShader(Flywheel.rl("internal/context/crumbling.vert"), Flywheel.rl("internal/context/crumbling.frag")));
public static final ContextShader EMBEDDED = REGISTRY.registerAndGet(new ContextShader(Flywheel.rl("internal/context/embedded.vert"), Flywheel.rl("internal/context/embedded.frag")));
public static final ContextShader DEFAULT = REGISTRY.registerAndGet(ContextShader.builder()
.vertexShader(Flywheel.rl("internal/context/default.vert"))
.fragmentShader(Flywheel.rl("internal/context/default.frag"))
.onLink($ -> {
})
.build());
public static final ContextShader CRUMBLING = REGISTRY.registerAndGet(ContextShader.builder()
.vertexShader(Flywheel.rl("internal/context/crumbling.vert"))
.fragmentShader(Flywheel.rl("internal/context/crumbling.frag"))
.onLink(program -> program.setSamplerBinding("_flw_crumblingTex", Samplers.CRUMBLING))
.build());
public static final ContextShader EMBEDDED = REGISTRY.registerAndGet(ContextShader.builder()
.vertexShader(Flywheel.rl("internal/context/embedded.vert"))
.fragmentShader(Flywheel.rl("internal/context/embedded.frag"))
.onLink($ -> {
})
.build());
public static ContextShader forEmbedding(@Nullable VisualEmbedding level) {
if (level == null) {

View File

@ -1,8 +1,12 @@
package com.jozufozu.flywheel.backend.compile;
import java.util.Objects;
import java.util.function.Consumer;
import org.jetbrains.annotations.Nullable;
import com.jozufozu.flywheel.api.instance.InstanceType;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import com.jozufozu.flywheel.backend.glsl.GlslVersion;
import com.jozufozu.flywheel.backend.glsl.SourceComponent;
@ -10,7 +14,8 @@ import net.minecraft.resources.ResourceLocation;
public record Pipeline(GlslVersion glslVersion, ResourceLocation vertexMain, ResourceLocation fragmentMain,
ResourceLocation vertexApiImpl, ResourceLocation fragmentApiImpl, InstanceAssembler assembler,
String compilerMarker) {
String compilerMarker, Consumer<GlProgram> onLink) {
@FunctionalInterface
public interface InstanceAssembler {
/**
@ -29,13 +34,22 @@ public record Pipeline(GlslVersion glslVersion, ResourceLocation vertexMain, Res
}
public static class Builder {
@Nullable
private GlslVersion glslVersion;
@Nullable
private ResourceLocation vertexMain;
@Nullable
private ResourceLocation fragmentMain;
@Nullable
private ResourceLocation vertexApiImpl;
@Nullable
private ResourceLocation fragmentApiImpl;
@Nullable
private InstanceAssembler assembler;
@Nullable
private String compilerMarker;
@Nullable
private Consumer<GlProgram> onLink;
public Builder glslVersion(GlslVersion glslVersion) {
this.glslVersion = glslVersion;
@ -72,6 +86,11 @@ public record Pipeline(GlslVersion glslVersion, ResourceLocation vertexMain, Res
return this;
}
public Builder onLink(Consumer<GlProgram> onLink) {
this.onLink = onLink;
return this;
}
public Pipeline build() {
Objects.requireNonNull(glslVersion);
Objects.requireNonNull(vertexMain);
@ -80,7 +99,8 @@ public record Pipeline(GlslVersion glslVersion, ResourceLocation vertexMain, Res
Objects.requireNonNull(fragmentApiImpl);
Objects.requireNonNull(assembler);
Objects.requireNonNull(compilerMarker);
return new Pipeline(glslVersion, vertexMain, fragmentMain, vertexApiImpl, fragmentApiImpl, assembler, compilerMarker);
Objects.requireNonNull(onLink);
return new Pipeline(glslVersion, vertexMain, fragmentMain, vertexApiImpl, fragmentApiImpl, assembler, compilerMarker, onLink);
}
}
}

View File

@ -5,6 +5,7 @@ import java.util.List;
import com.jozufozu.flywheel.backend.InternalVertex;
import com.jozufozu.flywheel.backend.compile.core.CompilationHarness;
import com.jozufozu.flywheel.backend.compile.core.Compile;
import com.jozufozu.flywheel.backend.engine.Samplers;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
import com.jozufozu.flywheel.backend.glsl.ShaderSources;
@ -64,9 +65,14 @@ public class PipelineCompiler {
program.bind();
program.setSamplerBinding("_flw_diffuseTex", 0);
program.setSamplerBinding("_flw_overlayTex", 1);
program.setSamplerBinding("_flw_lightTex", 2);
program.setSamplerBinding("flw_diffuseTex", Samplers.DIFFUSE);
program.setSamplerBinding("flw_overlayTex", Samplers.OVERLAY);
program.setSamplerBinding("flw_lightTex", Samplers.LIGHT);
pipeline.onLink()
.accept(program);
key.contextShader()
.onLink()
.accept(program);
GlProgram.unbind();
})

View File

@ -3,6 +3,7 @@ package com.jozufozu.flywheel.backend.compile;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.backend.compile.component.IndirectComponent;
import com.jozufozu.flywheel.backend.compile.component.SamplerBufferComponent;
import com.jozufozu.flywheel.backend.engine.Samplers;
import com.jozufozu.flywheel.backend.glsl.GlslVersion;
public final class Pipelines {
@ -14,6 +15,7 @@ public final class Pipelines {
.vertexApiImpl(Flywheel.rl("internal/instancing/api_impl.vert"))
.fragmentApiImpl(Flywheel.rl("internal/instancing/api_impl.frag"))
.assembler(SamplerBufferComponent::create)
.onLink(program -> program.setSamplerBinding("_flw_instances", Samplers.INSTANCE_BUFFER))
.build();
public static final Pipeline INDIRECT = Pipeline.builder()
.compilerMarker("indirect")
@ -23,5 +25,7 @@ public final class Pipelines {
.vertexApiImpl(Flywheel.rl("internal/indirect/api_impl.vert"))
.fragmentApiImpl(Flywheel.rl("internal/indirect/api_impl.frag"))
.assembler(IndirectComponent::create)
.onLink($ -> {
})
.build();
}

View File

@ -53,7 +53,7 @@ public class SamplerBufferComponent implements SourceComponent {
@Override
public ResourceLocation name() {
return Flywheel.rl("generated_indirect");
return Flywheel.rl("generated_instancing");
}
@Override

View File

@ -3,13 +3,9 @@ package com.jozufozu.flywheel.backend.engine;
import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.api.material.Transparency;
import com.jozufozu.flywheel.api.material.WriteMask;
import com.jozufozu.flywheel.backend.gl.GlTextureUnit;
import com.jozufozu.flywheel.lib.material.CutoutShaders;
import com.jozufozu.flywheel.lib.material.FogShaders;
import com.jozufozu.flywheel.lib.material.SimpleMaterial;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.Minecraft;
public class CommonCrumbling {
public static void applyCrumblingProperties(SimpleMaterial.Builder crumblingMaterial, Material baseMaterial) {
@ -23,17 +19,4 @@ public class CommonCrumbling {
.useLight(false)
.diffuse(false);
}
public static int getDiffuseTexture(Material material) {
return Minecraft.getInstance()
.getTextureManager()
.getTexture(material.texture())
.getId();
}
public static void setActiveAndBindForCrumbling(int diffuseTexture) {
GlTextureUnit.T1.makeActive();
RenderSystem.setShaderTexture(1, diffuseTexture);
RenderSystem.bindTexture(diffuseTexture);
}
}

View File

@ -8,7 +8,6 @@ import com.jozufozu.flywheel.api.material.DepthTest;
import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.api.material.Transparency;
import com.jozufozu.flywheel.api.material.WriteMask;
import com.jozufozu.flywheel.backend.gl.GlTextureUnit;
import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.systems.RenderSystem;
@ -38,7 +37,7 @@ public final class MaterialRenderState {
}
private static void setupTexture(Material material) {
GlTextureUnit.T0.makeActive();
Samplers.DIFFUSE.makeActive();
AbstractTexture texture = Minecraft.getInstance()
.getTextureManager()
.getTexture(material.texture());
@ -150,7 +149,7 @@ public final class MaterialRenderState {
}
private static void resetTexture() {
GlTextureUnit.T0.makeActive();
Samplers.DIFFUSE.makeActive();
RenderSystem.setShaderTexture(0, 0);
}

View File

@ -0,0 +1,11 @@
package com.jozufozu.flywheel.backend.engine;
import com.jozufozu.flywheel.backend.gl.GlTextureUnit;
public class Samplers {
public static final GlTextureUnit DIFFUSE = GlTextureUnit.T0;
public static final GlTextureUnit OVERLAY = GlTextureUnit.T1;
public static final GlTextureUnit LIGHT = GlTextureUnit.T2;
public static final GlTextureUnit CRUMBLING = GlTextureUnit.T3;
public static final GlTextureUnit INSTANCE_BUFFER = GlTextureUnit.T4;
}

View File

@ -1,62 +1,24 @@
package com.jozufozu.flywheel.backend.engine;
import static org.lwjgl.opengl.GL13.GL_TEXTURE0;
import com.jozufozu.flywheel.backend.gl.GlTextureUnit;
import com.mojang.blaze3d.systems.RenderSystem;
import it.unimi.dsi.fastutil.ints.Int2IntArrayMap;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import net.minecraft.client.Minecraft;
import net.minecraft.resources.ResourceLocation;
public class TextureBinder {
// TODO: some kind of cache eviction when the program changes
// so we don't always reset and bind the light and overlay textures?
private static final Int2IntMap texturesToSamplerUnits = new Int2IntArrayMap();
// 0 is reserved for diffuse
// 1 is overlay
// 2 is light
// 3 is the instance buffer
// 4..n are for whatever else the context needs
private static final int baseSamplerUnit = 4;
private static int nextSamplerUnit = baseSamplerUnit;
/**
* Binds the given texture to the next available texture unit, returning the unit it was bound to.
*
* @param id The id of the texture to bind.
* @return The texture unit the texture was bound to.
*/
public static int bindTexture(int id) {
return texturesToSamplerUnits.computeIfAbsent(id, i -> {
int unit = nextSamplerUnit++;
RenderSystem.activeTexture(GL_TEXTURE0 + unit);
RenderSystem.bindTexture(i);
return unit;
});
}
public static void resetTextureBindings() {
nextSamplerUnit = baseSamplerUnit;
for (Int2IntMap.Entry entry : texturesToSamplerUnits.int2IntEntrySet()) {
RenderSystem.activeTexture(GL_TEXTURE0 + entry.getIntValue());
RenderSystem.bindTexture(0);
}
texturesToSamplerUnits.clear();
public static void bind(ResourceLocation resourceLocation) {
RenderSystem.bindTexture(byName(resourceLocation));
}
public static void bindLightAndOverlay() {
var gameRenderer = Minecraft.getInstance().gameRenderer;
GlTextureUnit.T1.makeActive();
Samplers.OVERLAY.makeActive();
gameRenderer.overlayTexture()
.setupOverlayColor();
RenderSystem.bindTexture(RenderSystem.getShaderTexture(1));
GlTextureUnit.T2.makeActive();
Samplers.LIGHT.makeActive();
gameRenderer.lightTexture()
.turnOnLightLayer();
RenderSystem.bindTexture(RenderSystem.getShaderTexture(2));

View File

@ -29,7 +29,6 @@ import com.jozufozu.flywheel.backend.compile.ContextShaders;
import com.jozufozu.flywheel.backend.compile.IndirectPrograms;
import com.jozufozu.flywheel.backend.engine.MaterialRenderState;
import com.jozufozu.flywheel.backend.engine.MeshPool;
import com.jozufozu.flywheel.backend.engine.TextureBinder;
import com.jozufozu.flywheel.backend.engine.uniform.Uniforms;
import com.jozufozu.flywheel.backend.gl.Driver;
import com.jozufozu.flywheel.backend.gl.GlCompat;
@ -226,7 +225,6 @@ public class IndirectCullingGroup<I extends Instance> {
MaterialRenderState.setup(multiDraw.material);
multiDraw.submit();
TextureBinder.resetTextureBindings();
}
}

View File

@ -26,6 +26,7 @@ import com.jozufozu.flywheel.backend.engine.InstancerKey;
import com.jozufozu.flywheel.backend.engine.InstancerStorage;
import com.jozufozu.flywheel.backend.engine.MaterialRenderState;
import com.jozufozu.flywheel.backend.engine.MeshPool;
import com.jozufozu.flywheel.backend.engine.Samplers;
import com.jozufozu.flywheel.backend.engine.TextureBinder;
import com.jozufozu.flywheel.backend.engine.uniform.Uniforms;
import com.jozufozu.flywheel.backend.gl.GlStateTracker;
@ -167,8 +168,11 @@ public class IndirectDrawManager extends InstancerStorage<IndirectInstancer<?>>
var program = cullingGroups.get(instanceTypeEntry.getKey())
.bindWithContextShader(ContextShaders.CRUMBLING);
program.setSamplerBinding("crumblingTex", Samplers.CRUMBLING);
for (var progressEntry : byProgress.int2ObjectEntrySet()) {
program.setTexture("crumblingTex", TextureBinder.byName(ModelBakery.BREAKING_LOCATIONS.get(progressEntry.getIntKey())));
Samplers.CRUMBLING.makeActive();
TextureBinder.bind(ModelBakery.BREAKING_LOCATIONS.get(progressEntry.getIntKey()));
for (var instanceHandlePair : progressEntry.getValue()) {
IndirectInstancer<?> instancer = instanceHandlePair.first();
@ -190,7 +194,6 @@ public class IndirectDrawManager extends InstancerStorage<IndirectInstancer<?>>
}
}
TextureBinder.resetTextureBindings();
}
}

View File

@ -28,10 +28,10 @@ import com.jozufozu.flywheel.backend.engine.InstancerStorage;
import com.jozufozu.flywheel.backend.engine.MaterialEncoder;
import com.jozufozu.flywheel.backend.engine.MaterialRenderState;
import com.jozufozu.flywheel.backend.engine.MeshPool;
import com.jozufozu.flywheel.backend.engine.Samplers;
import com.jozufozu.flywheel.backend.engine.TextureBinder;
import com.jozufozu.flywheel.backend.engine.uniform.Uniforms;
import com.jozufozu.flywheel.backend.gl.GlStateTracker;
import com.jozufozu.flywheel.backend.gl.GlTextureUnit;
import com.jozufozu.flywheel.backend.gl.TextureBuffer;
import com.jozufozu.flywheel.backend.gl.array.GlVertexArray;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
@ -140,14 +140,11 @@ public class InstancedDrawManager extends InstancerStorage<InstancedInstancer<?>
MaterialRenderState.setup(material);
GlTextureUnit.T3.makeActive();
program.setSamplerBinding("_flw_instances", 3);
Samplers.INSTANCE_BUFFER.makeActive();
for (var drawCall : drawCalls) {
drawCall.render(instanceTexture);
}
TextureBinder.resetTextureBindings();
}
MaterialRenderState.reset();
@ -218,16 +215,14 @@ public class InstancedDrawManager extends InstancerStorage<InstancedInstancer<?>
continue;
}
program.setTexture("crumblingTex", TextureBinder.byName(ModelBakery.BREAKING_LOCATIONS.get(progressEntry.getIntKey())));
Samplers.CRUMBLING.makeActive();
TextureBinder.bind(ModelBakery.BREAKING_LOCATIONS.get(progressEntry.getIntKey()));
GlTextureUnit.T3.makeActive();
program.setSamplerBinding("_flw_instances", 3);
Samplers.INSTANCE_BUFFER.makeActive();
for (Consumer<TextureBuffer> drawCall : drawCalls) {
drawCall.accept(instanceTexture);
}
TextureBinder.resetTextureBindings();
}
}

View File

@ -18,8 +18,8 @@ import org.joml.Matrix3fc;
import org.joml.Matrix4fc;
import org.slf4j.Logger;
import com.jozufozu.flywheel.backend.engine.TextureBinder;
import com.jozufozu.flywheel.backend.gl.GlObject;
import com.jozufozu.flywheel.backend.gl.GlTextureUnit;
import com.mojang.blaze3d.shaders.ProgramManager;
import com.mojang.logging.LogUtils;
@ -43,18 +43,6 @@ public class GlProgram extends GlObject {
ProgramManager.glUseProgram(0);
}
public void setTexture(String glslName, int texture) {
int uniform = getUniformLocation(glslName);
if (uniform < 0) {
return;
}
int binding = TextureBinder.bindTexture(texture);
glUniform1i(uniform, binding);
}
public void setFloat(String glslName, float value) {
int uniform = getUniformLocation(glslName);
@ -148,6 +136,10 @@ public class GlProgram extends GlObject {
* @param name The name of the sampler uniform.
* @param binding The index of the texture unit.
*/
public void setSamplerBinding(String name, GlTextureUnit binding) {
setSamplerBinding(name, binding.number);
}
public void setSamplerBinding(String name, int binding) {
int samplerUniform = getUniformLocation(name);

View File

@ -6,16 +6,12 @@
layout (depth_greater) out float gl_FragDepth;
#endif
uniform sampler2D _flw_diffuseTex;
uniform sampler2D _flw_overlayTex;
uniform sampler2D _flw_lightTex;
out vec4 _flw_outputColor;
in vec4 _flw_debugColor;
void _flw_main() {
flw_sampleColor = texture(_flw_diffuseTex, flw_vertexTexCoord);
flw_sampleColor = texture(flw_diffuseTex, flw_vertexTexCoord);
flw_fragColor = flw_vertexColor * flw_sampleColor;
flw_fragOverlay = flw_vertexOverlay;
flw_fragLight = flw_vertexLight;
@ -40,12 +36,12 @@ void _flw_main() {
// Need to clamp the overlay texture coords to sane coordinates because integer vertex attributes explode on
// some drivers for some draw calls. This should only effect instances that don't write to overlay, but
// the internal vertex format is unfortunately subject to these issues.
vec4 overlayColor = texelFetch(_flw_overlayTex, clamp(flw_fragOverlay, 0, 10), 0);
vec4 overlayColor = texelFetch(flw_overlayTex, clamp(flw_fragOverlay, 0, 10), 0);
color.rgb = mix(overlayColor.rgb, color.rgb, overlayColor.a);
}
if (flw_material.useLight) {
vec4 lightColor = texture(_flw_lightTex, (flw_fragLight * 15.0 + 0.5) / 16.0);
vec4 lightColor = texture(flw_lightTex, (flw_fragLight * 15.0 + 0.5) / 16.0);
color *= lightColor;
}

View File

@ -19,3 +19,7 @@ bool flw_fragDiffuse;
vec4 flw_fragColor;
ivec2 flw_fragOverlay;
vec2 flw_fragLight;
uniform sampler2D flw_diffuseTex;
uniform sampler2D flw_overlayTex;
uniform sampler2D flw_lightTex;

View File

@ -1,4 +1,4 @@
uniform sampler2D crumblingTex;
uniform sampler2D _flw_crumblingTex;
in vec2 crumblingTexCoord;
@ -8,7 +8,7 @@ void flw_beginFragment() {
}
void flw_endFragment() {
crumblingSampleColor = texture(crumblingTex, crumblingTexCoord);
crumblingSampleColor = texture(_flw_crumblingTex, crumblingTexCoord);
// Make the crumbling overlay transparent when the fragment color after the material shader is transparent.
flw_fragColor.rgb = crumblingSampleColor.rgb;