Making materials matter more

- Materials are given the opportunity to setup/clear render state
 - Materials bind textures
 - Compile all combinations of shader components at load
 - Drastically simplify compilation context objects
 - Material shaders control cutout and fog
 - Remove all GameState related classes
 - Remove CoreShaderInfoMap
 - Sneaky optimization to instance storage
 - Simplify RenderLists
This commit is contained in:
Jozufozu 2022-07-23 18:18:48 -07:00
parent d0c6669a49
commit 0360c1faf8
38 changed files with 408 additions and 575 deletions

View file

@ -12,14 +12,12 @@ import com.jozufozu.flywheel.config.BackendTypeArgument;
import com.jozufozu.flywheel.config.FlwCommands;
import com.jozufozu.flywheel.config.FlwConfig;
import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.GameStateRegistry;
import com.jozufozu.flywheel.core.Models;
import com.jozufozu.flywheel.core.PartialModel;
import com.jozufozu.flywheel.core.QuadConverter;
import com.jozufozu.flywheel.core.StitchedSprite;
import com.jozufozu.flywheel.core.compile.ProgramCompiler;
import com.jozufozu.flywheel.core.crumbling.CrumblingRenderer;
import com.jozufozu.flywheel.core.shader.NormalDebugStateProvider;
import com.jozufozu.flywheel.event.EntityWorldHandler;
import com.jozufozu.flywheel.event.ForgeEvents;
import com.jozufozu.flywheel.event.ReloadRenderersEvent;
@ -111,8 +109,6 @@ public class Flywheel {
Components.init();
GameStateRegistry.register(NormalDebugStateProvider.INSTANCE);
VanillaInstances.init();
// https://github.com/Jozufozu/Flywheel/issues/69

View file

@ -4,4 +4,6 @@ import net.minecraft.core.BlockPos;
public interface Instance {
BlockPos getWorldPosition();
boolean isRemoved();
}

View file

@ -4,13 +4,18 @@ import com.jozufozu.flywheel.api.RenderStage;
import com.jozufozu.flywheel.core.source.FileResolution;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.resources.ResourceLocation;
public interface Material {
RenderStage getRenderStage();
RenderType getRenderType();
RenderType getBatchingRenderType();
FileResolution getVertexShader();
FileResolution getFragmentShader();
void setup();
void clear();
}

View file

@ -1,11 +1,18 @@
package com.jozufozu.flywheel.backend;
import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
import com.jozufozu.flywheel.core.ComponentRegistry;
import com.jozufozu.flywheel.core.compile.ContextShader;
import com.jozufozu.flywheel.core.compile.ProgramCompiler;
import com.jozufozu.flywheel.core.crumbling.CrumblingRenderer;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.core.source.ShaderLoadingException;
import com.jozufozu.flywheel.core.source.ShaderSources;
import com.jozufozu.flywheel.core.source.error.ErrorReporter;
import com.jozufozu.flywheel.util.StringUtil;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
@ -40,6 +47,8 @@ public class Loader implements ResourceManagerReloadListener {
var errorReporter = new ErrorReporter();
ShaderSources sources = new ShaderSources(errorReporter, manager);
Backend.LOGGER.info("Loaded all shader sources in " + sources.getLoadTime());
FileResolution.run(errorReporter, sources);
if (errorReporter.hasErrored()) {
@ -49,9 +58,28 @@ public class Loader implements ResourceManagerReloadListener {
sources.postResolve();
Backend.LOGGER.info("Successfully resolved all source files.");
FileResolution.checkAll(errorReporter);
Backend.LOGGER.info("Loaded all shader sources.");
Backend.LOGGER.info("All shaders passed checks.");
long compileStart = System.nanoTime();
for (Material material : ComponentRegistry.materials) {
for (StructType<?> structType : ComponentRegistry.structTypes) {
for (VertexType vertexType : ComponentRegistry.vertexTypes) {
for (ContextShader contextShader : ComponentRegistry.contextShaders) {
var ctx = new ProgramCompiler.Context(vertexType, material, structType.getInstanceShader(), contextShader);
ProgramCompiler.INSTANCE.getProgram(ctx);
}
}
}
}
long compileEnd = System.nanoTime();
Backend.LOGGER.info("Compiled all programs in " + StringUtil.formatTime(compileEnd - compileStart));
ClientLevel world = Minecraft.getInstance().level;
if (Backend.canUseInstancing(world)) {

View file

@ -86,7 +86,7 @@ public class GlVertexArray extends GlObject {
offsets[i] = offset;
strides[i] = stride;
GL20.glVertexAttribPointer(i++, attribute.size(), attribute.type().getGlEnum(), attribute.normalized(), stride, offset);
GL32.glVertexAttribPointer(i++, attribute.size(), attribute.type().getGlEnum(), attribute.normalized(), stride, offset);
offset += attribute.getByteWidth();
}

View file

@ -11,7 +11,6 @@ import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.gl.GlObject;
import com.jozufozu.flywheel.backend.gl.versioned.GlCompat;
import com.jozufozu.flywheel.core.compile.ShaderCompilationException;
import com.jozufozu.flywheel.core.shader.ShaderConstants;
import net.minecraft.client.Minecraft;
import net.minecraft.resources.ResourceLocation;
@ -20,12 +19,10 @@ public class GlShader extends GlObject {
public final ShaderType type;
private final List<ResourceLocation> parts;
private final ShaderConstants constants;
public GlShader(String source, ShaderType type, List<ResourceLocation> parts, ShaderConstants constants) throws ShaderCompilationException {
public GlShader(String source, ShaderType type, List<ResourceLocation> parts) throws ShaderCompilationException {
this.parts = parts;
this.type = type;
this.constants = constants;
int handle = GL20.glCreateShader(type.glEnum);
GlCompat.safeShaderSource(handle, source);
@ -50,7 +47,7 @@ public class GlShader extends GlObject {
.map(ResourceLocation::toString)
.map(s -> s.replaceAll("/", "_")
.replaceAll(":", "\\$"))
.collect(Collectors.joining(";")) + ';' + Integer.toHexString(constants.hashCode());
.collect(Collectors.joining(";"));
}
private void dumpSource(String source, ShaderType type) {

View file

@ -90,6 +90,11 @@ public abstract class AbstractInstance implements Instance, LightListener {
return removed;
}
@Override
public boolean isRemoved() {
return removed;
}
@Override
public void onLightUpdate(LightLayer type, ImmutableBox changed) {
updateLight();

View file

@ -0,0 +1,48 @@
package com.jozufozu.flywheel.backend.instancing;
import java.util.ArrayList;
import java.util.List;
import com.jozufozu.flywheel.api.instance.DynamicInstance;
import com.jozufozu.flywheel.api.instance.TickableInstance;
import com.jozufozu.flywheel.api.instancer.InstancerManager;
import com.jozufozu.flywheel.light.LightUpdater;
public abstract class AbstractStorage<T> implements Storage<T> {
protected final List<TickableInstance> tickableInstances;
protected final List<DynamicInstance> dynamicInstances;
protected final InstancerManager instancerManager;
protected AbstractStorage(InstancerManager instancerManager) {
this.instancerManager = instancerManager;
this.dynamicInstances = new ArrayList<>();
this.tickableInstances = new ArrayList<>();
}
@Override
public List<TickableInstance> getInstancesForTicking() {
return tickableInstances;
}
@Override
public List<DynamicInstance> getInstancesForUpdate() {
return dynamicInstances;
}
protected void setup(AbstractInstance renderer) {
renderer.init();
renderer.updateLight();
LightUpdater.get(renderer.level)
.addListener(renderer);
if (renderer instanceof TickableInstance r) {
tickableInstances.add(r);
r.tick();
}
if (renderer instanceof DynamicInstance r) {
dynamicInstances.add(r);
r.beginFrame();
}
}
}

View file

@ -2,6 +2,7 @@ package com.jozufozu.flywheel.backend.instancing;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@ -12,20 +13,12 @@ import com.jozufozu.flywheel.api.instance.TickableInstance;
import com.jozufozu.flywheel.api.instancer.InstancerManager;
import com.jozufozu.flywheel.light.LightUpdater;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
public abstract class One2OneStorage<T> implements Storage<T> {
public abstract class One2OneStorage<T> extends AbstractStorage<T> {
private final Map<T, AbstractInstance> instances;
private final Object2ObjectOpenHashMap<T, TickableInstance> tickableInstances;
private final Object2ObjectOpenHashMap<T, DynamicInstance> dynamicInstances;
protected final InstancerManager instancerManager;
public One2OneStorage(InstancerManager instancerManager) {
this.instancerManager = instancerManager;
super(instancerManager);
this.instances = new HashMap<>();
this.dynamicInstances = new Object2ObjectOpenHashMap<>();
this.tickableInstances = new Object2ObjectOpenHashMap<>();
}
@Override
@ -38,16 +31,6 @@ public abstract class One2OneStorage<T> implements Storage<T> {
return instances.values();
}
@Override
public List<TickableInstance> getInstancesForTicking() {
return new ArrayList<>(tickableInstances.values());
}
@Override
public List<DynamicInstance> getInstancesForUpdate() {
return new ArrayList<>(dynamicInstances.values());
}
@Override
public void invalidate() {
instances.values().forEach(AbstractInstance::remove);
@ -74,8 +57,8 @@ public abstract class One2OneStorage<T> implements Storage<T> {
}
instance.remove();
dynamicInstances.remove(obj);
tickableInstances.remove(obj);
dynamicInstances.remove(instance);
tickableInstances.remove(instance);
LightUpdater.get(instance.level)
.removeListener(instance);
}
@ -109,7 +92,7 @@ public abstract class One2OneStorage<T> implements Storage<T> {
AbstractInstance out = createRaw(obj);
if (out != null) {
setup(obj, out);
setup(out);
}
return out;
@ -120,7 +103,7 @@ public abstract class One2OneStorage<T> implements Storage<T> {
AbstractInstance renderer = createRaw(obj);
if (renderer != null) {
setup(obj, renderer);
setup(renderer);
instances.put(obj, renderer);
}
@ -128,20 +111,4 @@ public abstract class One2OneStorage<T> implements Storage<T> {
@Nullable
protected abstract AbstractInstance createRaw(T obj);
private void setup(T obj, AbstractInstance renderer) {
renderer.init();
renderer.updateLight();
LightUpdater.get(renderer.level)
.addListener(renderer);
if (renderer instanceof TickableInstance r) {
tickableInstances.put(obj, r);
r.tick();
}
if (renderer instanceof DynamicInstance r) {
dynamicInstances.put(obj, r);
r.beginFrame();
}
}
}

View file

@ -12,6 +12,7 @@ public class BatchLists {
public final Map<RenderType, List<TransformSet<?>>> renderLists = new HashMap<>();
public void add(TransformSet<?> set) {
renderLists.computeIfAbsent(set.material.getRenderType(), k -> new ArrayList<>()).add(set);
renderLists.computeIfAbsent(set.material.getBatchingRenderType(), k -> new ArrayList<>())
.add(set);
}
}

View file

@ -11,6 +11,7 @@ import com.jozufozu.flywheel.api.instance.DynamicInstance;
import com.jozufozu.flywheel.api.instance.TickableInstance;
import com.jozufozu.flywheel.api.instancer.InstancerManager;
import com.jozufozu.flywheel.backend.instancing.AbstractInstance;
import com.jozufozu.flywheel.backend.instancing.AbstractStorage;
import com.jozufozu.flywheel.backend.instancing.InstanceManager;
import com.jozufozu.flywheel.backend.instancing.Storage;
import com.jozufozu.flywheel.light.LightUpdater;
@ -33,18 +34,13 @@ public class EffectInstanceManager extends InstanceManager<Effect> {
return true;
}
public static class EffectStorage<T extends Effect> implements Storage<T> {
public static class EffectStorage<T extends Effect> extends AbstractStorage<T> {
private final Multimap<T, AbstractInstance> instances;
private final Set<DynamicInstance> dynamicInstances;
private final Set<TickableInstance> tickableInstances;
private final InstancerManager manager;
public EffectStorage(InstancerManager manager) {
super(manager);
this.instances = HashMultimap.create();
this.dynamicInstances = new HashSet<>();
this.tickableInstances = new HashSet<>();
this.manager = manager;
}
@Override
@ -57,16 +53,6 @@ public class EffectInstanceManager extends InstanceManager<Effect> {
return instances.values();
}
@Override
public List<TickableInstance> getInstancesForTicking() {
return new ArrayList<>(tickableInstances);
}
@Override
public List<DynamicInstance> getInstancesForUpdate() {
return new ArrayList<>(dynamicInstances);
}
@Override
public void invalidate() {
instances.values().forEach(AbstractInstance::removeAndMark);
@ -123,27 +109,11 @@ public class EffectInstanceManager extends InstanceManager<Effect> {
}
private void create(T obj) {
var instances = obj.createInstances(manager);
var instances = obj.createInstances(instancerManager);
this.instances.putAll(obj, instances);
instances.forEach(this::setup);
}
private void setup(AbstractInstance renderer) {
renderer.init();
renderer.updateLight();
LightUpdater.get(renderer.level)
.addListener(renderer);
if (renderer instanceof TickableInstance r) {
tickableInstances.add(r);
r.tick();
}
if (renderer instanceof DynamicInstance r) {
dynamicInstances.add(r);
r.beginFrame();
}
}
}
}

View file

@ -6,6 +6,7 @@ import java.util.List;
import java.util.Map;
import org.jetbrains.annotations.NotNull;
import org.lwjgl.opengl.GL32;
import com.google.common.collect.ListMultimap;
import com.jozufozu.flywheel.api.RenderStage;
@ -13,23 +14,21 @@ import com.jozufozu.flywheel.api.instancer.InstancedPart;
import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.gl.GlTextureUnit;
import com.jozufozu.flywheel.backend.instancing.Engine;
import com.jozufozu.flywheel.backend.instancing.InstanceManager;
import com.jozufozu.flywheel.backend.instancing.TaskEngine;
import com.jozufozu.flywheel.backend.model.MeshPool;
import com.jozufozu.flywheel.core.CoreShaderInfoMap.CoreShaderInfo;
import com.jozufozu.flywheel.core.GameStateRegistry;
import com.jozufozu.flywheel.core.RenderContext;
import com.jozufozu.flywheel.core.compile.ContextShader;
import com.jozufozu.flywheel.core.compile.ProgramCompiler;
import com.jozufozu.flywheel.core.shader.StateSnapshot;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.core.uniform.UniformBuffer;
import com.jozufozu.flywheel.util.Textures;
import com.jozufozu.flywheel.util.WeakHashSet;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.Camera;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Vec3i;
import net.minecraft.util.Mth;
@ -73,20 +72,11 @@ public class InstancingEngine implements Engine {
@Override
public void renderStage(TaskEngine taskEngine, RenderContext context, RenderStage stage) {
if (!renderLists.process(stage)) {
return;
}
var multimap = renderLists.get(stage);
var renderList = renderLists.get(stage);
for (var entry : renderList.entrySet()) {
var multimap = entry.getValue();
setup();
if (multimap.isEmpty()) {
return;
}
render(entry.getKey(), multimap);
}
render(multimap);
}
// TODO: Is this useful? Should it be added to the base interface? Currently it is only used for the old CrumblingRenderer.
@ -96,28 +86,29 @@ public class InstancingEngine implements Engine {
return;
}
for (RenderStage stage : renderLists.stagesToProcess) {
var renderList = renderLists.get(stage);
for (var entry : renderList.entrySet()) {
var multimap = entry.getValue();
setup();
for (var multimap : renderLists.getAll()) {
render(multimap);
}
}
private void setup() {
GlTextureUnit.T2.makeActive();
Minecraft.getInstance().gameRenderer.lightTexture().turnOnLightLayer();
RenderSystem.depthMask(true);
RenderSystem.colorMask(true, true, true, true);
RenderSystem.enableDepthTest();
RenderSystem.depthFunc(GL32.GL_LEQUAL);
RenderSystem.enableCull();
}
protected void render(ListMultimap<ShaderState, DrawCall> multimap) {
if (multimap.isEmpty()) {
return;
}
render(entry.getKey(), multimap);
}
}
renderLists.stagesToProcess.clear();
}
protected void render(RenderType type, ListMultimap<ShaderState, DrawCall> multimap) {
type.setupRenderState();
Textures.bindActiveTextures();
CoreShaderInfo coreShaderInfo = CoreShaderInfo.get();
StateSnapshot state = GameStateRegistry.takeSnapshot();
for (var entry : multimap.asMap().entrySet()) {
var shader = entry.getKey();
var drawCalls = entry.getValue();
@ -128,28 +119,29 @@ public class InstancingEngine implements Engine {
continue;
}
setup(shader, coreShaderInfo, state);
setup(shader);
shader.material().setup();
for (var drawCall : drawCalls) {
drawCall.render();
}
shader.material().clear();
}
}
type.clearRenderState();
}
protected void setup(ShaderState desc, CoreShaderInfo coreShaderInfo, StateSnapshot ctx) {
protected void setup(ShaderState desc) {
VertexType vertexType = desc.vertex();
FileResolution instanceShader = desc.instance()
.getInstanceShader();
Material material = desc.material();
var program = ProgramCompiler.INSTANCE.getProgram(new ProgramCompiler.Context(vertexType, material,
instanceShader, context, coreShaderInfo.getAdjustedAlphaDiscard(), coreShaderInfo.fogType(), ctx));
var ctx = new ProgramCompiler.Context(vertexType, material, instanceShader, context);
program.bind();
ProgramCompiler.INSTANCE.getProgram(ctx)
.bind();
UniformBuffer.getInstance().sync();
}
@ -197,8 +189,6 @@ public class InstancingEngine implements Engine {
}
uninitializedModels.clear();
renderLists.prepare();
MeshPool.getInstance()
.flush();
}

View file

@ -1,28 +1,23 @@
package com.jozufozu.flywheel.backend.instancing.instancing;
import java.util.Collections;
import java.util.Collection;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ListMultimap;
import com.jozufozu.flywheel.api.RenderStage;
import com.jozufozu.flywheel.api.material.Material;
import net.minecraft.client.renderer.RenderType;
public class RenderLists {
private final Map<RenderStage, Map<RenderType, ListMultimap<ShaderState, DrawCall>>> renderLists = new EnumMap<>(RenderStage.class);
public final Set<RenderStage> stagesToProcess = EnumSet.noneOf(RenderStage.class);
public final Map<RenderStage, ListMultimap<ShaderState, DrawCall>> renderLists = new EnumMap<>(RenderStage.class);
public Map<RenderType, ListMultimap<ShaderState, DrawCall>> get(RenderStage stage) {
public ListMultimap<ShaderState, DrawCall> get(RenderStage stage) {
var renderList = renderLists.get(stage);
if (renderList == null) {
return Collections.emptyMap();
return ImmutableListMultimap.of();
}
return renderList;
}
@ -30,28 +25,15 @@ public class RenderLists {
public void add(ShaderState shaderState, DrawCall layer) {
Material material = shaderState.material();
renderLists
.computeIfAbsent(material.getRenderStage(), k -> new HashMap<>())
.computeIfAbsent(material.getRenderType(), k -> ArrayListMultimap.create())
renderLists.computeIfAbsent(material.getRenderStage(), k -> ArrayListMultimap.create())
.put(shaderState, layer);
}
public void prepare() {
stagesToProcess.clear();
stagesToProcess.addAll(renderLists.keySet());
}
/**
* Check and mark a stage as processed.
* @param stage The stage to check.
* @return {@code true} if the stage should be processed.
*/
public boolean process(RenderStage stage) {
return stagesToProcess.remove(stage);
}
public boolean isEmpty() {
return stagesToProcess.isEmpty();
return renderLists.isEmpty();
}
public Collection<ListMultimap<ShaderState, DrawCall>> getAll() {
return renderLists.values();
}
}

View file

@ -16,44 +16,55 @@ import com.jozufozu.flywheel.core.compile.ContextShader;
import net.minecraft.resources.ResourceLocation;
public class ComponentRegistry {
private static final Registry<UniformProvider> uniformProviders = new Registry<>();
private static final Set<ResourceLocation> uniformProviderFiles = new HashSet<>();
private static final List<UniformProvider> uniformProviders = new ArrayList<>();
public static final Set<Material> materials = new HashSet<>();
public static final Set<StructType<?>> structTypes = new HashSet<>();
public static final Set<VertexType> vertexTypes = new HashSet<>();
public static final Set<ContextShader> contextShaders = new HashSet<>();
// TODO: fill out the rest of the registry
public static <T extends Material> T register(T material) {
materials.add(material);
return material;
}
public static <T extends StructType<?>> T register(T type) {
structTypes.add(type);
return type;
}
public static <T extends VertexType> T register(T vertexType) {
vertexTypes.add(vertexType);
return vertexType;
}
public static ContextShader register(ContextShader contextShader) {
contextShaders.add(contextShader);
return contextShader;
}
public static <T extends UniformProvider> T register(T provider) {
var file = provider.getUniformShader();
ResourceLocation location = file.getFileLoc();
if (uniformProviderFiles.contains(location)) {
throw new IllegalArgumentException("UniformProvider for '" + location + "' already registered");
}
uniformProviderFiles.add(location);
uniformProviders.add(provider);
return provider;
return uniformProviders.register(provider.getUniformShader()
.getFileLoc(), provider);
}
public static Collection<UniformProvider> getAllUniformProviders() {
return Collections.unmodifiableCollection(uniformProviders);
return Collections.unmodifiableCollection(uniformProviders.objects);
}
private static class Registry<T> {
private final Set<ResourceLocation> files = new HashSet<>();
private final List<T> objects = new ArrayList<>();
public <O extends T> O register(ResourceLocation loc, O object) {
if (files.contains(loc)) {
throw new IllegalArgumentException("Shader file already registered: " + loc);
}
files.add(loc);
objects.add(object);
return object;
}
}
}

View file

@ -5,7 +5,6 @@ import java.util.function.BiConsumer;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.core.compile.ContextShader;
import com.jozufozu.flywheel.core.crumbling.CrumblingProgram;
import com.jozufozu.flywheel.core.shader.WorldProgram;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.core.source.SourceChecks;
import com.jozufozu.flywheel.core.source.SourceFile;
@ -24,7 +23,7 @@ public class Components {
public static final ViewProvider VIEW_PROVIDER = ComponentRegistry.register(new ViewProvider());
public static final FogProvider FOG_PROVIDER = ComponentRegistry.register(new FogProvider());
public static final ContextShader WORLD = ComponentRegistry.register(new ContextShader(WorldProgram::new, Files.WORLD_VERTEX, Files.WORLD_FRAGMENT));
public static final ContextShader CRUMBLING = ComponentRegistry.register(new ContextShader(CrumblingProgram::new, Files.CRUMBLING_VERTEX, Files.CRUMBLING_FRAGMENT));
public static final ContextShader CRUMBLING = ComponentRegistry.register(new ContextShader(CrumblingProgram::new, Files.WORLD_VERTEX, Files.CRUMBLING_FRAGMENT));
public static void init() {
Files.init();
@ -42,43 +41,44 @@ public class Components {
public static final FileResolution TRANSFORMED = instanceVertex(ResourceUtil.subPath(Names.TRANSFORMED, ".vert"));
public static final FileResolution ORIENTED = instanceVertex(ResourceUtil.subPath(Names.ORIENTED, ".vert"));
public static final FileResolution DEFAULT_VERTEX = materialVertex(ResourceUtil.subPath(Names.DEFAULT, ".vert"));
public static final FileResolution DEFAULT_FRAGMENT = materialFragment(ResourceUtil.subPath(Names.DEFAULT, ".frag"));
public static final FileResolution SHADED_VERTEX = materialVertex(ResourceUtil.subPath(Names.SHADED, ".vert"));
public static final FileResolution DEFAULT_FRAGMENT = materialFragment(ResourceUtil.subPath(Names.DEFAULT, ".frag"));
public static final FileResolution CUTOUT_FRAGMENT = materialFragment(ResourceUtil.subPath(Names.CUTOUT, ".frag"));
public static final FileResolution WORLD_VERTEX = contextVertex(ResourceUtil.subPath(Names.WORLD, ".vert"));
public static final FileResolution WORLD_FRAGMENT = contextFragment(ResourceUtil.subPath(Names.WORLD, ".frag"));
public static final FileResolution CRUMBLING_VERTEX = contextVertex(ResourceUtil.subPath(Names.CRUMBLING, ".vert"));
public static final FileResolution CRUMBLING_FRAGMENT = contextFragment(ResourceUtil.subPath(Names.CRUMBLING, ".frag"));
public static FileResolution uniform(ResourceLocation location) {
private static FileResolution uniform(ResourceLocation location) {
return FileResolution.get(location);
}
public static FileResolution layoutVertex(ResourceLocation location) {
private static FileResolution layoutVertex(ResourceLocation location) {
return FileResolution.get(location)
.validateWith(Checks.LAYOUT_VERTEX);
}
public static FileResolution instanceVertex(ResourceLocation location) {
private static FileResolution instanceVertex(ResourceLocation location) {
return FileResolution.get(location)
.validateWith(Checks.INSTANCE_VERTEX);
}
public static FileResolution materialVertex(ResourceLocation location) {
private static FileResolution materialVertex(ResourceLocation location) {
return FileResolution.get(location)
.validateWith(Checks.MATERIAL_VERTEX);
}
public static FileResolution materialFragment(ResourceLocation location) {
private static FileResolution materialFragment(ResourceLocation location) {
return FileResolution.get(location)
.validateWith(Checks.MATERIAL_FRAGMENT);
}
public static FileResolution contextVertex(ResourceLocation location) {
private static FileResolution contextVertex(ResourceLocation location) {
return FileResolution.get(location)
.validateWith(Checks.CONTEXT_VERTEX);
}
public static FileResolution contextFragment(ResourceLocation location) {
private static FileResolution contextFragment(ResourceLocation location) {
return FileResolution.get(location)
.validateWith(Checks.CONTEXT_FRAGMENT);
}
@ -106,6 +106,7 @@ public class Components {
public static final ResourceLocation ORIENTED = Flywheel.rl("instance/oriented");
public static final ResourceLocation DEFAULT = Flywheel.rl("material/default");
public static final ResourceLocation CUTOUT = Flywheel.rl("material/cutout");
public static final ResourceLocation SHADED = Flywheel.rl("material/shaded");
public static final ResourceLocation WORLD = Flywheel.rl("context/world");
public static final ResourceLocation CRUMBLING = Flywheel.rl("context/crumbling");

View file

@ -1,122 +0,0 @@
package com.jozufozu.flywheel.core;
import static com.jozufozu.flywheel.core.CoreShaderInfoMap.CoreShaderInfo.FogType.COLOR_FOG;
import static com.jozufozu.flywheel.core.CoreShaderInfoMap.CoreShaderInfo.FogType.FADE_FOG;
import static com.jozufozu.flywheel.core.CoreShaderInfoMap.CoreShaderInfo.FogType.NO_FOG;
import java.util.HashMap;
import java.util.Map;
import org.jetbrains.annotations.Nullable;
import com.jozufozu.flywheel.backend.ShadersModHandler;
import com.jozufozu.flywheel.core.CoreShaderInfoMap.CoreShaderInfo.FogType;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.renderer.ShaderInstance;
public class CoreShaderInfoMap {
private static final Map<String, CoreShaderInfo> MAP = new HashMap<>();
static {
registerInfo("block", new CoreShaderInfo(-1, false, NO_FOG));
registerInfo("new_entity", new CoreShaderInfo(-1, false, NO_FOG));
registerInfo("particle", new CoreShaderInfo(0.1f, false, COLOR_FOG));
registerInfo("position", new CoreShaderInfo(-1, false, COLOR_FOG));
registerInfo("position_color", new CoreShaderInfo(0, false, NO_FOG));
registerInfo("position_color_lightmap", new CoreShaderInfo(-1, false, NO_FOG));
registerInfo("position_color_tex", new CoreShaderInfo(0.1f, false, NO_FOG));
registerInfo("position_color_tex_lightmap", new CoreShaderInfo(0.1f, false, NO_FOG));
registerInfo("position_tex", new CoreShaderInfo(0, false, NO_FOG));
registerInfo("position_tex_color", new CoreShaderInfo(0.1f, false, NO_FOG));
registerInfo("position_tex_color_normal", new CoreShaderInfo(0.1f, false, COLOR_FOG));
registerInfo("position_tex_lightmap_color", new CoreShaderInfo(0.1f, false, NO_FOG));
registerInfo("rendertype_solid", new CoreShaderInfo(-1, false, COLOR_FOG));
registerInfo("rendertype_cutout_mipped", new CoreShaderInfo(ShadersModHandler.isShaderPackInUse() ? 0.1f : 0.5f, false, COLOR_FOG));
registerInfo("rendertype_cutout", new CoreShaderInfo(0.1f, false, COLOR_FOG));
registerInfo("rendertype_translucent", new CoreShaderInfo(-1, false, COLOR_FOG));
registerInfo("rendertype_translucent_moving_block", new CoreShaderInfo(-1, false, NO_FOG));
registerInfo("rendertype_translucent_no_crumbling", new CoreShaderInfo(-1, false, NO_FOG));
registerInfo("rendertype_armor_cutout_no_cull", new CoreShaderInfo(0.1f, true, COLOR_FOG));
registerInfo("rendertype_entity_solid", new CoreShaderInfo(-1, true, COLOR_FOG));
registerInfo("rendertype_entity_cutout", new CoreShaderInfo(0.1f, true, COLOR_FOG));
registerInfo("rendertype_entity_cutout_no_cull", new CoreShaderInfo(0.1f, true, COLOR_FOG));
registerInfo("rendertype_entity_cutout_no_cull_z_offset", new CoreShaderInfo(0.1f, true, COLOR_FOG));
registerInfo("rendertype_item_entity_translucent_cull", new CoreShaderInfo(0.1f, true, COLOR_FOG));
registerInfo("rendertype_entity_translucent_cull", new CoreShaderInfo(0.1f, true, COLOR_FOG));
registerInfo("rendertype_entity_translucent", new CoreShaderInfo(0.1f, true, COLOR_FOG));
registerInfo("rendertype_entity_smooth_cutout", new CoreShaderInfo(0.1f, true, COLOR_FOG));
registerInfo("rendertype_beacon_beam", new CoreShaderInfo(-1, false, COLOR_FOG));
registerInfo("rendertype_entity_decal", new CoreShaderInfo(0.1f, true, COLOR_FOG));
registerInfo("rendertype_entity_no_outline", new CoreShaderInfo(-1, true, COLOR_FOG));
registerInfo("rendertype_entity_shadow", new CoreShaderInfo(-1, false, COLOR_FOG));
// Special alpha discard
registerInfo("rendertype_entity_alpha", new CoreShaderInfo(-1, false, NO_FOG));
registerInfo("rendertype_eyes", new CoreShaderInfo(-1, false, FADE_FOG));
registerInfo("rendertype_energy_swirl", new CoreShaderInfo(0.1f, false, FADE_FOG));
registerInfo("rendertype_leash", new CoreShaderInfo(-1, false, COLOR_FOG));
registerInfo("rendertype_water_mask", new CoreShaderInfo(-1, false, NO_FOG));
registerInfo("rendertype_outline", new CoreShaderInfo(0, false, NO_FOG));
registerInfo("rendertype_armor_glint", new CoreShaderInfo(0.1f, false, FADE_FOG));
registerInfo("rendertype_armor_entity_glint", new CoreShaderInfo(0.1f, false, FADE_FOG));
registerInfo("rendertype_glint_translucent", new CoreShaderInfo(0.1f, false, FADE_FOG));
registerInfo("rendertype_glint", new CoreShaderInfo(0.1f, false, FADE_FOG));
registerInfo("rendertype_glint_direct", new CoreShaderInfo(0.1f, false, FADE_FOG));
registerInfo("rendertype_entity_glint", new CoreShaderInfo(0.1f, false, FADE_FOG));
registerInfo("rendertype_entity_glint_direct", new CoreShaderInfo(0.1f, false, FADE_FOG));
registerInfo("rendertype_text", new CoreShaderInfo(0.1f, false, COLOR_FOG));
registerInfo("rendertype_text_intensity", new CoreShaderInfo(0.1f, false, COLOR_FOG));
registerInfo("rendertype_text_see_through", new CoreShaderInfo(0.1f, false, NO_FOG));
registerInfo("rendertype_text_intensity_see_through", new CoreShaderInfo(0.1f, false, NO_FOG));
registerInfo("rendertype_lightning", new CoreShaderInfo(-1, false, FADE_FOG));
registerInfo("rendertype_tripwire", new CoreShaderInfo(0.1f, false, COLOR_FOG));
registerInfo("rendertype_end_portal", new CoreShaderInfo(-1, false, NO_FOG));
registerInfo("rendertype_end_gateway", new CoreShaderInfo(-1, false, NO_FOG));
registerInfo("rendertype_lines", new CoreShaderInfo(-1, false, COLOR_FOG));
registerInfo("rendertype_crumbling", new CoreShaderInfo(0.1f, false, NO_FOG));
registerInfo("forge:rendertype_entity_unlit_translucent", new CoreShaderInfo(0.1f, false, COLOR_FOG));
}
public static void registerInfo(String name, CoreShaderInfo info) {
MAP.put(name, info);
}
@Nullable
public static CoreShaderInfo getInfo(String name) {
return MAP.get(name);
}
public record CoreShaderInfo(float alphaDiscard, boolean appliesDiffuse, FogType fogType) {
public static final CoreShaderInfo DEFAULT = new CoreShaderInfo(-1, false, NO_FOG);
public static CoreShaderInfo get() {
CoreShaderInfo out = null;
ShaderInstance coreShader = RenderSystem.getShader();
if (coreShader != null) {
String coreShaderName = coreShader.getName();
out = getInfo(coreShaderName);
}
if (out == null) {
out = DEFAULT;
}
return out;
}
public float getAdjustedAlphaDiscard() {
float alphaDiscard = alphaDiscard();
if (alphaDiscard == 0) {
alphaDiscard = 0.0001f;
} else if (alphaDiscard < 0) {
alphaDiscard = 0;
}
return alphaDiscard;
}
public enum FogType {
NO_FOG,
COLOR_FOG,
FADE_FOG;
}
}
}

View file

@ -1,54 +0,0 @@
package com.jozufozu.flywheel.core;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.List;
import com.jozufozu.flywheel.core.shader.GameStateProvider;
import com.jozufozu.flywheel.core.shader.ShaderConstants;
import com.jozufozu.flywheel.core.shader.StateSnapshot;
public class GameStateRegistry {
private static final List<GameStateProvider> PROVIDERS = new ArrayList<>();
/**
* Registers a game state provider.
* @param provider The provider to register.
*/
public static void register(GameStateProvider provider) {
PROVIDERS.add(provider);
}
/**
* Takes a snapshot of the current game state, storing it in a bit set.
* @return An object that represents the current game state.
*/
public static StateSnapshot takeSnapshot() {
BitSet bitSet = new BitSet(PROVIDERS.size());
for (int i = 0, listSize = PROVIDERS.size(); i < listSize; i++) {
if (PROVIDERS.get(i).isTrue()) {
bitSet.set(i);
}
}
return new StateSnapshot(bitSet);
}
/**
* Based on the given snapshot, gathers shader constants to be injected during shader compilation.
* @param snapshot The snapshot to use.
* @return A list of shader constants.
*/
public static ShaderConstants getShaderConstants(StateSnapshot snapshot) {
BitSet ctx = snapshot.ctx();
ShaderConstants shaderConstants = new ShaderConstants();
for (int i = 0, listSize = PROVIDERS.size(); i < listSize; i++) {
if (ctx.get(i)) {
PROVIDERS.get(i).alterConstants(shaderConstants);
}
}
return shaderConstants;
}
}

View file

@ -3,6 +3,7 @@ package com.jozufozu.flywheel.core;
import com.jozufozu.flywheel.api.RenderStage;
import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.core.material.SimpleMaterial;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.Sheets;
@ -10,11 +11,33 @@ import net.minecraft.resources.ResourceLocation;
public class Materials {
private static final ResourceLocation MINECART_LOCATION = new ResourceLocation("textures/entity/minecart.png");
public static final Material DEFAULT = ComponentRegistry.register(new SimpleMaterial(RenderStage.AFTER_SOLID_TERRAIN, RenderType.cutout(), Components.Files.SHADED_VERTEX, Components.Files.DEFAULT_FRAGMENT));
public static final Material CHEST = ComponentRegistry.register(new SimpleMaterial(RenderStage.AFTER_BLOCK_ENTITIES, Sheets.chestSheet(), Components.Files.SHADED_VERTEX, Components.Files.DEFAULT_FRAGMENT));
public static final Material SHULKER = ComponentRegistry.register(new SimpleMaterial(RenderStage.AFTER_BLOCK_ENTITIES, RenderType.entityCutoutNoCull(Sheets.SHULKER_SHEET), Components.Files.SHADED_VERTEX, Components.Files.DEFAULT_FRAGMENT));
public static final Material BELL = ComponentRegistry.register(new SimpleMaterial(RenderStage.AFTER_BLOCK_ENTITIES, Sheets.solidBlockSheet(), Components.Files.SHADED_VERTEX, Components.Files.DEFAULT_FRAGMENT));
public static final Material MINECART = ComponentRegistry.register(new SimpleMaterial(RenderStage.AFTER_ENTITIES, RenderType.entitySolid(MINECART_LOCATION), Components.Files.SHADED_VERTEX, Components.Files.DEFAULT_FRAGMENT));
public static final Material DEFAULT = SimpleMaterial.builder()
.stage(RenderStage.AFTER_SOLID_TERRAIN)
.renderType(RenderType.cutout())
.fragmentShader(Components.Files.CUTOUT_FRAGMENT)
.register();
public static final Material CHEST = SimpleMaterial.builder()
.stage(RenderStage.AFTER_BLOCK_ENTITIES)
.renderType(Sheets.chestSheet())
.diffuseTex(Sheets.CHEST_SHEET)
.register();
public static final Material SHULKER = SimpleMaterial.builder()
.stage(RenderStage.AFTER_BLOCK_ENTITIES)
.renderType(Sheets.shulkerBoxSheet())
.diffuseTex(Sheets.SHULKER_SHEET)
.fragmentShader(Components.Files.CUTOUT_FRAGMENT)
.alsoSetup(RenderSystem::disableCull)
.alsoClear(RenderSystem::enableCull)
.register();
public static final Material BELL = SimpleMaterial.builder()
.stage(RenderStage.AFTER_BLOCK_ENTITIES)
.renderType(Sheets.solidBlockSheet())
.register();
public static final Material MINECART = SimpleMaterial.builder()
.stage(RenderStage.AFTER_ENTITIES)
.renderType(RenderType.entitySolid(MINECART_LOCATION))
.diffuseTex(MINECART_LOCATION)
.register();
public static void init() {
// noop

View file

@ -1,4 +1,4 @@
package com.jozufozu.flywheel.core.shader;
package com.jozufozu.flywheel.core;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;

View file

@ -4,9 +4,6 @@ import com.google.common.collect.ImmutableList;
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.core.CoreShaderInfoMap.CoreShaderInfo.FogType;
import com.jozufozu.flywheel.core.shader.ShaderConstants;
import com.jozufozu.flywheel.core.shader.StateSnapshot;
import com.jozufozu.flywheel.core.source.CompilationContext;
import com.jozufozu.flywheel.core.source.SourceFile;
@ -24,10 +21,6 @@ public class FragmentCompiler extends Memoizer<FragmentCompiler.Context, GlShade
finalSource.append(CompileUtil.generateHeader(GLSLVersion.V420, ShaderType.FRAGMENT));
var shaderConstants = key.getShaderConstants();
shaderConstants.writeInto(finalSource);
finalSource.append('\n');
var ctx = new CompilationContext();
// MATERIAL
@ -45,7 +38,7 @@ public class FragmentCompiler extends Memoizer<FragmentCompiler.Context, GlShade
finalSource.append(generateFooter());
try {
return new GlShader(finalSource.toString(), ShaderType.FRAGMENT, ImmutableList.of(materialShader.name, contextShaderSource.name), shaderConstants);
return new GlShader(finalSource.toString(), ShaderType.FRAGMENT, ImmutableList.of(materialShader.name, contextShaderSource.name));
} catch (ShaderCompilationException e) {
throw e.withErrorLog(ctx);
}
@ -70,22 +63,10 @@ public class FragmentCompiler extends Memoizer<FragmentCompiler.Context, GlShade
/**
* Represents the conditions under which a shader is compiled.
*
* @param materialShader The fragment material shader source.
* @param alphaDiscard Alpha threshold below which fragments are discarded.
* @param fogType Which type of fog should be applied.
* @param ctx The shader constants to apply.
*/
public record Context(SourceFile materialShader, SourceFile contextShader, float alphaDiscard, FogType fogType, StateSnapshot ctx) {
public record Context(SourceFile materialShader, SourceFile contextShader) {
public ShaderConstants getShaderConstants() {
ShaderConstants shaderConstants = ctx.getShaderConstants();
if (alphaDiscard > 0) {
shaderConstants.define("ALPHA_DISCARD", alphaDiscard);
}
shaderConstants.define(fogType.name());
return shaderConstants;
}
}
}

View file

@ -3,8 +3,6 @@ package com.jozufozu.flywheel.core.compile;
import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import com.jozufozu.flywheel.core.CoreShaderInfoMap;
import com.jozufozu.flywheel.core.shader.StateSnapshot;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.event.ReloadRenderersEvent;
@ -52,15 +50,13 @@ public class ProgramCompiler extends Memoizer<ProgramCompiler.Context, GlProgram
protected GlProgram _create(ProgramCompiler.Context ctx) {
// TODO: try-catch here to prevent crashing if shaders failed to compile
Material material = ctx.material;
StateSnapshot snapshot = ctx.ctx();
FileResolution instanceShader = ctx.instanceShader();
ContextShader contextShader = ctx.contextShader;
var vertex = new VertexCompiler.Context(ctx.vertexType(), instanceShader.getFile(), material.getVertexShader().getFile(),
contextShader.getVertexShader(), snapshot);
contextShader.getVertexShader());
var fragment = new FragmentCompiler.Context(material.getFragmentShader().getFile(), contextShader.getFragmentShader(),
ctx.alphaDiscard(), ctx.fogType(), snapshot);
var fragment = new FragmentCompiler.Context(material.getFragmentShader().getFile(), contextShader.getFragmentShader());
return new ProgramAssembler(instanceShader.getFileLoc())
.attachShader(vertexCompiler.get(vertex))
@ -85,12 +81,8 @@ public class ProgramCompiler extends Memoizer<ProgramCompiler.Context, GlProgram
* @param material The material shader to use.
* @param instanceShader The instance shader to use.
* @param contextShader The context shader to use.
* @param alphaDiscard Alpha threshold below which pixels are discarded.
* @param fogType Which type of fog should be applied.
* @param ctx A snapshot of the game state.
*/
public record Context(VertexType vertexType, Material material, FileResolution instanceShader,
ContextShader contextShader, float alphaDiscard,
CoreShaderInfoMap.CoreShaderInfo.FogType fogType, StateSnapshot ctx) {
ContextShader contextShader) {
}
}

View file

@ -7,7 +7,6 @@ import com.jozufozu.flywheel.api.vertex.VertexType;
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.core.shader.StateSnapshot;
import com.jozufozu.flywheel.core.source.CompilationContext;
import com.jozufozu.flywheel.core.source.SourceFile;
import com.jozufozu.flywheel.core.source.parse.ShaderField;
@ -28,10 +27,6 @@ public class VertexCompiler extends Memoizer<VertexCompiler.Context, GlShader> {
finalSource.append(CompileUtil.generateHeader(GLSLVersion.V420, ShaderType.VERTEX));
var shaderConstants = key.ctx.getShaderConstants();
shaderConstants.writeInto(finalSource);
finalSource.append('\n');
var index = new CompilationContext();
// LAYOUT
@ -82,7 +77,7 @@ public class VertexCompiler extends Memoizer<VertexCompiler.Context, GlShader> {
""");
try {
return new GlShader(finalSource.toString(), ShaderType.VERTEX, ImmutableList.of(layoutShader.name, instanceShader.name, materialShader.name, contextShaderSource.name), shaderConstants);
return new GlShader(finalSource.toString(), ShaderType.VERTEX, ImmutableList.of(layoutShader.name, instanceShader.name, materialShader.name, contextShaderSource.name));
} catch (ShaderCompilationException e) {
throw e.withErrorLog(index);
}
@ -98,8 +93,7 @@ public class VertexCompiler extends Memoizer<VertexCompiler.Context, GlShader> {
* @param instanceShader The instance shader source.
* @param materialShader The vertex material shader source.
* @param contextShader The context shader source.
* @param ctx The shader constants to apply.
*/
public record Context(VertexType vertexType, SourceFile instanceShader, SourceFile materialShader, SourceFile contextShader, StateSnapshot ctx) {
public record Context(VertexType vertexType, SourceFile instanceShader, SourceFile materialShader, SourceFile contextShader) {
}
}

View file

@ -1,6 +1,6 @@
package com.jozufozu.flywheel.core.crumbling;
import com.jozufozu.flywheel.core.shader.WorldProgram;
import com.jozufozu.flywheel.core.WorldProgram;
import net.minecraft.resources.ResourceLocation;

View file

@ -145,14 +145,5 @@ public class CrumblingRenderer {
public CrumblingEngine() {
super(Components.CRUMBLING);
}
@Override
protected void render(RenderType type, ListMultimap<ShaderState, DrawCall> multimap) {
if (!type.affectsCrumbling()) {
return;
}
super.render(type, multimap);
}
}
}

View file

@ -1,22 +1,43 @@
package com.jozufozu.flywheel.core.material;
import org.jetbrains.annotations.Nullable;
import com.jozufozu.flywheel.api.RenderStage;
import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.backend.gl.GlTextureUnit;
import com.jozufozu.flywheel.core.ComponentRegistry;
import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.inventory.InventoryMenu;
public class SimpleMaterial implements Material {
protected final RenderStage stage;
protected final RenderType type;
protected final FileResolution vertexShader;
protected final FileResolution fragmentShader;
protected final ResourceLocation diffuseTex;
@Nullable
protected final Runnable setup;
@Nullable
protected final Runnable clear;
public SimpleMaterial(RenderStage stage, RenderType type, FileResolution vertexShader, FileResolution fragmentShader) {
public SimpleMaterial(RenderStage stage, RenderType type, FileResolution vertexShader, FileResolution fragmentShader, ResourceLocation diffuseTex, @Nullable Runnable setup, @Nullable Runnable clear) {
this.stage = stage;
this.type = type;
this.vertexShader = vertexShader;
this.fragmentShader = fragmentShader;
this.diffuseTex = diffuseTex;
this.setup = setup;
this.clear = clear;
}
public static Builder builder() {
return new Builder();
}
@Override
@ -25,7 +46,7 @@ public class SimpleMaterial implements Material {
}
@Override
public RenderType getRenderType() {
public RenderType getBatchingRenderType() {
return type;
}
@ -38,4 +59,88 @@ public class SimpleMaterial implements Material {
public FileResolution getFragmentShader() {
return fragmentShader;
}
@Override
public void setup() {
GlTextureUnit.T0.makeActive();
RenderSystem.setShaderTexture(0, diffuseTex);
Minecraft.getInstance().textureManager.bindForSetup(diffuseTex);
if (setup != null) {
setup.run();
}
}
@Override
public void clear() {
GlTextureUnit.T0.makeActive();
RenderSystem.setShaderTexture(0, 0);
if (clear != null) {
clear.run();
}
}
public static class Builder {
protected RenderStage stage = RenderStage.AFTER_SOLID_TERRAIN;
protected RenderType type = RenderType.solid();
protected FileResolution vertexShader = Components.Files.SHADED_VERTEX;
protected FileResolution fragmentShader = Components.Files.DEFAULT_FRAGMENT;
protected ResourceLocation diffuseTex = InventoryMenu.BLOCK_ATLAS;
protected Runnable setup = null;
protected Runnable clear = null;
public Builder() {
}
public Builder stage(RenderStage stage) {
this.stage = stage;
return this;
}
public Builder renderType(RenderType type) {
this.type = type;
return this;
}
public Builder vertexShader(FileResolution vertexShader) {
this.vertexShader = vertexShader;
return this;
}
public Builder fragmentShader(FileResolution fragmentShader) {
this.fragmentShader = fragmentShader;
return this;
}
public Builder shaded() {
this.vertexShader = Components.Files.SHADED_VERTEX;
return this;
}
public Builder unShaded() {
this.vertexShader = Components.Files.DEFAULT_VERTEX;
return this;
}
public Builder diffuseTex(ResourceLocation diffuseTex) {
this.diffuseTex = diffuseTex;
return this;
}
public Builder alsoSetup(Runnable runnable) {
this.setup = runnable;
return this;
}
public Builder alsoClear(Runnable clear) {
this.clear = clear;
return this;
}
public SimpleMaterial register() {
return ComponentRegistry.register(new SimpleMaterial(stage, type, vertexShader, fragmentShader, diffuseTex, setup, clear));
}
}
}

View file

@ -1,19 +0,0 @@
package com.jozufozu.flywheel.core.shader;
/**
* An object that provides a view of the current game state for shader compilation.
*/
public interface GameStateProvider {
/**
* Get the status of this game state provider.
* @return Returning {@code true} will cause #alterConstants to be called before compiling a shader.
*/
boolean isTrue();
/**
* Alter the constants for shader compilation.
* @param constants The shader constants.
*/
void alterConstants(ShaderConstants constants);
}

View file

@ -1,18 +0,0 @@
package com.jozufozu.flywheel.core.shader;
import com.jozufozu.flywheel.config.FlwConfig;
public enum NormalDebugStateProvider implements GameStateProvider {
INSTANCE;
@Override
public boolean isTrue() {
return FlwConfig.get()
.debugNormals();
}
@Override
public void alterConstants(ShaderConstants constants) {
constants.define("DEBUG_NORMAL");
}
}

View file

@ -1,69 +0,0 @@
package com.jozufozu.flywheel.core.shader;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* A class for manipulating a list of {@code #define} directives.
*
* <p>Based loosely on code by jellysquid3.
*/
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');
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ShaderConstants that = (ShaderConstants) o;
return Objects.equals(definitions, that.definitions);
}
@Override
public int hashCode() {
return Objects.hash(definitions);
}
}

View file

@ -1,13 +0,0 @@
package com.jozufozu.flywheel.core.shader;
import java.util.BitSet;
import com.jozufozu.flywheel.core.GameStateRegistry;
public record StateSnapshot(BitSet ctx) {
// TODO: is this needed?
public ShaderConstants getShaderConstants() {
return GameStateRegistry.getShaderConstants(this);
}
}

View file

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

View file

@ -29,7 +29,12 @@ public class ShaderSources implements SourceFinder {
public final Index index;
public final long loadTimeNs;
public final long indexTimeNs;
public final long totalTimeNs;
public ShaderSources(ErrorReporter errorReporter, ResourceManager manager) {
long loadStart = System.nanoTime();
for (ResourceLocation location : getValidShaderFiles(manager)) {
try (Resource resource = manager.getResource(location)) {
@ -42,8 +47,15 @@ public class ShaderSources implements SourceFinder {
//
}
}
long loadEnd = System.nanoTime();
long indexStart = System.nanoTime();
index = new Index(shaderSources);
long indexEnd = System.nanoTime();
loadTimeNs = loadEnd - loadStart;
indexTimeNs = indexEnd - indexStart;
totalTimeNs = indexEnd - loadStart;
}
public void postResolve() {
@ -67,4 +79,8 @@ public class ShaderSources implements SourceFinder {
return false;
});
}
public String getLoadTime() {
return StringUtil.formatTime(totalTimeNs);
}
}

View file

@ -8,6 +8,8 @@ import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.Arrays;
import java.util.stream.Collectors;
@ -15,6 +17,20 @@ import org.lwjgl.system.MemoryUtil;
public class StringUtil {
private static final NumberFormat timeFormat = new DecimalFormat("#0.000");
public static String formatTime(long ns) {
if (ns < 1000) {
return ns + " ns";
} else if (ns < 1000000) {
return timeFormat.format(ns / 1000.) + " μs";
} else if (ns < 1000000000) {
return timeFormat.format(ns / 1000000.) + " ms";
} else {
return timeFormat.format(ns / 1000000000.) + " s";
}
}
public static String args(String functionName, Object... args) {
return functionName + '(' + Arrays.stream(args)

View file

@ -19,3 +19,16 @@ in vec4 flw_var3;
vec4 flw_fragColor;
ivec2 flw_fragOverlay;
vec2 flw_fragLight;
/*
* Must be implemented by materials.
*/
vec4 flw_fogFilter(vec4 color);
/*
* May be implemented by materials.
* If implemented, a material must define FLW_DISCARD
*
* Guard calls with FLW_DISCARD
*/
bool flw_discardPredicate(vec4 finalColor);

View file

@ -4,11 +4,6 @@
#use "flywheel:uniform/view.glsl"
void flw_contextVertex() {
// TODO: remove this
#ifdef DEBUG_NORMAL
flw_vertexColor = vec4(flw_vertexNormal, 1.0);
#endif
flw_distance = fog_distance(flw_vertexPos.xyz, flw_cameraPos.xyz, flw_fogShape);
gl_Position = flw_viewProjection * flw_vertexPos;
flw_vertexNormal = normalize(flw_vertexNormal);

View file

@ -1,6 +1,4 @@
#use "flywheel:api/fragment.glsl"
#use "flywheel:util/fog.glsl"
#use "flywheel:uniform/fog.glsl"
uniform sampler2D flw_diffuseTex;
@ -36,17 +34,11 @@ void flw_initFragment() {
void flw_contextFragment() {
vec4 color = flw_fragColor;
#ifdef ALPHA_DISCARD
if (color.a < ALPHA_DISCARD) {
#ifdef FLW_DISCARD
if (flw_discardPredicate(color)) {
discard;
}
#endif
#ifdef COLOR_FOG
color = linear_fog(color, flw_distance, flw_fogRange.x, flw_fogRange.y, flw_fogColor);
#elif defined(FADE_FOG)
color = linear_fog_fade(color, flw_distance, flw_fogRange.x, flw_fogRange.y);
#endif
fragColor = color;
fragColor = flw_fogFilter(color);
}

View file

@ -1,6 +1,4 @@
#use "flywheel:api/fragment.glsl"
#use "flywheel:util/fog.glsl"
#use "flywheel:uniform/fog.glsl"
// optimize discard usage
#ifdef ALPHA_DISCARD
@ -30,17 +28,11 @@ void flw_contextFragment() {
color.rgb = mix(overlayColor.rgb, color.rgb, overlayColor.a);
color *= lightColor;
#ifdef ALPHA_DISCARD
if (color.a < ALPHA_DISCARD) {
#ifdef FLW_DISCARD
if (flw_discardPredicate(color)) {
discard;
}
#endif
#ifdef COLOR_FOG
color = linear_fog(color, flw_distance, flw_fogRange.x, flw_fogRange.y, flw_fogColor);
#elif defined(FADE_FOG)
color = linear_fog_fade(color, flw_distance, flw_fogRange.x, flw_fogRange.y);
#endif
fragColor = color;
fragColor = flw_fogFilter(color);
}

View file

@ -0,0 +1,15 @@
#use "flywheel:api/fragment.glsl"
#use "flywheel:util/fog.glsl"
#use "flywheel:uniform/fog.glsl"
void flw_materialFragment() {
}
#define FLW_DISCARD
bool flw_discardPredicate(vec4 finalColor) {
return finalColor.a < 0.1;
}
vec4 flw_fogFilter(vec4 color) {
return linear_fog(color, flw_distance, flw_fogRange.x, flw_fogRange.y, flw_fogColor);
}

View file

@ -1,4 +1,10 @@
#use "flywheel:api/fragment.glsl"
#use "flywheel:util/fog.glsl"
#use "flywheel:uniform/fog.glsl"
void flw_materialFragment() {
}
vec4 flw_fogFilter(vec4 color) {
return linear_fog(color, flw_distance, flw_fogRange.x, flw_fogRange.y, flw_fogColor);
}