Finally have time off

- Attempt at componentizing ShaderCompiler, starting with Includer
 - Begin refactoring uniform providers
 - Context as an interface
 - Separate ContextSet objects for Pipeline shaders and Culling shaders
 - Inline ProgramAssembler
 - Replace StringUtil#trimEnd with String#stripTrailing
 - Add StringUtil#trimPrefix and #trimSuffix
This commit is contained in:
Jozufozu 2022-12-20 21:20:08 -08:00
parent ef568d22f7
commit 9a746f700d
26 changed files with 437 additions and 221 deletions

View file

@ -0,0 +1,12 @@
package com.jozufozu.flywheel.api.context;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import com.jozufozu.flywheel.core.source.FileResolution;
public interface Context {
void setup(GlProgram program);
FileResolution vertexShader();
FileResolution fragmentShader();
}

View file

@ -3,6 +3,20 @@ package com.jozufozu.flywheel.api.context;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import com.jozufozu.flywheel.core.source.FileResolution;
public record ContextShader(GlProgram.Factory factory, FileResolution vertexShader, FileResolution fragmentShader) {
public record ContextShader(GlProgram.Factory factory, FileResolution vertexShader,
FileResolution fragmentShader) implements Context {
@Override
public void setup(GlProgram program) {
}
@Override
public FileResolution vertexShader() {
return vertexShader;
}
@Override
public FileResolution fragmentShader() {
return fragmentShader;
}
}

View file

@ -8,15 +8,16 @@ public interface UniformProvider {
FileResolution uniformShader();
ActiveUniformProvider activate(long ptr, Notifier notifier);
ActiveUniformProvider activate(long ptr);
interface ActiveUniformProvider {
void delete();
void poll();
}
interface Notifier {
void signalChanged();
/**
* Poll the provider for changes.
*
* @return {@code true} if the provider updated its backing store.
*/
boolean poll();
}
}

View file

@ -16,7 +16,7 @@ public class GlProgram extends GlObject {
setHandle(handle);
}
// TODO: Programs bind the uniform buffers they need
// TODO: Programs bind the uniform buffers they need, no more GlProgram inheritance
public void bind() {
ProgramManager.glUseProgram(handle());
}

View file

@ -83,7 +83,8 @@ public class Compilation {
.toString()));
}
fullSource.append(source);
fullSource.append(source)
.append('\n');
}
private String sourceHeader(SourceFile sourceFile) {

View file

@ -1,5 +1,11 @@
package com.jozufozu.flywheel.backend.instancing.compile;
import static org.lwjgl.opengl.GL20.GL_LINK_STATUS;
import static org.lwjgl.opengl.GL20.GL_TRUE;
import static org.lwjgl.opengl.GL20.glGetProgramInfoLog;
import static org.lwjgl.opengl.GL20.glGetProgrami;
import static org.lwjgl.opengl.GL20.glLinkProgram;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@ -7,6 +13,7 @@ import java.util.stream.Stream;
import org.jetbrains.annotations.NotNull;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.gl.GLSLVersion;
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
import com.jozufozu.flywheel.core.source.SourceFile;
@ -17,9 +24,7 @@ public class CompileUtil {
public static final Pattern matType = Pattern.compile("^mat([234])(?:x([234]))?$");
public static String generateHeader(GLSLVersion version, ShaderType type) {
return version.getVersionLine()
+ type.getDefineStatement()
+ '\n';
return version.getVersionLine() + type.getDefineStatement() + '\n';
}
public static int getElementCount(String type) {
@ -53,10 +58,31 @@ public class CompileUtil {
return 1;
}
@NotNull
public static String generateDebugName(SourceFile... stages) {
return Stream.of(stages)
.map(SourceFile::toString)
.collect(Collectors.joining(" -> "));
}
@NotNull
public static String generateDebugName(SourceFile... stages) {
return Stream.of(stages)
.map(SourceFile::toString)
.collect(Collectors.joining(" -> "));
}
/**
* Check the program info log for errors.
*
* @param handle The handle of the program to check.
*/
public static void checkLinkLog(int handle) {
glLinkProgram(handle);
String log = glGetProgramInfoLog(handle);
if (!log.isEmpty()) {
Backend.LOGGER.debug("Program link log: " + log);
}
int result = glGetProgrami(handle, GL_LINK_STATUS);
if (result != GL_TRUE) {
throw new RuntimeException("Shader program linking failed, see log for details");
}
}
}

View file

@ -0,0 +1,6 @@
package com.jozufozu.flywheel.backend.instancing.compile;
import com.jozufozu.flywheel.api.struct.StructType;
public record CullingContext(StructType<?> structType) {
}

View file

@ -0,0 +1,38 @@
package com.jozufozu.flywheel.backend.instancing.compile;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.core.ComponentRegistry;
public class CullingContextSet {
static CullingContextSet create() {
var builder = new CullingContextSet();
for (StructType<?> structType : ComponentRegistry.structTypes) {
builder.add(structType);
}
return builder;
}
private final List<CullingContext> contexts = new ArrayList<>();
private final List<CullingContext> contextView = Collections.unmodifiableList(contexts);
CullingContextSet() {
}
public List<CullingContext> all() {
return contextView;
}
public int size() {
return contexts.size();
}
private void add(StructType<?> structType) {
var ctx = new CullingContext(structType);
contexts.add(ctx);
}
}

View file

@ -13,6 +13,7 @@ import com.jozufozu.flywheel.core.source.SourceLines;
import com.jozufozu.flywheel.core.source.error.ErrorBuilder;
import com.jozufozu.flywheel.core.source.span.Span;
import com.jozufozu.flywheel.util.ConsoleColors;
import com.jozufozu.flywheel.util.StringUtil;
public class FailedCompilation {
private static final Pattern ERROR_LINE = Pattern.compile("(\\d+)\\((\\d+)\\) : (.*)");
@ -49,7 +50,8 @@ public class FailedCompilation {
if (matcher.find()) {
int fileId = Integer.parseInt(matcher.group(1));
int lineNo = Integer.parseInt(matcher.group(2));
var msg = matcher.group(3);
var msg = StringUtil.trimPrefix(matcher.group(3), "error")
.stripLeading();
if (fileId == 0) {
return interpretGeneratedError(lineNo, msg);

View file

@ -1,5 +1,9 @@
package com.jozufozu.flywheel.backend.instancing.compile;
import static org.lwjgl.opengl.GL20.glAttachShader;
import static org.lwjgl.opengl.GL20.glCreateProgram;
import static org.lwjgl.opengl.GL20.glLinkProgram;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@ -21,9 +25,7 @@ import com.jozufozu.flywheel.backend.gl.GLSLVersion;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
import com.jozufozu.flywheel.backend.instancing.indirect.IndirectComponent;
import com.jozufozu.flywheel.core.BackendTypes;
import com.jozufozu.flywheel.core.ComponentRegistry;
import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.Pipelines;
import com.jozufozu.flywheel.core.SourceComponent;
import com.jozufozu.flywheel.core.pipeline.SimplePipeline;
@ -41,7 +43,9 @@ public class FlwCompiler {
private final ShaderSources sources;
private final MaterialAdapterComponent vertexMaterialComponent;
private final MaterialAdapterComponent fragmentMaterialComponent;
private final List<PipelineContext> pipelineContexts;
private final PipelineContextSet pipelineContexts;
private final CullingContextSet cullingContexts;
final ShaderCompiler shaderCompiler;
final Multimap<Set<UniformProvider>, PipelineContext> uniformProviderGroups = ArrayListMultimap.create();
@ -50,7 +54,10 @@ public class FlwCompiler {
final List<FailedCompilation> errors = new ArrayList<>();
public FlwCompiler(ShaderSources sources) {
this.shaderCompiler = new ShaderCompiler(errors::add);
this.shaderCompiler = ShaderCompiler.builder()
.errorConsumer(errors::add)
.build();
this.sources = sources;
this.vertexMaterialComponent = MaterialAdapterComponent.builder(Flywheel.rl("vertex_material_adapter"))
.materialSources(ComponentRegistry.materials.vertexSources())
@ -73,21 +80,24 @@ public class FlwCompiler {
.switchOn(GlslExpr.variable("flw_materialFragmentID"))
.build(sources);
this.pipelineContexts = buildPipelineSet();
this.pipelineContexts = PipelineContextSet.create();
this.cullingContexts = CullingContextSet.create();
// TODO: analyze uniform providers and group them into sets; break up this ctor
for (PipelineContext context : pipelineContexts) {
compilePipelineContext(context);
}
for (StructType<?> type : ComponentRegistry.structTypes) {
compileComputeCuller(type);
}
doCompilation();
finish();
}
private void doCompilation() {
for (var ctx : pipelineContexts.all()) {
compilePipelineContext(ctx);
}
for (var ctx : cullingContexts.all()) {
compileComputeCuller(ctx);
}
}
private void finish() {
long compileEnd = System.nanoTime();
int programCount = pipelineContexts.size() + ComponentRegistry.structTypes.size();
@ -132,22 +142,31 @@ public class FlwCompiler {
return;
}
pipelinePrograms.put(ctx, ctx.contextShader()
.factory()
.create(new ProgramAssembler().attachShader(vertex)
.attachShader(fragment)
.link()));
var glProgram = link(vertex.handle(), fragment.handle());
ctx.contextShader()
.setup(glProgram);
pipelinePrograms.put(ctx, glProgram);
}
private void compileComputeCuller(StructType<?> structType) {
var result = shaderCompiler.compile(GLSLVersion.V460, ShaderType.COMPUTE, getComputeComponents(structType));
private void compileComputeCuller(CullingContext ctx) {
var computeComponents = getComputeComponents(ctx.structType());
var result = shaderCompiler.compile(GLSLVersion.V460, ShaderType.COMPUTE, computeComponents);
if (result == null) {
return;
}
cullingPrograms.put(structType, new GlProgram(new ProgramAssembler().attachShader(result)
.link()));
cullingPrograms.put(ctx.structType(), link(result.handle()));
}
private GlProgram link(int... shaders) {
var handle = glCreateProgram();
for (var shader : shaders) {
glAttachShader(handle, shader);
}
glLinkProgram(handle);
CompileUtil.checkLinkLog(handle);
return new GlProgram(handle);
}
private ImmutableList<SourceComponent> getVertexComponents(PipelineContext ctx) {
@ -189,16 +208,4 @@ public class FlwCompiler {
return ImmutableList.of(instanceAssembly, instance, pipeline);
}
private static List<PipelineContext> buildPipelineSet() {
ImmutableList.Builder<PipelineContext> builder = ImmutableList.builder();
for (SimplePipeline pipelineShader : BackendTypes.availablePipelineShaders()) {
for (StructType<?> structType : ComponentRegistry.structTypes) {
for (VertexType vertexType : ComponentRegistry.vertexTypes) {
// TODO: context ubershaders, or not?
builder.add(new PipelineContext(vertexType, structType, Components.WORLD, pipelineShader));
}
}
}
return builder.build();
}
}

View file

@ -0,0 +1,21 @@
package com.jozufozu.flywheel.backend.instancing.compile;
import java.util.function.Consumer;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.core.SourceComponent;
/**
* A component of a ShaderCompiler, responsible for expanding root sources into the complete set of included sources.
*/
public interface Includer {
/**
* Expand the given root sources into the complete set of included sources.
* <p> Each unique source will be seen exactly once.
*
* @param rootSources The root sources to expand.
* @param out A consumer to which all sources should be passed in the order they should be included.
*/
void expand(ImmutableList<SourceComponent> rootSources, Consumer<SourceComponent> out);
}

View file

@ -0,0 +1,48 @@
package com.jozufozu.flywheel.backend.instancing.compile;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.jozufozu.flywheel.api.context.ContextShader;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.core.BackendTypes;
import com.jozufozu.flywheel.core.ComponentRegistry;
import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.pipeline.SimplePipeline;
public class PipelineContextSet {
static PipelineContextSet create() {
var builder = new PipelineContextSet();
for (SimplePipeline pipelineShader : BackendTypes.availablePipelineShaders()) {
for (StructType<?> structType : ComponentRegistry.structTypes) {
for (VertexType vertexType : ComponentRegistry.vertexTypes) {
builder.add(vertexType, structType, Components.WORLD, pipelineShader);
}
}
}
return builder;
}
private final List<PipelineContext> contexts = new ArrayList<>();
private final List<PipelineContext> contextView = Collections.unmodifiableList(contexts);
PipelineContextSet() {
}
public List<PipelineContext> all() {
return contextView;
}
public int size() {
return contexts.size();
}
private void add(VertexType vertexType, StructType<?> structType, ContextShader world, SimplePipeline pipelineShader) {
var ctx = new PipelineContext(vertexType, structType, world, pipelineShader);
contexts.add(ctx);
}
}

View file

@ -1,47 +0,0 @@
package com.jozufozu.flywheel.backend.instancing.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.glCreateProgram;
import static org.lwjgl.opengl.GL20.glGetProgramInfoLog;
import static org.lwjgl.opengl.GL20.glGetProgrami;
import static org.lwjgl.opengl.GL20.glLinkProgram;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.gl.shader.GlShader;
@Deprecated
public class ProgramAssembler {
private final int program;
public ProgramAssembler() {
this.program = glCreateProgram();
}
/**
* Links the attached shaders to this program.
*/
public int link() {
glLinkProgram(this.program);
String log = glGetProgramInfoLog(this.program);
if (!log.isEmpty()) {
Backend.LOGGER.debug("Program link log: " + log);
}
int result = glGetProgrami(this.program, GL_LINK_STATUS);
if (result != GL_TRUE) {
throw new RuntimeException("Shader program linking failed, see log for details");
}
return program;
}
public ProgramAssembler attachShader(GlShader glShader) {
glAttachShader(this.program, glShader.handle());
return this;
}
}

View file

@ -0,0 +1,38 @@
package com.jozufozu.flywheel.backend.instancing.compile;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.function.Consumer;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.core.SourceComponent;
public class RecursiveIncluder implements Includer {
public static final RecursiveIncluder INSTANCE = new RecursiveIncluder();
private RecursiveIncluder() {
}
@Override
public void expand(ImmutableList<SourceComponent> rootSources, Consumer<SourceComponent> out) {
var included = depthFirstInclude(rootSources);
included.forEach(out);
rootSources.forEach(out);
}
private static LinkedHashSet<SourceComponent> depthFirstInclude(ImmutableList<SourceComponent> root) {
var included = new LinkedHashSet<SourceComponent>(); // linked to preserve order
for (var component : root) {
recursiveDepthFirstInclude(included, component);
}
return included;
}
private static void recursiveDepthFirstInclude(Set<SourceComponent> included, SourceComponent component) {
for (var include : component.included()) {
recursiveDepthFirstInclude(included, include);
}
included.addAll(component.included());
}
}

View file

@ -1,10 +1,8 @@
package com.jozufozu.flywheel.backend.instancing.compile;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
import org.jetbrains.annotations.NotNull;
@ -15,13 +13,18 @@ import com.jozufozu.flywheel.backend.gl.GLSLVersion;
import com.jozufozu.flywheel.backend.gl.shader.GlShader;
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
import com.jozufozu.flywheel.core.SourceComponent;
import com.jozufozu.flywheel.util.FlwUtil;
public class ShaderCompiler {
private final Map<ShaderKey, CompilationResult> shaderCache = new HashMap<>();
private final Consumer<FailedCompilation> errorConsumer;
private final CompilationFactory factory;
private final Includer includer;
public ShaderCompiler(Consumer<FailedCompilation> errorConsumer) {
public ShaderCompiler(Consumer<FailedCompilation> errorConsumer, CompilationFactory factory, Includer includer) {
this.errorConsumer = errorConsumer;
this.factory = factory;
this.includer = includer;
}
public int shaderCount() {
@ -36,7 +39,7 @@ public class ShaderCompiler {
return cached.unwrap();
}
CompilationResult out = compileUncached(glslVersion, shaderType, sourceComponents);
CompilationResult out = compileUncached(factory.create(glslVersion, shaderType), sourceComponents);
shaderCache.put(key, out);
return unwrapAndReportError(out);
}
@ -60,40 +63,51 @@ public class ShaderCompiler {
}
@NotNull
private static CompilationResult compileUncached(GLSLVersion glslVersion, ShaderType shaderType, ImmutableList<SourceComponent> sourceComponents) {
var ctx = new Compilation(glslVersion, shaderType);
private CompilationResult compileUncached(Compilation ctx, ImmutableList<SourceComponent> sourceComponents) {
ctx.enableExtension("GL_ARB_explicit_attrib_location");
ctx.enableExtension("GL_ARB_conservative_depth");
for (var include : depthFirstInclude(sourceComponents)) {
ctx.appendComponent(include);
}
for (var component : sourceComponents) {
ctx.appendComponent(component);
ctx.addComponentName(component.name());
}
includer.expand(sourceComponents, ctx::appendComponent);
return ctx.compile();
}
private static Set<SourceComponent> depthFirstInclude(ImmutableList<SourceComponent> root) {
var included = new LinkedHashSet<SourceComponent>(); // linked to preserve order
for (var component : root) {
recursiveDepthFirstInclude(included, component);
}
return included;
}
private static void recursiveDepthFirstInclude(Set<SourceComponent> included, SourceComponent component) {
for (var include : component.included()) {
recursiveDepthFirstInclude(included, include);
}
included.addAll(component.included());
}
private record ShaderKey(GLSLVersion glslVersion, ShaderType shaderType,
ImmutableList<SourceComponent> sourceComponents) {
}
public static Builder builder() {
return new Builder();
}
@FunctionalInterface
public interface CompilationFactory {
Compilation create(GLSLVersion version, ShaderType shaderType);
}
public static class Builder {
private Consumer<FailedCompilation> errorConsumer = FlwUtil::noop;
private CompilationFactory factory = Compilation::new;
private Includer includer = RecursiveIncluder.INSTANCE;
public Builder errorConsumer(Consumer<FailedCompilation> errorConsumer) {
this.errorConsumer = errorConsumer;
return this;
}
public Builder compilationFactory(CompilationFactory factory) {
this.factory = factory;
return this;
}
public Builder includer(Includer includer) {
this.includer = includer;
return this;
}
public ShaderCompiler build() {
return new ShaderCompiler(errorConsumer, factory, includer);
}
}
}

View file

@ -7,7 +7,6 @@ import org.jetbrains.annotations.NotNull;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.core.source.span.CharPos;
import com.jozufozu.flywheel.util.StringUtil;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
@ -102,7 +101,8 @@ public class SourceLines implements CharSequence {
int start = lines.getInt(i - 1);
int end = lines.getInt(i);
builder.add(StringUtil.trimEnd(source.substring(start, end)));
builder.add(source.substring(start, end)
.stripTrailing());
}
return builder.build();

View file

@ -11,6 +11,10 @@ public class GlslBuilder {
add(new Define(name, value));
}
public void undef(String key) {
add(new Undef(key));
}
public GlslStruct struct() {
return add(new GlslStruct());
}
@ -32,6 +36,10 @@ public class GlslBuilder {
elements.add(Separators.BLANK_LINE);
}
public void _addRaw(String sourceString) {
elements.add(() -> sourceString);
}
public String build() {
return elements.stream()
.map(Declaration::prettyPrint)
@ -65,4 +73,11 @@ public class GlslBuilder {
}
}
public record Undef(String name) implements Declaration {
@Override
public String prettyPrint() {
return "#undef " + name;
}
}
}

View file

@ -22,18 +22,16 @@ public class FogProvider implements UniformProvider {
}
@Override
public ActiveUniformProvider activate(long ptr, Notifier notifier) {
return new Active(ptr, notifier);
public ActiveUniformProvider activate(long ptr) {
return new Active(ptr);
}
public static class Active implements ActiveUniformProvider {
private final long ptr;
private final Notifier notifier;
public Active(long ptr, Notifier notifier) {
public Active(long ptr) {
this.ptr = ptr;
this.notifier = notifier;
}
@Override
@ -41,9 +39,9 @@ public class FogProvider implements UniformProvider {
}
@Override
public void poll() {
public boolean poll() {
if (!FOG_UPDATE) {
return;
return false;
}
var color = RenderSystem.getShaderFogColor();
@ -57,9 +55,9 @@ public class FogProvider implements UniformProvider {
MemoryUtil.memPutInt(ptr + 24, RenderSystem.getShaderFogShape()
.getIndex());
notifier.signalChanged();
FOG_UPDATE = false;
return true;
}
}
}

View file

@ -31,18 +31,17 @@ public class FrustumProvider implements UniformProvider {
}
@Override
public ActiveUniformProvider activate(long ptr, Notifier notifier) {
return new Active(ptr, notifier);
public ActiveUniformProvider activate(long ptr) {
return new Active(ptr);
}
static class Active implements ActiveUniformProvider, Consumer<BeginFrameEvent> {
private final long ptr;
private final Notifier notifier;
private boolean dirty = true;
public Active(long ptr, Notifier notifier) {
public Active(long ptr) {
this.ptr = ptr;
this.notifier = notifier;
MinecraftForge.EVENT_BUS.addListener(this);
}
@ -52,8 +51,12 @@ public class FrustumProvider implements UniformProvider {
}
@Override
public void poll() {
public boolean poll() {
if (dirty) {
dirty = false;
return true;
}
return false;
}
@Override
@ -78,7 +81,7 @@ public class FrustumProvider implements UniformProvider {
shiftedCuller.getJozuPackedPlanes(ptr);
notifier.signalChanged();
dirty = true;
CAPTURE = false;
}
}

View file

@ -22,7 +22,7 @@ public class UniformBuffer {
private static final boolean PO2_ALIGNMENT = RenderMath.isPowerOf2(OFFSET_ALIGNMENT);
private static UniformBuffer instance;
private final List<Allocated> allocatedProviders;
private final AllocatedProviderSet providerSet;
public static UniformBuffer getInstance() {
if (instance == null) {
@ -32,50 +32,19 @@ public class UniformBuffer {
}
private final GlBuffer buffer;
private final MemoryBlock data;
private final BitSet changedBytes;
private UniformBuffer() {
buffer = new GlBuffer(GlBufferType.UNIFORM_BUFFER);
Collection<UniformProvider> providers = ComponentRegistry.getAllUniformProviders();
var builder = ImmutableList.<Allocated>builder();
int totalBytes = 0;
int index = 0;
for (UniformProvider provider : providers) {
int size = align16(provider.byteSize());
builder.add(new Allocated(provider, totalBytes, size, index));
totalBytes = alignUniformBuffer(totalBytes + size);
index++;
}
allocatedProviders = builder.build();
data = MemoryBlock.mallocTracked(totalBytes);
changedBytes = new BitSet(totalBytes);
for (Allocated p : allocatedProviders) {
p.updatePtr(data);
}
providerSet = new AllocatedProviderSet(ComponentRegistry.getAllUniformProviders());
}
public void sync() {
allocatedProviders.forEach(Allocated::pollActive);
if (changedBytes.isEmpty()) {
return;
}
providerSet.sync();
// TODO: upload only changed bytes
changedBytes.clear();
buffer.upload(data);
buffer.upload(providerSet.data);
int handle = buffer.handle();
for (Allocated p : allocatedProviders) {
for (Allocated p : providerSet.allocatedProviders) {
GL32.glBindBufferRange(GL32.GL_UNIFORM_BUFFER, p.index, handle, p.offset, p.size);
}
}
@ -93,7 +62,7 @@ public class UniformBuffer {
return (numToRound + 16 - 1) & -16;
}
private class Allocated implements UniformProvider.Notifier {
private static class Allocated {
private final UniformProvider provider;
private final int offset;
private final int size;
@ -107,20 +76,11 @@ public class UniformBuffer {
this.index = index;
}
@Override
public void signalChanged() {
changedBytes.set(offset, offset + size);
}
private void updatePtr(MemoryBlock bufferBase) {
if (activeProvider != null) {
activeProvider.delete();
}
activeProvider = provider.activate(bufferBase.ptr() + offset, this);
}
public UniformProvider provider() {
return provider;
activeProvider = provider.activate(bufferBase.ptr() + offset);
}
public int offset() {
@ -135,9 +95,46 @@ public class UniformBuffer {
return index;
}
public void pollActive() {
if (activeProvider != null) {
activeProvider.poll();
public boolean maybePoll() {
return activeProvider != null && activeProvider.poll();
}
}
private static class AllocatedProviderSet {
private final List<Allocated> allocatedProviders;
private final MemoryBlock data;
private final BitSet changedBytes;
private AllocatedProviderSet(final Collection<UniformProvider> providers) {
var builder = ImmutableList.<Allocated>builder();
int totalBytes = 0;
int index = 0;
for (UniformProvider provider : providers) {
int size = align16(provider.byteSize());
builder.add(new Allocated(provider, totalBytes, size, index));
totalBytes = alignUniformBuffer(totalBytes + size);
index++;
}
allocatedProviders = builder.build();
data = MemoryBlock.mallocTracked(totalBytes);
changedBytes = new BitSet(totalBytes);
for (Allocated p : allocatedProviders) {
p.updatePtr(data);
}
}
public void sync() {
for (Allocated p : allocatedProviders) {
if (p.maybePoll()) {
changedBytes.set(p.offset(), p.offset() + p.size());
}
}
}
}

View file

@ -32,17 +32,16 @@ public class ViewProvider implements UniformProvider {
}
@Override
public ActiveUniformProvider activate(long ptr, Notifier notifier) {
return new Active(ptr, notifier);
public ActiveUniformProvider activate(long ptr) {
return new Active(ptr);
}
public static class Active implements ActiveUniformProvider, Consumer<BeginFrameEvent> {
private final long ptr;
private final Notifier notifier;
private boolean dirty = true;
public Active(long ptr, Notifier notifier) {
public Active(long ptr) {
this.ptr = ptr;
this.notifier = notifier;
MinecraftForge.EVENT_BUS.addListener(this);
}
@ -52,7 +51,12 @@ public class ViewProvider implements UniformProvider {
}
@Override
public void poll() {
public boolean poll() {
if (dirty) {
dirty = false;
return true;
}
return false;
}
@Override
@ -85,7 +89,7 @@ public class ViewProvider implements UniformProvider {
MemoryUtil.memPutFloat(ptr + 72, camZ);
MemoryUtil.memPutInt(ptr + 76, constantAmbientLight);
notifier.signalChanged();
dirty = true;
}
}
}

View file

@ -70,6 +70,11 @@ public class FlwUtil {
}
public static <R> Stream<R> mapValues(Map<?, R> map) {
return map.values().stream();
return map.values()
.stream();
}
public static <T> void noop(T object) {
// noop
}
}

View file

@ -64,13 +64,20 @@ public class StringUtil {
.collect(Collectors.joining(", ")) + ')';
}
public static String trimEnd(String value) {
int len = value.length();
int st = 0;
while ((st < len) && Character.isWhitespace(value.charAt(len - 1))) {
len--;
public static String trimPrefix(String s, String prefix) {
if (s.startsWith(prefix)) {
return s.substring(prefix.length());
} else {
return s;
}
}
public static String trimSuffix(String s, String prefix) {
if (s.endsWith(prefix)) {
return s.substring(0, s.length() - prefix.length());
} else {
return s;
}
return value.substring(0, len);
}
/**

View file

@ -1,4 +1,6 @@
// TODO: Transform uniform shaders
vec4 flw_fogColor;
vec2 flw_fogRange;
int flw_fogShape;
// TODO: inject FLW_UNIFORM_BINDING definitions
layout(std140, binding = FLW_UNIFORM_BINDING) uniform flw_fog {
vec4 flw_fogColor;
vec2 flw_fogRange;
int flw_fogShape;
};

View file

@ -9,4 +9,6 @@ struct FLWPackedPlanes {
vec2 zW; // <nz.w, pz.w>
};
FLWPackedPlanes flw_planes;
layout(std140, binding = FLW_UNIFORM_BINDING) uniform FLWFrustum {
FLWPackedPlanes flw_planes;
};

View file

@ -1,3 +1,5 @@
mat4 flw_viewProjection;
vec4 flw_cameraPos;
int flw_constantAmbientLight;
layout(std140, binding = FLW_UNIFORM_BINDING) uniform flw_view {
mat4 flw_viewProjection;
vec4 flw_cameraPos;
int flw_constantAmbientLight;
};