Update uniform providers (again)

- Remove uniform api.
- Do not generate uniform interface blocks.
- Move uniform shader into internal/ and manually include it in the api
  impl headers.
- Add flw_ prefix to existing uniforms.
- Separate fog uniforms into their own UBO, uploaded in FogUpdateMixin.
- Drastically simplify UniformBuffer.
- Do not poll for uniform buffer updates. Instead, do the upload at the
  beginning of a frame when the engine is flushing.
This commit is contained in:
Jozufozu 2024-01-18 17:07:10 -08:00
parent 3376bf9809
commit 655712aadf
31 changed files with 265 additions and 473 deletions

View file

@ -10,7 +10,7 @@ import com.jozufozu.flywheel.api.visualization.VisualizationManager;
import com.jozufozu.flywheel.backend.Backends;
import com.jozufozu.flywheel.backend.ShaderIndices;
import com.jozufozu.flywheel.backend.compile.FlwPrograms;
import com.jozufozu.flywheel.backend.engine.UniformBuffer;
import com.jozufozu.flywheel.backend.engine.uniform.Uniforms;
import com.jozufozu.flywheel.config.BackendArgument;
import com.jozufozu.flywheel.config.FlwCommands;
import com.jozufozu.flywheel.config.FlwConfig;
@ -95,7 +95,7 @@ public class Flywheel {
forgeEventBus.addListener(FlwCommands::registerClientCommands);
forgeEventBus.addListener(UniformBuffer::onReloadLevelRenderer);
forgeEventBus.addListener(Uniforms::onReloadLevelRenderer);
forgeEventBus.addListener(LightUpdater::onClientTick);
forgeEventBus.addListener((LevelEvent.Unload e) -> LevelAttached.onUnloadLevel(e));

View file

@ -1,33 +0,0 @@
package com.jozufozu.flywheel.api.uniform;
import com.jozufozu.flywheel.api.registry.Registry;
import com.jozufozu.flywheel.impl.RegistryImpl;
import net.minecraft.resources.ResourceLocation;
public interface ShaderUniforms {
static Registry<ShaderUniforms> REGISTRY = RegistryImpl.createForShaderUniforms();
Provider activate(long ptr);
ResourceLocation uniformShader();
int byteSize();
interface Provider {
/**
* Delete this provider.<p>
* <p>
* Do not free the ptr passed to {@link #activate(long)}.<br>
* Clean up other resources, and unsubscribe from events.
*/
void delete();
/**
* Poll the provider for changes.
*
* @return {@code true} if the provider updated its backing store.
*/
boolean poll();
}
}

View file

@ -6,10 +6,8 @@ import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.api.context.Context;
import com.jozufozu.flywheel.api.instance.InstanceType;
import com.jozufozu.flywheel.api.uniform.ShaderUniforms;
import com.jozufozu.flywheel.backend.ShaderIndices;
import com.jozufozu.flywheel.backend.compile.component.UberShaderComponent;
import com.jozufozu.flywheel.backend.compile.component.UniformComponent;
import com.jozufozu.flywheel.backend.compile.core.CompilerStats;
import com.jozufozu.flywheel.backend.compile.core.SourceLoader;
import com.jozufozu.flywheel.backend.glsl.ShaderSources;
@ -30,12 +28,11 @@ public final class FlwPrograms {
var loadChecker = new SourceLoader(sources, preLoadStats);
var pipelineKeys = createPipelineKeys();
var uniformComponent = createUniformComponent(loadChecker);
List<SourceComponent> vertexComponents = List.of(createVertexMaterialComponent(loadChecker));
List<SourceComponent> fragmentComponents = List.of(createFragmentMaterialComponent(loadChecker), createFogComponent(loadChecker), createCutoutComponent(loadChecker));
InstancingPrograms.reload(sources, pipelineKeys, uniformComponent, vertexComponents, fragmentComponents);
IndirectPrograms.reload(sources, pipelineKeys, uniformComponent, vertexComponents, fragmentComponents);
InstancingPrograms.reload(sources, pipelineKeys, vertexComponents, fragmentComponents);
IndirectPrograms.reload(sources, pipelineKeys, vertexComponents, fragmentComponents);
if (preLoadStats.errored()) {
Flywheel.LOGGER.error(preLoadStats.generateErrorLog());
@ -96,15 +93,6 @@ public final class FlwPrograms {
.build(loadChecker);
}
private static UniformComponent createUniformComponent(SourceLoader loadChecker) {
return UniformComponent.builder(Flywheel.rl("uniforms"))
.sources(ShaderUniforms.REGISTRY.getAll()
.stream()
.map(ShaderUniforms::uniformShader)
.toList())
.build(loadChecker);
}
public static class ResourceReloadListener implements ResourceManagerReloadListener {
public static final ResourceReloadListener INSTANCE = new ResourceReloadListener();

View file

@ -10,7 +10,6 @@ import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.api.context.Context;
import com.jozufozu.flywheel.api.instance.InstanceType;
import com.jozufozu.flywheel.backend.compile.component.IndirectComponent;
import com.jozufozu.flywheel.backend.compile.component.UniformComponent;
import com.jozufozu.flywheel.backend.compile.core.CompilationHarness;
import com.jozufozu.flywheel.backend.compile.core.Compile;
import com.jozufozu.flywheel.backend.gl.GlCompat;
@ -45,11 +44,11 @@ public class IndirectPrograms extends AbstractPrograms {
this.scatter = scatter;
}
static void reload(ShaderSources sources, ImmutableList<PipelineProgramKey> pipelineKeys, UniformComponent uniformComponent, List<SourceComponent> vertexComponents, List<SourceComponent> fragmentComponents) {
static void reload(ShaderSources sources, ImmutableList<PipelineProgramKey> pipelineKeys, List<SourceComponent> vertexComponents, List<SourceComponent> fragmentComponents) {
IndirectPrograms newInstance = null;
var pipelineCompiler = PipelineCompiler.create(sources, Pipelines.INDIRECT, uniformComponent, vertexComponents, fragmentComponents);
var cullingCompiler = createCullingCompiler(uniformComponent, sources);
var pipelineCompiler = PipelineCompiler.create(sources, Pipelines.INDIRECT, vertexComponents, fragmentComponents);
var cullingCompiler = createCullingCompiler(sources);
var applyCompiler = createUtilCompiler(sources);
try {
@ -71,15 +70,14 @@ public class IndirectPrograms extends AbstractPrograms {
setInstance(newInstance);
}
private static CompilationHarness<InstanceType<?>> createCullingCompiler(UniformComponent uniformComponent, ShaderSources sources) {
private static CompilationHarness<InstanceType<?>> createCullingCompiler(ShaderSources sources) {
return CULL.program()
.link(CULL.shader(GlslVersion.V460, ShaderType.COMPUTE)
.define("_FLW_SUBGROUP_SIZE", GlCompat.SUBGROUP_SIZE)
.withComponent(uniformComponent)
.withComponent(IndirectComponent::create)
.withResource(InstanceType::cullShader)
.withResource(CULL_SHADER_MAIN))
.then((key, program) -> program.setUniformBlockBinding("FlwUniforms", 0))
.then((key, program) -> program.setUniformBlockBinding("_FlwFrameUniforms", 0))
.harness(sources);
}

View file

@ -9,7 +9,6 @@ import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.api.context.Context;
import com.jozufozu.flywheel.api.instance.InstanceType;
import com.jozufozu.flywheel.backend.compile.component.UniformComponent;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import com.jozufozu.flywheel.backend.glsl.ShaderSources;
import com.jozufozu.flywheel.backend.glsl.SourceComponent;
@ -24,10 +23,10 @@ public class InstancingPrograms extends AbstractPrograms {
this.pipeline = pipeline;
}
static void reload(ShaderSources sources, ImmutableList<PipelineProgramKey> pipelineKeys, UniformComponent uniformComponent, List<SourceComponent> vertexComponents, List<SourceComponent> fragmentComponents) {
static void reload(ShaderSources sources, ImmutableList<PipelineProgramKey> pipelineKeys, List<SourceComponent> vertexComponents, List<SourceComponent> fragmentComponents) {
InstancingPrograms newInstance = null;
var pipelineCompiler = PipelineCompiler.create(sources, Pipelines.INSTANCED_ARRAYS, uniformComponent, vertexComponents, fragmentComponents);
var pipelineCompiler = PipelineCompiler.create(sources, Pipelines.INSTANCED_ARRAYS, vertexComponents, fragmentComponents);
try {
var pipelineResult = pipelineCompiler.compileAndReportErrors(pipelineKeys);

View file

@ -3,7 +3,6 @@ package com.jozufozu.flywheel.backend.compile;
import java.util.List;
import com.jozufozu.flywheel.backend.InternalVertex;
import com.jozufozu.flywheel.backend.compile.component.UniformComponent;
import com.jozufozu.flywheel.backend.compile.core.CompilationHarness;
import com.jozufozu.flywheel.backend.compile.core.Compile;
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
@ -13,10 +12,9 @@ import com.jozufozu.flywheel.backend.glsl.SourceComponent;
public class PipelineCompiler {
private static final Compile<PipelineProgramKey> PIPELINE = new Compile<>();
static CompilationHarness<PipelineProgramKey> create(ShaderSources sources, Pipeline pipeline, UniformComponent uniformComponent, List<SourceComponent> vertexComponents, List<SourceComponent> fragmentComponents) {
static CompilationHarness<PipelineProgramKey> create(ShaderSources sources, Pipeline pipeline, List<SourceComponent> vertexComponents, List<SourceComponent> fragmentComponents) {
return PIPELINE.program()
.link(PIPELINE.shader(pipeline.glslVersion(), ShaderType.VERTEX)
.withComponent(uniformComponent)
.withResource(pipeline.vertexApiImpl())
.withResource(InternalVertex.LAYOUT_SHADER)
.withComponent(key -> pipeline.assembler()
@ -29,7 +27,6 @@ public class PipelineCompiler {
.withResource(pipeline.vertexMain()))
.link(PIPELINE.shader(pipeline.glslVersion(), ShaderType.FRAGMENT)
.enableExtension("GL_ARB_conservative_depth")
.withComponent(uniformComponent)
.withResource(pipeline.fragmentApiImpl())
.withComponents(fragmentComponents)
.withResource(key -> key.contextShader()
@ -38,7 +35,8 @@ public class PipelineCompiler {
.then((key, program) -> {
key.contextShader()
.onProgramLink(program);
program.setUniformBlockBinding("FlwUniforms", 0);
program.setUniformBlockBinding("_FlwFrameUniforms", 0);
program.setUniformBlockBinding("_FlwFogUniforms", 1);
})
.harness(sources);
}

View file

@ -1,85 +0,0 @@
package com.jozufozu.flywheel.backend.compile.component;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.backend.compile.core.SourceLoader;
import com.jozufozu.flywheel.backend.glsl.SourceComponent;
import com.jozufozu.flywheel.backend.glsl.SourceFile;
import com.jozufozu.flywheel.backend.glsl.generate.GlslBuilder;
import net.minecraft.resources.ResourceLocation;
public class UniformComponent implements SourceComponent {
private final ResourceLocation name;
private final ImmutableList<SourceFile> uniformShaders;
public static Builder builder(ResourceLocation uniforms) {
return new Builder(uniforms);
}
private UniformComponent(ResourceLocation name, ImmutableList<SourceFile> uniformShaders) {
this.name = name;
this.uniformShaders = uniformShaders;
}
@Override
public Collection<? extends SourceComponent> included() {
return uniformShaders;
}
@Override
public String source() {
var builder = new GlslBuilder();
builder.uniformBlock()
.layout("std140")
.name("FlwUniforms")
.member("FlywheelUniforms", "flywheel");
builder.blankLine();
return builder.build();
}
@Override
public ResourceLocation name() {
return name;
}
public static class Builder {
private final ResourceLocation name;
private final List<ResourceLocation> uniformShaders = new ArrayList<>();
public Builder(ResourceLocation name) {
this.name = name;
}
public Builder sources(List<ResourceLocation> sources) {
this.uniformShaders.addAll(sources);
return this;
}
public UniformComponent build(SourceLoader sources) {
var out = ImmutableList.<SourceFile>builder();
boolean errored = false;
for (ResourceLocation uniformShader : uniformShaders) {
SourceFile sourceFile = sources.find(uniformShader);
if (sourceFile != null) {
out.add(sourceFile);
} else {
errored = true;
}
}
if (errored) {
return null;
}
return new UniformComponent(name, out.build());
}
}
}

View file

@ -1,129 +0,0 @@
package com.jozufozu.flywheel.backend.engine;
import java.util.List;
import java.util.Set;
import org.lwjgl.opengl.GL32;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.api.event.ReloadLevelRendererEvent;
import com.jozufozu.flywheel.api.uniform.ShaderUniforms;
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
import com.jozufozu.flywheel.lib.math.MoreMath;
import com.jozufozu.flywheel.lib.memory.MemoryBlock;
public class UniformBuffer {
// private static final int OFFSET_ALIGNMENT = GL32.glGetInteger(GL32.GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT);
// private static final int MAX_SIZE = GL32.glGetInteger(GL32.GL_MAX_UNIFORM_BLOCK_SIZE);
// private static final int MAX_BINDINGS = GL32.glGetInteger(GL32.GL_MAX_UNIFORM_BUFFER_BINDINGS);
// private static final boolean PO2_ALIGNMENT = Mth.isPowerOfTwo(OFFSET_ALIGNMENT);
private static UniformBuffer instance;
private final GlBuffer buffer;
private final ProviderSet providerSet;
private UniformBuffer() {
buffer = new GlBuffer();
providerSet = new ProviderSet(ShaderUniforms.REGISTRY.getAll());
}
public static UniformBuffer get() {
if (instance == null) {
instance = new UniformBuffer();
}
return instance;
}
public void sync() {
if (providerSet.pollUpdates()) {
buffer.upload(providerSet.data);
}
GL32.glBindBufferRange(GL32.GL_UNIFORM_BUFFER, 0, buffer.handle(), 0, providerSet.data.size());
}
private void delete() {
providerSet.delete();
buffer.delete();
}
public static void onReloadLevelRenderer(ReloadLevelRendererEvent event) {
if (instance != null) {
instance.delete();
instance = null;
}
}
// // https://stackoverflow.com/questions/3407012/rounding-up-to-the-nearest-multiple-of-a-number
// private static int alignUniformBuffer(int numToRound) {
// if (PO2_ALIGNMENT) {
// return (numToRound + OFFSET_ALIGNMENT - 1) & -OFFSET_ALIGNMENT;
// } else {
// return ((numToRound + OFFSET_ALIGNMENT - 1) / OFFSET_ALIGNMENT) * OFFSET_ALIGNMENT;
// }
// }
private static class LiveProvider {
private final ShaderUniforms shaderUniforms;
private final int offset;
private final int size;
private ShaderUniforms.Provider provider;
private LiveProvider(ShaderUniforms shaderUniforms, int offset, int size) {
this.shaderUniforms = shaderUniforms;
this.offset = offset;
this.size = size;
}
private void updatePtr(MemoryBlock bufferBase) {
if (provider != null) {
provider.delete();
}
provider = shaderUniforms.activate(bufferBase.ptr() + offset);
}
public boolean maybePoll() {
return provider != null && provider.poll();
}
}
private static class ProviderSet {
private final List<LiveProvider> allocatedProviders;
private final MemoryBlock data;
private ProviderSet(final Set<ShaderUniforms> providers) {
var builder = ImmutableList.<LiveProvider>builder();
int totalBytes = 0;
for (ShaderUniforms provider : providers) {
int size = MoreMath.align16(provider.byteSize());
builder.add(new LiveProvider(provider, totalBytes, size));
totalBytes += size;
}
allocatedProviders = builder.build();
data = MemoryBlock.mallocTracked(totalBytes);
for (LiveProvider p : allocatedProviders) {
p.updatePtr(data);
}
}
public boolean pollUpdates() {
boolean changed = false;
for (LiveProvider p : allocatedProviders) {
changed |= p.maybePoll();
}
return changed;
}
public void delete() {
allocatedProviders.forEach(p -> p.provider.delete());
data.free();
}
}
}

View file

@ -24,7 +24,7 @@ import com.jozufozu.flywheel.api.model.Mesh;
import com.jozufozu.flywheel.api.model.Model;
import com.jozufozu.flywheel.backend.compile.IndirectPrograms;
import com.jozufozu.flywheel.backend.engine.MaterialRenderState;
import com.jozufozu.flywheel.backend.engine.UniformBuffer;
import com.jozufozu.flywheel.backend.engine.uniform.Uniforms;
import com.jozufozu.flywheel.backend.gl.Driver;
import com.jozufozu.flywheel.backend.gl.GlCompat;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
@ -100,7 +100,7 @@ public class IndirectCullingGroup<I extends Instance> {
return;
}
UniformBuffer.get().sync();
Uniforms.bindFrame();
cullProgram.bind();
buffers.bindForCompute();
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
@ -184,7 +184,7 @@ public class IndirectCullingGroup<I extends Instance> {
return;
}
UniformBuffer.get().sync();
Uniforms.bindForDraw();
drawProgram.bind();
meshPool.bindForDraw();
buffers.bindForDraw();
@ -204,8 +204,7 @@ public class IndirectCullingGroup<I extends Instance> {
program.bind();
UniformBuffer.get()
.sync();
Uniforms.bindForDraw();
meshPool.bindForDraw();
buffers.bindForCrumbling();

View file

@ -11,6 +11,7 @@ import com.jozufozu.flywheel.backend.engine.AbstractEngine;
import com.jozufozu.flywheel.backend.engine.AbstractInstancer;
import com.jozufozu.flywheel.backend.engine.InstancerStorage;
import com.jozufozu.flywheel.backend.engine.MaterialRenderState;
import com.jozufozu.flywheel.backend.engine.uniform.Uniforms;
import com.jozufozu.flywheel.backend.gl.GlStateTracker;
import com.jozufozu.flywheel.backend.gl.GlTextureUnit;
import com.jozufozu.flywheel.lib.task.Flag;
@ -39,8 +40,9 @@ public class IndirectEngine extends AbstractEngine {
return SyncedPlan.of(this::flushDrawManager);
}
private void flushDrawManager() {
private void flushDrawManager(RenderContext ctx) {
try (var state = GlStateTracker.getRestoreState()) {
Uniforms.updateContext(ctx);
drawManager.flush();
}
flushFlag.raise();

View file

@ -13,7 +13,7 @@ import com.jozufozu.flywheel.backend.compile.InstancingPrograms;
import com.jozufozu.flywheel.backend.engine.CommonCrumbling;
import com.jozufozu.flywheel.backend.engine.InstanceHandleImpl;
import com.jozufozu.flywheel.backend.engine.MaterialRenderState;
import com.jozufozu.flywheel.backend.engine.UniformBuffer;
import com.jozufozu.flywheel.backend.engine.uniform.Uniforms;
import com.jozufozu.flywheel.backend.gl.GlStateTracker;
import com.jozufozu.flywheel.lib.context.Contexts;
import com.jozufozu.flywheel.lib.material.SimpleMaterial;
@ -51,7 +51,7 @@ public class InstancedCrumbling {
var program = programs.get(shader.instanceType(), Contexts.CRUMBLING);
program.bind();
UniformBuffer.get().sync();
Uniforms.bindForDraw();
InstancingEngine.uploadMaterialUniform(program, crumblingMaterial);
for (Int2ObjectMap.Entry<List<Runnable>> progressEntry : byProgress.int2ObjectEntrySet()) {

View file

@ -16,7 +16,7 @@ import com.jozufozu.flywheel.backend.engine.AbstractInstancer;
import com.jozufozu.flywheel.backend.engine.InstancerStorage;
import com.jozufozu.flywheel.backend.engine.MaterialEncoder;
import com.jozufozu.flywheel.backend.engine.MaterialRenderState;
import com.jozufozu.flywheel.backend.engine.UniformBuffer;
import com.jozufozu.flywheel.backend.engine.uniform.Uniforms;
import com.jozufozu.flywheel.backend.gl.GlStateTracker;
import com.jozufozu.flywheel.backend.gl.GlTextureUnit;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
@ -46,8 +46,9 @@ public class InstancingEngine extends AbstractEngine {
return SyncedPlan.of(this::flushDrawManager);
}
private void flushDrawManager() {
private void flushDrawManager(RenderContext ctx) {
try (var restoreState = GlStateTracker.getRestoreState()) {
Uniforms.updateContext(ctx);
drawManager.flush();
}
flushFlag.raise();
@ -118,7 +119,7 @@ public class InstancingEngine extends AbstractEngine {
var program = programs.get(shader.instanceType(), Contexts.DEFAULT);
program.bind();
UniformBuffer.get().sync();
Uniforms.bindForDraw();
uploadMaterialUniform(program, shader.material());
MaterialRenderState.setup(shader.material());

View file

@ -0,0 +1,27 @@
package com.jozufozu.flywheel.backend.engine.uniform;
import org.lwjgl.system.MemoryUtil;
import com.mojang.blaze3d.systems.RenderSystem;
public class FogUniforms implements UniformProvider {
public static final int SIZE = 28;
public int byteSize() {
return SIZE;
}
@Override
public void write(long ptr) {
var color = RenderSystem.getShaderFogColor();
MemoryUtil.memPutFloat(ptr, color[0]);
MemoryUtil.memPutFloat(ptr + 4, color[1]);
MemoryUtil.memPutFloat(ptr + 8, color[2]);
MemoryUtil.memPutFloat(ptr + 12, color[3]);
MemoryUtil.memPutFloat(ptr + 16, RenderSystem.getShaderFogStart());
MemoryUtil.memPutFloat(ptr + 20, RenderSystem.getShaderFogEnd());
MemoryUtil.memPutInt(ptr + 24, RenderSystem.getShaderFogShape()
.getIndex());
}
}

View file

@ -0,0 +1,61 @@
package com.jozufozu.flywheel.backend.engine.uniform;
import org.joml.Matrix4f;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.api.event.RenderContext;
import com.jozufozu.flywheel.api.visualization.VisualizationManager;
import com.jozufozu.flywheel.lib.math.MatrixMath;
import net.minecraft.core.Vec3i;
import net.minecraft.world.phys.Vec3;
public class FrameUniforms implements UniformProvider {
public static final int SIZE = 192;
private RenderContext context;
public int byteSize() {
return SIZE;
}
private final Matrix4f viewProjection = new Matrix4f();
public void setContext(RenderContext context) {
this.context = context;
}
@Override
public void write(long ptr) {
Vec3i renderOrigin = VisualizationManager.getOrThrow(context.level())
.getRenderOrigin();
Vec3 camera = context.camera()
.getPosition();
var camX = (float) (camera.x - renderOrigin.getX());
var camY = (float) (camera.y - renderOrigin.getY());
var camZ = (float) (camera.z - renderOrigin.getZ());
viewProjection.set(context.viewProjection());
viewProjection.translate(-camX, -camY, -camZ);
MatrixMath.writeUnsafe(viewProjection, ptr);
MemoryUtil.memPutFloat(ptr + 64, camX);
MemoryUtil.memPutFloat(ptr + 68, camY);
MemoryUtil.memPutFloat(ptr + 72, camZ);
MemoryUtil.memPutFloat(ptr + 76, 0f); // vec4 alignment
MemoryUtil.memPutInt(ptr + 80, getConstantAmbientLightFlag(context));
if (!Uniforms.frustumPaused || Uniforms.frustumCapture) {
MatrixMath.writePackedFrustumPlanes(ptr + 96, viewProjection);
Uniforms.frustumCapture = false;
}
}
private static int getConstantAmbientLightFlag(RenderContext context) {
var constantAmbientLight = context.level()
.effects()
.constantAmbientLight();
return constantAmbientLight ? 1 : 0;
}
}

View file

@ -0,0 +1,38 @@
package com.jozufozu.flywheel.backend.engine.uniform;
import org.lwjgl.opengl.GL32;
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferUsage;
import com.jozufozu.flywheel.lib.memory.MemoryBlock;
public class UniformBuffer<T extends UniformProvider> {
// private static final int MAX_SIZE = GL32.glGetInteger(GL32.GL_MAX_UNIFORM_BLOCK_SIZE);
private final int index;
private final GlBuffer buffer;
public final T provider;
private final MemoryBlock data;
public UniformBuffer(int index, T provider) {
this.index = index;
this.buffer = new GlBuffer(GlBufferUsage.DYNAMIC_DRAW);
this.provider = provider;
this.data = MemoryBlock.mallocTracked(provider.byteSize());
}
public void update() {
provider.write(data.ptr());
buffer.upload(data);
}
public void bind() {
GL32.glBindBufferRange(GL32.GL_UNIFORM_BUFFER, index, buffer.handle(), 0, data.size());
}
public void delete() {
data.free();
buffer.delete();
}
}

View file

@ -0,0 +1,7 @@
package com.jozufozu.flywheel.backend.engine.uniform;
public interface UniformProvider {
void write(long ptr);
int byteSize();
}

View file

@ -0,0 +1,60 @@
package com.jozufozu.flywheel.backend.engine.uniform;
import com.jozufozu.flywheel.api.event.ReloadLevelRendererEvent;
import com.jozufozu.flywheel.api.event.RenderContext;
public class Uniforms {
public static boolean frustumPaused = false;
public static boolean frustumCapture = false;
private static UniformBuffer<FrameUniforms> frame;
private static UniformBuffer<FogUniforms> fog;
public static UniformBuffer<FrameUniforms> frame() {
if (frame == null) {
frame = new UniformBuffer<>(0, new FrameUniforms());
}
return frame;
}
public static UniformBuffer<FogUniforms> fog() {
if (fog == null) {
fog = new UniformBuffer<>(1, new FogUniforms());
}
return fog;
}
public static void bindForDraw() {
bindFrame();
bindFog();
}
public static void bindFog() {
if (fog != null) {
fog.bind();
}
}
public static void bindFrame() {
if (frame != null) {
frame.bind();
}
}
public static void updateContext(RenderContext ctx) {
var ubo = frame();
ubo.provider.setContext(ctx);
ubo.update();
}
public static void onReloadLevelRenderer(ReloadLevelRendererEvent event) {
if (frame != null) {
frame.delete();
frame = null;
}
if (fog != null) {
fog.delete();
fog = null;
}
}
}

View file

@ -4,7 +4,7 @@ import java.util.function.BiConsumer;
import com.jozufozu.flywheel.api.backend.Backend;
import com.jozufozu.flywheel.api.backend.BackendManager;
import com.jozufozu.flywheel.lib.uniform.FlwShaderUniforms;
import com.jozufozu.flywheel.backend.engine.uniform.Uniforms;
import com.mojang.brigadier.Command;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
@ -135,18 +135,18 @@ public class FlwCommands {
command.then(Commands.literal("debugFrustum")
.then(Commands.literal("pause")
.executes(context -> {
FlwShaderUniforms.frustumPaused = true;
Uniforms.frustumPaused = true;
return 1;
}))
.then(Commands.literal("unpause")
.executes(context -> {
FlwShaderUniforms.frustumPaused = false;
Uniforms.frustumPaused = false;
return 1;
}))
.then(Commands.literal("capture")
.executes(context -> {
FlwShaderUniforms.frustumPaused = true;
FlwShaderUniforms.frustumCapture = true;
Uniforms.frustumPaused = true;
Uniforms.frustumCapture = true;
return 1;
})));

View file

@ -6,14 +6,12 @@ import java.util.Set;
import org.jetbrains.annotations.Unmodifiable;
import com.jozufozu.flywheel.api.registry.Registry;
import com.jozufozu.flywheel.api.uniform.ShaderUniforms;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import it.unimi.dsi.fastutil.objects.ObjectList;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import it.unimi.dsi.fastutil.objects.ObjectSet;
import it.unimi.dsi.fastutil.objects.ObjectSets;
import net.minecraft.resources.ResourceLocation;
public class RegistryImpl<T> implements Registry<T> {
private static final ObjectList<RegistryImpl<?>> ALL = new ObjectArrayList<>();
@ -31,20 +29,6 @@ public class RegistryImpl<T> implements Registry<T> {
return new RegistryImpl<>();
}
public static <T extends ShaderUniforms> Registry<T> createForShaderUniforms() {
return new RegistryImpl<>() {
private final ObjectSet<ResourceLocation> files = new ObjectOpenHashSet<>();
@Override
public void register(T object) {
if (!files.add(object.uniformShader())) {
throw new IllegalArgumentException();
}
super.register(object);
}
};
}
@Override
public void register(T object) {
if (frozen) {

View file

@ -1,134 +0,0 @@
package com.jozufozu.flywheel.lib.uniform;
import java.util.function.Consumer;
import org.joml.Matrix4f;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.api.event.BeginFrameEvent;
import com.jozufozu.flywheel.api.event.RenderContext;
import com.jozufozu.flywheel.api.uniform.ShaderUniforms;
import com.jozufozu.flywheel.api.visualization.VisualizationManager;
import com.jozufozu.flywheel.lib.math.MatrixMath;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.core.Vec3i;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.common.MinecraftForge;
public class FlwShaderUniforms implements ShaderUniforms {
public static final FlwShaderUniforms INSTANCE = ShaderUniforms.REGISTRY.registerAndGet(new FlwShaderUniforms());
public static final ResourceLocation FILE = Flywheel.rl("uniform/flywheel.glsl");
public static final int SIZE = 224;
public static boolean frustumPaused = false;
public static boolean frustumCapture = false;
public static boolean fogUpdate = true;
@Override
public int byteSize() {
return SIZE;
}
@Override
public ResourceLocation uniformShader() {
return FILE;
}
@Override
public Provider activate(long ptr) {
return new Active(ptr);
}
public static class Active implements Provider, Consumer<BeginFrameEvent> {
private final long ptr;
private boolean dirty;
private final Matrix4f viewProjection = new Matrix4f();
public Active(long ptr) {
this.ptr = ptr;
MinecraftForge.EVENT_BUS.addListener(this);
}
@Override
public void delete() {
MinecraftForge.EVENT_BUS.unregister(this);
}
@Override
public boolean poll() {
boolean updated = maybeUpdateFog();
updated |= dirty;
dirty = false;
return updated;
}
@Override
public void accept(BeginFrameEvent event) {
if (ptr == MemoryUtil.NULL) {
return;
}
RenderContext context = event.getContext();
Vec3i renderOrigin = VisualizationManager.getOrThrow(context.level())
.getRenderOrigin();
Vec3 camera = context.camera()
.getPosition();
var camX = (float) (camera.x - renderOrigin.getX());
var camY = (float) (camera.y - renderOrigin.getY());
var camZ = (float) (camera.z - renderOrigin.getZ());
viewProjection.set(context.viewProjection());
viewProjection.translate(-camX, -camY, -camZ);
MatrixMath.writeUnsafe(viewProjection, ptr + 32);
MemoryUtil.memPutFloat(ptr + 96, camX);
MemoryUtil.memPutFloat(ptr + 100, camY);
MemoryUtil.memPutFloat(ptr + 104, camZ);
MemoryUtil.memPutFloat(ptr + 108, 0f); // vec4 alignment
MemoryUtil.memPutInt(ptr + 112, getConstantAmbientLightFlag(context));
if (!frustumPaused || frustumCapture) {
MatrixMath.writePackedFrustumPlanes(ptr + 128, viewProjection);
frustumCapture = false;
}
dirty = true;
}
private static int getConstantAmbientLightFlag(RenderContext context) {
var constantAmbientLight = context.level()
.effects()
.constantAmbientLight();
return constantAmbientLight ? 1 : 0;
}
private boolean maybeUpdateFog() {
if (!fogUpdate || ptr == MemoryUtil.NULL) {
return false;
}
var color = RenderSystem.getShaderFogColor();
MemoryUtil.memPutFloat(ptr, color[0]);
MemoryUtil.memPutFloat(ptr + 4, color[1]);
MemoryUtil.memPutFloat(ptr + 8, color[2]);
MemoryUtil.memPutFloat(ptr + 12, color[3]);
MemoryUtil.memPutFloat(ptr + 16, RenderSystem.getShaderFogStart());
MemoryUtil.memPutFloat(ptr + 20, RenderSystem.getShaderFogEnd());
MemoryUtil.memPutInt(ptr + 24, RenderSystem.getShaderFogShape()
.getIndex());
fogUpdate = false;
return true;
}
}
}

View file

@ -6,7 +6,8 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.jozufozu.flywheel.lib.uniform.FlwShaderUniforms;
import com.jozufozu.flywheel.backend.engine.uniform.Uniforms;
import com.jozufozu.flywheel.backend.gl.GlStateTracker;
import net.minecraft.client.renderer.FogRenderer;
@ -14,7 +15,10 @@ import net.minecraft.client.renderer.FogRenderer;
abstract class FogUpdateMixin {
@Unique
private static void flywheel$updateFog() {
FlwShaderUniforms.fogUpdate = true;
try (var restoreState = GlStateTracker.getRestoreState()) {
Uniforms.fog()
.update();
}
}
@Inject(method = "setupNoFog()V", at = @At("RETURN"))

View file

@ -8,5 +8,5 @@ vec4 linearFog(vec4 color, float distance, float fogStart, float fogEnd, vec4 fo
}
vec4 flw_fogFilter(vec4 color) {
return linearFog(color, flw_distance, flywheel.fogRange.x, flywheel.fogRange.y, flywheel.fogColor);
return linearFog(color, flw_distance, flw_fogRange.x, flw_fogRange.y, flw_fogColor);
}

View file

@ -9,5 +9,5 @@ vec4 linearFogFade(vec4 color, float distance, float fogStart, float fogEnd) {
}
vec4 flw_fogFilter(vec4 color) {
return linearFogFade(color, flw_distance, flywheel.fogRange.x, flywheel.fogRange.y);
return linearFogFade(color, flw_distance, flw_fogRange.x, flw_fogRange.y);
}

View file

@ -31,7 +31,7 @@ void _flw_main() {
if (flw_fragDiffuse) {
float diffuseFactor;
if (flywheel.constantAmbientLight == 1) {
if (flw_constantAmbientLight == 1) {
diffuseFactor = diffuseNether(flw_vertexNormal);
} else {
diffuseFactor = diffuse(flw_vertexNormal);

View file

@ -15,6 +15,6 @@ void _flw_main(in FlwInstance instance) {
_flw_vertexDiffuse = uint(flw_vertexDiffuse);
flw_distance = fogDistance(flw_vertexPos.xyz, flywheel.cameraPos.xyz, flywheel.fogShape);
gl_Position = flywheel.viewProjection * flw_vertexPos;
flw_distance = fogDistance(flw_vertexPos.xyz, flw_cameraPos.xyz, flw_fogShape);
gl_Position = flw_viewProjection * flw_vertexPos;
}

View file

@ -1,4 +1,6 @@
#include "flywheel:internal/material.glsl"
#include "flywheel:internal/uniforms/frame.glsl"
#include "flywheel:internal/uniforms/fog.glsl"
in vec4 flw_vertexPos;
in vec4 flw_vertexColor;

View file

@ -1,4 +1,6 @@
#include "flywheel:internal/material.glsl"
#include "flywheel:internal/uniforms/frame.glsl"
#include "flywheel:internal/uniforms/fog.glsl"
// TODO: can we combine some of these internally to use fewer in/out slots?
out vec4 flw_vertexPos;

View file

@ -1,6 +1,7 @@
#include "flywheel:internal/indirect/buffers.glsl"
#include "flywheel:internal/indirect/model_descriptor.glsl"
#include "flywheel:internal/indirect/object.glsl"
#include "flywheel:internal/uniforms/frame.glsl"
layout(local_size_x = _FLW_SUBGROUP_SIZE) in;
@ -23,8 +24,8 @@ layout(std430, binding = _FLW_MODEL_BUFFER_BINDING) restrict buffer ModelBuffer
// com.jozufozu.flywheel.lib.math.MatrixMath.writePackedFrustumPlanes
// org.joml.FrustumIntersection.testSphere
bool _flw_testSphere(vec3 center, float radius) {
bvec4 xyInside = greaterThanEqual(fma(flywheel.planes.xyX, center.xxxx, fma(flywheel.planes.xyY, center.yyyy, fma(flywheel.planes.xyZ, center.zzzz, flywheel.planes.xyW))), -radius.xxxx);
bvec2 zInside = greaterThanEqual(fma(flywheel.planes.zX, center.xx, fma(flywheel.planes.zY, center.yy, fma(flywheel.planes.zZ, center.zz, flywheel.planes.zW))), -radius.xx);
bvec4 xyInside = greaterThanEqual(fma(flw_frustumPlanes.xyX, center.xxxx, fma(flw_frustumPlanes.xyY, center.yyyy, fma(flw_frustumPlanes.xyZ, center.zzzz, flw_frustumPlanes.xyW))), -radius.xxxx);
bvec2 zInside = greaterThanEqual(fma(flw_frustumPlanes.zX, center.xx, fma(flw_frustumPlanes.zY, center.yy, fma(flw_frustumPlanes.zZ, center.zz, flw_frustumPlanes.zW))), -radius.xx);
return all(xyInside) && all(zInside);
}

View file

@ -0,0 +1,5 @@
layout(std140) uniform _FlwFogUniforms {
vec4 flw_fogColor;
vec2 flw_fogRange;
int flw_fogShape;
};

View file

@ -0,0 +1,17 @@
struct FrustumPlanes {
vec4 xyX;// <nx.x, px.x, ny.x, py.x>
vec4 xyY;// <nx.y, px.y, ny.y, py.y>
vec4 xyZ;// <nx.z, px.z, ny.z, py.z>
vec4 xyW;// <nx.w, px.w, ny.w, py.w>
vec2 zX;// <nz.x, pz.x>
vec2 zY;// <nz.y, pz.y>
vec2 zZ;// <nz.z, pz.z>
vec2 zW;// <nz.w, pz.w>
};
layout(std140) uniform _FlwFrameUniforms {
mat4 flw_viewProjection;
vec4 flw_cameraPos;
int flw_constantAmbientLight;
FrustumPlanes flw_frustumPlanes;
};

View file

@ -1,20 +0,0 @@
struct FrustumPlanes {
vec4 xyX; // <nx.x, px.x, ny.x, py.x>
vec4 xyY; // <nx.y, px.y, ny.y, py.y>
vec4 xyZ; // <nx.z, px.z, ny.z, py.z>
vec4 xyW; // <nx.w, px.w, ny.w, py.w>
vec2 zX; // <nz.x, pz.x>
vec2 zY; // <nz.y, pz.y>
vec2 zZ; // <nz.z, pz.z>
vec2 zW; // <nz.w, pz.w>
};
struct FlywheelUniforms {
vec4 fogColor;
vec2 fogRange;
int fogShape;
mat4 viewProjection;
vec4 cameraPos;
int constantAmbientLight;
FrustumPlanes planes;
};