Skip to content

Commit

Permalink
Reject eagerness return to lazy
Browse files Browse the repository at this point in the history
- Eagerly load ALL shaders in ShaderSources, resolving imports there
- Compile and cache programs on-demand
- Move gl state try blocks to EngineImpl
- EngineImpl catches shader exceptions and triggers a fallback
  • Loading branch information
Jozufozu committed Sep 21, 2024
1 parent cb58f60 commit 27e5b60
Show file tree
Hide file tree
Showing 13 changed files with 283 additions and 250 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.ImmutableList;

import dev.engine_room.flywheel.api.Flywheel;
import dev.engine_room.flywheel.api.instance.InstanceType;
import dev.engine_room.flywheel.api.material.LightShader;
import dev.engine_room.flywheel.backend.MaterialShaderIndices;
import dev.engine_room.flywheel.backend.compile.component.UberShaderComponent;
import dev.engine_room.flywheel.backend.compile.core.CompilerStats;
Expand Down Expand Up @@ -43,9 +39,11 @@ static void reload(ResourceManager resourceManager) {
var vertexComponentsHeader = loader.find(COMPONENTS_HEADER_VERT);
var fragmentComponentsHeader = loader.find(COMPONENTS_HEADER_FRAG);

// TODO: de-uber material shaders
var vertexMaterialComponent = createVertexMaterialComponent(loader);
var fragmentMaterialComponent = createFragmentMaterialComponent(loader);
var fogComponent = createFogComponent(loader);
// TODO: separate compilation for cutout OFF, but keep the rest uber'd
var cutoutComponent = createCutoutComponent(loader);

if (stats.errored() || vertexComponentsHeader == null || fragmentComponentsHeader == null || vertexMaterialComponent == null || fragmentMaterialComponent == null || fogComponent == null || cutoutComponent == null) {
Expand All @@ -57,21 +55,8 @@ static void reload(ResourceManager resourceManager) {
List<SourceComponent> vertexComponents = List.of(vertexComponentsHeader, vertexMaterialComponent);
List<SourceComponent> fragmentComponents = List.of(fragmentComponentsHeader, fragmentMaterialComponent, fogComponent, cutoutComponent);

var pipelineKeys = createPipelineKeys();
InstancingPrograms.reload(sources, pipelineKeys, vertexComponents, fragmentComponents);
IndirectPrograms.reload(sources, pipelineKeys, vertexComponents, fragmentComponents);
}

private static ImmutableList<PipelineProgramKey> createPipelineKeys() {
ImmutableList.Builder<PipelineProgramKey> builder = ImmutableList.builder();
for (ContextShader contextShader : ContextShader.values()) {
for (InstanceType<?> instanceType : InstanceType.REGISTRY) {
for (LightShader light : LightShader.REGISTRY.getAll()) {
builder.add(new PipelineProgramKey(instanceType, contextShader, light));
}
}
}
return builder.build();
InstancingPrograms.reload(sources, vertexComponents, fragmentComponents);
IndirectPrograms.reload(sources, vertexComponents, fragmentComponents);
}

@Nullable
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package dev.engine_room.flywheel.backend.compile;

import java.util.List;
import java.util.Map;

import org.jetbrains.annotations.Nullable;

Expand Down Expand Up @@ -43,11 +42,11 @@ public class IndirectPrograms extends AtomicReferenceCounted {
@Nullable
private static IndirectPrograms instance;

private final Map<PipelineProgramKey, GlProgram> pipeline;
private final Map<InstanceType<?>, GlProgram> culling;
private final Map<ResourceLocation, GlProgram> utils;
private final CompilationHarness<PipelineProgramKey> pipeline;
private final CompilationHarness<InstanceType<?>> culling;
private final CompilationHarness<ResourceLocation> utils;

private IndirectPrograms(Map<PipelineProgramKey, GlProgram> pipeline, Map<InstanceType<?>, GlProgram> culling, Map<ResourceLocation, GlProgram> utils) {
private IndirectPrograms(CompilationHarness<PipelineProgramKey> pipeline, CompilationHarness<InstanceType<?>> culling, CompilationHarness<ResourceLocation> utils) {
this.pipeline = pipeline;
this.culling = culling;
this.utils = utils;
Expand Down Expand Up @@ -81,32 +80,16 @@ private static List<String> getComputeExtensions(GlslVersion glslVersion) {
return extensions.build();
}

static void reload(ShaderSources sources, ImmutableList<PipelineProgramKey> pipelineKeys, List<SourceComponent> vertexComponents, List<SourceComponent> fragmentComponents) {
static void reload(ShaderSources sources, List<SourceComponent> vertexComponents, List<SourceComponent> fragmentComponents) {
if (!GlCompat.SUPPORTS_INDIRECT) {
return;
}

IndirectPrograms newInstance = null;

var pipelineCompiler = PipelineCompiler.create(sources, Pipelines.INDIRECT, vertexComponents, fragmentComponents, EXTENSIONS);
var cullingCompiler = createCullingCompiler(sources);
var utilCompiler = createUtilCompiler(sources);

try {
var pipelineResult = pipelineCompiler.compileAndReportErrors(pipelineKeys);
var cullingResult = cullingCompiler.compileAndReportErrors(createCullingKeys());
var utils = utilCompiler.compileAndReportErrors(UTIL_SHADERS);

if (pipelineResult != null && cullingResult != null && utils != null) {
newInstance = new IndirectPrograms(pipelineResult, cullingResult, utils);
}
} catch (Throwable t) {
FlwPrograms.LOGGER.error("Failed to compile indirect programs", t);
}

pipelineCompiler.delete();
cullingCompiler.delete();
utilCompiler.delete();
IndirectPrograms newInstance = new IndirectPrograms(pipelineCompiler, cullingCompiler, utilCompiler);

setInstance(newInstance);
}
Expand Down Expand Up @@ -142,14 +125,6 @@ private static CompilationHarness<ResourceLocation> createUtilCompiler(ShaderSou
.harness("utilities", sources);
}

private static ImmutableList<InstanceType<?>> createCullingKeys() {
ImmutableList.Builder<InstanceType<?>> builder = ImmutableList.builder();
for (InstanceType<?> instanceType : InstanceType.REGISTRY) {
builder.add(instanceType);
}
return builder.build();
}

static void setInstance(@Nullable IndirectPrograms newInstance) {
if (instance != null) {
instance.release();
Expand All @@ -169,6 +144,10 @@ public static boolean allLoaded() {
return instance != null;
}

public static void kill() {
setInstance(null);
}

public GlProgram getIndirectProgram(InstanceType<?> instanceType, ContextShader contextShader, LightShader light) {
return pipeline.get(new PipelineProgramKey(instanceType, contextShader, light));
}
Expand All @@ -195,11 +174,8 @@ public GlProgram getDownsampleSecondProgram() {

@Override
protected void _delete() {
pipeline.values()
.forEach(GlProgram::delete);
culling.values()
.forEach(GlProgram::delete);
utils.values()
.forEach(GlProgram::delete);
pipeline.delete();
culling.delete();
utils.delete();
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package dev.engine_room.flywheel.backend.compile;

import java.util.List;
import java.util.Map;

import org.jetbrains.annotations.Nullable;

import com.google.common.collect.ImmutableList;

import dev.engine_room.flywheel.api.instance.InstanceType;
import dev.engine_room.flywheel.api.material.LightShader;
import dev.engine_room.flywheel.backend.compile.core.CompilationHarness;
import dev.engine_room.flywheel.backend.gl.GlCompat;
import dev.engine_room.flywheel.backend.gl.shader.GlProgram;
import dev.engine_room.flywheel.backend.glsl.GlslVersion;
Expand All @@ -22,9 +22,9 @@ public class InstancingPrograms extends AtomicReferenceCounted {
@Nullable
private static InstancingPrograms instance;

private final Map<PipelineProgramKey, GlProgram> pipeline;
private final CompilationHarness<PipelineProgramKey> pipeline;

private InstancingPrograms(Map<PipelineProgramKey, GlProgram> pipeline) {
private InstancingPrograms(CompilationHarness<PipelineProgramKey> pipeline) {
this.pipeline = pipeline;
}

Expand All @@ -36,26 +36,14 @@ private static List<String> getExtensions(GlslVersion glslVersion) {
return extensions.build();
}

static void reload(ShaderSources sources, ImmutableList<PipelineProgramKey> pipelineKeys, List<SourceComponent> vertexComponents, List<SourceComponent> fragmentComponents) {
static void reload(ShaderSources sources, List<SourceComponent> vertexComponents, List<SourceComponent> fragmentComponents) {
if (!GlCompat.SUPPORTS_INSTANCING) {
return;
}

InstancingPrograms newInstance = null;

var pipelineCompiler = PipelineCompiler.create(sources, Pipelines.INSTANCING, vertexComponents, fragmentComponents, EXTENSIONS);

try {
var pipelineResult = pipelineCompiler.compileAndReportErrors(pipelineKeys);

if (pipelineResult != null) {
newInstance = new InstancingPrograms(pipelineResult);
}
} catch (Throwable t) {
FlwPrograms.LOGGER.error("Failed to compile instancing programs", t);
}

pipelineCompiler.delete();
InstancingPrograms newInstance = new InstancingPrograms(pipelineCompiler);

setInstance(newInstance);
}
Expand All @@ -79,13 +67,16 @@ public static boolean allLoaded() {
return instance != null;
}

public static void kill() {
setInstance(null);
}

public GlProgram get(InstanceType<?> instanceType, ContextShader contextShader, LightShader light) {
return pipeline.get(new PipelineProgramKey(instanceType, contextShader, light));
}

@Override
protected void _delete() {
pipeline.values()
.forEach(GlProgram::delete);
pipeline.delete();
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package dev.engine_room.flywheel.backend.compile.core;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

Expand All @@ -16,6 +15,8 @@ public class CompilationHarness<K> {
private final ProgramLinker programLinker;
private final CompilerStats stats;

private final Map<K, GlProgram> programs = new HashMap<>();

public CompilationHarness(String marker, ShaderSources sources, KeyCompiler<K> compiler) {
this.compiler = compiler;
stats = new CompilerStats(marker);
Expand All @@ -24,30 +25,27 @@ public CompilationHarness(String marker, ShaderSources sources, KeyCompiler<K> c
programLinker = new ProgramLinker(stats);
}

@Nullable
public Map<K, GlProgram> compileAndReportErrors(Collection<K> keys) {
stats.start();
Map<K, GlProgram> out = new HashMap<>();
for (var key : keys) {
GlProgram glProgram = compiler.compile(key, sourceLoader, shaderCache, programLinker);
if (out != null && glProgram != null) {
out.put(key, glProgram);
} else {
out = null; // Return null when a preloading error occurs.
}
}
stats.finish();
public GlProgram get(K key) {
return programs.computeIfAbsent(key, this::compile);
}

if (stats.errored()) {
stats.emitErrorLog();
return null;
private GlProgram compile(K key) {
var out = compiler.compile(key, sourceLoader, shaderCache, programLinker);

if (out == null) {
// TODO: populate exception with error details
throw new ShaderException();
}

return out;
}

public void delete() {
shaderCache.delete();

for (var program : programs.values()) {
program.delete();
}
}

public interface KeyCompiler<K> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package dev.engine_room.flywheel.backend.compile.core;

public class ShaderException extends RuntimeException {
}
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ public void delete() {
initializationQueue.clear();
}

public abstract void triggerFallback();

protected record UninitializedInstancer<N, I extends Instance>(InstancerKey<I> key, N instancer) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import dev.engine_room.flywheel.api.visualization.VisualEmbedding;
import dev.engine_room.flywheel.api.visualization.VisualType;
import dev.engine_room.flywheel.api.visualization.VisualizationContext;
import dev.engine_room.flywheel.backend.compile.core.ShaderException;
import dev.engine_room.flywheel.backend.engine.embed.EmbeddedEnvironment;
import dev.engine_room.flywheel.backend.engine.embed.Environment;
import dev.engine_room.flywheel.backend.engine.embed.EnvironmentStorage;
Expand Down Expand Up @@ -90,17 +91,27 @@ public void setupRender(RenderContext context) {
Uniforms.update(context);
environmentStorage.flush();
drawManager.flush(lightStorage, environmentStorage);
} catch (ShaderException e) {
triggerFallback();
}
}

@Override
public void render(RenderContext context, VisualType visualType) {
drawManager.render(visualType);
try (var state = GlStateTracker.getRestoreState()) {
drawManager.render(visualType);
} catch (ShaderException e) {
triggerFallback();
}
}

@Override
public void renderCrumbling(RenderContext context, List<CrumblingBlock> crumblingBlocks) {
drawManager.renderCrumbling(crumblingBlocks);
try (var state = GlStateTracker.getRestoreState()) {
drawManager.renderCrumbling(crumblingBlocks);
} catch (ShaderException e) {
triggerFallback();
}
}

@Override
Expand All @@ -110,6 +121,10 @@ public void delete() {
environmentStorage.delete();
}

private void triggerFallback() {
drawManager.triggerFallback();
}

public <I extends Instance> Instancer<I> instancer(Environment environment, InstanceType<I> type, Model model, VisualType visualType, int bias) {
return drawManager.getInstancer(environment, type, model, visualType, bias);
}
Expand Down
Loading

0 comments on commit 27e5b60

Please sign in to comment.