diff --git a/src/main/java/com/jozufozu/flywheel/FlywheelClient.java b/src/main/java/com/jozufozu/flywheel/FlywheelClient.java index d3a46a9a7..4b74ab5ce 100644 --- a/src/main/java/com/jozufozu/flywheel/FlywheelClient.java +++ b/src/main/java/com/jozufozu/flywheel/FlywheelClient.java @@ -1,13 +1,16 @@ package com.jozufozu.flywheel; import com.jozufozu.flywheel.backend.Backend; +import com.jozufozu.flywheel.backend.OptifineHandler; 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; @@ -15,20 +18,21 @@ import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; public class FlywheelClient { public static void clientInit() { - CrashReportCallables.registerCrashCallable("Flywheel Backend", () -> - Backend.getInstance().getBackendDescriptor()); + CrashReportCallables.registerCrashCallable("Flywheel Backend", Backend::getBackendDescriptor); + OptifineHandler.init(); Backend.init(); IEventBus modEventBus = FMLJavaModLoadingContext.get() .getModEventBus(); modEventBus.addListener(Contexts::flwInit); - modEventBus.addListener(Materials::flwInit); modEventBus.addListener(PartialModel::onModelRegistry); modEventBus.addListener(PartialModel::onModelBake); modEventBus.addListener(StitchedSprite::onTextureStitchPre); modEventBus.addListener(StitchedSprite::onTextureStitchPost); + MinecraftForge.EVENT_BUS.addListener(ProgramCompiler::invalidateAll); + VanillaInstances.init(); // https://github.com/Jozufozu/Flywheel/issues/69 diff --git a/src/main/java/com/jozufozu/flywheel/api/vertex/VertexType.java b/src/main/java/com/jozufozu/flywheel/api/vertex/VertexType.java index f785f4ac3..5b7afb654 100644 --- a/src/main/java/com/jozufozu/flywheel/api/vertex/VertexType.java +++ b/src/main/java/com/jozufozu/flywheel/api/vertex/VertexType.java @@ -32,6 +32,8 @@ public interface VertexType { */ VertexList createReader(ByteBuffer buffer, int vertexCount); + String getShaderHeader(); + default int getStride() { return getLayout().getStride(); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/Backend.java b/src/main/java/com/jozufozu/flywheel/backend/Backend.java index b365f2458..b8cd61e82 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/Backend.java +++ b/src/main/java/com/jozufozu/flywheel/backend/Backend.java @@ -1,25 +1,15 @@ package com.jozufozu.flywheel.backend; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - import javax.annotation.Nullable; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.lwjgl.opengl.GL; -import org.lwjgl.opengl.GLCapabilities; import com.jozufozu.flywheel.api.FlywheelWorld; -import com.jozufozu.flywheel.api.InstanceData; -import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.backend.gl.versioned.GlCompat; import com.jozufozu.flywheel.config.FlwConfig; import com.jozufozu.flywheel.config.FlwEngine; -import com.jozufozu.flywheel.core.shader.spec.ProgramSpec; +import com.jozufozu.flywheel.core.shader.ProgramSpec; import net.minecraft.client.Minecraft; import net.minecraft.resources.ResourceLocation; @@ -29,101 +19,39 @@ import net.minecraft.world.level.LevelAccessor; public class Backend { public static final Logger LOGGER = LogManager.getLogger(Backend.class); - protected static final Backend INSTANCE = new Backend(); - public static Backend getInstance() { - return INSTANCE; - } + private static FlwEngine engine; - private FlwEngine engine; + public static GlCompat compat; - public GLCapabilities capabilities; - public GlCompat compat; - - public final Loader loader; - private final List> contexts = new ArrayList<>(); - private final Map> materialRegistry = new HashMap<>(); - private final Map programSpecRegistry = new HashMap<>(); - - protected Backend() { - loader = new Loader(this); - - OptifineHandler.init(); - } + private static final Loader loader = new Loader(); /** * Get a string describing the Flywheel backend. When there are eventually multiple backends * (Meshlet, MDI, GL31 Draw Instanced are planned), this will name which one is in use. */ - public String getBackendDescriptor() { + public static String getBackendDescriptor() { return engine == null ? "" : engine.getProperName(); } - public FlwEngine getEngine() { + public static FlwEngine getEngine() { return engine; } - /** - * Register a shader program. - */ - public ProgramSpec register(ProgramSpec spec) { - ResourceLocation name = spec.name; - if (programSpecRegistry.containsKey(name)) { - throw new IllegalStateException("Program spec '" + name + "' already registered."); - } - programSpecRegistry.put(name, spec); - return spec; + @Nullable + public static ProgramSpec getSpec(ResourceLocation name) { + return loader.get(name); } - /** - * Register a shader context. - */ - public > C register(C spec) { - contexts.add(spec); - return spec; - } - - /** - * Register an instancing material. - */ - public StructType register(ResourceLocation name, StructType spec) { - if (materialRegistry.containsKey(name)) { - throw new IllegalStateException("Material spec '" + name + "' already registered."); - } - materialRegistry.put(name, spec); - - LOGGER.debug("registered material '" + name + "' with instance size " + spec.getLayout().getStride()); - - return spec; - } - - public ProgramSpec getSpec(ResourceLocation name) { - return programSpecRegistry.get(name); - } - - public void refresh() { + public static void refresh() { OptifineHandler.refresh(); - capabilities = GL.createCapabilities(); - - compat = new GlCompat(capabilities); + compat = new GlCompat(); engine = chooseEngine(compat); } - public Collection> allMaterials() { - return materialRegistry.values(); - } - - public Collection allPrograms() { - return programSpecRegistry.values(); - } - - public Collection> allContexts() { - return contexts; - } - public static boolean isOn() { - return getInstance().engine != FlwEngine.OFF; + return engine != FlwEngine.OFF; } public static boolean canUseInstancing(@Nullable Level world) { @@ -151,20 +79,6 @@ public class Backend { RenderWork.enqueue(Minecraft.getInstance().levelRenderer::allChanged); } - /** - * INTERNAL USE ONLY - */ - void _clearContexts() { - GameStateRegistry.clear(); - programSpecRegistry.clear(); - contexts.forEach(ShaderContext::delete); - contexts.clear(); - materialRegistry.clear(); - } - - public static void init() { - } - private static FlwEngine chooseEngine(GlCompat compat) { FlwEngine preferredChoice = FlwConfig.get() .getEngine(); @@ -178,4 +92,12 @@ public class Backend { return canUseEngine ? preferredChoice : FlwEngine.OFF; } + + public static void init() { + // noop + } + + private Backend() { + throw new UnsupportedOperationException("Backend is a static class!"); + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/GameStateRegistry.java b/src/main/java/com/jozufozu/flywheel/backend/GameStateRegistry.java deleted file mode 100644 index 04aa431e6..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/GameStateRegistry.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.jozufozu.flywheel.backend; - -import java.util.HashMap; -import java.util.Map; - -import com.jozufozu.flywheel.core.shader.gamestate.GameStateProvider; - -import net.minecraft.resources.ResourceLocation; - -public class GameStateRegistry { - - private static final Map registeredStateProviders = new HashMap<>(); - - static void clear() { - registeredStateProviders.clear(); - } - - public static GameStateProvider getStateProvider(ResourceLocation location) { - GameStateProvider out = registeredStateProviders.get(location); - - if (out == null) { - throw new IllegalArgumentException("State provider '" + location + "' does not exist."); - } - - return out; - } - - public static void register(GameStateProvider context) { - if (registeredStateProviders.containsKey(context.getID())) { - throw new IllegalStateException("Duplicate game state provider: " + context.getID()); - } - - registeredStateProviders.put(context.getID(), context); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/Loader.java b/src/main/java/com/jozufozu/flywheel/backend/Loader.java index 188900381..9f0f9b43c 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/Loader.java +++ b/src/main/java/com/jozufozu/flywheel/backend/Loader.java @@ -1,16 +1,20 @@ package com.jozufozu.flywheel.backend; import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import javax.annotation.Nullable; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonElement; import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; -import com.jozufozu.flywheel.backend.source.Resolver; -import com.jozufozu.flywheel.backend.source.ShaderLoadingException; -import com.jozufozu.flywheel.backend.source.ShaderSources; +import com.jozufozu.flywheel.core.GameStateRegistry; import com.jozufozu.flywheel.core.crumbling.CrumblingRenderer; -import com.jozufozu.flywheel.core.shader.spec.ProgramSpec; +import com.jozufozu.flywheel.core.shader.ProgramSpec; +import com.jozufozu.flywheel.core.source.Resolver; +import com.jozufozu.flywheel.core.source.ShaderSources; import com.jozufozu.flywheel.event.GatherContextEvent; import com.jozufozu.flywheel.util.ResourceUtil; import com.jozufozu.flywheel.util.StringUtil; @@ -38,14 +42,11 @@ public class Loader implements ResourceManagerReloadListener { public static final String PROGRAM_DIR = "flywheel/programs/"; private static final Gson GSON = new GsonBuilder().create(); - private final Backend backend; - private boolean shouldCrash; + private final Map programSpecRegistry = new HashMap<>(); private boolean firstLoad = true; - public Loader(Backend backend) { - this.backend = backend; - + Loader() { // Can be null when running datagenerators due to the unfortunate time we call this Minecraft minecraft = Minecraft.getInstance(); if (minecraft != null) { @@ -56,36 +57,28 @@ public class Loader implements ResourceManagerReloadListener { } } - public void notifyError() { - shouldCrash = true; + @Nullable + public ProgramSpec get(ResourceLocation name) { + return programSpecRegistry.get(name); } @Override public void onResourceManagerReload(ResourceManager manager) { - backend.refresh(); + Backend.refresh(); - shouldCrash = false; - backend._clearContexts(); + GameStateRegistry._clear(); Resolver.INSTANCE.invalidate(); ModLoader.get() - .postEvent(new GatherContextEvent(backend, firstLoad)); + .postEvent(new GatherContextEvent(firstLoad)); ShaderSources sources = new ShaderSources(manager); loadProgramSpecs(manager); - Resolver.INSTANCE.resolve(sources); + Resolver.INSTANCE.run(sources); - for (ShaderContext context : backend.allContexts()) { - context.load(); - } - - if (shouldCrash) { - throw new ShaderLoadingException("Could not load all shaders, see log for details"); - } - - Backend.LOGGER.info("Loaded all shader programs."); + Backend.LOGGER.info("Loaded all shader sources."); ClientLevel world = Minecraft.getInstance().level; if (Backend.canUseInstancing(world)) { @@ -116,10 +109,21 @@ public class Loader implements ResourceManagerReloadListener { spec.setName(specName); - backend.register(spec); + register(spec); } catch (Exception e) { Backend.LOGGER.error(e); } } } + + /** + * Register a shader program. + */ + private void register(ProgramSpec spec) { + ResourceLocation name = spec.name; + if (programSpecRegistry.containsKey(name)) { + throw new IllegalStateException("Program spec '" + name + "' already registered."); + } + programSpecRegistry.put(name, spec); + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/RenderLayer.java b/src/main/java/com/jozufozu/flywheel/backend/RenderLayer.java index 615187b43..3f5a6dcaa 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/RenderLayer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/RenderLayer.java @@ -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; + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/ShaderContext.java b/src/main/java/com/jozufozu/flywheel/backend/ShaderContext.java deleted file mode 100644 index 66585908e..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/ShaderContext.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.jozufozu.flywheel.backend; - -import java.util.function.Supplier; - -import com.jozufozu.flywheel.backend.gl.shader.GlProgram; - -import net.minecraft.resources.ResourceLocation; - -public interface ShaderContext

{ - - default P getProgram(ResourceLocation loc) { - return this.getProgramSupplier(loc) - .get(); - } - - Supplier

getProgramSupplier(ResourceLocation loc); - - /** - * Load all programs associated with this context. This might be just one, if the context is very specialized. - */ - void load(); - - void delete(); -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/GlNumericType.java b/src/main/java/com/jozufozu/flywheel/backend/gl/GlNumericType.java index 29baffd7f..aaf69e61d 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/GlNumericType.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/GlNumericType.java @@ -26,15 +26,15 @@ public enum GlNumericType { private static final GlNumericType[] VALUES = values(); private static final Map NAME_LOOKUP = Arrays.stream(VALUES) - .collect(Collectors.toMap(GlNumericType::getDisplayName, type -> type)); + .collect(Collectors.toMap(GlNumericType::getTypeName, type -> type)); private final int byteWidth; - private final String displayName; + private final String typeName; private final int glEnum; GlNumericType(int bytes, String name, int glEnum) { this.byteWidth = bytes; - this.displayName = name; + this.typeName = name; this.glEnum = glEnum; } @@ -42,8 +42,8 @@ public enum GlNumericType { return this.byteWidth; } - public String getDisplayName() { - return this.displayName; + public String getTypeName() { + return this.typeName; } public int getGlEnum() { @@ -64,4 +64,9 @@ public enum GlNumericType { public static GlNumericType byName(String name) { return name == null ? null : NAME_LOOKUP.get(name.toLowerCase(Locale.ROOT)); } + + @Override + public String toString() { + return typeName; + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/GlVertexArray.java b/src/main/java/com/jozufozu/flywheel/backend/gl/GlVertexArray.java index b74c5fee5..d757cfbb8 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/GlVertexArray.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/GlVertexArray.java @@ -39,8 +39,8 @@ public class GlVertexArray extends GlObject { int offset = 0; for (LayoutItem spec : type.getLayoutItems()) { spec.vertexAttribPointer(type.getStride(), startIndex, offset); - startIndex += spec.getAttributeCount(); - offset += spec.getSize(); + startIndex += spec.attributeCount(); + offset += spec.size(); } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/GlBuffer.java b/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/GlBuffer.java index 2c2e31e25..346db8814 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/GlBuffer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/GlBuffer.java @@ -21,7 +21,7 @@ public abstract class GlBuffer extends GlObject { * @return A buffer that will be persistent if the driver supports it. */ public static GlBuffer requestPersistent(GlBufferType type) { - if (Backend.getInstance().compat.bufferStorageSupported()) { + if (Backend.compat.bufferStorageSupported()) { return new PersistentGlBuffer(type); } else { return new MappedGlBuffer(type); diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/PersistentGlBuffer.java b/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/PersistentGlBuffer.java index fb783dc4f..6c4b474cd 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/PersistentGlBuffer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/PersistentGlBuffer.java @@ -46,7 +46,7 @@ public class PersistentGlBuffer extends GlBuffer implements Mappable { fence.clear(); - Backend.getInstance().compat.bufferStorage.bufferStorage(type, size, flags); + Backend.compat.bufferStorage.bufferStorage(type, size, flags); ByteBuffer byteBuffer = GL30.glMapBufferRange(type.glEnum, 0, size, flags); diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/shader/GlProgram.java b/src/main/java/com/jozufozu/flywheel/backend/gl/shader/GlProgram.java index ba247186a..465766adb 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/shader/GlProgram.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/shader/GlProgram.java @@ -7,6 +7,8 @@ import static org.lwjgl.opengl.GL20.glUniformMatrix4fv; import java.nio.FloatBuffer; +import javax.annotation.Nonnull; + import org.lwjgl.system.MemoryStack; import com.jozufozu.flywheel.backend.Backend; @@ -83,4 +85,13 @@ public abstract class GlProgram extends GlObject { public String toString() { return "program " + name; } + + /** + * A factory interface to create a {@link GlProgram}. + */ + public interface Factory

{ + + @Nonnull + P create(ResourceLocation name, int handle); + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/shader/GlShader.java b/src/main/java/com/jozufozu/flywheel/backend/gl/shader/GlShader.java index 9aab8e5f0..7148e134e 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/shader/GlShader.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/shader/GlShader.java @@ -2,9 +2,9 @@ package com.jozufozu.flywheel.backend.gl.shader; import org.lwjgl.opengl.GL20; -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.source.ShaderLoadingException; import net.minecraft.resources.ResourceLocation; @@ -13,7 +13,7 @@ public class GlShader extends GlObject { public final ResourceLocation name; public final ShaderType type; - public GlShader(ResourceLocation name, ShaderType type, CharSequence source) { + public GlShader(ResourceLocation name, ShaderType type, String source) { this.name = name; this.type = type; int handle = GL20.glCreateShader(type.glEnum); @@ -23,14 +23,12 @@ public class GlShader extends GlObject { String log = GL20.glGetShaderInfoLog(handle); - if (!log.isEmpty()) { - Backend.LOGGER.error("Shader compilation log for " + name + ": " + log); - Backend.LOGGER.error(source); - } - //Backend.log.debug(shader.printSource()); +// if (!log.isEmpty()) { +// env.printShaderInfoLog(source, log, this.name); +// } if (GL20.glGetShaderi(handle, GL20.GL_COMPILE_STATUS) != GL20.GL_TRUE) { - throw new RuntimeException("Could not compile " + name + ". See log for details."); + throw new ShaderLoadingException("Could not compile " + name + ". See log for details."); } setHandle(handle); @@ -40,4 +38,5 @@ public class GlShader extends GlObject { protected void deleteInternal(int handle) { GL20.glDeleteShader(handle); } + } diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/shader/ShaderType.java b/src/main/java/com/jozufozu/flywheel/backend/gl/shader/ShaderType.java index a7146ef56..2b10e7578 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/shader/ShaderType.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/shader/ShaderType.java @@ -16,4 +16,8 @@ public enum ShaderType { this.define = define; this.glEnum = glEnum; } + + public String getDefineStatement() { + return "#define " + define + "\n"; + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/versioned/GlCompat.java b/src/main/java/com/jozufozu/flywheel/backend/gl/versioned/GlCompat.java index c44355940..0d550124f 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/versioned/GlCompat.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/versioned/GlCompat.java @@ -4,6 +4,7 @@ import java.nio.ByteBuffer; import java.util.Arrays; import org.lwjgl.PointerBuffer; +import org.lwjgl.opengl.GL; import org.lwjgl.opengl.GL20C; import org.lwjgl.opengl.GLCapabilities; import org.lwjgl.system.MemoryStack; @@ -23,11 +24,11 @@ public class GlCompat { public final BufferStorage bufferStorage; public final boolean amd; - public GlCompat(GLCapabilities caps) { + public GlCompat() { + GLCapabilities caps = GL.createCapabilities(); instancedArrays = getLatest(InstancedArrays.class, caps); bufferStorage = getLatest(BufferStorage.class, caps); - if (Util.getPlatform() == Util.OS.WINDOWS) { String vendor = GL20C.glGetString(GL20C.GL_VENDOR); // vendor string I got was "ATI Technologies Inc." diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java index 8274a2e06..387c1c1c4 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java @@ -39,8 +39,7 @@ public class InstanceWorld { this.taskEngine = new ParallelTaskEngine("Flywheel " + world.dimension().location()); this.taskEngine.startWorkers(); - FlwEngine engine = Backend.getInstance() - .getEngine(); + FlwEngine engine = Backend.getEngine(); switch (engine) { case INSTANCING -> { diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/GPUInstancer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/GPUInstancer.java index 5d0e20bd2..ffac02ae3 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/GPUInstancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/GPUInstancer.java @@ -151,11 +151,17 @@ public class GPUInstancer extends AbstractInstancer { final StructWriter 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) { @@ -192,7 +198,7 @@ public class GPUInstancer extends AbstractInstancer { vao.bindAttributes(attributeBaseIndex, instanceFormat); for (int i = 0; i < instanceFormat.getAttributeCount(); i++) { - Backend.getInstance().compat.instancedArrays.vertexAttribDivisor(attributeBaseIndex + i, 1); + Backend.compat.instancedArrays.vertexAttribDivisor(attributeBaseIndex + i, 1); } } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterial.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterial.java index a72dcf415..becb71114 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterial.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterial.java @@ -1,20 +1,17 @@ package com.jozufozu.flywheel.backend.instancing.instancing; -import java.util.concurrent.ExecutionException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.function.Supplier; -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; import com.jozufozu.flywheel.api.InstanceData; import com.jozufozu.flywheel.api.Instancer; import com.jozufozu.flywheel.api.Material; import com.jozufozu.flywheel.api.struct.Instanced; -import com.jozufozu.flywheel.backend.Backend; -import com.jozufozu.flywheel.backend.RenderWork; -import com.jozufozu.flywheel.backend.model.ImmediateAllocator; import com.jozufozu.flywheel.backend.model.ModelAllocator; -import com.jozufozu.flywheel.backend.model.ModelPool; -import com.jozufozu.flywheel.core.Formats; import com.jozufozu.flywheel.core.model.Model; /** @@ -23,24 +20,14 @@ import com.jozufozu.flywheel.core.model.Model; */ public class InstancedMaterial implements Material { - final ModelAllocator allocator; - protected final Cache> models; + protected final ModelAllocator allocator; + protected final Map> models = new HashMap<>(); protected final Instanced type; + protected final List> uninitialized = new ArrayList<>(); - public InstancedMaterial(Instanced type) { + public InstancedMaterial(Instanced type, ModelAllocator allocator) { this.type = type; - - if (Backend.getInstance().compat.onAMDWindows()) { - allocator = ImmediateAllocator.INSTANCE; - } else { - allocator = new ModelPool(Formats.POS_TEX_NORMAL, 64); - } - this.models = CacheBuilder.newBuilder() - .removalListener(notification -> { - GPUInstancer instancer = (GPUInstancer) notification.getValue(); - RenderWork.enqueue(instancer::delete); - }) - .build(); + this.allocator = allocator; } /** @@ -52,32 +39,33 @@ public class InstancedMaterial implements Material { */ @Override public Instancer model(Object key, Supplier modelSupplier) { - try { - return models.get(key, () -> new GPUInstancer<>(type, modelSupplier.get(), allocator)); - } catch (ExecutionException e) { - throw new RuntimeException("error creating instancer", e); - } + return models.computeIfAbsent(key, $ -> { + GPUInstancer instancer = new GPUInstancer<>(type, modelSupplier.get(), allocator); + uninitialized.add(instancer); + return instancer; + }); } public boolean nothingToRender() { - return models.size() > 0 && models.asMap() - .values() + return models.size() > 0 && models.values() .stream() .allMatch(GPUInstancer::isEmpty); } public void delete() { - models.invalidateAll(); - if (allocator instanceof ModelPool pool) pool.delete(); + models.values().forEach(GPUInstancer::delete); + models.clear(); } /** * Clear all instance data without freeing resources. */ public void clear() { - models.asMap() - .values() + models.values() .forEach(GPUInstancer::clear); } + public Collection> getAllInstancers() { + return models.values(); + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterialGroup.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterialGroup.java index 8938686f1..3e1ba850d 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterialGroup.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterialGroup.java @@ -1,6 +1,5 @@ package com.jozufozu.flywheel.backend.instancing.instancing; -import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -8,7 +7,13 @@ import com.jozufozu.flywheel.api.InstanceData; import com.jozufozu.flywheel.api.MaterialGroup; import com.jozufozu.flywheel.api.struct.Instanced; import com.jozufozu.flywheel.api.struct.StructType; +import com.jozufozu.flywheel.backend.Backend; +import com.jozufozu.flywheel.backend.RenderLayer; +import com.jozufozu.flywheel.backend.model.FallbackAllocator; +import com.jozufozu.flywheel.backend.model.ModelAllocator; import com.jozufozu.flywheel.backend.model.ModelPool; +import com.jozufozu.flywheel.core.Formats; +import com.jozufozu.flywheel.core.compile.ProgramContext; import com.jozufozu.flywheel.core.shader.WorldProgram; import com.jozufozu.flywheel.util.Textures; import com.mojang.math.Matrix4f; @@ -27,49 +32,55 @@ public class InstancedMaterialGroup

implements MaterialG protected final RenderType type; private final Map, InstancedMaterial> materials = new HashMap<>(); + private final ModelAllocator allocator; public InstancedMaterialGroup(InstancingEngine

owner, RenderType type) { this.owner = owner; this.type = type; + if (Backend.compat.onAMDWindows()) { + this.allocator = FallbackAllocator.INSTANCE; + } else { + this.allocator = new ModelPool(Formats.POS_TEX_NORMAL, 2048); + } } @SuppressWarnings("unchecked") @Override public InstancedMaterial material(StructType type) { if (type instanceof Instanced instanced) { - return (InstancedMaterial) materials.computeIfAbsent(instanced, InstancedMaterial::new); + return (InstancedMaterial) materials.computeIfAbsent(instanced, t -> new InstancedMaterial<>(t, allocator)); } else { throw new ClassCastException("Cannot use type '" + type + "' with GPU instancing."); } } - public void render(Matrix4f viewProjection, double camX, double camY, double camZ) { + public void render(Matrix4f viewProjection, double camX, double camY, double camZ, RenderLayer layer) { type.setupRenderState(); Textures.bindActiveTextures(); - renderAll(viewProjection, camX, camY, camZ); + renderAll(viewProjection, camX, camY, camZ, layer); type.clearRenderState(); } - protected void renderAll(Matrix4f viewProjection, double camX, double camY, double camZ) { + protected void renderAll(Matrix4f viewProjection, double camX, double camY, double camZ, RenderLayer layer) { + // initialize all uninitialized instancers... + for (InstancedMaterial material : materials.values()) { + for (GPUInstancer instancer : material.uninitialized) { + instancer.init(); + } + material.uninitialized.clear(); + } + + if (allocator instanceof ModelPool pool) { + // ...and then flush the model arena in case anything was marked for upload + pool.flush(); + } + for (Map.Entry, InstancedMaterial> entry : materials.entrySet()) { InstancedMaterial material = entry.getValue(); if (material.nothingToRender()) continue; - Collection> instancers = material.models.asMap() - .values(); - - // initialize all uninitialized instancers... - for (GPUInstancer gpuInstancer : instancers) { - gpuInstancer.init(); - } - - if (material.allocator instanceof ModelPool pool) { - // ...and then flush the model arena in case anything was marked for upload - pool.flush(); - } - - P program = owner.getProgram(entry.getKey() - .getProgramSpec()).get(); + P program = owner.context.getProgram(ProgramContext.create(entry.getKey() + .getProgramSpec(), Formats.POS_TEX_NORMAL, layer)); program.bind(); program.uploadViewProjection(viewProjection); @@ -77,7 +88,7 @@ public class InstancedMaterialGroup

implements MaterialG setup(program); - for (GPUInstancer instancer : instancers) { + for (GPUInstancer instancer : material.getAllInstancers()) { instancer.render(); } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java index c75fff017..651be0865 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java @@ -3,7 +3,6 @@ package com.jozufozu.flywheel.backend.instancing.instancing; import java.util.EnumMap; import java.util.HashMap; import java.util.Map; -import java.util.function.Supplier; import java.util.stream.Stream; import javax.annotation.Nullable; @@ -12,7 +11,7 @@ import com.jozufozu.flywheel.api.MaterialGroup; import com.jozufozu.flywheel.backend.RenderLayer; import com.jozufozu.flywheel.backend.instancing.Engine; import com.jozufozu.flywheel.backend.instancing.TaskEngine; -import com.jozufozu.flywheel.core.WorldContext; +import com.jozufozu.flywheel.core.compile.ProgramCompiler; import com.jozufozu.flywheel.core.shader.WorldProgram; import com.jozufozu.flywheel.event.RenderLayerEvent; import com.jozufozu.flywheel.util.WeakHashSet; @@ -22,7 +21,6 @@ import net.minecraft.client.Camera; import net.minecraft.client.renderer.RenderType; import net.minecraft.core.BlockPos; import net.minecraft.core.Vec3i; -import net.minecraft.resources.ResourceLocation; import net.minecraft.util.Mth; public class InstancingEngine

implements Engine { @@ -31,7 +29,7 @@ public class InstancingEngine

implements Engine { protected BlockPos originCoordinate = BlockPos.ZERO; - protected final WorldContext

context; + protected final ProgramCompiler

context; protected final GroupFactory

groupFactory; protected final boolean ignoreOriginCoordinate; @@ -39,15 +37,11 @@ public class InstancingEngine

implements Engine { private final WeakHashSet listeners; - public InstancingEngine(WorldContext

context, TaskEngine taskEngine) { - this(context, InstancedMaterialGroup::new, false); - } - - public static

Builder

builder(WorldContext

context) { + public static

Builder

builder(ProgramCompiler

context) { return new Builder<>(context); } - public InstancingEngine(WorldContext

context, GroupFactory

groupFactory, boolean ignoreOriginCoordinate) { + public InstancingEngine(ProgramCompiler

context, GroupFactory

groupFactory, boolean ignoreOriginCoordinate) { this.context = context; this.ignoreOriginCoordinate = ignoreOriginCoordinate; @@ -95,7 +89,7 @@ public class InstancingEngine

implements Engine { viewProjection = event.viewProjection; } - getGroupsToRender(event.getLayer()).forEach(group -> group.render(viewProjection, camX, camY, camZ)); + getGroupsToRender(event.getLayer()).forEach(group -> group.render(viewProjection, camX, camY, camZ, event.getLayer())); } private Stream> getGroupsToRender(@Nullable RenderLayer layer) { @@ -120,10 +114,6 @@ public class InstancingEngine

implements Engine { } } - public Supplier

getProgram(ResourceLocation name) { - return context.getProgramSupplier(name); - } - @Override public Vec3i getOriginCoordinate() { return originCoordinate; @@ -171,11 +161,11 @@ public class InstancingEngine

implements Engine { } public static class Builder

{ - protected final WorldContext

context; + protected final ProgramCompiler

context; protected GroupFactory

groupFactory = InstancedMaterialGroup::new; protected boolean ignoreOriginCoordinate; - public Builder(WorldContext

context) { + public Builder(ProgramCompiler

context) { this.context = context; } diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/ImmediateAllocator.java b/src/main/java/com/jozufozu/flywheel/backend/model/FallbackAllocator.java similarity index 66% rename from src/main/java/com/jozufozu/flywheel/backend/model/ImmediateAllocator.java rename to src/main/java/com/jozufozu/flywheel/backend/model/FallbackAllocator.java index 30240d8d8..7520afff2 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/model/ImmediateAllocator.java +++ b/src/main/java/com/jozufozu/flywheel/backend/model/FallbackAllocator.java @@ -2,9 +2,8 @@ package com.jozufozu.flywheel.backend.model; import com.jozufozu.flywheel.core.model.Model; -public class ImmediateAllocator implements ModelAllocator { - - public static final ImmediateAllocator INSTANCE = new ImmediateAllocator(); +public enum FallbackAllocator implements ModelAllocator { + INSTANCE; @Override public BufferedModel alloc(Model model, Callback allocationCallback) { diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/InstancingProgramMetaData.java b/src/main/java/com/jozufozu/flywheel/backend/pipeline/InstancingProgramMetaData.java deleted file mode 100644 index c033b7b8c..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/InstancingProgramMetaData.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.jozufozu.flywheel.backend.pipeline; - -import java.util.Optional; - -import com.google.common.collect.ImmutableList; -import com.jozufozu.flywheel.backend.source.ShaderLoadingException; -import com.jozufozu.flywheel.backend.source.SourceFile; -import com.jozufozu.flywheel.backend.source.error.ErrorReporter; -import com.jozufozu.flywheel.backend.source.parse.ShaderFunction; -import com.jozufozu.flywheel.backend.source.parse.ShaderStruct; -import com.jozufozu.flywheel.backend.source.parse.Variable; -import com.jozufozu.flywheel.backend.source.span.Span; - -public class InstancingProgramMetaData { - - public final SourceFile file; - public final ShaderFunction vertexMain; - public final ShaderFunction fragmentMain; - public final Span interpolantName; - public final Span vertexName; - public final Span instanceName; - public final ShaderStruct interpolant; - public final ShaderStruct vertex; - public final ShaderStruct instance; - - public InstancingProgramMetaData(SourceFile file) { - this.file = file; - - Optional vertexFunc = file.findFunction("vertex"); - Optional fragmentFunc = file.findFunction("fragment"); - - if (fragmentFunc.isEmpty()) { - ErrorReporter.generateMissingFunction(file, "fragment", "\"fragment\" function not defined"); - } - if (vertexFunc.isEmpty()) { - ErrorReporter.generateFileError(file, "could not find \"vertex\" function"); - } - - if (fragmentFunc.isEmpty() || vertexFunc.isEmpty()) { - throw new ShaderLoadingException(); - } - - fragmentMain = fragmentFunc.get(); - vertexMain = vertexFunc.get(); - ImmutableList parameters = fragmentMain.getParameters(); - ImmutableList vertexParams = vertexMain.getParameters(); - - if (parameters.size() != 1) { - ErrorReporter.generateSpanError(fragmentMain.getArgs(), "instancing requires fragment function to have 1 argument"); - } - - if (vertexParams.size() != 2) { - ErrorReporter.generateSpanError(vertexMain.getArgs(), "instancing requires vertex function to have 2 arguments"); - throw new ShaderLoadingException(); - } - - interpolantName = vertexMain.getType(); - vertexName = vertexParams.get(0) - .typeName(); - instanceName = vertexParams.get(1) - .typeName(); - - Optional maybeInterpolant = file.findStruct(interpolantName); - Optional maybeVertex = file.findStruct(vertexName); - Optional maybeInstance = file.findStruct(instanceName); - - if (maybeVertex.isEmpty()) { - ErrorReporter.generateMissingStruct(file, vertexName, "struct not defined"); - } - - if (maybeInterpolant.isEmpty()) { - ErrorReporter.generateMissingStruct(file, interpolantName, "struct not defined"); - } - - if (maybeInstance.isEmpty()) { - ErrorReporter.generateMissingStruct(file, instanceName, "struct not defined"); - } - - if (maybeVertex.isEmpty() || maybeInterpolant.isEmpty() || maybeInstance.isEmpty()) { - throw new ShaderLoadingException(); - } - - interpolant = maybeInterpolant.get(); - vertex = maybeVertex.get(); - instance = maybeInstance.get(); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/InstancingTemplate.java b/src/main/java/com/jozufozu/flywheel/backend/pipeline/InstancingTemplate.java deleted file mode 100644 index fdb4e4381..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/InstancingTemplate.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.jozufozu.flywheel.backend.pipeline; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import com.jozufozu.flywheel.backend.gl.shader.ShaderType; -import com.jozufozu.flywheel.backend.source.SourceFile; - -public class InstancingTemplate extends Template { - - public static final InstancingTemplate INSTANCE = new InstancingTemplate(); - - public InstancingTemplate() { - super(InstancingProgramMetaData::new); - } - - @Override - public void generateTemplateSource(StringBuilder builder, ShaderType type, SourceFile file) { - if (type == ShaderType.VERTEX) { - vertexFooter(builder, file); - } else if (type == ShaderType.FRAGMENT) { - fragmentFooter(builder, file); - } - } - - @Override - public Collection getShaderInputs(SourceFile file) { - InstancingProgramMetaData data = getMetadata(file); - - List inputs = new ArrayList<>(ShaderInput.fromStruct(data.vertex, "a_v_")); - inputs.addAll(ShaderInput.fromStruct(data.instance, "a_i_")); - - return inputs; - } - - public void vertexFooter(StringBuilder template, SourceFile file) { - InstancingProgramMetaData data = getMetadata(file); - - Template.prefixFields(template, data.vertex, "in", "a_v_"); - Template.prefixFields(template, data.instance, "in", "a_i_"); - Template.prefixFields(template, data.interpolant, "out", "v2f_"); - - template.append("void main() {\n"); - template.append(data.vertexName) - .append(" v;\n"); - Template.assignFields(template, data.vertex, "v.", "a_v_"); - - template.append(data.instanceName) - .append(" i;\n"); - Template.assignFields(template, data.instance, "i.", "a_i_"); - - template.append(data.interpolantName) - .append(" o = ") - .append(data.vertexMain.call("v", "i")) - .append(";\n"); - - Template.assignFields(template, data.interpolant, "v2f_", "o."); - - template.append('}'); - } - - public void fragmentFooter(StringBuilder template, SourceFile file) { - InstancingProgramMetaData data = getMetadata(file); - - Template.prefixFields(template, data.interpolant, "in", "v2f_"); - - template.append("void main() {\n"); - template.append(data.interpolantName) - .append(" o;\n"); - Template.assignFields(template, data.interpolant, "o.", "v2f_"); - - template.append(data.fragmentMain.call("o")) - .append(";\n"); - - template.append('}'); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/OneShotProgramMetaData.java b/src/main/java/com/jozufozu/flywheel/backend/pipeline/OneShotProgramMetaData.java deleted file mode 100644 index 67562a68b..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/OneShotProgramMetaData.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.jozufozu.flywheel.backend.pipeline; - -import java.util.Optional; - -import com.google.common.collect.ImmutableList; -import com.jozufozu.flywheel.backend.source.SourceFile; -import com.jozufozu.flywheel.backend.source.error.ErrorReporter; -import com.jozufozu.flywheel.backend.source.parse.ShaderFunction; -import com.jozufozu.flywheel.backend.source.parse.ShaderStruct; -import com.jozufozu.flywheel.backend.source.parse.Variable; -import com.jozufozu.flywheel.backend.source.span.Span; - -public class OneShotProgramMetaData { - - public final SourceFile file; - public final ShaderFunction vertexMain; - public final Span interpolantName; - public final Span vertexName; - public final ShaderStruct interpolant; - public final ShaderStruct vertex; - public final ShaderFunction fragmentMain; - - public OneShotProgramMetaData(SourceFile file) { - this.file = file; - - Optional maybeVertexMain = file.findFunction("vertex"); - Optional maybeFragmentMain = file.findFunction("fragment"); - - if (maybeVertexMain.isEmpty()) { - ErrorReporter.generateFileError(file, "could not find \"vertex\" function"); - } - - if (maybeFragmentMain.isEmpty()) { - ErrorReporter.generateMissingFunction(file, "fragment", "\"fragment\" function not defined"); - } - - if (maybeVertexMain.isEmpty() || maybeFragmentMain.isEmpty()) { - throw new RuntimeException(); - } - - vertexMain = maybeVertexMain.get(); - fragmentMain = maybeFragmentMain.get(); - ImmutableList fragmentParameters = fragmentMain.getParameters(); - ImmutableList vertexParameters = vertexMain.getParameters(); - - if (vertexParameters.size() != 1) { - ErrorReporter.generateSpanError(vertexMain.getArgs(), "a basic model requires vertex function to have one argument"); - } - - if (fragmentParameters.size() != 1) { - ErrorReporter.generateSpanError(fragmentMain.getArgs(), "fragment function must have exactly one argument"); - } - - if (vertexParameters.size() != 1 || fragmentParameters.size() != 1) { - throw new RuntimeException(); - } - - interpolantName = vertexMain.getType(); - vertexName = vertexParameters.get(0) - .typeName(); - - Optional maybeInterpolant = file.findStruct(interpolantName); - Optional maybeVertex = file.findStruct(vertexName); - - if (maybeVertex.isEmpty()) - ErrorReporter.generateMissingStruct(file, vertexName, "struct not defined"); - - if (maybeInterpolant.isEmpty()) - ErrorReporter.generateMissingStruct(file, interpolantName, "struct not defined"); - - if (maybeVertex.isEmpty() || maybeInterpolant.isEmpty()) { - throw new RuntimeException(); - } - - interpolant = maybeInterpolant.get(); - vertex = maybeVertex.get(); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/OneShotTemplate.java b/src/main/java/com/jozufozu/flywheel/backend/pipeline/OneShotTemplate.java deleted file mode 100644 index f457ebfc4..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/OneShotTemplate.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.jozufozu.flywheel.backend.pipeline; - -import java.util.Collection; - -import com.jozufozu.flywheel.backend.gl.shader.ShaderType; -import com.jozufozu.flywheel.backend.source.SourceFile; - -public class OneShotTemplate extends Template { - - public static final OneShotTemplate INSTANCE = new OneShotTemplate(); - - public OneShotTemplate() { - super(OneShotProgramMetaData::new); - } - - @Override - public void generateTemplateSource(StringBuilder builder, ShaderType type, SourceFile file) { - if (type == ShaderType.VERTEX) { - vertexFooter(builder, file); - } else if (type == ShaderType.FRAGMENT) { - fragmentFooter(builder, file); - } - } - - @Override - public Collection getShaderInputs(SourceFile file) { - OneShotProgramMetaData data = getMetadata(file); - - return ShaderInput.fromStruct(data.vertex, "a_v_"); - } - - public void vertexFooter(StringBuilder template, SourceFile file) { - OneShotProgramMetaData data = getMetadata(file); - - Template.prefixFields(template, data.vertex, "in", "a_v_"); - Template.prefixFields(template, data.interpolant, "out", "v2f_"); - - template.append("void main() {\n"); - template.append(data.vertexName) - .append(" v;\n"); - Template.assignFields(template, data.vertex, "v.", "a_v_"); - - template.append(data.interpolantName) - .append(" o = ") - .append(data.vertexMain.call("v")) - .append(";\n"); - - Template.assignFields(template, data.interpolant, "v2f_", "o."); - - template.append('}'); - } - - public void fragmentFooter(StringBuilder template, SourceFile file) { - OneShotProgramMetaData data = getMetadata(file); - - Template.prefixFields(template, data.interpolant, "in", "v2f_"); - - template.append("void main() {\n"); - template.append(data.interpolant.name) - .append(" o;\n"); - Template.assignFields(template, data.interpolant, "o.", "v2f_"); - - template.append(data.fragmentMain.call("o")) - .append(";\n"); - - template.append('}'); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/Shader.java b/src/main/java/com/jozufozu/flywheel/backend/pipeline/Shader.java deleted file mode 100644 index bf7aece43..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/Shader.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.jozufozu.flywheel.backend.pipeline; - -public class Shader { - - -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/ShaderInput.java b/src/main/java/com/jozufozu/flywheel/backend/pipeline/ShaderInput.java deleted file mode 100644 index bf9b3b95c..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/ShaderInput.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.jozufozu.flywheel.backend.pipeline; - -import java.util.Collection; -import java.util.stream.Collectors; - -import com.jozufozu.flywheel.backend.source.parse.ShaderStruct; -import com.jozufozu.flywheel.backend.source.parse.StructField; - -/** - * A single input to a shader. - */ -public class ShaderInput { - public final CharSequence name; - public final int attribCount; - - public ShaderInput(CharSequence name, int attribCount) { - this.name = name; - this.attribCount = attribCount; - } - - public ShaderInput withPrefix(CharSequence prefix) { - return new ShaderInput(prefix.toString() + name, attribCount); - } - - public static Collection fromStruct(ShaderStruct struct, String prefix) { - return struct.getFields() - .stream() - .map(ShaderInput::from) - .map(a -> a.withPrefix(prefix)) - .collect(Collectors.toList()); - } - - public static ShaderInput from(StructField field) { - int attributeCount = TypeHelper.getAttributeCount(field.type); - - return new ShaderInput(field.name, attributeCount); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/ShaderPipeline.java b/src/main/java/com/jozufozu/flywheel/backend/pipeline/ShaderPipeline.java deleted file mode 100644 index 0caa59d57..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/ShaderPipeline.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.jozufozu.flywheel.backend.pipeline; - -import com.jozufozu.flywheel.core.shader.ContextAwareProgram; -import com.jozufozu.flywheel.core.shader.WorldProgram; -import com.jozufozu.flywheel.core.shader.spec.ProgramSpec; - -/** - * The main interface for compiling usable shaders from program specs. - * @param

the type of the program that this pipeline compiles. - */ -public interface ShaderPipeline

{ - - ContextAwareProgram

compile(ProgramSpec spec); - -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/Template.java b/src/main/java/com/jozufozu/flywheel/backend/pipeline/Template.java deleted file mode 100644 index 8b5b621e8..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/Template.java +++ /dev/null @@ -1,83 +0,0 @@ -package com.jozufozu.flywheel.backend.pipeline; - -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.function.Function; - -import com.google.common.collect.ImmutableList; -import com.jozufozu.flywheel.backend.gl.GLSLVersion; -import com.jozufozu.flywheel.backend.gl.shader.ShaderType; -import com.jozufozu.flywheel.backend.source.SourceFile; -import com.jozufozu.flywheel.backend.source.parse.ShaderStruct; -import com.jozufozu.flywheel.backend.source.parse.StructField; - -/** - * A class that generates glsl glue code given a SourceFile. - * - *

- * Shader files are written somewhat abstractly. Subclasses of Template handle those abstractions, using SourceFile - * metadata to generate shader code that OpenGL can use to call into our shader programs. - *

- * @param Holds metadata, generates errors. - */ -public abstract class Template { - - private final Map metadata = new HashMap<>(); - - private final Function reader; - - protected Template(Function reader) { - this.reader = reader; - } - - /** - * Generate the necessary glue code here. - * - *

- * See {@link InstancingTemplate} and {@link OneShotTemplate} for examples. - *

- * @param builder The builder to generate the source into. - * @param type The shader stage glue code is needed for. - * @param file The SourceFile with user written code. - */ - public abstract void generateTemplateSource(StringBuilder builder, ShaderType type, SourceFile file); - - public abstract Collection getShaderInputs(SourceFile file); - - public D getMetadata(SourceFile file) { - // lazily read files, cache results - return metadata.computeIfAbsent(file, reader); - } - - public GLSLVersion getVersion() { - return GLSLVersion.V150; - } - - public static void prefixFields(StringBuilder builder, ShaderStruct struct, String qualifier, String prefix) { - ImmutableList fields = struct.getFields(); - - for (StructField field : fields) { - builder.append(qualifier) - .append(' ') - .append(field.type) - .append(' ') - .append(prefix) - .append(field.name) - .append(";\n"); - } - } - - public static void assignFields(StringBuilder builder, ShaderStruct struct, String prefix1, String prefix2) { - ImmutableList fields = struct.getFields(); - - for (StructField field : fields) { - builder.append(prefix1) - .append(field.name) - .append(" = ") - .append(prefix2) - .append(field.name) - .append(";\n"); - } - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/WorldShader.java b/src/main/java/com/jozufozu/flywheel/backend/pipeline/WorldShader.java deleted file mode 100644 index cffa9142b..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/WorldShader.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.jozufozu.flywheel.backend.pipeline; - -import java.util.List; -import java.util.Optional; - -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 net.minecraft.resources.ResourceLocation; - -public class WorldShader { - - public final ResourceLocation name; - public final Template template; - public final CharSequence header; - - public SourceFile mainFile; - - private CharSequence source; - private StringBuilder defines; - - public WorldShader(ResourceLocation name, Template template, FileResolution header) { - this.name = name; - this.template = template; - this.header = Optional.ofNullable(header.getFile()) - .map(SourceFile::generateFinalSource) - .orElse(""); - } - - public WorldShader setDefines(List defs) { - defines = new StringBuilder(); - - for (String def : defs) { - defines.append("#define ") - .append(def) - .append('\n'); - } - return this; - } - - public WorldShader setMainSource(SourceFile file) { - if (mainFile == file) return this; - - mainFile = file; - source = file.generateFinalSource(); - - return this; - } - - public GlShader compile(ShaderType type) { - - StringBuilder finalSource = new StringBuilder(); - - finalSource.append("#version ") - .append(template.getVersion()) - .append('\n') - .append("#extension GL_ARB_conservative_depth : enable\n") - .append("#define ") - .append(type.define) // special case shader type declaration - .append('\n') - .append(defines != null ? defines : "") - .append(header) - .append('\n') - .append(source) - .append('\n'); - - template.generateTemplateSource(finalSource, type, mainFile); - - return new GlShader(name, type, finalSource); - } - - public ProtoProgram createProgram() { - return new ProtoProgram(this); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/WorldShaderPipeline.java b/src/main/java/com/jozufozu/flywheel/backend/pipeline/WorldShaderPipeline.java deleted file mode 100644 index 9129cb514..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/WorldShaderPipeline.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.jozufozu.flywheel.backend.pipeline; - -import java.util.List; - -import javax.annotation.Nullable; - -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.ContextAwareProgram; -import com.jozufozu.flywheel.core.shader.ExtensibleGlProgram; -import com.jozufozu.flywheel.core.shader.GameStateProgram; -import com.jozufozu.flywheel.core.shader.WorldProgram; -import com.jozufozu.flywheel.core.shader.spec.ProgramSpec; -import com.jozufozu.flywheel.core.shader.spec.ProgramState; - -import net.minecraft.resources.ResourceLocation; - -public class WorldShaderPipeline

implements ShaderPipeline

{ - - private final ExtensibleGlProgram.Factory

factory; - - private final Template template; - private final FileResolution header; - - public WorldShaderPipeline(ExtensibleGlProgram.Factory

factory, Template template, FileResolution header) { - this.factory = factory; - this.template = template; - this.header = header; - } - - public ContextAwareProgram

compile(ProgramSpec spec) { - - SourceFile file = spec.getSource().getFile(); - - return compile(spec.name, file, spec.getStates()); - } - - public ContextAwareProgram

compile(ResourceLocation name, SourceFile file, List variants) { - WorldShader shader = new WorldShader(name, template, header) - .setMainSource(file); - - GameStateProgram.Builder

builder = GameStateProgram.builder(compile(shader, null)); - - for (ProgramState variant : variants) { - builder.withVariant(variant.context(), compile(shader, variant)); - } - - return builder.build(); - } - - private P compile(WorldShader shader, @Nullable ProgramState variant) { - - if (variant != null) { - shader.setDefines(variant.defines()); - } - - ProtoProgram program = shader.createProgram() - .compilePart(ShaderType.VERTEX) - .compilePart(ShaderType.FRAGMENT) - .link() - .deleteLinkedShaders(); - - return factory.create(shader.name, program.program); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/Resolver.java b/src/main/java/com/jozufozu/flywheel/backend/source/Resolver.java deleted file mode 100644 index 202b7a94d..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/source/Resolver.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.jozufozu.flywheel.backend.source; - -import java.util.HashMap; -import java.util.Map; - -import net.minecraft.resources.ResourceLocation; - -/** - * Manages deferred file resolution. - * - *

- * Interns all referenced files in all sources, duplicating the final lookups and allowing for more dev-friendly - * error reporting. - *

- * See {@link FileResolution} for more information. - *

- */ -public class Resolver { - - public static final Resolver INSTANCE = new Resolver(); - - private final Map resolutions = new HashMap<>(); - - public FileResolution findShader(ResourceLocation fileLoc) { - return resolutions.computeIfAbsent(fileLoc, FileResolution::new); - } - - /** - * Try and resolve all referenced source files, printing errors if any aren't found. - */ - public void resolve(SourceFinder sources) { - for (FileResolution resolution : resolutions.values()) { - resolution.resolve(sources); - } - } - - /** - * Invalidates all FileResolutions. - * - *

- * Called on resource reload. - *

- */ - public void invalidate() { - resolutions.values().forEach(FileResolution::invalidate); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/ShaderLoadingException.java b/src/main/java/com/jozufozu/flywheel/backend/source/ShaderLoadingException.java deleted file mode 100644 index 02e013091..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/source/ShaderLoadingException.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.jozufozu.flywheel.backend.source; - -public class ShaderLoadingException extends RuntimeException { - public ShaderLoadingException() { - } - - public ShaderLoadingException(String message) { - super(message); - } - - public ShaderLoadingException(String message, Throwable cause) { - super(message, cause); - } - - public ShaderLoadingException(Throwable cause) { - super(cause); - } - - public ShaderLoadingException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { - super(message, cause, enableSuppression, writableStackTrace); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/error/ErrorBuilder.java b/src/main/java/com/jozufozu/flywheel/backend/source/error/ErrorBuilder.java deleted file mode 100644 index 8a908f6dc..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/source/error/ErrorBuilder.java +++ /dev/null @@ -1,104 +0,0 @@ -package com.jozufozu.flywheel.backend.source.error; - -import java.util.ArrayList; -import java.util.List; - -import javax.annotation.Nullable; - -import com.jozufozu.flywheel.backend.source.SourceFile; -import com.jozufozu.flywheel.backend.source.SourceLines; -import com.jozufozu.flywheel.backend.source.error.lines.ErrorLine; -import com.jozufozu.flywheel.backend.source.error.lines.FileLine; -import com.jozufozu.flywheel.backend.source.error.lines.HeaderLine; -import com.jozufozu.flywheel.backend.source.error.lines.SourceLine; -import com.jozufozu.flywheel.backend.source.error.lines.SpanHighlightLine; -import com.jozufozu.flywheel.backend.source.span.Span; -import com.jozufozu.flywheel.util.FlwUtil; - -public class ErrorBuilder { - - private final List lines = new ArrayList<>(); - - private final Level level; - - public ErrorBuilder(Level level, CharSequence msg) { - this.level = level; - - lines.add(new HeaderLine(level.toString(), msg)); - } - - public static ErrorBuilder error(CharSequence msg) { - return new ErrorBuilder(Level.ERROR, msg); - } - - public static ErrorBuilder warn(CharSequence msg) { - return new ErrorBuilder(Level.WARN, msg); - } - - public ErrorBuilder pointAtFile(SourceFile file) { - lines.add(new FileLine(file.name.toString())); - return this; - } - - public ErrorBuilder hintIncludeFor(@Nullable Span span, CharSequence msg) { - if (span == null) return this; - - String builder = "add " + span.getSourceFile() - .importStatement() + ' ' + msg; - - lines.add(new HeaderLine("hint", builder)); - - return this.pointAtFile(span.getSourceFile()) - .pointAt(span, 1); - } - - public ErrorBuilder pointAt(Span span, int ctxLines) { - - if (span.lines() == 1) { - SourceLines lines = span.getSourceFile().lines; - - int spanLine = span.firstLine(); - - int firstLine = Math.max(0, spanLine - ctxLines); - int lastLine = Math.min(lines.getLineCount(), spanLine + ctxLines); - - int firstCol = span.getStart() - .col(); - int lastCol = span.getEnd() - .col(); - - for (int i = firstLine; i <= lastLine; i++) { - CharSequence line = lines.getLine(i); - - this.lines.add(SourceLine.numbered(i + 1, line.toString())); - - if (i == spanLine) { - this.lines.add(new SpanHighlightLine(firstCol, lastCol)); - } - } - } - - return this; - } - - public CharSequence build() { - - int maxLength = -1; - for (ErrorLine line : lines) { - int length = line.neededMargin(); - - if (length > maxLength) maxLength = length; - } - - StringBuilder builder = new StringBuilder(); - for (ErrorLine line : lines) { - int length = line.neededMargin(); - - builder.append(FlwUtil.repeatChar(' ', maxLength - length)) - .append(line.build()) - .append('\n'); - } - - return builder; - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/parse/Variable.java b/src/main/java/com/jozufozu/flywheel/backend/source/parse/Variable.java deleted file mode 100644 index 270acee61..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/source/parse/Variable.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.jozufozu.flywheel.backend.source.parse; - -import com.jozufozu.flywheel.backend.source.span.Span; - -public class Variable extends AbstractShaderElement { - - private final Span type; - private final Span name; - - public Variable(Span self, Span type, Span name) { - super(self); - this.type = type; - this.name = name; - } - - public Span typeName() { - return type; - } - - public Span getName() { - return name; - } - - @Override - public String toString() { - return type + " " + name; - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/struct/BufferWriter.java b/src/main/java/com/jozufozu/flywheel/backend/struct/BufferWriter.java index 0033496b3..31a23c472 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/struct/BufferWriter.java +++ b/src/main/java/com/jozufozu/flywheel/backend/struct/BufferWriter.java @@ -33,6 +33,6 @@ public abstract class BufferWriter implements StructWriter { @Override public void seek(int pos) { - backingBuffer.position(pos * stride); + backingBuffer.position(pos * stride); } } diff --git a/src/main/java/com/jozufozu/flywheel/core/Contexts.java b/src/main/java/com/jozufozu/flywheel/core/Contexts.java index 744ef5ae6..db5d448f6 100644 --- a/src/main/java/com/jozufozu/flywheel/core/Contexts.java +++ b/src/main/java/com/jozufozu/flywheel/core/Contexts.java @@ -1,16 +1,12 @@ package com.jozufozu.flywheel.core; import com.jozufozu.flywheel.Flywheel; -import com.jozufozu.flywheel.backend.Backend; -import com.jozufozu.flywheel.backend.GameStateRegistry; -import com.jozufozu.flywheel.backend.pipeline.InstancingTemplate; -import com.jozufozu.flywheel.backend.pipeline.ShaderPipeline; -import com.jozufozu.flywheel.backend.pipeline.WorldShaderPipeline; -import com.jozufozu.flywheel.backend.source.FileResolution; -import com.jozufozu.flywheel.backend.source.Resolver; +import com.jozufozu.flywheel.core.compile.ProgramCompiler; import com.jozufozu.flywheel.core.crumbling.CrumblingProgram; +import com.jozufozu.flywheel.core.shader.NormalDebugStateProvider; import com.jozufozu.flywheel.core.shader.WorldProgram; -import com.jozufozu.flywheel.core.shader.gamestate.NormalDebugStateProvider; +import com.jozufozu.flywheel.core.source.FileResolution; +import com.jozufozu.flywheel.core.source.Resolver; import com.jozufozu.flywheel.event.GatherContextEvent; import com.jozufozu.flywheel.util.ResourceUtil; @@ -21,22 +17,17 @@ import net.minecraftforge.api.distmarker.OnlyIn; @OnlyIn(Dist.CLIENT) public class Contexts { - public static WorldContext WORLD; - public static WorldContext CRUMBLING; + public static ProgramCompiler WORLD; + public static ProgramCompiler CRUMBLING; public static void flwInit(GatherContextEvent event) { - Backend backend = event.getBackend(); - GameStateRegistry.register(NormalDebugStateProvider.INSTANCE); - FileResolution crumblingBuiltins = Resolver.INSTANCE.findShader(ResourceUtil.subPath(Names.CRUMBLING, ".glsl")); - FileResolution worldBuiltins = Resolver.INSTANCE.findShader(ResourceUtil.subPath(Names.WORLD, ".glsl")); + FileResolution worldBuiltins = Resolver.INSTANCE.get(ResourceUtil.subPath(Names.WORLD, ".glsl")); + FileResolution crumblingBuiltins = Resolver.INSTANCE.get(ResourceUtil.subPath(Names.CRUMBLING, ".glsl")); - ShaderPipeline crumblingPipeline = new WorldShaderPipeline<>(CrumblingProgram::new, InstancingTemplate.INSTANCE, crumblingBuiltins); - ShaderPipeline worldPipeline = new WorldShaderPipeline<>(WorldProgram::new, InstancingTemplate.INSTANCE, worldBuiltins); - - CRUMBLING = backend.register(WorldContext.builder(backend, Names.CRUMBLING).build(crumblingPipeline)); - WORLD = backend.register(WorldContext.builder(backend, Names.WORLD).build(worldPipeline)); + WORLD = ProgramCompiler.create(Templates.INSTANCING, WorldProgram::new, worldBuiltins); + CRUMBLING = ProgramCompiler.create(Templates.INSTANCING, CrumblingProgram::new, crumblingBuiltins); } public static class Names { diff --git a/src/main/java/com/jozufozu/flywheel/core/GameStateRegistry.java b/src/main/java/com/jozufozu/flywheel/core/GameStateRegistry.java new file mode 100644 index 000000000..67a5c2b44 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/GameStateRegistry.java @@ -0,0 +1,58 @@ +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 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; + } + + public static void _clear() { + PROVIDERS.clear(); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/Materials.java b/src/main/java/com/jozufozu/flywheel/core/Materials.java index f5470dc83..fbf704aaa 100644 --- a/src/main/java/com/jozufozu/flywheel/core/Materials.java +++ b/src/main/java/com/jozufozu/flywheel/core/Materials.java @@ -2,12 +2,10 @@ package com.jozufozu.flywheel.core; import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.api.struct.StructType; -import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.core.materials.model.ModelData; import com.jozufozu.flywheel.core.materials.model.ModelType; import com.jozufozu.flywheel.core.materials.oriented.OrientedData; import com.jozufozu.flywheel.core.materials.oriented.OrientedType; -import com.jozufozu.flywheel.event.GatherContextEvent; import net.minecraft.resources.ResourceLocation; import net.minecraftforge.api.distmarker.Dist; @@ -19,14 +17,9 @@ public class Materials { public static final StructType ORIENTED = new OrientedType(); public static final StructType TRANSFORMED = new ModelType(); - public static void flwInit(GatherContextEvent event) { - Backend backend = event.getBackend(); - backend.register(Names.ORIENTED, ORIENTED); - backend.register(Names.MODEL, TRANSFORMED); - } - public static class Names { public static final ResourceLocation MODEL = Flywheel.rl("model"); public static final ResourceLocation ORIENTED = Flywheel.rl("oriented"); + public static final ResourceLocation PASSTHRU = Flywheel.rl("passthru"); } } diff --git a/src/main/java/com/jozufozu/flywheel/core/Templates.java b/src/main/java/com/jozufozu/flywheel/core/Templates.java new file mode 100644 index 000000000..59b538a2f --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/Templates.java @@ -0,0 +1,14 @@ +package com.jozufozu.flywheel.core; + +import com.jozufozu.flywheel.backend.gl.GLSLVersion; +import com.jozufozu.flywheel.core.compile.FragmentTemplateData; +import com.jozufozu.flywheel.core.compile.InstancingTemplateData; +import com.jozufozu.flywheel.core.compile.OneShotTemplateData; +import com.jozufozu.flywheel.core.compile.Template; + +public class Templates { + + public static final Template INSTANCING = new Template<>(GLSLVersion.V330, InstancingTemplateData::new); + public static final Template ONE_SHOT = new Template<>(GLSLVersion.V150, OneShotTemplateData::new); + public static final Template FRAGMENT = new Template<>(GLSLVersion.V150, FragmentTemplateData::new); +} diff --git a/src/main/java/com/jozufozu/flywheel/core/WorldContext.java b/src/main/java/com/jozufozu/flywheel/core/WorldContext.java deleted file mode 100644 index 92320a0f9..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/WorldContext.java +++ /dev/null @@ -1,99 +0,0 @@ -package com.jozufozu.flywheel.core; - -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import java.util.function.Supplier; -import java.util.stream.Stream; - -import com.jozufozu.flywheel.api.struct.Instanced; -import com.jozufozu.flywheel.backend.Backend; -import com.jozufozu.flywheel.backend.ShaderContext; -import com.jozufozu.flywheel.backend.pipeline.ShaderPipeline; -import com.jozufozu.flywheel.core.shader.ContextAwareProgram; -import com.jozufozu.flywheel.core.shader.WorldProgram; -import com.jozufozu.flywheel.core.shader.spec.ProgramSpec; - -import net.minecraft.resources.ResourceLocation; - -public class WorldContext

implements ShaderContext

{ - public final Backend backend; - protected final Map> programs = new HashMap<>(); - protected final ResourceLocation name; - protected final Supplier> specStream; - - public final ShaderPipeline

pipeline; - - public WorldContext(Backend backend, ResourceLocation name, Supplier> specStream, ShaderPipeline

pipeline) { - this.backend = backend; - this.name = name; - this.specStream = specStream; - this.pipeline = pipeline; - } - - @Override - public void load() { - - Backend.LOGGER.info("Loading context '{}'", name); - - specStream.get() - .map(backend::getSpec) - .forEach(this::loadSpec); - } - - private void loadSpec(ProgramSpec spec) { - - try { - programs.put(spec.name, pipeline.compile(spec)); - - Backend.LOGGER.debug("Loaded program {}", spec.name); - } catch (Exception e) { - Backend.LOGGER.error("Error loading program {}", spec.name); - Backend.LOGGER.error("", e); - backend.loader.notifyError(); - } - } - - @Override - public Supplier

getProgramSupplier(ResourceLocation spec) { - return programs.get(spec); - } - - @Override - public void delete() { - programs.values() - .forEach(ContextAwareProgram::delete); - programs.clear(); - } - - public static Builder builder(Backend backend, ResourceLocation name) { - return new Builder(backend, name); - } - - public static class Builder { - private final Backend backend; - private final ResourceLocation name; - private Supplier> specStream; - - public Builder(Backend backend, ResourceLocation name) { - this.backend = backend; - this.name = name; - } - - public Builder setSpecStream(Supplier> specStream) { - this.specStream = specStream; - return this; - } - - public

WorldContext

build(ShaderPipeline

pipeline) { - if (specStream == null) { - specStream = () -> backend.allMaterials() - .stream() - .map(t -> t instanceof Instanced i ? i : null) - .filter(Objects::nonNull) - .map(Instanced::getProgramSpec); - } - return new WorldContext<>(backend, name, specStream, pipeline); - } - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/TypeHelper.java b/src/main/java/com/jozufozu/flywheel/core/compile/CompileUtil.java similarity index 62% rename from src/main/java/com/jozufozu/flywheel/backend/pipeline/TypeHelper.java rename to src/main/java/com/jozufozu/flywheel/core/compile/CompileUtil.java index 283f51a69..ca312e347 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/TypeHelper.java +++ b/src/main/java/com/jozufozu/flywheel/core/compile/CompileUtil.java @@ -1,13 +1,25 @@ -package com.jozufozu.flywheel.backend.pipeline; +package com.jozufozu.flywheel.core.compile; import java.util.regex.Matcher; import java.util.regex.Pattern; -public class TypeHelper { +import com.jozufozu.flywheel.backend.gl.GLSLVersion; +import com.jozufozu.flywheel.backend.gl.shader.ShaderType; + +public class CompileUtil { public static final Pattern vecType = Pattern.compile("^[biud]?vec([234])$"); public static final Pattern matType = Pattern.compile("^mat([234])(?:x([234]))?$"); + 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(); + } + public static int getElementCount(String type) { Matcher vec = vecType.matcher(type); if (vec.find()) return Integer.parseInt(vec.group(1)); diff --git a/src/main/java/com/jozufozu/flywheel/core/compile/FragmentCompiler.java b/src/main/java/com/jozufozu/flywheel/core/compile/FragmentCompiler.java new file mode 100644 index 000000000..e5600cc66 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/compile/FragmentCompiler.java @@ -0,0 +1,102 @@ +package com.jozufozu.flywheel.core.compile; + +import java.util.Objects; + +import com.jozufozu.flywheel.backend.gl.shader.GlShader; +import com.jozufozu.flywheel.backend.gl.shader.ShaderType; +import com.jozufozu.flywheel.core.shader.ShaderConstants; +import com.jozufozu.flywheel.core.shader.StateSnapshot; +import com.jozufozu.flywheel.core.source.FileIndexImpl; +import com.jozufozu.flywheel.core.source.FileResolution; +import com.jozufozu.flywheel.core.source.SourceFile; + +public class FragmentCompiler extends Memoizer { + private final FileResolution header; + private final Template fragment; + + public FragmentCompiler(Template fragment, FileResolution header) { + this.header = header; + this.fragment = fragment; + } + + @Override + protected GlShader _create(Context key) { + SourceFile fragmentFile = key.file; + 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(fragmentFile.name, ShaderType.FRAGMENT, builder.toString()); + } + + @Override + protected void _destroy(GlShader value) { + value.delete(); + } + + /** + * Represents the conditions under which a shader is compiled. + */ + public static final class Context { + /** + * The file to compile. + */ + private final SourceFile file; + + /** + * The shader constants to apply. + */ + private final StateSnapshot ctx; + + /** + * Alpha threshold below which fragments are discarded. + */ + private final float alphaDiscard; + + public Context(SourceFile file, StateSnapshot ctx, float alphaDiscard) { + this.file = file; + this.ctx = ctx; + this.alphaDiscard = alphaDiscard; + } + + public ShaderConstants getShaderConstants() { + ShaderConstants shaderConstants = ctx.getShaderConstants(); + + if (alphaDiscard > 0) { + shaderConstants.define("ALPHA_DISCARD", alphaDiscard); + } + + return shaderConstants; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (obj == null || obj.getClass() != this.getClass()) return false; + var that = (Context) obj; + return this.file == that.file && Objects.equals(this.ctx, that.ctx) && Float.floatToIntBits(this.alphaDiscard) == Float.floatToIntBits(that.alphaDiscard); + } + + @Override + public int hashCode() { + return Objects.hash(file, ctx, alphaDiscard); + } + + @Override + public String toString() { + return "Context[" + "file=" + file + ", " + "ctx=" + ctx + ", " + "alphaDiscard=" + alphaDiscard + ']'; + } + + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/compile/FragmentData.java b/src/main/java/com/jozufozu/flywheel/core/compile/FragmentData.java new file mode 100644 index 000000000..b50d3b2c5 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/compile/FragmentData.java @@ -0,0 +1,8 @@ +package com.jozufozu.flywheel.core.compile; + +public interface FragmentData { + /** + * Generate the necessary glue code here. + */ + String generateFooter(); +} diff --git a/src/main/java/com/jozufozu/flywheel/core/compile/FragmentTemplateData.java b/src/main/java/com/jozufozu/flywheel/core/compile/FragmentTemplateData.java new file mode 100644 index 000000000..0a31773ac --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/compile/FragmentTemplateData.java @@ -0,0 +1,88 @@ +package com.jozufozu.flywheel.core.compile; + +import java.util.Optional; + +import com.google.common.collect.ImmutableList; +import com.jozufozu.flywheel.core.source.SourceFile; +import com.jozufozu.flywheel.core.source.error.ErrorReporter; +import com.jozufozu.flywheel.core.source.parse.ShaderFunction; +import com.jozufozu.flywheel.core.source.parse.ShaderStruct; +import com.jozufozu.flywheel.core.source.parse.StructField; +import com.jozufozu.flywheel.core.source.parse.Variable; +import com.jozufozu.flywheel.core.source.span.Span; + +public class FragmentTemplateData implements FragmentData { + public final SourceFile file; + public final Span interpolantName; + public final ShaderStruct interpolant; + public final ShaderFunction fragmentMain; + + public FragmentTemplateData(SourceFile file) { + this.file = file; + + Optional maybeFragmentMain = file.findFunction("fragment"); + + if (maybeFragmentMain.isEmpty()) { + ErrorReporter.generateMissingFunction(file, "fragment", "\"fragment\" function not defined"); + throw new RuntimeException(); + } + + fragmentMain = maybeFragmentMain.get(); + ImmutableList fragmentParameters = fragmentMain.getParameters(); + + + if (fragmentParameters.size() != 1) { + ErrorReporter.generateSpanError(fragmentMain.getArgs(), "fragment function must have exactly one argument"); + throw new RuntimeException(); + } + + interpolantName = fragmentMain.getParameters().get(0).type; + + Optional maybeInterpolant = file.findStruct(interpolantName); + + if (maybeInterpolant.isEmpty()) { + ErrorReporter.generateMissingStruct(file, interpolantName, "struct not defined"); + + throw new RuntimeException(); + } + + interpolant = maybeInterpolant.get(); + } + + @Override + public String generateFooter() { + StringBuilder builder = new StringBuilder(); + prefixFields(builder, interpolant, "in", "v2f_"); + + builder.append(String.format(""" + void main() { + Fragment o; + o.color = v2f_color; + o.texCoords = v2f_texCoords; + o.light = v2f_light; + o.diffuse = v2f_diffuse; + + vec4 color = %s; + FLWFinalizeColor(color); + } + """, + fragmentMain.call("o") + )); + + return builder.toString(); + } + + public static void prefixFields(StringBuilder builder, ShaderStruct struct, String qualifier, String prefix) { + ImmutableList fields = struct.getFields(); + + for (StructField field : fields) { + builder.append(qualifier) + .append(' ') + .append(field.type) + .append(' ') + .append(prefix) + .append(field.name) + .append(";\n"); + } + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/compile/InstancingTemplateData.java b/src/main/java/com/jozufozu/flywheel/core/compile/InstancingTemplateData.java new file mode 100644 index 000000000..1b473ba85 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/compile/InstancingTemplateData.java @@ -0,0 +1,135 @@ +package com.jozufozu.flywheel.core.compile; + +import java.util.Optional; + +import com.google.common.collect.ImmutableList; +import com.jozufozu.flywheel.api.vertex.VertexType; +import com.jozufozu.flywheel.core.source.FileIndex; +import com.jozufozu.flywheel.core.source.ShaderLoadingException; +import com.jozufozu.flywheel.core.source.SourceFile; +import com.jozufozu.flywheel.core.source.error.ErrorReporter; +import com.jozufozu.flywheel.core.source.parse.ShaderFunction; +import com.jozufozu.flywheel.core.source.parse.ShaderStruct; +import com.jozufozu.flywheel.core.source.parse.StructField; +import com.jozufozu.flywheel.core.source.parse.Variable; +import com.jozufozu.flywheel.core.source.span.Span; + +public class InstancingTemplateData implements VertexData { + + public final SourceFile file; + public final ShaderFunction vertexMain; + public final Span vertexName; + public final Span instanceName; + public final ShaderStruct instance; + + public InstancingTemplateData(SourceFile file) { + this.file = file; + + Optional vertexFunc = file.findFunction("vertex"); + + if (vertexFunc.isEmpty()) { + ErrorReporter.generateFileError(file, "could not find \"vertex\" function"); + throw new ShaderLoadingException(); + } + + vertexMain = vertexFunc.get(); + ImmutableList vertexParams = vertexMain.getParameters(); + + if (vertexParams.size() != 2) { + ErrorReporter.generateSpanError(vertexMain.getArgs(), "instancing requires vertex function to have 2 arguments"); + throw new ShaderLoadingException(); + } + + Variable vertexParam = vertexParams.get(0); + vertexName = vertexParam.type; + + boolean namedVertex = vertexParam.type + .toString() + .equals("Vertex"); + + + if (!(namedVertex && vertexParam.qualifier == Variable.Qualifier.INOUT)) { + ErrorReporter.generateSpanError(vertexParam.qualifierSpan, "first parameter must be inout Vertex"); + throw new ShaderLoadingException(); + } + + instanceName = vertexParams.get(1).type; + + Optional maybeInstance = file.findStruct(instanceName); + + if (maybeInstance.isEmpty()) { + ErrorReporter.generateMissingStruct(file, instanceName, "struct not defined"); + throw new ShaderLoadingException(); + } + + instance = maybeInstance.get(); + } + + @Override + public String generateFooter(FileIndex shader, VertexType vertexType) { + ImmutableList fields = instance.getFields(); + + int attributeBinding = vertexType.getLayout() + .getAttributeCount(); + + StringBuilder template = new StringBuilder(); + + for (StructField field : fields) { + template.append("layout(location = ") + .append(attributeBinding) + .append(") in") + .append(' ') + .append(field.type) + .append(' ') + .append("a_i_") + .append(field.name) + .append(";\n"); + attributeBinding += CompileUtil.getAttributeCount(field.type); + } + template.append(String.format(""" + out vec4 v2f_color; + out vec2 v2f_texCoords; + out vec2 v2f_light; + out float v2f_diffuse; + + void main() { + Vertex v = FLWCreateVertex(); + %s i; + %s + vertex(v, i); + gl_Position = FLWVertex(v); + v.normal = normalize(v.normal); + + v2f_color = v.color; + v2f_texCoords = v.texCoords; + v2f_light = v.light; + v2f_diffuse = diffuse(v.normal); + #if defined(DEBUG_NORMAL) + v2f_color = vec4(v.normal, 1.); + #endif + } + """, + instanceName, + assignFields(instance, "i.", "a_i_") + )); + + return template.toString(); + } + + public static StringBuilder assignFields(ShaderStruct struct, String prefix1, String prefix2) { + ImmutableList fields = struct.getFields(); + + StringBuilder builder = new StringBuilder(); + + for (StructField field : fields) { + builder.append(prefix1) + .append(field.name) + .append(" = ") + .append(prefix2) + .append(field.name) + .append(";\n"); + } + + return builder; + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/compile/Memoizer.java b/src/main/java/com/jozufozu/flywheel/core/compile/Memoizer.java new file mode 100644 index 000000000..f2dbc95f1 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/compile/Memoizer.java @@ -0,0 +1,22 @@ +package com.jozufozu.flywheel.core.compile; + +import java.util.HashMap; +import java.util.Map; + +public abstract class Memoizer { + + private final Map 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); +} diff --git a/src/main/java/com/jozufozu/flywheel/core/compile/OneShotTemplateData.java b/src/main/java/com/jozufozu/flywheel/core/compile/OneShotTemplateData.java new file mode 100644 index 000000000..79a1f1abc --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/compile/OneShotTemplateData.java @@ -0,0 +1,73 @@ +package com.jozufozu.flywheel.core.compile; + +import java.util.Optional; + +import com.google.common.collect.ImmutableList; +import com.jozufozu.flywheel.api.vertex.VertexType; +import com.jozufozu.flywheel.core.source.FileIndex; +import com.jozufozu.flywheel.core.source.ShaderLoadingException; +import com.jozufozu.flywheel.core.source.SourceFile; +import com.jozufozu.flywheel.core.source.error.ErrorReporter; +import com.jozufozu.flywheel.core.source.parse.ShaderFunction; +import com.jozufozu.flywheel.core.source.parse.Variable; + +public class OneShotTemplateData implements VertexData { + + public final SourceFile file; + public final ShaderFunction vertexMain; + + public OneShotTemplateData(SourceFile file) { + this.file = file; + + Optional maybeVertexMain = file.findFunction("vertex"); + + if (maybeVertexMain.isEmpty()) { + ErrorReporter.generateFileError(file, "could not find \"vertex\" function"); + throw new RuntimeException(); + } + + vertexMain = maybeVertexMain.get(); + ImmutableList vertexParameters = vertexMain.getParameters(); + + if (vertexParameters.size() != 1) { + ErrorReporter.generateSpanError(vertexMain.getArgs(), "a basic model requires vertex function to have one argument"); + throw new RuntimeException(); + } + + Variable vertexParam = vertexMain.getParameters().get(0); + + boolean namedVertex = vertexParam.type + .toString() + .equals("Vertex"); + + if (!(namedVertex && vertexParam.qualifier == Variable.Qualifier.INOUT)) { + ErrorReporter.generateSpanError(vertexParam.qualifierSpan, "first parameter must be inout Vertex"); + throw new ShaderLoadingException(); + } + } + + @Override + public String generateFooter(FileIndex file, VertexType vertexType) { + return """ + out vec4 v2f_color; + out vec2 v2f_texCoords; + out vec2 v2f_light; + out float v2f_diffuse; + + void main() { + Vertex v = FLWCreateVertex(); + vertex(v); + gl_Position = FLWVertex(v); + v.normal = normalize(v.normal); + + v2f_color = v.color; + v2f_texCoords = v.texCoords; + v2f_light = v.light; + v2f_diffuse = diffuse(v.normal); + #if defined(DEBUG_NORMAL) + v2f_color = vec4(v.normal, 1.); + #endif + } + """; + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/ProtoProgram.java b/src/main/java/com/jozufozu/flywheel/core/compile/ProgramAssembler.java similarity index 53% rename from src/main/java/com/jozufozu/flywheel/backend/pipeline/ProtoProgram.java rename to src/main/java/com/jozufozu/flywheel/core/compile/ProgramAssembler.java index db4f9353e..420859520 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/ProtoProgram.java +++ b/src/main/java/com/jozufozu/flywheel/core/compile/ProgramAssembler.java @@ -1,9 +1,8 @@ -package com.jozufozu.flywheel.backend.pipeline; +package com.jozufozu.flywheel.core.compile; import static org.lwjgl.opengl.GL11.GL_TRUE; import static org.lwjgl.opengl.GL20.GL_LINK_STATUS; import static org.lwjgl.opengl.GL20.glAttachShader; -import static org.lwjgl.opengl.GL20.glBindAttribLocation; import static org.lwjgl.opengl.GL20.glCreateProgram; import static org.lwjgl.opengl.GL20.glGetProgramInfoLog; import static org.lwjgl.opengl.GL20.glGetProgrami; @@ -12,45 +11,33 @@ import static org.lwjgl.opengl.GL20.glLinkProgram; import java.util.List; import com.jozufozu.flywheel.backend.Backend; +import com.jozufozu.flywheel.backend.gl.shader.GlProgram; import com.jozufozu.flywheel.backend.gl.shader.GlShader; -import com.jozufozu.flywheel.backend.gl.shader.ShaderType; import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import net.minecraft.resources.ResourceLocation; -public class ProtoProgram { +public class ProgramAssembler { public final int program; - public final WorldShader parent; + private final ResourceLocation name; - private int attributeIndex; + private final List shaders = new ObjectArrayList<>(); - private final List shaders; - - public ProtoProgram(WorldShader parent) { - this.parent = parent; + public ProgramAssembler(ResourceLocation name) { + this.name = name; this.program = glCreateProgram(); - shaders = new ObjectArrayList<>(); - } - - public ProtoProgram compilePart(ShaderType type) { - GlShader shader = parent.compile(type); - attachShader(shader); - return this; } /** * Links the attached shaders to this program. */ - public ProtoProgram link() { - - parent.template.getShaderInputs(parent.mainFile) - .forEach(this::addAttribute); - + public ProgramAssembler link() { glLinkProgram(this.program); String log = glGetProgramInfoLog(this.program); if (!log.isEmpty()) { - Backend.LOGGER.debug("Program link log for " + parent.name + ": " + log); + Backend.LOGGER.debug("Program link log for " + name + ": " + log); } int result = glGetProgrami(this.program, GL_LINK_STATUS); @@ -62,19 +49,18 @@ public class ProtoProgram { return this; } - public ProtoProgram deleteLinkedShaders() { + public ProgramAssembler deleteLinkedShaders() { shaders.forEach(GlShader::delete); return this; } - private void attachShader(GlShader glShader) { + public ProgramAssembler attachShader(GlShader glShader) { shaders.add(glShader); glAttachShader(this.program, glShader.handle()); + return this; } - private void addAttribute(ShaderInput shaderInput) { - glBindAttribLocation(this.program, attributeIndex, shaderInput.name); - attributeIndex += shaderInput.attribCount; + public

P build(GlProgram.Factory

factory) { + return factory.create(name, program); } - } diff --git a/src/main/java/com/jozufozu/flywheel/core/compile/ProgramCompiler.java b/src/main/java/com/jozufozu/flywheel/core/compile/ProgramCompiler.java new file mode 100644 index 000000000..4931c63fd --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/compile/ProgramCompiler.java @@ -0,0 +1,80 @@ +package com.jozufozu.flywheel.core.compile; + +import java.util.ArrayList; +import java.util.List; + +import com.jozufozu.flywheel.backend.gl.shader.GlProgram; +import com.jozufozu.flywheel.core.Templates; +import com.jozufozu.flywheel.core.source.FileResolution; +import com.jozufozu.flywheel.event.ReloadRenderersEvent; + +/** + * A caching compiler. + * + *

+ * This class is responsible for compiling programs on the fly. An instance of this class will keep a cache of + * compiled programs, and will only compile a program if it is not already in the cache. + *

+ */ +public class ProgramCompiler

extends Memoizer { + + private static final List> ALL_COMPILERS = new ArrayList<>(); + + private final GlProgram.Factory

factory; + private final VertexCompiler vertexCompiler; + private final FragmentCompiler fragmentCompiler; + + public ProgramCompiler(GlProgram.Factory

factory, VertexCompiler vertexCompiler, FragmentCompiler fragmentCompiler) { + this.factory = factory; + this.vertexCompiler = vertexCompiler; + this.fragmentCompiler = fragmentCompiler; + + ALL_COMPILERS.add(this); + } + + /** + * Creates a program compiler using this template. + * @param template The vertex template to use. + * @param factory A factory to add meaning to compiled programs. + * @param header The header file to use for the program. + * @param

The type of program to compile. + * @return A program compiler. + */ + public static ProgramCompiler

create(Template template, GlProgram.Factory

factory, FileResolution header) { + return new ProgramCompiler<>(factory, new VertexCompiler(template, header), new FragmentCompiler(Templates.FRAGMENT, header)); + } + + /** + * Get or compile a spec to the given vertex type, accounting for all game state conditions specified by the spec. + * + * @param ctx The context of compilation. + * @return A compiled GlProgram. + */ + public P getProgram(ProgramContext ctx) { + return super.get(ctx); + } + + public void invalidate() { + super.invalidate(); + vertexCompiler.invalidate(); + fragmentCompiler.invalidate(); + } + + @Override + protected P _create(ProgramContext ctx) { + return new ProgramAssembler(ctx.spec.name) + .attachShader(vertexCompiler.get(new VertexCompiler.Context(ctx.spec.getVertexFile(), ctx.ctx, ctx.vertexType))) + .attachShader(fragmentCompiler.get(new FragmentCompiler.Context(ctx.spec.getFragmentFile(), ctx.ctx, ctx.alphaDiscard))) + .link() + .build(this.factory); + } + + @Override + protected void _destroy(P value) { + value.delete(); + } + + public static void invalidateAll(ReloadRenderersEvent event) { + ALL_COMPILERS.forEach(ProgramCompiler::invalidate); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/compile/ProgramContext.java b/src/main/java/com/jozufozu/flywheel/core/compile/ProgramContext.java new file mode 100644 index 000000000..2fefb12b3 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/compile/ProgramContext.java @@ -0,0 +1,83 @@ +package com.jozufozu.flywheel.core.compile; + +import java.util.Objects; + +import javax.annotation.Nullable; + +import com.jozufozu.flywheel.api.vertex.VertexType; +import com.jozufozu.flywheel.backend.Backend; +import com.jozufozu.flywheel.backend.RenderLayer; +import com.jozufozu.flywheel.core.GameStateRegistry; +import com.jozufozu.flywheel.core.shader.ProgramSpec; +import com.jozufozu.flywheel.core.shader.StateSnapshot; + +import net.minecraft.resources.ResourceLocation; + +/** + * Represents the entire context of a program's usage. + */ +public final class ProgramContext { + /** + * Creates a compilation context for the given program, vertex type and render layer. + * + * @param programName The name of the program to use. + * @param vertexType The vertex type to use. + * @param layer If cutout, the alpha discard threshold is 0.1, otherwise 0. + * @return A compilation context. + */ + public static ProgramContext create(ResourceLocation programName, VertexType vertexType, @Nullable RenderLayer layer) { + ProgramSpec spec = Backend.getSpec(programName); + + if (spec == null) { + throw new NullPointerException("Cannot compile shader because '" + programName + "' is not recognized."); + } + + return new ProgramContext(spec, getAlphaDiscard(layer), vertexType, GameStateRegistry.takeSnapshot()); + } + + /** + * 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 final ProgramSpec spec; + public final float alphaDiscard; + public final VertexType vertexType; + public final StateSnapshot ctx; + + /** + * @param spec The program to use. + * @param alphaDiscard Alpha threshold below which pixels are discarded. + * @param vertexType The vertexType the program should be adapted for. + * @param ctx A snapshot of the game state. + */ + public ProgramContext(ProgramSpec spec, float alphaDiscard, VertexType vertexType, StateSnapshot ctx) { + this.spec = spec; + this.alphaDiscard = alphaDiscard; + this.vertexType = vertexType; + this.ctx = ctx; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + var that = (ProgramContext) o; + return spec == that.spec && vertexType == that.vertexType && ctx.equals(that.ctx) && Float.floatToIntBits(alphaDiscard) == Float.floatToIntBits(that.alphaDiscard); + } + + @Override + public int hashCode() { + return Objects.hash(spec, alphaDiscard, vertexType, ctx); + } + + @Override + public String toString() { + return "ProgramContext{" + "spec=" + spec + ", alphaDiscard=" + alphaDiscard + ", vertexType=" + vertexType + ", ctx=" + ctx + '}'; + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/compile/Template.java b/src/main/java/com/jozufozu/flywheel/core/compile/Template.java new file mode 100644 index 000000000..4f78a5efc --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/compile/Template.java @@ -0,0 +1,52 @@ +package com.jozufozu.flywheel.core.compile; + +import java.util.function.Function; + +import com.jozufozu.flywheel.backend.gl.GLSLVersion; +import com.jozufozu.flywheel.core.source.SourceFile; + +/** + * A class that generates glsl glue code given a SourceFile. + * + *

+ * Shader files are written somewhat abstractly. Subclasses of Template handle those abstractions, using SourceFile + * metadata to generate shader code that OpenGL can use to call into our shader programs. + *

+ */ +public class Template extends Memoizer { + + private final Function reader; + private final GLSLVersion glslVersion; + + public Template(GLSLVersion glslVersion, Function reader) { + this.reader = reader; + this.glslVersion = glslVersion; + } + + /** + * Verify that the given SourceFile is valid for this Template and return the metadata. + * @param file The SourceFile to apply this Template to. + * @return The applied template metadata. + */ + public T apply(SourceFile file) { + // lazily read files, cache results + return super.get(file); + } + + /** + * @return The GLSL version this template requires. + */ + public GLSLVersion getVersion() { + return glslVersion; + } + + @Override + protected T _create(SourceFile key) { + return reader.apply(key); + } + + @Override + protected void _destroy(T value) { + // noop + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/compile/VertexCompiler.java b/src/main/java/com/jozufozu/flywheel/core/compile/VertexCompiler.java new file mode 100644 index 000000000..6487c45c4 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/compile/VertexCompiler.java @@ -0,0 +1,93 @@ +package com.jozufozu.flywheel.core.compile; + +import java.util.Objects; + +import com.jozufozu.flywheel.api.vertex.VertexType; +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.FileIndexImpl; +import com.jozufozu.flywheel.core.source.FileResolution; +import com.jozufozu.flywheel.core.source.SourceFile; + +public class VertexCompiler extends Memoizer { + private final Template template; + private final FileResolution header; + + public VertexCompiler(Template template, FileResolution header) { + this.template = template; + this.header = header; + } + + @Override + protected GlShader _create(Context key) { + StringBuilder finalSource = new StringBuilder(); + + finalSource.append(CompileUtil.generateHeader(template.getVersion(), ShaderType.VERTEX)); + + key.ctx.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); + + key.file.generateFinalSource(index, finalSource); + + VertexData appliedTemplate = template.apply(key.file); + finalSource.append(appliedTemplate.generateFooter(index, key.vertexType)); + + return new GlShader(key.file.name, ShaderType.VERTEX, finalSource.toString()); + } + + @Override + protected void _destroy(GlShader value) { + value.delete(); + } + + public static class Context { + /** + * The file to compile. + */ + private final SourceFile file; + + /** + * The shader constants to apply. + */ + private final StateSnapshot ctx; + + /** + * The vertex type to use. + */ + private final VertexType vertexType; + + public Context(SourceFile file, StateSnapshot ctx, VertexType vertexType) { + this.file = file; + this.ctx = ctx; + this.vertexType = vertexType; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + var that = (Context) o; + return file == that.file && vertexType == that.vertexType && ctx.equals(that.ctx); + } + + @Override + public int hashCode() { + return Objects.hash(file, ctx, vertexType); + } + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/compile/VertexData.java b/src/main/java/com/jozufozu/flywheel/core/compile/VertexData.java new file mode 100644 index 000000000..7e3e40b34 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/compile/VertexData.java @@ -0,0 +1,12 @@ +package com.jozufozu.flywheel.core.compile; + +import com.jozufozu.flywheel.api.vertex.VertexType; +import com.jozufozu.flywheel.core.source.FileIndex; + +public interface VertexData { + /** + * Generate the necessary glue code here. + * @param file The SourceFile with user written code. + */ + String generateFooter(FileIndex file, VertexType vertexType); +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/package-info.java b/src/main/java/com/jozufozu/flywheel/core/compile/package-info.java similarity index 78% rename from src/main/java/com/jozufozu/flywheel/backend/source/package-info.java rename to src/main/java/com/jozufozu/flywheel/core/compile/package-info.java index bb8d86f6d..d81aeffbb 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/source/package-info.java +++ b/src/main/java/com/jozufozu/flywheel/core/compile/package-info.java @@ -1,5 +1,5 @@ @ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault -package com.jozufozu.flywheel.backend.source; +package com.jozufozu.flywheel.core.compile; import javax.annotation.ParametersAreNonnullByDefault; diff --git a/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingGroup.java b/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingGroup.java index 19385b5a3..ac27997f3 100644 --- a/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingGroup.java +++ b/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingGroup.java @@ -1,5 +1,6 @@ package com.jozufozu.flywheel.core.crumbling; +import com.jozufozu.flywheel.backend.RenderLayer; import com.jozufozu.flywheel.backend.instancing.instancing.InstancedMaterialGroup; import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine; import com.jozufozu.flywheel.util.Textures; @@ -18,7 +19,7 @@ public class CrumblingGroup

extends InstancedMateria } @Override - public void render(Matrix4f viewProjection, double camX, double camY, double camZ) { + public void render(Matrix4f viewProjection, double camX, double camY, double camZ, RenderLayer layer) { type.setupRenderState(); int renderTex = RenderSystem.getShaderTexture(0); @@ -35,7 +36,7 @@ public class CrumblingGroup

extends InstancedMateria RenderSystem.setShaderTexture(4, breakingTex); Textures.bindActiveTextures(); - renderAll(viewProjection, camX, camY, camZ); + renderAll(viewProjection, camX, camY, camZ, layer); CrumblingRenderer._currentLayer.clearRenderState(); } diff --git a/src/main/java/com/jozufozu/flywheel/core/layout/BufferLayout.java b/src/main/java/com/jozufozu/flywheel/core/layout/BufferLayout.java index d7111f706..ecdde9ea3 100644 --- a/src/main/java/com/jozufozu/flywheel/core/layout/BufferLayout.java +++ b/src/main/java/com/jozufozu/flywheel/core/layout/BufferLayout.java @@ -27,8 +27,8 @@ public class BufferLayout { int numAttributes = 0, stride = 0; for (LayoutItem spec : allAttributes) { - numAttributes += spec.getAttributeCount(); - stride += spec.getSize(); + numAttributes += spec.attributeCount(); + stride += spec.size(); } this.numAttributes = numAttributes; this.stride = stride; diff --git a/src/main/java/com/jozufozu/flywheel/core/layout/CommonItems.java b/src/main/java/com/jozufozu/flywheel/core/layout/CommonItems.java index 1021296ac..232879964 100644 --- a/src/main/java/com/jozufozu/flywheel/core/layout/CommonItems.java +++ b/src/main/java/com/jozufozu/flywheel/core/layout/CommonItems.java @@ -19,6 +19,6 @@ public class CommonItems { public static final PrimitiveItem LIGHT_SHORT = new PrimitiveItem(GlNumericType.USHORT, 2, true); public static final PrimitiveItem NORMALIZED_BYTE = new PrimitiveItem(GlNumericType.BYTE, 1, true); - public static final LayoutItem PADDING_BYTE = new PaddingItem(1); + public static final LayoutItem PADDING_BYTE = new Padding(1); } diff --git a/src/main/java/com/jozufozu/flywheel/core/layout/LayoutItem.java b/src/main/java/com/jozufozu/flywheel/core/layout/LayoutItem.java index ae910dc51..cc0f6fabe 100644 --- a/src/main/java/com/jozufozu/flywheel/core/layout/LayoutItem.java +++ b/src/main/java/com/jozufozu/flywheel/core/layout/LayoutItem.java @@ -4,7 +4,7 @@ public interface LayoutItem { void vertexAttribPointer(int stride, int index, int offset); - int getSize(); + int size(); - int getAttributeCount(); + int attributeCount(); } diff --git a/src/main/java/com/jozufozu/flywheel/core/layout/MatrixItems.java b/src/main/java/com/jozufozu/flywheel/core/layout/MatrixItems.java index f9d8ffe2e..1ca6f2fec 100644 --- a/src/main/java/com/jozufozu/flywheel/core/layout/MatrixItems.java +++ b/src/main/java/com/jozufozu/flywheel/core/layout/MatrixItems.java @@ -26,12 +26,13 @@ public enum MatrixItems implements LayoutItem { } @Override - public int getSize() { + public int size() { return GlNumericType.FLOAT.getByteWidth() * rows * cols; } @Override - public int getAttributeCount() { + public int attributeCount() { return rows; } + } diff --git a/src/main/java/com/jozufozu/flywheel/core/layout/PaddingItem.java b/src/main/java/com/jozufozu/flywheel/core/layout/Padding.java similarity index 62% rename from src/main/java/com/jozufozu/flywheel/core/layout/PaddingItem.java rename to src/main/java/com/jozufozu/flywheel/core/layout/Padding.java index 788aa9b3d..7b3abbed3 100644 --- a/src/main/java/com/jozufozu/flywheel/core/layout/PaddingItem.java +++ b/src/main/java/com/jozufozu/flywheel/core/layout/Padding.java @@ -1,6 +1,6 @@ package com.jozufozu.flywheel.core.layout; -record PaddingItem(int bytes) implements LayoutItem { +record Padding(int bytes) implements LayoutItem { @Override public void vertexAttribPointer(int stride, int index, int offset) { @@ -8,12 +8,13 @@ record PaddingItem(int bytes) implements LayoutItem { } @Override - public int getSize() { + public int size() { return bytes; } @Override - public int getAttributeCount() { + public int attributeCount() { return 0; } + } diff --git a/src/main/java/com/jozufozu/flywheel/core/layout/PrimitiveItem.java b/src/main/java/com/jozufozu/flywheel/core/layout/PrimitiveItem.java index be1373867..c7c1bd6c0 100644 --- a/src/main/java/com/jozufozu/flywheel/core/layout/PrimitiveItem.java +++ b/src/main/java/com/jozufozu/flywheel/core/layout/PrimitiveItem.java @@ -30,12 +30,13 @@ public class PrimitiveItem implements LayoutItem { } @Override - public int getSize() { + public int size() { return size; } @Override - public int getAttributeCount() { + public int attributeCount() { return attributeCount; } + } diff --git a/src/main/java/com/jozufozu/flywheel/core/materials/BasicData.java b/src/main/java/com/jozufozu/flywheel/core/materials/BasicData.java index 955aba918..8f1e07de8 100644 --- a/src/main/java/com/jozufozu/flywheel/core/materials/BasicData.java +++ b/src/main/java/com/jozufozu/flywheel/core/materials/BasicData.java @@ -1,6 +1,7 @@ package com.jozufozu.flywheel.core.materials; import com.jozufozu.flywheel.api.InstanceData; +import com.jozufozu.flywheel.util.Color; import net.minecraft.client.renderer.LightTexture; @@ -33,6 +34,15 @@ public abstract class BasicData extends InstanceData implements FlatLit - */ -public interface ContextAwareProgram

extends Supplier

{ - - /** - * Get the shader program most suited for the current game state. - * - * @return The one true program. - */ - P get(); - - /** - * Delete all shader programs encapsulated by your implementation. - */ - void delete(); -} diff --git a/src/main/java/com/jozufozu/flywheel/core/shader/ExtensibleGlProgram.java b/src/main/java/com/jozufozu/flywheel/core/shader/ExtensibleGlProgram.java deleted file mode 100644 index 05ef8dc73..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/shader/ExtensibleGlProgram.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.jozufozu.flywheel.core.shader; - -import java.util.ArrayList; -import java.util.List; - -import javax.annotation.Nonnull; - -import com.jozufozu.flywheel.backend.ShaderContext; -import com.jozufozu.flywheel.backend.gl.shader.GlProgram; -import com.jozufozu.flywheel.core.shader.extension.ExtensionInstance; - -import net.minecraft.resources.ResourceLocation; - -/** - * A shader program that be arbitrarily "extended". This class can take in any number of program extensions, and - * will initialize them and then call their {@link ExtensionInstance#bind() bind} function every subsequent time this - * program is bound. An "extension" is something that interacts with the shader program in a way that is invisible to - * the caller using the program. This is used by some programs to implement the different fog modes. Other uses might - * include binding extra textures to allow for blocks to have normal maps, for example. As the extensions are - * per-program, this also allows for same extra specialization within a - * {@link ShaderContext ShaderContext}. - */ -public class ExtensibleGlProgram extends GlProgram { - - protected final List extensions = new ArrayList<>(); - - public ExtensibleGlProgram(ResourceLocation name, int handle) { - super(name, handle); - } - - @Override - public void bind() { - super.bind(); - - extensions.forEach(ExtensionInstance::bind); - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("program ") - .append(name) - .append('['); - - for (ExtensionInstance extension : extensions) { - builder.append(extension) - .append('+'); - } - - builder.append(']'); - - return builder.toString(); - } - - /** - * A factory interface to create {@link GlProgram}s parameterized by a list of extensions. This doesn't necessarily - * have to return an {@link ExtensibleGlProgram} if implementors want more flexibility for whatever reason. - */ - public interface Factory

{ - - @Nonnull - P create(ResourceLocation name, int handle); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/core/shader/GameStateProgram.java b/src/main/java/com/jozufozu/flywheel/core/shader/GameStateProgram.java deleted file mode 100644 index ce6ae31cd..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/shader/GameStateProgram.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.jozufozu.flywheel.core.shader; - -import java.util.ArrayList; -import java.util.List; - -import com.google.common.collect.ImmutableList; -import com.jozufozu.flywheel.backend.gl.shader.GlProgram; -import com.jozufozu.flywheel.core.shader.spec.GameStateCondition; -import com.jozufozu.flywheel.util.Pair; - -public class GameStateProgram

implements ContextAwareProgram

{ - - private final List> variants; - private final P fallback; - - protected GameStateProgram(List> variants, P fallback) { - this.variants = variants; - this.fallback = fallback; - } - - @Override - public P get() { - for (Pair variant : variants) { - if (variant.first() - .isMet()) return variant.second(); - } - - return fallback; - } - - @Override - public void delete() { - for (Pair variant : variants) { - variant.second() - .delete(); - } - - fallback.delete(); - } - - public static

Builder

builder(P fallback) { - return new Builder<>(fallback); - } - - public static class Builder

{ - private final P fallback; - private final List> variants = new ArrayList<>(); - - public Builder(P fallback) { - this.fallback = fallback; - } - - public Builder

withVariant(GameStateCondition condition, P program) { - variants.add(Pair.of(condition, program)); - return this; - } - - public ContextAwareProgram

build() { - return new GameStateProgram<>(ImmutableList.copyOf(variants), fallback); - } - } -} diff --git a/src/main/java/com/jozufozu/flywheel/core/shader/GameStateProvider.java b/src/main/java/com/jozufozu/flywheel/core/shader/GameStateProvider.java new file mode 100644 index 000000000..2ec67744e --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/shader/GameStateProvider.java @@ -0,0 +1,19 @@ +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); +} diff --git a/src/main/java/com/jozufozu/flywheel/core/shader/NormalDebugStateProvider.java b/src/main/java/com/jozufozu/flywheel/core/shader/NormalDebugStateProvider.java new file mode 100644 index 000000000..50efebd9f --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/shader/NormalDebugStateProvider.java @@ -0,0 +1,18 @@ +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"); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/shader/spec/ProgramSpec.java b/src/main/java/com/jozufozu/flywheel/core/shader/ProgramSpec.java similarity index 53% rename from src/main/java/com/jozufozu/flywheel/core/shader/spec/ProgramSpec.java rename to src/main/java/com/jozufozu/flywheel/core/shader/ProgramSpec.java index e293a2732..729203ba8 100644 --- a/src/main/java/com/jozufozu/flywheel/core/shader/spec/ProgramSpec.java +++ b/src/main/java/com/jozufozu/flywheel/core/shader/ProgramSpec.java @@ -1,11 +1,8 @@ -package com.jozufozu.flywheel.core.shader.spec; +package com.jozufozu.flywheel.core.shader; -import java.util.Collections; -import java.util.List; - -import com.jozufozu.flywheel.backend.source.FileResolution; -import com.jozufozu.flywheel.backend.source.Resolver; -import com.jozufozu.flywheel.backend.source.SourceFile; +import com.jozufozu.flywheel.core.source.FileResolution; +import com.jozufozu.flywheel.core.source.Resolver; +import com.jozufozu.flywheel.core.source.SourceFile; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; @@ -26,39 +23,46 @@ import net.minecraft.resources.ResourceLocation; */ public class ProgramSpec { - // TODO: Block model style inheritance? public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( - ResourceLocation.CODEC.fieldOf("source") + ResourceLocation.CODEC.fieldOf("vertex") .forGetter(ProgramSpec::getSourceLoc), - ProgramState.CODEC.listOf() - .optionalFieldOf("states", Collections.emptyList()) - .forGetter(ProgramSpec::getStates)) + ResourceLocation.CODEC.fieldOf("fragment") + .forGetter(ProgramSpec::getFragmentLoc)) .apply(instance, ProgramSpec::new)); public ResourceLocation name; - public final FileResolution source; + public final FileResolution vertex; + public final FileResolution fragment; - public final List states; - - public ProgramSpec(ResourceLocation source, List states) { - this.source = Resolver.INSTANCE.findShader(source); - this.states = states; + public ProgramSpec(ResourceLocation vertex, ResourceLocation fragment) { + this.vertex = Resolver.INSTANCE.get(vertex); + this.fragment = Resolver.INSTANCE.get(fragment); } public void setName(ResourceLocation name) { this.name = name; + this.vertex.addSpec(name); + this.fragment.addSpec(name); } public ResourceLocation getSourceLoc() { - return source.getFileLoc(); + return vertex.getFileLoc(); } - public FileResolution getSource() { - return source; + public ResourceLocation getFragmentLoc() { + return fragment.getFileLoc(); } - public List getStates() { - return states; + public SourceFile getVertexFile() { + return vertex.getFile(); } + public SourceFile getFragmentFile() { + return fragment.getFile(); + } + + @Override + public String toString() { + return name.toString(); + } } diff --git a/src/main/java/com/jozufozu/flywheel/core/shader/ShaderConstants.java b/src/main/java/com/jozufozu/flywheel/core/shader/ShaderConstants.java new file mode 100644 index 000000000..ed56ab006 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/shader/ShaderConstants.java @@ -0,0 +1,55 @@ +package com.jozufozu.flywheel.core.shader; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * A class for manipulating a list of {@code #define} directives. + * + *

Based loosely on code by jellysquid3. + */ +public class ShaderConstants { + + private final Map 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 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 e : definitions.entrySet()) { + acc.append("#define ") + .append(e.getKey()); + if (e.getValue().length() > 0) { + acc.append(' ') + .append(e.getValue()); + } + acc.append('\n'); + } + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/shader/StateSnapshot.java b/src/main/java/com/jozufozu/flywheel/core/shader/StateSnapshot.java new file mode 100644 index 000000000..6a01a90f1 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/shader/StateSnapshot.java @@ -0,0 +1,13 @@ +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); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/shader/extension/WorldFog.java b/src/main/java/com/jozufozu/flywheel/core/shader/WorldFog.java similarity index 62% rename from src/main/java/com/jozufozu/flywheel/core/shader/extension/WorldFog.java rename to src/main/java/com/jozufozu/flywheel/core/shader/WorldFog.java index dd371debd..6d94a42c5 100644 --- a/src/main/java/com/jozufozu/flywheel/core/shader/extension/WorldFog.java +++ b/src/main/java/com/jozufozu/flywheel/core/shader/WorldFog.java @@ -1,16 +1,11 @@ -package com.jozufozu.flywheel.core.shader.extension; +package com.jozufozu.flywheel.core.shader; import org.lwjgl.opengl.GL20; -import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.backend.gl.shader.GlProgram; import com.mojang.blaze3d.systems.RenderSystem; -import net.minecraft.resources.ResourceLocation; - -public class WorldFog implements ExtensionInstance { - - public static final ResourceLocation NAME = Flywheel.rl("fog"); +public class WorldFog { private final int uFogColor; private final int uFogRange; @@ -20,14 +15,8 @@ public class WorldFog implements ExtensionInstance { this.uFogRange = program.getUniformLocation("uFogRange"); } - @Override public void bind() { GL20.glUniform2f(uFogRange, RenderSystem.getShaderFogStart(), RenderSystem.getShaderFogEnd()); GL20.glUniform4fv(uFogColor, RenderSystem.getShaderFogColor()); } - - @Override - public ResourceLocation name() { - return NAME; - } } diff --git a/src/main/java/com/jozufozu/flywheel/core/shader/WorldProgram.java b/src/main/java/com/jozufozu/flywheel/core/shader/WorldProgram.java index 09c29270d..26fa6838f 100644 --- a/src/main/java/com/jozufozu/flywheel/core/shader/WorldProgram.java +++ b/src/main/java/com/jozufozu/flywheel/core/shader/WorldProgram.java @@ -4,7 +4,7 @@ import static org.lwjgl.opengl.GL20.glUniform1f; import static org.lwjgl.opengl.GL20.glUniform2f; import static org.lwjgl.opengl.GL20.glUniform3f; -import com.jozufozu.flywheel.core.shader.extension.WorldFog; +import com.jozufozu.flywheel.backend.gl.shader.GlProgram; import com.jozufozu.flywheel.util.AnimationTickHolder; import com.mojang.blaze3d.platform.Window; import com.mojang.math.Matrix4f; @@ -12,11 +12,12 @@ import com.mojang.math.Matrix4f; import net.minecraft.client.Minecraft; import net.minecraft.resources.ResourceLocation; -public class WorldProgram extends ExtensibleGlProgram { +public class WorldProgram extends GlProgram { protected final int uTime = getUniformLocation("uTime"); protected final int uViewProjection = getUniformLocation("uViewProjection"); protected final int uCameraPos = getUniformLocation("uCameraPos"); protected final int uWindowSize = getUniformLocation("uWindowSize"); + private final WorldFog fog; protected int uBlockAtlas; protected int uLightMap; @@ -24,7 +25,7 @@ public class WorldProgram extends ExtensibleGlProgram { public WorldProgram(ResourceLocation name, int handle) { super(name, handle); - this.extensions.add(new WorldFog(this)); + fog = new WorldFog(this); super.bind(); registerSamplers(); @@ -67,7 +68,7 @@ public class WorldProgram extends ExtensibleGlProgram { @Override public void bind() { super.bind(); - + fog.bind(); uploadWindowSize(); uploadTime(AnimationTickHolder.getRenderTime()); } diff --git a/src/main/java/com/jozufozu/flywheel/core/shader/extension/ExtensionInstance.java b/src/main/java/com/jozufozu/flywheel/core/shader/extension/ExtensionInstance.java deleted file mode 100644 index 6499820c9..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/shader/extension/ExtensionInstance.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.jozufozu.flywheel.core.shader.extension; - -import net.minecraft.resources.ResourceLocation; - -public interface ExtensionInstance { - - /** - * Bind the extra program state. It is recommended to grab the state information from global variables. - */ - void bind(); - - ResourceLocation name(); -} diff --git a/src/main/java/com/jozufozu/flywheel/core/shader/extension/UnitExtensionInstance.java b/src/main/java/com/jozufozu/flywheel/core/shader/extension/UnitExtensionInstance.java deleted file mode 100644 index 70d1bff4b..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/shader/extension/UnitExtensionInstance.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.jozufozu.flywheel.core.shader.extension; - -import com.jozufozu.flywheel.Flywheel; -import com.jozufozu.flywheel.backend.gl.shader.GlProgram; - -import net.minecraft.resources.ResourceLocation; - -public class UnitExtensionInstance implements ExtensionInstance { - - public static final ResourceLocation NAME = Flywheel.rl("unit"); - - public UnitExtensionInstance(GlProgram program) { - } - - @Override - public void bind() { - - } - - @Override - public ResourceLocation name() { - return NAME; - } -} diff --git a/src/main/java/com/jozufozu/flywheel/core/shader/gamestate/GameStateProvider.java b/src/main/java/com/jozufozu/flywheel/core/shader/gamestate/GameStateProvider.java deleted file mode 100644 index a7f7fb0ba..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/shader/gamestate/GameStateProvider.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.jozufozu.flywheel.core.shader.gamestate; - -import com.jozufozu.flywheel.backend.GameStateRegistry; -import com.mojang.serialization.Codec; - -import net.minecraft.resources.ResourceLocation; - -public interface GameStateProvider { - - Codec CODEC = ResourceLocation.CODEC.xmap(GameStateRegistry::getStateProvider, GameStateProvider::getID); - - ResourceLocation getID(); - - Object getValue(); -} diff --git a/src/main/java/com/jozufozu/flywheel/core/shader/gamestate/NormalDebugStateProvider.java b/src/main/java/com/jozufozu/flywheel/core/shader/gamestate/NormalDebugStateProvider.java deleted file mode 100644 index 01646f027..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/shader/gamestate/NormalDebugStateProvider.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.jozufozu.flywheel.core.shader.gamestate; - -import com.jozufozu.flywheel.Flywheel; -import com.jozufozu.flywheel.config.FlwConfig; -import com.jozufozu.flywheel.core.shader.spec.BooleanStateProvider; - -import net.minecraft.resources.ResourceLocation; - -public class NormalDebugStateProvider implements BooleanStateProvider { - - public static final NormalDebugStateProvider INSTANCE = new NormalDebugStateProvider(); - public static final ResourceLocation NAME = Flywheel.rl("normal_debug"); - - protected NormalDebugStateProvider() { - - } - - @Override - public boolean isTrue() { - return FlwConfig.get() - .debugNormals(); - } - - @Override - public ResourceLocation getID() { - return NAME; - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/package-info.java b/src/main/java/com/jozufozu/flywheel/core/shader/package-info.java similarity index 78% rename from src/main/java/com/jozufozu/flywheel/backend/pipeline/package-info.java rename to src/main/java/com/jozufozu/flywheel/core/shader/package-info.java index 25c300762..d25693290 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/package-info.java +++ b/src/main/java/com/jozufozu/flywheel/core/shader/package-info.java @@ -1,5 +1,5 @@ @ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault -package com.jozufozu.flywheel.backend.pipeline; +package com.jozufozu.flywheel.core.shader; import javax.annotation.ParametersAreNonnullByDefault; diff --git a/src/main/java/com/jozufozu/flywheel/core/shader/spec/BooleanGameStateCondition.java b/src/main/java/com/jozufozu/flywheel/core/shader/spec/BooleanGameStateCondition.java deleted file mode 100644 index 9ff461f6b..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/shader/spec/BooleanGameStateCondition.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.jozufozu.flywheel.core.shader.spec; - -import com.jozufozu.flywheel.core.shader.gamestate.GameStateProvider; -import com.mojang.serialization.Codec; - -import net.minecraft.resources.ResourceLocation; - -public class BooleanGameStateCondition implements GameStateCondition { - - public static final Codec BOOLEAN_SUGAR = GameStateProvider.CODEC.xmap(gameContext -> { - if (gameContext instanceof BooleanStateProvider) { - return new BooleanGameStateCondition(((BooleanStateProvider) gameContext)); - } - - return null; - }, GameStateCondition::getStateProvider); - protected final BooleanStateProvider context; - - public BooleanGameStateCondition(BooleanStateProvider context) { - this.context = context; - } - - @Override - public ResourceLocation getID() { - return context.getID(); - } - - @Override - public GameStateProvider getStateProvider() { - return context; - } - - @Override - public boolean isMet() { - return context.isTrue(); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/core/shader/spec/BooleanStateProvider.java b/src/main/java/com/jozufozu/flywheel/core/shader/spec/BooleanStateProvider.java deleted file mode 100644 index 1cf0b1da2..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/shader/spec/BooleanStateProvider.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.jozufozu.flywheel.core.shader.spec; - -import com.jozufozu.flywheel.core.shader.gamestate.GameStateProvider; - -public interface BooleanStateProvider extends GameStateProvider { - - boolean isTrue(); - - @Override - default Boolean getValue() { - return isTrue(); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/core/shader/spec/GameStateCondition.java b/src/main/java/com/jozufozu/flywheel/core/shader/spec/GameStateCondition.java deleted file mode 100644 index c8ad1e1cf..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/shader/spec/GameStateCondition.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.jozufozu.flywheel.core.shader.spec; - -import com.jozufozu.flywheel.core.shader.gamestate.GameStateProvider; - -import net.minecraft.resources.ResourceLocation; - -public interface GameStateCondition { - - ResourceLocation getID(); - - GameStateProvider getStateProvider(); - - boolean isMet(); -} diff --git a/src/main/java/com/jozufozu/flywheel/core/shader/spec/ProgramState.java b/src/main/java/com/jozufozu/flywheel/core/shader/spec/ProgramState.java deleted file mode 100644 index 7f4fc7782..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/shader/spec/ProgramState.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.jozufozu.flywheel.core.shader.spec; - -import java.util.Collections; -import java.util.List; - -import com.jozufozu.flywheel.util.CodecUtil; -import com.mojang.datafixers.util.Either; -import com.mojang.serialization.Codec; -import com.mojang.serialization.DataResult; -import com.mojang.serialization.codecs.RecordCodecBuilder; - -public record ProgramState(GameStateCondition context, List defines) { - - // TODO: Use Codec.dispatch - private static final Codec WHEN = Codec.either(BooleanGameStateCondition.BOOLEAN_SUGAR, SpecificValueCondition.CODEC) - .flatXmap(either -> either.map(DataResult::success, DataResult::success), any -> { - if (any instanceof BooleanGameStateCondition) { - return DataResult.success(Either.left((BooleanGameStateCondition) any)); - } - - if (any instanceof SpecificValueCondition) { - return DataResult.success(Either.right((SpecificValueCondition) any)); - } - - return DataResult.error("unknown context condition"); - }); - - public static final Codec CODEC = RecordCodecBuilder.create(state -> state.group(WHEN.fieldOf("when") - .forGetter(ProgramState::context), CodecUtil.oneOrMore(Codec.STRING) - .optionalFieldOf("define", Collections.emptyList()) - .forGetter(ProgramState::defines)) - .apply(state, ProgramState::new)); -} diff --git a/src/main/java/com/jozufozu/flywheel/core/shader/spec/SpecificValueCondition.java b/src/main/java/com/jozufozu/flywheel/core/shader/spec/SpecificValueCondition.java deleted file mode 100644 index fc530536e..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/shader/spec/SpecificValueCondition.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.jozufozu.flywheel.core.shader.spec; - -import com.jozufozu.flywheel.core.shader.gamestate.GameStateProvider; -import com.mojang.serialization.Codec; -import com.mojang.serialization.codecs.RecordCodecBuilder; - -import net.minecraft.resources.ResourceLocation; - -public class SpecificValueCondition implements GameStateCondition { - - public static final Codec CODEC = RecordCodecBuilder.create(condition -> condition.group(GameStateProvider.CODEC.fieldOf("provider") - .forGetter(SpecificValueCondition::getStateProvider), Codec.STRING.fieldOf("value") - .forGetter(SpecificValueCondition::getValue)) - .apply(condition, SpecificValueCondition::new)); - - private final String required; - private final GameStateProvider context; - - public SpecificValueCondition(GameStateProvider context, String required) { - this.required = required; - this.context = context; - } - - @Override - public ResourceLocation getID() { - return context.getID(); - } - - public String getValue() { - return required; - } - - @Override - public GameStateProvider getStateProvider() { - return context; - } - - @Override - public boolean isMet() { - return required.equals(context.getValue() - .toString()); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/core/source/FileIndex.java b/src/main/java/com/jozufozu/flywheel/core/source/FileIndex.java new file mode 100644 index 000000000..91ce1e36e --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/source/FileIndex.java @@ -0,0 +1,19 @@ +package com.jozufozu.flywheel.core.source; + +import com.jozufozu.flywheel.core.source.span.Span; + +public interface FileIndex { + /** + * Returns an arbitrary file ID for use this compilation context, or generates one if missing. + * + * @param sourceFile The file to retrieve the ID for. + * @return A file ID unique to the given sourceFile. + */ + int getFileID(SourceFile sourceFile); + + SourceFile getFile(int fileID); + + default Span getLineSpan(int fileId, int lineNo) { + return getFile(fileId).getLineSpanNoWhitespace(lineNo); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/source/FileIndexImpl.java b/src/main/java/com/jozufozu/flywheel/core/source/FileIndexImpl.java new file mode 100644 index 000000000..90a1cc4ba --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/source/FileIndexImpl.java @@ -0,0 +1,76 @@ +package com.jozufozu.flywheel.core.source; + +import java.util.ArrayList; +import java.util.List; + +import javax.annotation.Nullable; + +import com.jozufozu.flywheel.backend.Backend; +import com.jozufozu.flywheel.core.source.error.ErrorBuilder; +import com.jozufozu.flywheel.core.source.error.ErrorReporter; + +import net.minecraft.resources.ResourceLocation; + +public class FileIndexImpl implements FileIndex { + public final List files = new ArrayList<>(); + + /** + * Returns an arbitrary file ID for use this compilation context, or generates one if missing. + * @param sourceFile The file to retrieve the ID for. + * @return A file ID unique to the given sourceFile. + */ + @Override + public int getFileID(SourceFile sourceFile) { + int i = files.indexOf(sourceFile); + if (i != -1) { + return i; + } + + int size = files.size(); + files.add(sourceFile); + return size; + } + + @Override + public SourceFile getFile(int fileId) { + return files.get(fileId); + } + + + public void printShaderInfoLog(String source, String log, ResourceLocation name) { + List lines = log.lines() + .toList(); + + boolean needsSourceDump = false; + + StringBuilder errors = new StringBuilder(); + for (String line : lines) { + ErrorBuilder builder = parseCompilerError(line); + + if (builder != null) { + errors.append(builder.build()); + } else { + errors.append(line).append('\n'); + needsSourceDump = true; + } + } + Backend.LOGGER.error("Errors compiling '" + name + "': \n" + errors); + if (needsSourceDump) { + // TODO: generated code gets its own "file" + ErrorReporter.printLines(source); + } + } + + @Nullable + private ErrorBuilder parseCompilerError(String line) { + try { + ErrorBuilder error = ErrorBuilder.fromLogLine(this, line); + if (error != null) { + return error; + } + } catch (Exception ignored) { + } + + return null; + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/FileResolution.java b/src/main/java/com/jozufozu/flywheel/core/source/FileResolution.java similarity index 54% rename from src/main/java/com/jozufozu/flywheel/backend/source/FileResolution.java rename to src/main/java/com/jozufozu/flywheel/core/source/FileResolution.java index 648d9def7..86c9dadd0 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/source/FileResolution.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/FileResolution.java @@ -1,13 +1,12 @@ -package com.jozufozu.flywheel.backend.source; +package com.jozufozu.flywheel.core.source; import java.util.ArrayList; import java.util.List; - -import javax.annotation.Nullable; +import java.util.function.Consumer; import com.jozufozu.flywheel.backend.Backend; -import com.jozufozu.flywheel.backend.source.error.ErrorBuilder; -import com.jozufozu.flywheel.backend.source.span.Span; +import com.jozufozu.flywheel.core.source.error.ErrorBuilder; +import com.jozufozu.flywheel.core.source.span.Span; import net.minecraft.resources.ResourceLocation; @@ -23,13 +22,13 @@ import net.minecraft.resources.ResourceLocation; public class FileResolution { /** - * Spans that have references that resolved to this. + * Extra info about where this resolution is required. Includes ProgramSpecs and shader Spans. */ - private final List foundSpans = new ArrayList<>(); + private final List> extraCrashInfoProviders = new ArrayList<>(); private final ResourceLocation fileLoc; private SourceFile file; - public FileResolution(ResourceLocation fileLoc) { + FileResolution(ResourceLocation fileLoc) { this.fileLoc = fileLoc; } @@ -37,7 +36,10 @@ public class FileResolution { return fileLoc; } - @Nullable + /** + * Non-null if this file is resolved because there would have been a crash otherwise. + * @return The file that this resolution resolves to. + */ public SourceFile getFile() { return file; } @@ -51,34 +53,44 @@ public class FileResolution { * @param span A span where this file is referenced. */ public FileResolution addSpan(Span span) { - foundSpans.add(span); + extraCrashInfoProviders.add(builder -> builder.pointAtFile(span.getSourceFile()) + .pointAt(span, 1)); return this; } + public void addSpec(ResourceLocation name) { + extraCrashInfoProviders.add(builder -> builder.extra("needed by spec: " + name + ".json")); + } + /** * Check to see if this file actually resolves to something. * *

* Called after all files are loaded. If we can't find the file here, it doesn't exist. *

+ * + * @return True if this file is resolved. */ - void resolve(SourceFinder sources) { + boolean resolve(SourceFinder sources) { + file = sources.findSource(fileLoc); - try { - file = sources.findSource(fileLoc); - } catch (RuntimeException error) { + if (file == null) { ErrorBuilder builder = ErrorBuilder.error(String.format("could not find source for file %s", fileLoc)); - // print the location of all places where this file was referenced - for (Span span : foundSpans) { - builder.pointAtFile(span.getSourceFile()) - .pointAt(span, 2); + for (Consumer consumer : extraCrashInfoProviders) { + consumer.accept(builder); } Backend.LOGGER.error(builder.build()); + + return false; } + + // Let the GC do its thing + extraCrashInfoProviders.clear(); + return true; } void invalidate() { - foundSpans.clear(); + extraCrashInfoProviders.clear(); file = null; } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/Index.java b/src/main/java/com/jozufozu/flywheel/core/source/Index.java similarity index 85% rename from src/main/java/com/jozufozu/flywheel/backend/source/Index.java rename to src/main/java/com/jozufozu/flywheel/core/source/Index.java index 4ebc6540c..dfcad42c5 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/source/Index.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/Index.java @@ -1,12 +1,12 @@ -package com.jozufozu.flywheel.backend.source; +package com.jozufozu.flywheel.core.source; import java.util.Collection; import java.util.Map; import com.google.common.collect.Multimap; import com.google.common.collect.MultimapBuilder; -import com.jozufozu.flywheel.backend.source.parse.ShaderFunction; -import com.jozufozu.flywheel.backend.source.parse.ShaderStruct; +import com.jozufozu.flywheel.core.source.parse.ShaderFunction; +import com.jozufozu.flywheel.core.source.parse.ShaderStruct; import net.minecraft.resources.ResourceLocation; diff --git a/src/main/java/com/jozufozu/flywheel/core/source/Resolver.java b/src/main/java/com/jozufozu/flywheel/core/source/Resolver.java new file mode 100644 index 000000000..9abea7354 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/source/Resolver.java @@ -0,0 +1,70 @@ +package com.jozufozu.flywheel.core.source; + +import java.util.HashMap; +import java.util.Map; + +import net.minecraft.resources.ResourceLocation; + +/** + * Manages deferred file resolution. + * + *

+ * Interns all file names in shader sources and program specs, deduplicating the final lookups and allowing for more + * dev-friendly error reporting. + *

+ * + * @see FileResolution + */ +public class Resolver { + + public static final Resolver INSTANCE = new Resolver(); + + private final Map resolutions = new HashMap<>(); + private boolean hasRun = false; + + public FileResolution get(ResourceLocation file) { + if (!hasRun) { + return resolutions.computeIfAbsent(file, FileResolution::new); + } else { + // Lock the map after resolution has run. + FileResolution fileResolution = resolutions.get(file); + + // ...so crash immediately if the file isn't found. + if (fileResolution == null) { + throw new RuntimeException("could not find source for file: " + file); + } + + return fileResolution; + } + } + + /** + * Try and resolve all referenced source files, printing errors if any aren't found. + */ + public void run(SourceFinder sources) { + boolean needsCrash = false; + for (FileResolution resolution : resolutions.values()) { + if (!resolution.resolve(sources)) { + needsCrash = true; + } + } + + if (needsCrash) { + throw new ShaderLoadingException("Failed to resolve all source files, see log for details"); + } + + hasRun = true; + } + + /** + * Invalidates all FileResolutions. + * + *

+ * Called on resource reload. + *

+ */ + public void invalidate() { + resolutions.values().forEach(FileResolution::invalidate); + hasRun = false; + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/source/ShaderLoadingException.java b/src/main/java/com/jozufozu/flywheel/core/source/ShaderLoadingException.java new file mode 100644 index 000000000..36ca1dc73 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/source/ShaderLoadingException.java @@ -0,0 +1,11 @@ +package com.jozufozu.flywheel.core.source; + +public class ShaderLoadingException extends RuntimeException { + + public ShaderLoadingException() { + } + + public ShaderLoadingException(String message) { + super(message); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/ShaderSources.java b/src/main/java/com/jozufozu/flywheel/core/source/ShaderSources.java similarity index 87% rename from src/main/java/com/jozufozu/flywheel/backend/source/ShaderSources.java rename to src/main/java/com/jozufozu/flywheel/core/source/ShaderSources.java index 03a2b8401..b7e29c2a4 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/source/ShaderSources.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/ShaderSources.java @@ -1,4 +1,4 @@ -package com.jozufozu.flywheel.backend.source; +package com.jozufozu.flywheel.core.source; import java.io.IOException; import java.util.ArrayList; @@ -6,6 +6,8 @@ import java.util.Collection; import java.util.HashMap; import java.util.Map; +import javax.annotation.Nullable; + import com.google.common.collect.Lists; import com.jozufozu.flywheel.util.ResourceUtil; import com.jozufozu.flywheel.util.StringUtil; @@ -51,13 +53,9 @@ public class ShaderSources implements SourceFinder { } @Override + @Nullable public SourceFile findSource(ResourceLocation name) { - SourceFile source = shaderSources.get(name); - if (source == null) { - throw new ShaderLoadingException(String.format("shader '%s' does not exist", name)); - } - - return source; + return shaderSources.get(name); } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/SourceFile.java b/src/main/java/com/jozufozu/flywheel/core/source/SourceFile.java similarity index 83% rename from src/main/java/com/jozufozu/flywheel/backend/source/SourceFile.java rename to src/main/java/com/jozufozu/flywheel/core/source/SourceFile.java index 24a8d1836..c2a13f21c 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/source/SourceFile.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/SourceFile.java @@ -1,4 +1,4 @@ -package com.jozufozu.flywheel.backend.source; +package com.jozufozu.flywheel.core.source; import java.util.ArrayList; import java.util.HashMap; @@ -10,12 +10,12 @@ import java.util.regex.Pattern; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import com.jozufozu.flywheel.backend.source.parse.Import; -import com.jozufozu.flywheel.backend.source.parse.ShaderFunction; -import com.jozufozu.flywheel.backend.source.parse.ShaderStruct; -import com.jozufozu.flywheel.backend.source.span.ErrorSpan; -import com.jozufozu.flywheel.backend.source.span.Span; -import com.jozufozu.flywheel.backend.source.span.StringSpan; +import com.jozufozu.flywheel.core.source.parse.Import; +import com.jozufozu.flywheel.core.source.parse.ShaderFunction; +import com.jozufozu.flywheel.core.source.parse.ShaderStruct; +import com.jozufozu.flywheel.core.source.span.ErrorSpan; +import com.jozufozu.flywheel.core.source.span.Span; +import com.jozufozu.flywheel.core.source.span.StringSpan; import net.minecraft.resources.ResourceLocation; @@ -74,6 +74,23 @@ public class SourceFile { this.elided = elideSource(source, elisions).toString(); } + public Span getLineSpan(int line) { + int begin = lines.getLineStart(line); + int end = begin + lines.getLine(line).length(); + return new StringSpan(this, lines.getCharPos(begin), lines.getCharPos(end)); + } + + public Span getLineSpanNoWhitespace(int line) { + int begin = lines.getLineStart(line); + int end = begin + lines.getLine(line).length(); + + while (begin < end && Character.isWhitespace(source.charAt(begin))) { + begin++; + } + + return new StringSpan(this, lines.getCharPos(begin), lines.getCharPos(end)); + } + /** * Search this file and recursively search all imports to find a struct definition matching the given name. * @@ -120,30 +137,23 @@ public class SourceFile { return "#use " + '"' + name + '"'; } - public CharSequence generateFinalSource() { - StringBuilder builder = new StringBuilder(); - generateFinalSource(builder); - return builder; - } - - public void generateFinalSource(StringBuilder source) { + public void generateFinalSource(FileIndex env, StringBuilder source) { for (Import include : imports) { SourceFile file = include.getFile(); - if (file != null) file.generateFinalSource(source); + if (file != null) file.generateFinalSource(env, source); } + + source.append("#line ") + .append(0) + .append(' ') + .append(env.getFileID(this)) + .append('\n'); source.append(elided); } public String printSource() { - StringBuilder builder = new StringBuilder(); - - builder.append("Source for shader '") - .append(name) - .append("':\n") - .append(lines.printLinesWithNumbers()); - - return builder.toString(); + return "Source for shader '" + name + "':\n" + lines.printLinesWithNumbers(); } private static CharSequence elideSource(String source, List elisions) { @@ -257,4 +267,8 @@ public class SourceFile { return -1; } + @Override + public String toString() { + return name.toString(); + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/SourceFinder.java b/src/main/java/com/jozufozu/flywheel/core/source/SourceFinder.java similarity index 69% rename from src/main/java/com/jozufozu/flywheel/backend/source/SourceFinder.java rename to src/main/java/com/jozufozu/flywheel/core/source/SourceFinder.java index 7dfa41ac3..50ced811f 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/source/SourceFinder.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/SourceFinder.java @@ -1,4 +1,6 @@ -package com.jozufozu.flywheel.backend.source; +package com.jozufozu.flywheel.core.source; + +import javax.annotation.Nullable; import net.minecraft.resources.ResourceLocation; @@ -8,5 +10,6 @@ import net.minecraft.resources.ResourceLocation; @FunctionalInterface public interface SourceFinder { + @Nullable SourceFile findSource(ResourceLocation name); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/SourceLines.java b/src/main/java/com/jozufozu/flywheel/core/source/SourceLines.java similarity index 92% rename from src/main/java/com/jozufozu/flywheel/backend/source/SourceLines.java rename to src/main/java/com/jozufozu/flywheel/core/source/SourceLines.java index 1c04bdee7..f9ade475c 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/source/SourceLines.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/SourceLines.java @@ -1,10 +1,10 @@ -package com.jozufozu.flywheel.backend.source; +package com.jozufozu.flywheel.core.source; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.google.common.collect.ImmutableList; -import com.jozufozu.flywheel.backend.source.span.CharPos; +import com.jozufozu.flywheel.core.source.span.CharPos; import com.jozufozu.flywheel.util.StringUtil; import it.unimi.dsi.fastutil.ints.IntArrayList; @@ -29,6 +29,48 @@ public class SourceLines { this.lines = getLines(source, lineStarts); } + public int getLineCount() { + return lines.size(); + } + + public String getLine(int lineNo) { + return lines.get(lineNo); + } + + public int getLineStart(int lineNo) { + + return lineStarts.getInt(lineNo); + } + + public CharPos getCharPos(int charPos) { + int lineNo = 0; + for (; lineNo < lineStarts.size(); lineNo++) { + int ls = lineStarts.getInt(lineNo); + + if (charPos < ls) { + break; + } + } + + lineNo -= 1; + + int lineStart = lineStarts.getInt(lineNo); + + return new CharPos(charPos, lineNo, charPos - lineStart); + } + + public String printLinesWithNumbers() { + StringBuilder builder = new StringBuilder(); + + for (int i = 0, linesSize = lines.size(); i < linesSize; i++) { + builder.append(String.format("%1$4s: ", i + 1)) + .append(lines.get(i)) + .append('\n'); + } + + return builder.toString(); + } + /** * Scan the source for line breaks, recording the position of the first character of each line. * @param source @@ -57,41 +99,4 @@ public class SourceLines { return builder.build(); } - - public CharPos getCharPos(int charPos) { - int lineNo = 0; - for (; lineNo < lineStarts.size(); lineNo++) { - int ls = lineStarts.getInt(lineNo); - - if (charPos < ls) { - break; - } - } - - lineNo -= 1; - - int lineStart = lineStarts.getInt(lineNo); - - return new CharPos(charPos, lineNo, charPos - lineStart); - } - - public int getLineCount() { - return lines.size(); - } - - public String getLine(int lineNo) { - return lines.get(lineNo); - } - - public String printLinesWithNumbers() { - StringBuilder builder = new StringBuilder(); - - for (int i = 0, linesSize = lines.size(); i < linesSize; i++) { - builder.append(String.format("%1$4s: ", i + 1)) - .append(lines.get(i)) - .append('\n'); - } - - return builder.toString(); - } } diff --git a/src/main/java/com/jozufozu/flywheel/core/source/error/ErrorBuilder.java b/src/main/java/com/jozufozu/flywheel/core/source/error/ErrorBuilder.java new file mode 100644 index 000000000..2732a97fb --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/source/error/ErrorBuilder.java @@ -0,0 +1,141 @@ +package com.jozufozu.flywheel.core.source.error; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.annotation.Nullable; + +import com.jozufozu.flywheel.core.source.FileIndex; +import com.jozufozu.flywheel.core.source.SourceFile; +import com.jozufozu.flywheel.core.source.SourceLines; +import com.jozufozu.flywheel.core.source.error.lines.ErrorLine; +import com.jozufozu.flywheel.core.source.error.lines.FileLine; +import com.jozufozu.flywheel.core.source.error.lines.HeaderLine; +import com.jozufozu.flywheel.core.source.error.lines.SourceLine; +import com.jozufozu.flywheel.core.source.error.lines.SpanHighlightLine; +import com.jozufozu.flywheel.core.source.error.lines.TextLine; +import com.jozufozu.flywheel.core.source.span.Span; +import com.jozufozu.flywheel.util.FlwUtil; + +public class ErrorBuilder { + + private static final Pattern ERROR_LINE = Pattern.compile("(\\d+)\\((\\d+)\\) : (.*)"); + + private final List lines = new ArrayList<>(); + + public ErrorBuilder() { + + } + + public static ErrorBuilder error(CharSequence msg) { + return new ErrorBuilder() + .header(ErrorLevel.ERROR, msg); + } + + public static ErrorBuilder compError(CharSequence msg) { + return new ErrorBuilder() + .extra(msg); + } + + public static ErrorBuilder warn(CharSequence msg) { + return new ErrorBuilder() + .header(ErrorLevel.WARN, msg); + } + + @Nullable + public static ErrorBuilder fromLogLine(FileIndex env, String s) { + Matcher matcher = ERROR_LINE.matcher(s); + + if (matcher.find()) { + String fileId = matcher.group(1); + String lineNo = matcher.group(2); + String msg = matcher.group(3); + Span span = env.getLineSpan(Integer.parseInt(fileId), Integer.parseInt(lineNo)); + return ErrorBuilder.compError(msg) + .pointAtFile(span.getSourceFile()) + .pointAt(span, 1); + } else { + return null; + } + } + + public ErrorBuilder header(ErrorLevel level, CharSequence msg) { + lines.add(new HeaderLine(level.toString(), msg)); + return this; + } + + public ErrorBuilder extra(CharSequence msg) { + lines.add(new TextLine(msg.toString())); + return this; + } + + public ErrorBuilder pointAtFile(SourceFile file) { + lines.add(new FileLine(file.name.toString())); + return this; + } + + public ErrorBuilder hintIncludeFor(@Nullable Span span, CharSequence msg) { + if (span == null) return this; + SourceFile sourceFile = span.getSourceFile(); + + String builder = "add " + sourceFile.importStatement() + ' ' + msg + "\n defined here:"; + + header(ErrorLevel.HINT, builder); + + return this.pointAtFile(sourceFile) + .pointAt(span, 0); + } + + public ErrorBuilder pointAt(Span span, int ctxLines) { + + if (span.lines() == 1) { + SourceLines lines = span.getSourceFile().lines; + + int spanLine = span.firstLine(); + + int firstLine = Math.max(0, spanLine - ctxLines); + int lastLine = Math.min(lines.getLineCount(), spanLine + ctxLines); + + int firstCol = span.getStart() + .col(); + int lastCol = span.getEnd() + .col(); + + for (int i = firstLine; i <= lastLine; i++) { + CharSequence line = lines.getLine(i); + + this.lines.add(SourceLine.numbered(i + 1, line.toString())); + + if (i == spanLine) { + this.lines.add(new SpanHighlightLine(firstCol, lastCol)); + } + } + } + + return this; + } + + public CharSequence build() { + + int maxLength = -1; + for (ErrorLine line : lines) { + int length = line.neededMargin(); + + if (length > maxLength) maxLength = length; + } + + StringBuilder builder = new StringBuilder(); + builder.append('\n'); + for (ErrorLine line : lines) { + int length = line.neededMargin(); + + builder.append(FlwUtil.repeatChar(' ', maxLength - length)) + .append(line.build()) + .append('\n'); + } + + return builder; + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/error/Level.java b/src/main/java/com/jozufozu/flywheel/core/source/error/ErrorLevel.java similarity index 56% rename from src/main/java/com/jozufozu/flywheel/backend/source/error/Level.java rename to src/main/java/com/jozufozu/flywheel/core/source/error/ErrorLevel.java index ecf151fdd..e45345124 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/source/error/Level.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/error/ErrorLevel.java @@ -1,13 +1,14 @@ -package com.jozufozu.flywheel.backend.source.error; +package com.jozufozu.flywheel.core.source.error; -public enum Level { +public enum ErrorLevel { WARN("warn"), ERROR("error"), + HINT("hint"), ; private final String error; - Level(String error) { + ErrorLevel(String error) { this.error = error; } diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/error/ErrorReporter.java b/src/main/java/com/jozufozu/flywheel/core/source/error/ErrorReporter.java similarity index 64% rename from src/main/java/com/jozufozu/flywheel/backend/source/error/ErrorReporter.java rename to src/main/java/com/jozufozu/flywheel/core/source/error/ErrorReporter.java index 20e70d2eb..fc9a1568d 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/source/error/ErrorReporter.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/error/ErrorReporter.java @@ -1,12 +1,15 @@ -package com.jozufozu.flywheel.backend.source.error; +package com.jozufozu.flywheel.core.source.error; +import java.util.List; import java.util.Optional; +import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.backend.Backend; -import com.jozufozu.flywheel.backend.source.SourceFile; -import com.jozufozu.flywheel.backend.source.parse.ShaderFunction; -import com.jozufozu.flywheel.backend.source.parse.ShaderStruct; -import com.jozufozu.flywheel.backend.source.span.Span; +import com.jozufozu.flywheel.core.source.SourceFile; +import com.jozufozu.flywheel.core.source.parse.ShaderFunction; +import com.jozufozu.flywheel.core.source.parse.ShaderStruct; +import com.jozufozu.flywheel.core.source.span.Span; +import com.jozufozu.flywheel.util.FlwUtil; public class ErrorReporter { @@ -42,7 +45,7 @@ public class ErrorReporter { ErrorBuilder error = ErrorBuilder.error(msg) .pointAtFile(file) - .pointAt(vertexName, 2) + .pointAt(vertexName, 1) .hintIncludeFor(span.orElse(null), hint); Backend.LOGGER.error(error.build()); @@ -64,4 +67,29 @@ public class ErrorReporter { Backend.LOGGER.error(error.build()); } + + public static void printLines(CharSequence source) { + String string = source.toString(); + + List lines = string.lines() + .toList(); + + int size = lines.size(); + + int maxWidth = FlwUtil.numDigits(size) + 1; + + StringBuilder builder = new StringBuilder().append('\n'); + + for (int i = 0; i < size; i++) { + + builder.append(i) + .append(FlwUtil.repeatChar(' ', maxWidth - FlwUtil.numDigits(i))) + .append("| ") + .append(lines.get(i)) + .append('\n'); + } + + Flywheel.LOGGER.error(builder.toString()); + } + } diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/error/lines/Divider.java b/src/main/java/com/jozufozu/flywheel/core/source/error/lines/Divider.java similarity index 75% rename from src/main/java/com/jozufozu/flywheel/backend/source/error/lines/Divider.java rename to src/main/java/com/jozufozu/flywheel/core/source/error/lines/Divider.java index a24c21c4b..c9206c740 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/source/error/lines/Divider.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/error/lines/Divider.java @@ -1,4 +1,4 @@ -package com.jozufozu.flywheel.backend.source.error.lines; +package com.jozufozu.flywheel.core.source.error.lines; public enum Divider { BAR(" | "), diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/error/lines/ErrorLine.java b/src/main/java/com/jozufozu/flywheel/core/source/error/lines/ErrorLine.java similarity index 61% rename from src/main/java/com/jozufozu/flywheel/backend/source/error/lines/ErrorLine.java rename to src/main/java/com/jozufozu/flywheel/core/source/error/lines/ErrorLine.java index fe3f6ec57..985275be6 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/source/error/lines/ErrorLine.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/error/lines/ErrorLine.java @@ -1,4 +1,4 @@ -package com.jozufozu.flywheel.backend.source.error.lines; +package com.jozufozu.flywheel.core.source.error.lines; public interface ErrorLine { @@ -14,6 +14,10 @@ public interface ErrorLine { return left() + divider() + right(); } - String left(); - String right(); + default String left() { + return ""; + } + default String right() { + return ""; + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/error/lines/FileLine.java b/src/main/java/com/jozufozu/flywheel/core/source/error/lines/FileLine.java similarity index 80% rename from src/main/java/com/jozufozu/flywheel/backend/source/error/lines/FileLine.java rename to src/main/java/com/jozufozu/flywheel/core/source/error/lines/FileLine.java index b39bf3494..f1facc2d5 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/source/error/lines/FileLine.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/error/lines/FileLine.java @@ -1,4 +1,4 @@ -package com.jozufozu.flywheel.backend.source.error.lines; +package com.jozufozu.flywheel.core.source.error.lines; public record FileLine(String fileName) implements ErrorLine { diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/error/lines/HeaderLine.java b/src/main/java/com/jozufozu/flywheel/core/source/error/lines/HeaderLine.java similarity index 56% rename from src/main/java/com/jozufozu/flywheel/backend/source/error/lines/HeaderLine.java rename to src/main/java/com/jozufozu/flywheel/core/source/error/lines/HeaderLine.java index 2488095e8..e806cc399 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/source/error/lines/HeaderLine.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/error/lines/HeaderLine.java @@ -1,4 +1,4 @@ -package com.jozufozu.flywheel.backend.source.error.lines; +package com.jozufozu.flywheel.core.source.error.lines; public record HeaderLine(String level, CharSequence message) implements ErrorLine { @@ -11,14 +11,4 @@ public record HeaderLine(String level, CharSequence message) implements ErrorLin public String build() { return level + ": " + message; } - - @Override - public String left() { - return null; - } - - @Override - public String right() { - return null; - } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/error/lines/SourceLine.java b/src/main/java/com/jozufozu/flywheel/core/source/error/lines/SourceLine.java similarity index 84% rename from src/main/java/com/jozufozu/flywheel/backend/source/error/lines/SourceLine.java rename to src/main/java/com/jozufozu/flywheel/core/source/error/lines/SourceLine.java index 496e411fc..f790fd08c 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/source/error/lines/SourceLine.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/error/lines/SourceLine.java @@ -1,4 +1,4 @@ -package com.jozufozu.flywheel.backend.source.error.lines; +package com.jozufozu.flywheel.core.source.error.lines; public record SourceLine(String number, String line) implements ErrorLine { diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/error/lines/SpanHighlightLine.java b/src/main/java/com/jozufozu/flywheel/core/source/error/lines/SpanHighlightLine.java similarity index 88% rename from src/main/java/com/jozufozu/flywheel/backend/source/error/lines/SpanHighlightLine.java rename to src/main/java/com/jozufozu/flywheel/core/source/error/lines/SpanHighlightLine.java index 379ab82ea..2d2bf9431 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/source/error/lines/SpanHighlightLine.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/error/lines/SpanHighlightLine.java @@ -1,4 +1,4 @@ -package com.jozufozu.flywheel.backend.source.error.lines; +package com.jozufozu.flywheel.core.source.error.lines; public class SpanHighlightLine implements ErrorLine { private final String line; diff --git a/src/main/java/com/jozufozu/flywheel/core/source/error/lines/TextLine.java b/src/main/java/com/jozufozu/flywheel/core/source/error/lines/TextLine.java new file mode 100644 index 000000000..e5ff86ff0 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/source/error/lines/TextLine.java @@ -0,0 +1,9 @@ +package com.jozufozu.flywheel.core.source.error.lines; + +public record TextLine(String msg) implements ErrorLine { + + @Override + public String build() { + return msg; + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/span/package-info.java b/src/main/java/com/jozufozu/flywheel/core/source/error/package-info.java similarity index 77% rename from src/main/java/com/jozufozu/flywheel/backend/source/span/package-info.java rename to src/main/java/com/jozufozu/flywheel/core/source/error/package-info.java index 4119b8241..3533088ae 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/source/span/package-info.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/error/package-info.java @@ -1,5 +1,5 @@ @ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault -package com.jozufozu.flywheel.backend.source.span; +package com.jozufozu.flywheel.core.source.error; import javax.annotation.ParametersAreNonnullByDefault; diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/error/package-info.java b/src/main/java/com/jozufozu/flywheel/core/source/package-info.java similarity index 76% rename from src/main/java/com/jozufozu/flywheel/backend/source/error/package-info.java rename to src/main/java/com/jozufozu/flywheel/core/source/package-info.java index 250db26d8..18f315f87 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/source/error/package-info.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/package-info.java @@ -1,5 +1,5 @@ @ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault -package com.jozufozu.flywheel.backend.source.error; +package com.jozufozu.flywheel.core.source; import javax.annotation.ParametersAreNonnullByDefault; diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/parse/AbstractShaderElement.java b/src/main/java/com/jozufozu/flywheel/core/source/parse/AbstractShaderElement.java similarity index 57% rename from src/main/java/com/jozufozu/flywheel/backend/source/parse/AbstractShaderElement.java rename to src/main/java/com/jozufozu/flywheel/core/source/parse/AbstractShaderElement.java index 594ae4d02..d230d013e 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/source/parse/AbstractShaderElement.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/parse/AbstractShaderElement.java @@ -1,6 +1,6 @@ -package com.jozufozu.flywheel.backend.source.parse; +package com.jozufozu.flywheel.core.source.parse; -import com.jozufozu.flywheel.backend.source.span.Span; +import com.jozufozu.flywheel.core.source.span.Span; public abstract class AbstractShaderElement { diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/parse/Import.java b/src/main/java/com/jozufozu/flywheel/core/source/parse/Import.java similarity index 73% rename from src/main/java/com/jozufozu/flywheel/backend/source/parse/Import.java rename to src/main/java/com/jozufozu/flywheel/core/source/parse/Import.java index 23a7f279a..0a357671b 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/source/parse/Import.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/parse/Import.java @@ -1,4 +1,4 @@ -package com.jozufozu.flywheel.backend.source.parse; +package com.jozufozu.flywheel.core.source.parse; import java.util.ArrayList; import java.util.List; @@ -6,11 +6,11 @@ import java.util.Optional; import javax.annotation.Nullable; -import com.jozufozu.flywheel.backend.source.FileResolution; -import com.jozufozu.flywheel.backend.source.Resolver; -import com.jozufozu.flywheel.backend.source.SourceFile; -import com.jozufozu.flywheel.backend.source.error.ErrorReporter; -import com.jozufozu.flywheel.backend.source.span.Span; +import com.jozufozu.flywheel.core.source.FileResolution; +import com.jozufozu.flywheel.core.source.Resolver; +import com.jozufozu.flywheel.core.source.SourceFile; +import com.jozufozu.flywheel.core.source.error.ErrorReporter; +import com.jozufozu.flywheel.core.source.span.Span; import net.minecraft.resources.ResourceLocation; @@ -26,7 +26,7 @@ public class Import extends AbstractShaderElement { super(self); this.file = file; - resolution = resolver.findShader(toRL(file)) + resolution = resolver.get(toRL(file)) .addSpan(file); IMPORTS.add(this); diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/parse/ShaderFunction.java b/src/main/java/com/jozufozu/flywheel/core/source/parse/ShaderFunction.java similarity index 77% rename from src/main/java/com/jozufozu/flywheel/backend/source/parse/ShaderFunction.java rename to src/main/java/com/jozufozu/flywheel/core/source/parse/ShaderFunction.java index 1621debf7..9dfe551de 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/source/parse/ShaderFunction.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/parse/ShaderFunction.java @@ -1,15 +1,15 @@ -package com.jozufozu.flywheel.backend.source.parse; +package com.jozufozu.flywheel.core.source.parse; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import com.google.common.collect.ImmutableList; -import com.jozufozu.flywheel.backend.source.span.Span; +import com.jozufozu.flywheel.core.source.span.Span; public class ShaderFunction extends AbstractShaderElement { - public static final Pattern argument = Pattern.compile("(\\w+)\\s+(\\w+)"); + public static final Pattern argument = Pattern.compile("(?:(inout|in|out) )?(\\w+)\\s+(\\w+)"); public static final Pattern assignment = Pattern.compile("(\\w+)\\s*="); private final Span type; @@ -66,10 +66,11 @@ public class ShaderFunction extends AbstractShaderElement { while (arguments.find()) { Span self = Span.fromMatcher(args, arguments); - Span type = Span.fromMatcher(args, arguments, 1); - Span name = Span.fromMatcher(args, arguments, 2); + Span qualifier = Span.fromMatcher(args, arguments, 1); + Span type = Span.fromMatcher(args, arguments, 2); + Span name = Span.fromMatcher(args, arguments, 3); - builder.add(new Variable(self, type, name)); + builder.add(new Variable(self, qualifier, type, name)); } return builder.build(); @@ -79,7 +80,7 @@ public class ShaderFunction extends AbstractShaderElement { public String toString() { String p = parameters.stream() - .map(Variable::typeName) + .map(variable -> variable.type) .map(Span::get) .collect(Collectors.joining(",")); diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/parse/ShaderStruct.java b/src/main/java/com/jozufozu/flywheel/core/source/parse/ShaderStruct.java similarity index 93% rename from src/main/java/com/jozufozu/flywheel/backend/source/parse/ShaderStruct.java rename to src/main/java/com/jozufozu/flywheel/core/source/parse/ShaderStruct.java index 80200ff32..e3db7fd29 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/source/parse/ShaderStruct.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/parse/ShaderStruct.java @@ -1,11 +1,11 @@ -package com.jozufozu.flywheel.backend.source.parse; +package com.jozufozu.flywheel.core.source.parse; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import com.jozufozu.flywheel.backend.source.span.Span; +import com.jozufozu.flywheel.core.source.span.Span; public class ShaderStruct extends AbstractShaderElement { diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/parse/StructField.java b/src/main/java/com/jozufozu/flywheel/core/source/parse/StructField.java similarity index 82% rename from src/main/java/com/jozufozu/flywheel/backend/source/parse/StructField.java rename to src/main/java/com/jozufozu/flywheel/core/source/parse/StructField.java index 77e03f555..961ea3441 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/source/parse/StructField.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/parse/StructField.java @@ -1,8 +1,8 @@ -package com.jozufozu.flywheel.backend.source.parse; +package com.jozufozu.flywheel.core.source.parse; import java.util.regex.Pattern; -import com.jozufozu.flywheel.backend.source.span.Span; +import com.jozufozu.flywheel.core.source.span.Span; public class StructField extends AbstractShaderElement { public static final Pattern fieldPattern = Pattern.compile("(\\S+)\\s*(\\S+);"); diff --git a/src/main/java/com/jozufozu/flywheel/core/source/parse/Variable.java b/src/main/java/com/jozufozu/flywheel/core/source/parse/Variable.java new file mode 100644 index 000000000..0d6f9aa99 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/source/parse/Variable.java @@ -0,0 +1,44 @@ +package com.jozufozu.flywheel.core.source.parse; + +import com.jozufozu.flywheel.core.source.span.Span; + +public class Variable extends AbstractShaderElement { + + public final Span qualifierSpan; + public final Span type; + public final Span name; + public final Qualifier qualifier; + + public Variable(Span self, Span qualifier, Span type, Span name) { + super(self); + this.qualifierSpan = qualifier; + this.type = type; + this.name = name; + this.qualifier = Qualifier.fromSpan(qualifierSpan); + } + + @Override + public String toString() { + return type + " " + name; + } + + public enum Qualifier { + NONE, + IN, + OUT, + INOUT, + ERROR; + + public static Qualifier fromSpan(Span s) { + String span = s.toString(); + + return switch (span) { + case "" -> NONE; + case "in" -> IN; + case "inout" -> INOUT; + case "out" -> OUT; + default -> ERROR; + }; + } + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/parse/package-info.java b/src/main/java/com/jozufozu/flywheel/core/source/parse/package-info.java similarity index 76% rename from src/main/java/com/jozufozu/flywheel/backend/source/parse/package-info.java rename to src/main/java/com/jozufozu/flywheel/core/source/parse/package-info.java index 61225f714..0984aedc8 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/source/parse/package-info.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/parse/package-info.java @@ -1,6 +1,6 @@ @ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault -package com.jozufozu.flywheel.backend.source.parse; +package com.jozufozu.flywheel.core.source.parse; import javax.annotation.ParametersAreNonnullByDefault; diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/span/CharPos.java b/src/main/java/com/jozufozu/flywheel/core/source/span/CharPos.java similarity index 63% rename from src/main/java/com/jozufozu/flywheel/backend/source/span/CharPos.java rename to src/main/java/com/jozufozu/flywheel/core/source/span/CharPos.java index d402a65fc..617cfa0a7 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/source/span/CharPos.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/span/CharPos.java @@ -1,4 +1,4 @@ -package com.jozufozu.flywheel.backend.source.span; +package com.jozufozu.flywheel.core.source.span; /** * A position in a file. diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/span/ErrorSpan.java b/src/main/java/com/jozufozu/flywheel/core/source/span/ErrorSpan.java similarity index 85% rename from src/main/java/com/jozufozu/flywheel/backend/source/span/ErrorSpan.java rename to src/main/java/com/jozufozu/flywheel/core/source/span/ErrorSpan.java index c73ba195e..5c4cd39e1 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/source/span/ErrorSpan.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/span/ErrorSpan.java @@ -1,6 +1,6 @@ -package com.jozufozu.flywheel.backend.source.span; +package com.jozufozu.flywheel.core.source.span; -import com.jozufozu.flywheel.backend.source.SourceFile; +import com.jozufozu.flywheel.core.source.SourceFile; /** * Represents a (syntactically) malformed segment of code. diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/span/Span.java b/src/main/java/com/jozufozu/flywheel/core/source/span/Span.java similarity index 95% rename from src/main/java/com/jozufozu/flywheel/backend/source/span/Span.java rename to src/main/java/com/jozufozu/flywheel/core/source/span/Span.java index 1b812fde9..61961a979 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/source/span/Span.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/span/Span.java @@ -1,8 +1,8 @@ -package com.jozufozu.flywheel.backend.source.span; +package com.jozufozu.flywheel.core.source.span; import java.util.regex.Matcher; -import com.jozufozu.flywheel.backend.source.SourceFile; +import com.jozufozu.flywheel.core.source.SourceFile; /** * A segment of code in a {@link SourceFile}. diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/span/StringSpan.java b/src/main/java/com/jozufozu/flywheel/core/source/span/StringSpan.java similarity index 82% rename from src/main/java/com/jozufozu/flywheel/backend/source/span/StringSpan.java rename to src/main/java/com/jozufozu/flywheel/core/source/span/StringSpan.java index 4712c6b74..f91be1f6d 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/source/span/StringSpan.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/span/StringSpan.java @@ -1,6 +1,6 @@ -package com.jozufozu.flywheel.backend.source.span; +package com.jozufozu.flywheel.core.source.span; -import com.jozufozu.flywheel.backend.source.SourceFile; +import com.jozufozu.flywheel.core.source.SourceFile; public class StringSpan extends Span { diff --git a/src/main/java/com/jozufozu/flywheel/core/source/span/package-info.java b/src/main/java/com/jozufozu/flywheel/core/source/span/package-info.java new file mode 100644 index 000000000..1ee6ddb46 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/source/span/package-info.java @@ -0,0 +1,6 @@ +@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault +package com.jozufozu.flywheel.core.source.span; + +import javax.annotation.ParametersAreNonnullByDefault; + +import net.minecraft.MethodsReturnNonnullByDefault; diff --git a/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertex.java b/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertex.java index 4a6664d0b..e382d0ab7 100644 --- a/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertex.java +++ b/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertex.java @@ -36,6 +36,27 @@ public class BlockVertex implements VertexType { return new BlockVertexListUnsafe(buffer, vertexCount); } + @Override + public String getShaderHeader() { + return """ +layout (location = 0) in vec3 _flw_v_pos; +layout (location = 1) in vec4 _flw_v_color; +layout (location = 2) in vec2 _flw_v_texCoords; +layout (location = 3) in vec2 _flw_v_light; +layout (location = 4) in vec3 _flw_v_normal; + +Vertex FLWCreateVertex() { + Vertex v; + v.pos = _flw_v_pos; + v.color = _flw_v_color; + v.texCoords = _flw_v_texCoords; + v.light = _flw_v_light; + v.normal = _flw_v_normal; + return v; +} + """; + } + public VertexList createReader(BufferBuilder bufferBuilder) { // TODO: try to avoid virtual model rendering Pair pair = bufferBuilder.popNextBuffer(); diff --git a/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalVertex.java b/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalVertex.java index 85daec969..b10139c88 100644 --- a/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalVertex.java +++ b/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalVertex.java @@ -26,4 +26,23 @@ public class PosTexNormalVertex implements VertexType { public PosTexNormalVertexListUnsafe createReader(ByteBuffer buffer, int vertexCount) { return new PosTexNormalVertexListUnsafe(buffer, vertexCount); } + + @Override + public String getShaderHeader() { + return """ +layout (location = 0) in vec3 _flw_v_pos; +layout (location = 1) in vec2 _flw_v_texCoords; +layout (location = 2) in vec3 _flw_v_normal; + +Vertex FLWCreateVertex() { + Vertex v; + v.pos = _flw_v_pos; + v.color = vec4(1.); + v.texCoords = _flw_v_texCoords; + v.light = vec2(0.); + v.normal = _flw_v_normal; + return v; +} + """; + } } diff --git a/src/main/java/com/jozufozu/flywheel/event/ForgeEvents.java b/src/main/java/com/jozufozu/flywheel/event/ForgeEvents.java index c91713235..9ca6cd351 100644 --- a/src/main/java/com/jozufozu/flywheel/event/ForgeEvents.java +++ b/src/main/java/com/jozufozu/flywheel/event/ForgeEvents.java @@ -24,8 +24,7 @@ public class ForgeEvents { ArrayList right = event.getRight(); - String text = "Flywheel: " + Backend.getInstance() - .getBackendDescriptor(); + String text = "Flywheel: " + Backend.getBackendDescriptor(); if (right.size() < 10) { right.add(""); right.add(text); diff --git a/src/main/java/com/jozufozu/flywheel/event/GatherContextEvent.java b/src/main/java/com/jozufozu/flywheel/event/GatherContextEvent.java index df5a6307b..ffbc76341 100644 --- a/src/main/java/com/jozufozu/flywheel/event/GatherContextEvent.java +++ b/src/main/java/com/jozufozu/flywheel/event/GatherContextEvent.java @@ -1,24 +1,16 @@ package com.jozufozu.flywheel.event; -import com.jozufozu.flywheel.backend.Backend; - import net.minecraftforge.eventbus.api.Event; import net.minecraftforge.fml.event.IModBusEvent; public class GatherContextEvent extends Event implements IModBusEvent { - private final Backend backend; private final boolean firstLoad; - public GatherContextEvent(Backend backend, boolean firstLoad) { - this.backend = backend; + public GatherContextEvent(boolean firstLoad) { this.firstLoad = firstLoad; } - public Backend getBackend() { - return backend; - } - /** * @return true iff it is the first time the event is fired. */ diff --git a/src/main/java/com/jozufozu/flywheel/event/RenderLayerEvent.java b/src/main/java/com/jozufozu/flywheel/event/RenderLayerEvent.java index 9aacbe054..f0b54fc49 100644 --- a/src/main/java/com/jozufozu/flywheel/event/RenderLayerEvent.java +++ b/src/main/java/com/jozufozu/flywheel/event/RenderLayerEvent.java @@ -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 diff --git a/src/main/java/com/jozufozu/flywheel/mixin/LevelRendererMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/LevelRendererMixin.java index 7b8a88c8a..497a81ee5 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/LevelRendererMixin.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/LevelRendererMixin.java @@ -90,8 +90,7 @@ public class LevelRendererMixin { @Inject(at = @At("TAIL"), method = "allChanged") private void refresh(CallbackInfo ci) { - Backend.getInstance() - .refresh(); + Backend.refresh(); MinecraftForge.EVENT_BUS.post(new ReloadRenderersEvent(level)); } diff --git a/src/main/resources/assets/flywheel/flywheel/programs/model.json b/src/main/resources/assets/flywheel/flywheel/programs/model.json index 1352b31b2..dbee0ddd4 100644 --- a/src/main/resources/assets/flywheel/flywheel/programs/model.json +++ b/src/main/resources/assets/flywheel/flywheel/programs/model.json @@ -1,12 +1,4 @@ { - "source": "flywheel:model.vert", - "states": [ - { - "when": { - "provider": "flywheel:normal_debug", - "value": "true" - }, - "define": "DEBUG_NORMAL" - } - ] + "vertex": "flywheel:model.vert", + "fragment": "flywheel:block.frag" } diff --git a/src/main/resources/assets/flywheel/flywheel/programs/oriented.json b/src/main/resources/assets/flywheel/flywheel/programs/oriented.json index a22a568cf..f012f819b 100644 --- a/src/main/resources/assets/flywheel/flywheel/programs/oriented.json +++ b/src/main/resources/assets/flywheel/flywheel/programs/oriented.json @@ -1,12 +1,4 @@ { - "source": "flywheel:oriented.vert", - "states": [ - { - "when": { - "provider": "flywheel:normal_debug", - "value": "true" - }, - "define": "DEBUG_NORMAL" - } - ] + "vertex": "flywheel:oriented.vert", + "fragment": "flywheel:block.frag" } diff --git a/src/main/resources/assets/flywheel/flywheel/programs/passthru.json b/src/main/resources/assets/flywheel/flywheel/programs/passthru.json new file mode 100644 index 000000000..9e180f2de --- /dev/null +++ b/src/main/resources/assets/flywheel/flywheel/programs/passthru.json @@ -0,0 +1,4 @@ +{ + "vertex": "flywheel:passthru.vert", + "fragment": "flywheel:block.frag" +} diff --git a/src/main/resources/assets/flywheel/flywheel/shaders/block.frag b/src/main/resources/assets/flywheel/flywheel/shaders/block.frag index d8e5169fe..366d2cc63 100644 --- a/src/main/resources/assets/flywheel/flywheel/shaders/block.frag +++ b/src/main/resources/assets/flywheel/flywheel/shaders/block.frag @@ -1,17 +1,13 @@ -struct BlockFrag { +struct Fragment { vec2 texCoords; vec4 color; float diffuse; vec2 light; }; -#if defined(FRAGMENT_SHADER) -void fragment(BlockFrag r) { +vec4 fragment(Fragment r) { vec4 tex = FLWBlockTexture(r.texCoords); - vec4 color = vec4(tex.rgb * FLWLight(r.light).rgb * r.diffuse, tex.a) * r.color; - - FLWFinalizeColor(color); + return vec4(tex.rgb * FLWLight(r.light).rgb * r.diffuse, tex.a) * r.color; } -#endif diff --git a/src/main/resources/assets/flywheel/flywheel/shaders/context/crumbling.glsl b/src/main/resources/assets/flywheel/flywheel/shaders/context/crumbling.glsl index c9ab41cbe..958941126 100644 --- a/src/main/resources/assets/flywheel/flywheel/shaders/context/crumbling.glsl +++ b/src/main/resources/assets/flywheel/flywheel/shaders/context/crumbling.glsl @@ -1,4 +1,5 @@ #use "flywheel:context/fog.glsl" +#use "flywheel:core/diffuse.glsl" uniform float uTime; uniform mat4 uViewProjection; @@ -11,17 +12,11 @@ uniform sampler2D uCrumbling; uniform vec2 uWindowSize; -void FLWFinalizeNormal(inout vec3 normal) { - // noop -} - #if defined(VERTEX_SHADER) -void FLWFinalizeWorldPos(inout vec4 worldPos) { - #if defined(USE_FOG) - FragDistance = cylindrical_distance(worldPos.xyz, uCameraPos); - #endif +vec4 FLWVertex(inout Vertex v) { + FragDistance = cylindrical_distance(v.pos, uCameraPos); - gl_Position = uViewProjection * worldPos; + return uViewProjection * vec4(v.pos, 1.); } #elif defined(FRAGMENT_SHADER) diff --git a/src/main/resources/assets/flywheel/flywheel/shaders/context/world.glsl b/src/main/resources/assets/flywheel/flywheel/shaders/context/world.glsl index 1503ed993..28e36ab3d 100644 --- a/src/main/resources/assets/flywheel/flywheel/shaders/context/world.glsl +++ b/src/main/resources/assets/flywheel/flywheel/shaders/context/world.glsl @@ -1,4 +1,5 @@ #use "flywheel:context/fog.glsl" +#use "flywheel:core/diffuse.glsl" uniform float uTime; uniform mat4 uViewProjection; @@ -10,21 +11,16 @@ uniform sampler2D uLightMap; uniform vec2 uWindowSize; -void FLWFinalizeNormal(inout vec3 normal) { - // noop -} - #if defined(VERTEX_SHADER) -void FLWFinalizeWorldPos(inout vec4 worldPos) { - FragDistance = cylindrical_distance(worldPos.xyz, uCameraPos); - gl_Position = uViewProjection * worldPos; +vec4 FLWVertex(inout Vertex v) { + FragDistance = cylindrical_distance(v.pos, uCameraPos); + + return uViewProjection * vec4(v.pos, 1.); } #elif defined(FRAGMENT_SHADER) #use "flywheel:core/lightutil.glsl" - -#define ALPHA_DISCARD 0.1 // optimize discard usage #if defined(ALPHA_DISCARD) #if defined(GL_ARB_conservative_depth) diff --git a/src/main/resources/assets/flywheel/flywheel/shaders/data/modelvertex.glsl b/src/main/resources/assets/flywheel/flywheel/shaders/data/modelvertex.glsl deleted file mode 100644 index 48e713b43..000000000 --- a/src/main/resources/assets/flywheel/flywheel/shaders/data/modelvertex.glsl +++ /dev/null @@ -1,5 +0,0 @@ -struct Vertex { - vec3 pos; - vec2 texCoords; - vec3 normal; -}; diff --git a/src/main/resources/assets/flywheel/flywheel/shaders/model.vert b/src/main/resources/assets/flywheel/flywheel/shaders/model.vert index 603d17e54..0feeb9a1b 100644 --- a/src/main/resources/assets/flywheel/flywheel/shaders/model.vert +++ b/src/main/resources/assets/flywheel/flywheel/shaders/model.vert @@ -1,6 +1,3 @@ -#use "flywheel:core/diffuse.glsl" -#use "flywheel:data/modelvertex.glsl" -#use "flywheel:block.frag" struct Instance { vec2 light; @@ -9,26 +6,9 @@ struct Instance { mat3 normalMat; }; -#if defined(VERTEX_SHADER) -BlockFrag vertex(Vertex v, Instance i) { - vec4 worldPos = i.transform * vec4(v.pos, 1.); - - vec3 norm = i.normalMat * v.normal; - - FLWFinalizeWorldPos(worldPos); - FLWFinalizeNormal(norm); - - norm = normalize(norm); - - BlockFrag b; - b.diffuse = diffuse(norm); - b.texCoords = v.texCoords; - b.light = i.light; - #if defined(DEBUG_NORMAL) - b.color = vec4(norm, 1.); - #else - b.color = i.color; - #endif - return b; +void vertex(inout Vertex v, Instance i) { + v.pos = (i.transform * vec4(v.pos, 1.)).xyz; + v.normal = i.normalMat * v.normal; + v.color = i.color; + v.light = i.light; } -#endif diff --git a/src/main/resources/assets/flywheel/flywheel/shaders/oriented.vert b/src/main/resources/assets/flywheel/flywheel/shaders/oriented.vert index bac28858b..8478e8b4a 100644 --- a/src/main/resources/assets/flywheel/flywheel/shaders/oriented.vert +++ b/src/main/resources/assets/flywheel/flywheel/shaders/oriented.vert @@ -1,8 +1,4 @@ #use "flywheel:core/quaternion.glsl" -#use "flywheel:core/diffuse.glsl" - -#use "flywheel:data/modelvertex.glsl" -#use "flywheel:block.frag" struct Oriented { vec2 light; @@ -12,24 +8,9 @@ struct Oriented { vec4 rotation; }; -#if defined(VERTEX_SHADER) -BlockFrag vertex(Vertex v, Oriented o) { - vec4 worldPos = vec4(rotateVertexByQuat(v.pos - o.pivot, o.rotation) + o.pivot + o.pos, 1.); - - vec3 norm = rotateVertexByQuat(v.normal, o.rotation); - - FLWFinalizeWorldPos(worldPos); - FLWFinalizeNormal(norm); - - BlockFrag b; - b.diffuse = diffuse(norm); - b.texCoords = v.texCoords; - b.light = o.light; - #if defined(DEBUG_NORMAL) - b.color = vec4(norm, 1.); - #else - b.color = o.color; - #endif - return b; +void vertex(inout Vertex v, Oriented o) { + v.pos = rotateVertexByQuat(v.pos - o.pivot, o.rotation) + o.pivot + o.pos; + v.normal = rotateVertexByQuat(v.normal, o.rotation); + v.color = o.color; + v.light = o.light; } -#endif diff --git a/src/main/resources/assets/flywheel/flywheel/shaders/passthru.vert b/src/main/resources/assets/flywheel/flywheel/shaders/passthru.vert new file mode 100644 index 000000000..a7642d2ab --- /dev/null +++ b/src/main/resources/assets/flywheel/flywheel/shaders/passthru.vert @@ -0,0 +1,4 @@ + +void vertex(inout Vertex v) { + +} diff --git a/src/main/resources/assets/flywheel/flywheel/shaders/smooth_oriented.glsl b/src/main/resources/assets/flywheel/flywheel/shaders/smooth_oriented.glsl deleted file mode 100644 index c657678aa..000000000 --- a/src/main/resources/assets/flywheel/flywheel/shaders/smooth_oriented.glsl +++ /dev/null @@ -1,53 +0,0 @@ -#use "flywheel:core/matutils.glsl" -#use "flywheel:core/quaternion.glsl" -#use "flywheel:core/diffuse.glsl" - -struct Oriented { -// each vec 4 is 2 light coords packed -// x z - vec4 lightA;// lo, lo - vec4 lightB;// hi, lo - vec4 lightC;// hi, hi - vec4 lightD;// lo, hi - - vec3 loCorner; - vec3 size; - - vec4 color; - vec3 pos; - vec3 pivot; - vec4 rotation; -}; - -#use "flywheel:data/modelvertex.glsl" -#use "flywheel:block.frag" - -BlockFrag vertex(Vertex v, Oriented o) { - vec4 worldPos = vec4(rotateVertexByQuat(v.pos - o.pivot, o.rotation) + o.pivot + o.pos, 1.); - - vec3 norm = rotateVertexByQuat(v.normal, o.rotation); - - FLWFinalizeWorldPos(worldPos); - FLWFinalizeNormal(norm); - - // manual trilinear interpolation - vec3 lightPos = (worldPos.xyz - o.loCorner) / o.size; - - vec4 lightLoZ = mix(lightA, lightB, lightPos.x);// lo z - vec4 lightHiZ = mix(lightD, lightC, lightPos.x);// hi z - - vec4 lightE = mix(lightLoZ, lightHiZ, lightPos.z);// - - vec2 lightCoord = mix(lightE.xy, lightE.zw, lightPos.y); - - BlockFrag b; - b.diffuse = diffuse(norm); - b.texCoords = v.texCoords; - b.light = lightCoord; - #if defined(DEBUG_NORMAL) - b.color = vec4(norm, 1.); - #else - b.color = o.color; - #endif - return b; -}