mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2024-11-14 22:43:56 +01:00
Merge branch '1.20/dev' into feat/multi-loader-1.21
This commit is contained in:
commit
a3bb585a15
@ -12,4 +12,8 @@ public interface Instance {
|
|||||||
default void delete() {
|
default void delete() {
|
||||||
handle().setDeleted();
|
handle().setDeleted();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default void setVisible(boolean visible) {
|
||||||
|
handle().setVisible(visible);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,11 +6,7 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
|
|
||||||
import dev.engine_room.flywheel.api.Flywheel;
|
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.MaterialShaderIndices;
|
||||||
import dev.engine_room.flywheel.backend.compile.component.UberShaderComponent;
|
import dev.engine_room.flywheel.backend.compile.component.UberShaderComponent;
|
||||||
import dev.engine_room.flywheel.backend.compile.core.CompilerStats;
|
import dev.engine_room.flywheel.backend.compile.core.CompilerStats;
|
||||||
@ -43,55 +39,20 @@ public final class FlwPrograms {
|
|||||||
var vertexComponentsHeader = loader.find(COMPONENTS_HEADER_VERT);
|
var vertexComponentsHeader = loader.find(COMPONENTS_HEADER_VERT);
|
||||||
var fragmentComponentsHeader = loader.find(COMPONENTS_HEADER_FRAG);
|
var fragmentComponentsHeader = loader.find(COMPONENTS_HEADER_FRAG);
|
||||||
|
|
||||||
var vertexMaterialComponent = createVertexMaterialComponent(loader);
|
|
||||||
var fragmentMaterialComponent = createFragmentMaterialComponent(loader);
|
|
||||||
var fogComponent = createFogComponent(loader);
|
var fogComponent = createFogComponent(loader);
|
||||||
var cutoutComponent = createCutoutComponent(loader);
|
|
||||||
|
|
||||||
if (stats.errored() || vertexComponentsHeader == null || fragmentComponentsHeader == null || vertexMaterialComponent == null || fragmentMaterialComponent == null || fogComponent == null || cutoutComponent == null) {
|
// TODO: separate compilation for cutout OFF, but keep the rest uber'd?
|
||||||
|
if (stats.errored() || vertexComponentsHeader == null || fragmentComponentsHeader == null || fogComponent == null) {
|
||||||
// Probably means the shader sources are missing.
|
// Probably means the shader sources are missing.
|
||||||
stats.emitErrorLog();
|
stats.emitErrorLog();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<SourceComponent> vertexComponents = List.of(vertexComponentsHeader, vertexMaterialComponent);
|
List<SourceComponent> vertexComponents = List.of(vertexComponentsHeader);
|
||||||
List<SourceComponent> fragmentComponents = List.of(fragmentComponentsHeader, fragmentMaterialComponent, fogComponent, cutoutComponent);
|
List<SourceComponent> fragmentComponents = List.of(fragmentComponentsHeader, fogComponent);
|
||||||
|
|
||||||
var pipelineKeys = createPipelineKeys();
|
InstancingPrograms.reload(sources, vertexComponents, fragmentComponents);
|
||||||
InstancingPrograms.reload(sources, pipelineKeys, vertexComponents, fragmentComponents);
|
IndirectPrograms.reload(sources, 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private static UberShaderComponent createVertexMaterialComponent(SourceLoader loader) {
|
|
||||||
return UberShaderComponent.builder(Flywheel.rl("material_vertex"))
|
|
||||||
.materialSources(MaterialShaderIndices.vertexSources()
|
|
||||||
.all())
|
|
||||||
.adapt(FnSignature.ofVoid("flw_materialVertex"))
|
|
||||||
.switchOn(GlslExpr.variable("_flw_uberMaterialVertexIndex"))
|
|
||||||
.build(loader);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private static UberShaderComponent createFragmentMaterialComponent(SourceLoader loader) {
|
|
||||||
return UberShaderComponent.builder(Flywheel.rl("material_fragment"))
|
|
||||||
.materialSources(MaterialShaderIndices.fragmentSources()
|
|
||||||
.all())
|
|
||||||
.adapt(FnSignature.ofVoid("flw_materialFragment"))
|
|
||||||
.switchOn(GlslExpr.variable("_flw_uberMaterialFragmentIndex"))
|
|
||||||
.build(loader);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package dev.engine_room.flywheel.backend.compile;
|
package dev.engine_room.flywheel.backend.compile;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
@ -9,7 +8,9 @@ import com.google.common.collect.ImmutableList;
|
|||||||
|
|
||||||
import dev.engine_room.flywheel.api.Flywheel;
|
import dev.engine_room.flywheel.api.Flywheel;
|
||||||
import dev.engine_room.flywheel.api.instance.InstanceType;
|
import dev.engine_room.flywheel.api.instance.InstanceType;
|
||||||
|
import dev.engine_room.flywheel.api.material.CutoutShader;
|
||||||
import dev.engine_room.flywheel.api.material.LightShader;
|
import dev.engine_room.flywheel.api.material.LightShader;
|
||||||
|
import dev.engine_room.flywheel.api.material.MaterialShaders;
|
||||||
import dev.engine_room.flywheel.backend.compile.component.InstanceStructComponent;
|
import dev.engine_room.flywheel.backend.compile.component.InstanceStructComponent;
|
||||||
import dev.engine_room.flywheel.backend.compile.component.SsboInstanceComponent;
|
import dev.engine_room.flywheel.backend.compile.component.SsboInstanceComponent;
|
||||||
import dev.engine_room.flywheel.backend.compile.core.CompilationHarness;
|
import dev.engine_room.flywheel.backend.compile.core.CompilationHarness;
|
||||||
@ -43,11 +44,11 @@ public class IndirectPrograms extends AtomicReferenceCounted {
|
|||||||
@Nullable
|
@Nullable
|
||||||
private static IndirectPrograms instance;
|
private static IndirectPrograms instance;
|
||||||
|
|
||||||
private final Map<PipelineProgramKey, GlProgram> pipeline;
|
private final CompilationHarness<PipelineProgramKey> pipeline;
|
||||||
private final Map<InstanceType<?>, GlProgram> culling;
|
private final CompilationHarness<InstanceType<?>> culling;
|
||||||
private final Map<ResourceLocation, GlProgram> utils;
|
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.pipeline = pipeline;
|
||||||
this.culling = culling;
|
this.culling = culling;
|
||||||
this.utils = utils;
|
this.utils = utils;
|
||||||
@ -81,32 +82,16 @@ public class IndirectPrograms extends AtomicReferenceCounted {
|
|||||||
return extensions.build();
|
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) {
|
if (!GlCompat.SUPPORTS_INDIRECT) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
IndirectPrograms newInstance = null;
|
|
||||||
|
|
||||||
var pipelineCompiler = PipelineCompiler.create(sources, Pipelines.INDIRECT, vertexComponents, fragmentComponents, EXTENSIONS);
|
var pipelineCompiler = PipelineCompiler.create(sources, Pipelines.INDIRECT, vertexComponents, fragmentComponents, EXTENSIONS);
|
||||||
var cullingCompiler = createCullingCompiler(sources);
|
var cullingCompiler = createCullingCompiler(sources);
|
||||||
var utilCompiler = createUtilCompiler(sources);
|
var utilCompiler = createUtilCompiler(sources);
|
||||||
|
|
||||||
try {
|
IndirectPrograms newInstance = new IndirectPrograms(pipelineCompiler, cullingCompiler, utilCompiler);
|
||||||
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();
|
|
||||||
|
|
||||||
setInstance(newInstance);
|
setInstance(newInstance);
|
||||||
}
|
}
|
||||||
@ -142,14 +127,6 @@ public class IndirectPrograms extends AtomicReferenceCounted {
|
|||||||
.harness("utilities", sources);
|
.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) {
|
static void setInstance(@Nullable IndirectPrograms newInstance) {
|
||||||
if (instance != null) {
|
if (instance != null) {
|
||||||
instance.release();
|
instance.release();
|
||||||
@ -169,8 +146,12 @@ public class IndirectPrograms extends AtomicReferenceCounted {
|
|||||||
return instance != null;
|
return instance != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GlProgram getIndirectProgram(InstanceType<?> instanceType, ContextShader contextShader, LightShader light) {
|
public static void kill() {
|
||||||
return pipeline.get(new PipelineProgramKey(instanceType, contextShader, light));
|
setInstance(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GlProgram getIndirectProgram(InstanceType<?> instanceType, ContextShader contextShader, LightShader light, CutoutShader cutout, MaterialShaders shaders) {
|
||||||
|
return pipeline.get(new PipelineProgramKey(instanceType, contextShader, light, cutout, shaders));
|
||||||
}
|
}
|
||||||
|
|
||||||
public GlProgram getCullingProgram(InstanceType<?> instanceType) {
|
public GlProgram getCullingProgram(InstanceType<?> instanceType) {
|
||||||
@ -195,11 +176,8 @@ public class IndirectPrograms extends AtomicReferenceCounted {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void _delete() {
|
protected void _delete() {
|
||||||
pipeline.values()
|
pipeline.delete();
|
||||||
.forEach(GlProgram::delete);
|
culling.delete();
|
||||||
culling.values()
|
utils.delete();
|
||||||
.forEach(GlProgram::delete);
|
|
||||||
utils.values()
|
|
||||||
.forEach(GlProgram::delete);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
package dev.engine_room.flywheel.backend.compile;
|
package dev.engine_room.flywheel.backend.compile;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
|
||||||
import dev.engine_room.flywheel.api.instance.InstanceType;
|
import dev.engine_room.flywheel.api.instance.InstanceType;
|
||||||
|
import dev.engine_room.flywheel.api.material.CutoutShader;
|
||||||
import dev.engine_room.flywheel.api.material.LightShader;
|
import dev.engine_room.flywheel.api.material.LightShader;
|
||||||
|
import dev.engine_room.flywheel.api.material.MaterialShaders;
|
||||||
|
import dev.engine_room.flywheel.backend.compile.core.CompilationHarness;
|
||||||
import dev.engine_room.flywheel.backend.gl.GlCompat;
|
import dev.engine_room.flywheel.backend.gl.GlCompat;
|
||||||
import dev.engine_room.flywheel.backend.gl.shader.GlProgram;
|
import dev.engine_room.flywheel.backend.gl.shader.GlProgram;
|
||||||
import dev.engine_room.flywheel.backend.glsl.GlslVersion;
|
import dev.engine_room.flywheel.backend.glsl.GlslVersion;
|
||||||
@ -22,9 +24,9 @@ public class InstancingPrograms extends AtomicReferenceCounted {
|
|||||||
@Nullable
|
@Nullable
|
||||||
private static InstancingPrograms instance;
|
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;
|
this.pipeline = pipeline;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,26 +38,14 @@ public class InstancingPrograms extends AtomicReferenceCounted {
|
|||||||
return extensions.build();
|
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) {
|
if (!GlCompat.SUPPORTS_INSTANCING) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
InstancingPrograms newInstance = null;
|
|
||||||
|
|
||||||
var pipelineCompiler = PipelineCompiler.create(sources, Pipelines.INSTANCING, vertexComponents, fragmentComponents, EXTENSIONS);
|
var pipelineCompiler = PipelineCompiler.create(sources, Pipelines.INSTANCING, vertexComponents, fragmentComponents, EXTENSIONS);
|
||||||
|
InstancingPrograms newInstance = new InstancingPrograms(pipelineCompiler);
|
||||||
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();
|
|
||||||
|
|
||||||
setInstance(newInstance);
|
setInstance(newInstance);
|
||||||
}
|
}
|
||||||
@ -79,13 +69,16 @@ public class InstancingPrograms extends AtomicReferenceCounted {
|
|||||||
return instance != null;
|
return instance != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GlProgram get(InstanceType<?> instanceType, ContextShader contextShader, LightShader light) {
|
public static void kill() {
|
||||||
return pipeline.get(new PipelineProgramKey(instanceType, contextShader, light));
|
setInstance(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GlProgram get(InstanceType<?> instanceType, ContextShader contextShader, LightShader light, CutoutShader cutout, MaterialShaders materialShaders) {
|
||||||
|
return pipeline.get(new PipelineProgramKey(instanceType, contextShader, light, cutout, materialShaders));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void _delete() {
|
protected void _delete() {
|
||||||
pipeline.values()
|
pipeline.delete();
|
||||||
.forEach(GlProgram::delete);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,9 +35,11 @@ public final class PipelineCompiler {
|
|||||||
var instance = ResourceUtil.toDebugFileNameNoExtension(key.instanceType()
|
var instance = ResourceUtil.toDebugFileNameNoExtension(key.instanceType()
|
||||||
.vertexShader());
|
.vertexShader());
|
||||||
|
|
||||||
|
var material = ResourceUtil.toDebugFileNameNoExtension(key.materialShaders()
|
||||||
|
.vertexSource());
|
||||||
var context = key.contextShader()
|
var context = key.contextShader()
|
||||||
.nameLowerCase();
|
.nameLowerCase();
|
||||||
return "pipeline/" + pipeline.compilerMarker() + "/" + instance + "_" + context;
|
return "pipeline/" + pipeline.compilerMarker() + "/" + instance + "/" + material + "_" + context;
|
||||||
})
|
})
|
||||||
.requireExtensions(extensions)
|
.requireExtensions(extensions)
|
||||||
.onCompile((key, comp) -> key.contextShader()
|
.onCompile((key, comp) -> key.contextShader()
|
||||||
@ -47,6 +49,8 @@ public final class PipelineCompiler {
|
|||||||
.withComponent(key -> new InstanceStructComponent(key.instanceType()))
|
.withComponent(key -> new InstanceStructComponent(key.instanceType()))
|
||||||
.withResource(key -> key.instanceType()
|
.withResource(key -> key.instanceType()
|
||||||
.vertexShader())
|
.vertexShader())
|
||||||
|
.withResource(key -> key.materialShaders()
|
||||||
|
.vertexSource())
|
||||||
.withComponents(vertexComponents)
|
.withComponents(vertexComponents)
|
||||||
.withResource(InternalVertex.LAYOUT_SHADER)
|
.withResource(InternalVertex.LAYOUT_SHADER)
|
||||||
.withComponent(key -> pipeline.assembler()
|
.withComponent(key -> pipeline.assembler()
|
||||||
@ -56,8 +60,16 @@ public final class PipelineCompiler {
|
|||||||
.nameMapper(key -> {
|
.nameMapper(key -> {
|
||||||
var context = key.contextShader()
|
var context = key.contextShader()
|
||||||
.nameLowerCase();
|
.nameLowerCase();
|
||||||
return "pipeline/" + pipeline.compilerMarker() + "/" + ResourceUtil.toDebugFileNameNoExtension(key.light()
|
|
||||||
.source()) + "_" + context;
|
var material = ResourceUtil.toDebugFileNameNoExtension(key.materialShaders()
|
||||||
|
.fragmentSource());
|
||||||
|
|
||||||
|
var cutout = ResourceUtil.toDebugFileNameNoExtension(key.cutout()
|
||||||
|
.source());
|
||||||
|
|
||||||
|
var light = ResourceUtil.toDebugFileNameNoExtension(key.light()
|
||||||
|
.source());
|
||||||
|
return "pipeline/" + pipeline.compilerMarker() + "/frag/" + material + "/" + light + "_" + cutout + "_" + context;
|
||||||
})
|
})
|
||||||
.requireExtensions(extensions)
|
.requireExtensions(extensions)
|
||||||
.enableExtension("GL_ARB_conservative_depth")
|
.enableExtension("GL_ARB_conservative_depth")
|
||||||
@ -65,9 +77,13 @@ public final class PipelineCompiler {
|
|||||||
.onCompile(comp))
|
.onCompile(comp))
|
||||||
.onCompile((key, comp) -> lightSmoothness.onCompile(comp))
|
.onCompile((key, comp) -> lightSmoothness.onCompile(comp))
|
||||||
.withResource(API_IMPL_FRAG)
|
.withResource(API_IMPL_FRAG)
|
||||||
|
.withResource(key -> key.materialShaders()
|
||||||
|
.fragmentSource())
|
||||||
.withComponents(fragmentComponents)
|
.withComponents(fragmentComponents)
|
||||||
.withResource(key -> key.light()
|
.withResource(key -> key.light()
|
||||||
.source())
|
.source())
|
||||||
|
.withResource(key -> key.cutout()
|
||||||
|
.source())
|
||||||
.withResource(pipeline.fragmentMain()))
|
.withResource(pipeline.fragmentMain()))
|
||||||
.preLink((key, program) -> {
|
.preLink((key, program) -> {
|
||||||
program.bindAttribLocation("_flw_aPos", 0);
|
program.bindAttribLocation("_flw_aPos", 0);
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package dev.engine_room.flywheel.backend.compile;
|
package dev.engine_room.flywheel.backend.compile;
|
||||||
|
|
||||||
import dev.engine_room.flywheel.api.instance.InstanceType;
|
import dev.engine_room.flywheel.api.instance.InstanceType;
|
||||||
|
import dev.engine_room.flywheel.api.material.CutoutShader;
|
||||||
import dev.engine_room.flywheel.api.material.LightShader;
|
import dev.engine_room.flywheel.api.material.LightShader;
|
||||||
|
import dev.engine_room.flywheel.api.material.MaterialShaders;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the entire context of a program's usage.
|
* Represents the entire context of a program's usage.
|
||||||
@ -10,5 +12,6 @@ import dev.engine_room.flywheel.api.material.LightShader;
|
|||||||
* @param contextShader The context shader to use.
|
* @param contextShader The context shader to use.
|
||||||
* @param light The light shader to use.
|
* @param light The light shader to use.
|
||||||
*/
|
*/
|
||||||
public record PipelineProgramKey(InstanceType<?> instanceType, ContextShader contextShader, LightShader light) {
|
public record PipelineProgramKey(InstanceType<?> instanceType, ContextShader contextShader, LightShader light,
|
||||||
|
CutoutShader cutout, MaterialShaders materialShaders) {
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package dev.engine_room.flywheel.backend.compile.core;
|
package dev.engine_room.flywheel.backend.compile.core;
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@ -16,6 +15,8 @@ public class CompilationHarness<K> {
|
|||||||
private final ProgramLinker programLinker;
|
private final ProgramLinker programLinker;
|
||||||
private final CompilerStats stats;
|
private final CompilerStats stats;
|
||||||
|
|
||||||
|
private final Map<K, GlProgram> programs = new HashMap<>();
|
||||||
|
|
||||||
public CompilationHarness(String marker, ShaderSources sources, KeyCompiler<K> compiler) {
|
public CompilationHarness(String marker, ShaderSources sources, KeyCompiler<K> compiler) {
|
||||||
this.compiler = compiler;
|
this.compiler = compiler;
|
||||||
stats = new CompilerStats(marker);
|
stats = new CompilerStats(marker);
|
||||||
@ -24,23 +25,16 @@ public class CompilationHarness<K> {
|
|||||||
programLinker = new ProgramLinker(stats);
|
programLinker = new ProgramLinker(stats);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
public GlProgram get(K key) {
|
||||||
public Map<K, GlProgram> compileAndReportErrors(Collection<K> keys) {
|
return programs.computeIfAbsent(key, this::compile);
|
||||||
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();
|
|
||||||
|
|
||||||
if (stats.errored()) {
|
private GlProgram compile(K key) {
|
||||||
stats.emitErrorLog();
|
var out = compiler.compile(key, sourceLoader, shaderCache, programLinker);
|
||||||
return null;
|
|
||||||
|
if (out == null) {
|
||||||
|
// TODO: populate exception with error details
|
||||||
|
throw new ShaderException();
|
||||||
}
|
}
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
@ -48,6 +42,10 @@ public class CompilationHarness<K> {
|
|||||||
|
|
||||||
public void delete() {
|
public void delete() {
|
||||||
shaderCache.delete();
|
shaderCache.delete();
|
||||||
|
|
||||||
|
for (var program : programs.values()) {
|
||||||
|
program.delete();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface KeyCompiler<K> {
|
public interface KeyCompiler<K> {
|
||||||
|
@ -12,12 +12,14 @@ import java.util.function.Function;
|
|||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import dev.engine_room.flywheel.backend.compile.FlwPrograms;
|
||||||
import dev.engine_room.flywheel.backend.gl.shader.GlProgram;
|
import dev.engine_room.flywheel.backend.gl.shader.GlProgram;
|
||||||
import dev.engine_room.flywheel.backend.gl.shader.GlShader;
|
import dev.engine_room.flywheel.backend.gl.shader.GlShader;
|
||||||
import dev.engine_room.flywheel.backend.gl.shader.ShaderType;
|
import dev.engine_room.flywheel.backend.gl.shader.ShaderType;
|
||||||
import dev.engine_room.flywheel.backend.glsl.GlslVersion;
|
import dev.engine_room.flywheel.backend.glsl.GlslVersion;
|
||||||
import dev.engine_room.flywheel.backend.glsl.ShaderSources;
|
import dev.engine_room.flywheel.backend.glsl.ShaderSources;
|
||||||
import dev.engine_room.flywheel.backend.glsl.SourceComponent;
|
import dev.engine_room.flywheel.backend.glsl.SourceComponent;
|
||||||
|
import dev.engine_room.flywheel.lib.util.StringUtil;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -122,6 +124,8 @@ public class Compile<K> {
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private GlShader compile(K key, ShaderCache compiler, SourceLoader loader) {
|
private GlShader compile(K key, ShaderCache compiler, SourceLoader loader) {
|
||||||
|
long start = System.nanoTime();
|
||||||
|
|
||||||
var components = new ArrayList<SourceComponent>();
|
var components = new ArrayList<SourceComponent>();
|
||||||
boolean ok = true;
|
boolean ok = true;
|
||||||
for (var fetcher : fetchers) {
|
for (var fetcher : fetchers) {
|
||||||
@ -137,7 +141,14 @@ public class Compile<K> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Consumer<Compilation> cb = ctx -> compilationCallbacks.accept(key, ctx);
|
Consumer<Compilation> cb = ctx -> compilationCallbacks.accept(key, ctx);
|
||||||
return compiler.compile(glslVersion, shaderType, nameMapper.apply(key), cb, components);
|
var name = nameMapper.apply(key);
|
||||||
|
var out = compiler.compile(glslVersion, shaderType, name, cb, components);
|
||||||
|
|
||||||
|
long end = System.nanoTime();
|
||||||
|
|
||||||
|
FlwPrograms.LOGGER.debug("Compiled {} in {}", name, StringUtil.formatTime(end - start));
|
||||||
|
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,6 +188,8 @@ public class Compile<K> {
|
|||||||
throw new IllegalStateException("No shader compilers were added!");
|
throw new IllegalStateException("No shader compilers were added!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long start = System.nanoTime();
|
||||||
|
|
||||||
List<GlShader> shaders = new ArrayList<>();
|
List<GlShader> shaders = new ArrayList<>();
|
||||||
|
|
||||||
boolean ok = true;
|
boolean ok = true;
|
||||||
@ -198,6 +211,10 @@ public class Compile<K> {
|
|||||||
postLink.accept(key, out);
|
postLink.accept(key, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long end = System.nanoTime();
|
||||||
|
|
||||||
|
FlwPrograms.LOGGER.debug("Linked {} in {}", key, StringUtil.formatTime(end - start));
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
package dev.engine_room.flywheel.backend.compile.core;
|
||||||
|
|
||||||
|
public class ShaderException extends RuntimeException {
|
||||||
|
}
|
@ -1,7 +1,6 @@
|
|||||||
package dev.engine_room.flywheel.backend.engine;
|
package dev.engine_room.flywheel.backend.engine;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
@ -11,10 +10,10 @@ import dev.engine_room.flywheel.api.instance.Instancer;
|
|||||||
import dev.engine_room.flywheel.backend.engine.embed.Environment;
|
import dev.engine_room.flywheel.backend.engine.embed.Environment;
|
||||||
import dev.engine_room.flywheel.backend.util.AtomicBitSet;
|
import dev.engine_room.flywheel.backend.util.AtomicBitSet;
|
||||||
|
|
||||||
public abstract class AbstractInstancer<I extends Instance> implements Instancer<I> {
|
public abstract class AbstractInstancer<I extends Instance> implements Instancer<I>, InstanceHandleImpl.State<I> {
|
||||||
public final InstanceType<I> type;
|
public final InstanceType<I> type;
|
||||||
public final Environment environment;
|
public final Environment environment;
|
||||||
private final Supplier<AbstractInstancer<I>> recreate;
|
private final Recreate<I> recreate;
|
||||||
|
|
||||||
// Lock for all instances, only needs to be used in methods that may run on the TaskExecutor.
|
// Lock for all instances, only needs to be used in methods that may run on the TaskExecutor.
|
||||||
protected final Object lock = new Object();
|
protected final Object lock = new Object();
|
||||||
@ -24,28 +23,60 @@ public abstract class AbstractInstancer<I extends Instance> implements Instancer
|
|||||||
protected final AtomicBitSet changed = new AtomicBitSet();
|
protected final AtomicBitSet changed = new AtomicBitSet();
|
||||||
protected final AtomicBitSet deleted = new AtomicBitSet();
|
protected final AtomicBitSet deleted = new AtomicBitSet();
|
||||||
|
|
||||||
protected AbstractInstancer(InstancerKey<I> key, Supplier<AbstractInstancer<I>> recreate) {
|
protected AbstractInstancer(InstancerKey<I> key, Recreate<I> recreate) {
|
||||||
this.type = key.type();
|
this.type = key.type();
|
||||||
this.environment = key.environment();
|
this.environment = key.environment();
|
||||||
this.recreate = recreate;
|
this.recreate = recreate;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public I createInstance() {
|
public InstanceHandleImpl.State<I> setChanged(int index) {
|
||||||
synchronized (lock) {
|
notifyDirty(index);
|
||||||
var i = instances.size();
|
return this;
|
||||||
var handle = new InstanceHandleImpl<I>();
|
}
|
||||||
handle.instancer = this;
|
|
||||||
handle.recreate = recreate;
|
|
||||||
handle.index = i;
|
|
||||||
I instance = type.create(handle);
|
|
||||||
handle.instance = instance;
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InstanceHandleImpl.State<I> setDeleted(int index) {
|
||||||
|
notifyRemoval(index);
|
||||||
|
return InstanceHandleImpl.Deleted.instance();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InstanceHandleImpl.State<I> setVisible(InstanceHandleImpl<I> handle, int index, boolean visible) {
|
||||||
|
if (visible) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyRemoval(index);
|
||||||
|
|
||||||
|
I instance;
|
||||||
|
synchronized (lock) {
|
||||||
|
// I think we need to lock to prevent wacky stuff from happening if the array gets resized.
|
||||||
|
instance = instances.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new InstanceHandleImpl.Hidden<>(recreate, instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public I createInstance() {
|
||||||
|
var handle = new InstanceHandleImpl<>(this);
|
||||||
|
I instance = type.create(handle);
|
||||||
|
|
||||||
|
synchronized (lock) {
|
||||||
|
handle.index = instances.size();
|
||||||
addLocked(instance, handle);
|
addLocked(instance, handle);
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void revealInstance(InstanceHandleImpl<I> handle, I instance) {
|
||||||
|
synchronized (lock) {
|
||||||
|
handle.index = instances.size();
|
||||||
|
addLocked(instance, handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stealInstance(@Nullable I instance) {
|
public void stealInstance(@Nullable I instance) {
|
||||||
if (instance == null) {
|
if (instance == null) {
|
||||||
@ -60,9 +91,19 @@ public abstract class AbstractInstancer<I extends Instance> implements Instancer
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Should InstanceType have an isInstance method?
|
// Should InstanceType have an isInstance method?
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
var handle = (InstanceHandleImpl<I>) instanceHandle;
|
var handle = (InstanceHandleImpl<I>) instanceHandle;
|
||||||
|
|
||||||
if (handle.instancer == this && handle.visible) {
|
// No need to steal if this instance is already owned by this instancer.
|
||||||
|
if (handle.state == this) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Not allowed to steal deleted instances.
|
||||||
|
if (handle.state instanceof InstanceHandleImpl.Deleted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// No need to steal if the instance will recreate to us.
|
||||||
|
if (handle.state instanceof InstanceHandleImpl.Hidden<I> hidden && recreate.equals(hidden.recreate())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,21 +111,21 @@ public abstract class AbstractInstancer<I extends Instance> implements Instancer
|
|||||||
// is somehow being stolen by 2 different instancers between threads.
|
// is somehow being stolen by 2 different instancers between threads.
|
||||||
// That seems kinda impossible so I'm fine leaving it as is for now.
|
// That seems kinda impossible so I'm fine leaving it as is for now.
|
||||||
|
|
||||||
// Remove the instance from its old instancer.
|
|
||||||
// This won't have any unwanted effect when the old instancer
|
|
||||||
// is filtering deleted instances later, so is safe.
|
|
||||||
handle.setDeleted();
|
|
||||||
|
|
||||||
// Add the instance to this instancer.
|
// Add the instance to this instancer.
|
||||||
handle.instancer = this;
|
if (handle.state instanceof AbstractInstancer<I> other) {
|
||||||
handle.recreate = recreate;
|
// Remove the instance from its old instancer.
|
||||||
|
// This won't have any unwanted effect when the old instancer
|
||||||
|
// is filtering deleted instances later, so is safe.
|
||||||
|
other.notifyRemoval(handle.index);
|
||||||
|
|
||||||
if (handle.visible) {
|
handle.state = this;
|
||||||
// Only lock now that we'll be mutating our state.
|
// Only lock now that we'll be mutating our state.
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
handle.index = instances.size();
|
handle.index = instances.size();
|
||||||
addLocked(instance, handle);
|
addLocked(instance, handle);
|
||||||
}
|
}
|
||||||
|
} else if (handle.state instanceof InstanceHandleImpl.Hidden<I>) {
|
||||||
|
handle.state = new InstanceHandleImpl.Hidden<>(recreate, instance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,7 +135,7 @@ public abstract class AbstractInstancer<I extends Instance> implements Instancer
|
|||||||
private void addLocked(I instance, InstanceHandleImpl<I> handle) {
|
private void addLocked(I instance, InstanceHandleImpl<I> handle) {
|
||||||
instances.add(instance);
|
instances.add(instance);
|
||||||
handles.add(handle);
|
handles.add(handle);
|
||||||
changed.set(handle.index);
|
setIndexChanged(handle.index);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int instanceCount() {
|
public int instanceCount() {
|
||||||
@ -105,6 +146,10 @@ public abstract class AbstractInstancer<I extends Instance> implements Instancer
|
|||||||
if (index < 0 || index >= instanceCount()) {
|
if (index < 0 || index >= instanceCount()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
setIndexChanged(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setIndexChanged(int index) {
|
||||||
changed.set(index);
|
changed.set(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,8 +228,9 @@ public abstract class AbstractInstancer<I extends Instance> implements Instancer
|
|||||||
// clearing it here would cause significant visual artifacts and instance leaks.
|
// clearing it here would cause significant visual artifacts and instance leaks.
|
||||||
// At the same time, we need to clear handles we own to prevent
|
// At the same time, we need to clear handles we own to prevent
|
||||||
// instances from changing/deleting positions in this instancer that no longer exist.
|
// instances from changing/deleting positions in this instancer that no longer exist.
|
||||||
if (handle.instancer == this) {
|
if (handle.state == this) {
|
||||||
handle.clear();
|
handle.clear();
|
||||||
|
handle.state = InstanceHandleImpl.Deleted.instance();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
instances.clear();
|
instances.clear();
|
||||||
@ -199,4 +245,10 @@ public abstract class AbstractInstancer<I extends Instance> implements Instancer
|
|||||||
public String toString() {
|
public String toString() {
|
||||||
return "AbstractInstancer[" + instanceCount() + ']';
|
return "AbstractInstancer[" + instanceCount() + ']';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public record Recreate<I extends Instance>(InstancerKey<I> key, DrawManager<?> drawManager) {
|
||||||
|
public AbstractInstancer<I> recreate() {
|
||||||
|
return drawManager.getInstancer(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import dev.engine_room.flywheel.api.material.Transparency;
|
|||||||
import dev.engine_room.flywheel.api.material.WriteMask;
|
import dev.engine_room.flywheel.api.material.WriteMask;
|
||||||
import dev.engine_room.flywheel.lib.material.CutoutShaders;
|
import dev.engine_room.flywheel.lib.material.CutoutShaders;
|
||||||
import dev.engine_room.flywheel.lib.material.FogShaders;
|
import dev.engine_room.flywheel.lib.material.FogShaders;
|
||||||
|
import dev.engine_room.flywheel.lib.material.LightShaders;
|
||||||
import dev.engine_room.flywheel.lib.material.SimpleMaterial;
|
import dev.engine_room.flywheel.lib.material.SimpleMaterial;
|
||||||
|
|
||||||
public class CommonCrumbling {
|
public class CommonCrumbling {
|
||||||
@ -12,6 +13,7 @@ public class CommonCrumbling {
|
|||||||
crumblingMaterial.copyFrom(baseMaterial)
|
crumblingMaterial.copyFrom(baseMaterial)
|
||||||
.fog(FogShaders.NONE)
|
.fog(FogShaders.NONE)
|
||||||
.cutout(CutoutShaders.ONE_TENTH)
|
.cutout(CutoutShaders.ONE_TENTH)
|
||||||
|
.light(LightShaders.SMOOTH_WHEN_EMBEDDED)
|
||||||
.polygonOffset(true)
|
.polygonOffset(true)
|
||||||
.transparency(Transparency.CRUMBLING)
|
.transparency(Transparency.CRUMBLING)
|
||||||
.writeMask(WriteMask.COLOR)
|
.writeMask(WriteMask.COLOR)
|
||||||
|
@ -115,8 +115,11 @@ public abstract class DrawManager<N extends AbstractInstancer<?>> {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
AbstractInstancer<?> abstractInstancer = impl.instancer;
|
InstanceHandleImpl.State<?> abstractInstancer = impl.state;
|
||||||
|
// AbstractInstancer directly implement HandleState, so this check is valid.
|
||||||
if (!clazz.isInstance(abstractInstancer)) {
|
if (!clazz.isInstance(abstractInstancer)) {
|
||||||
|
// This rejects instances that were created by a different engine,
|
||||||
|
// and also instances that are hidden or deleted.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,6 +138,8 @@ public abstract class DrawManager<N extends AbstractInstancer<?>> {
|
|||||||
initializationQueue.clear();
|
initializationQueue.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public abstract void triggerFallback();
|
||||||
|
|
||||||
protected record UninitializedInstancer<N, I extends Instance>(InstancerKey<I> key, N instancer) {
|
protected record UninitializedInstancer<N, I extends Instance>(InstancerKey<I> key, N instancer) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ import dev.engine_room.flywheel.api.task.Plan;
|
|||||||
import dev.engine_room.flywheel.api.visualization.VisualEmbedding;
|
import dev.engine_room.flywheel.api.visualization.VisualEmbedding;
|
||||||
import dev.engine_room.flywheel.api.visualization.VisualType;
|
import dev.engine_room.flywheel.api.visualization.VisualType;
|
||||||
import dev.engine_room.flywheel.api.visualization.VisualizationContext;
|
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.EmbeddedEnvironment;
|
||||||
import dev.engine_room.flywheel.backend.engine.embed.Environment;
|
import dev.engine_room.flywheel.backend.engine.embed.Environment;
|
||||||
import dev.engine_room.flywheel.backend.engine.embed.EnvironmentStorage;
|
import dev.engine_room.flywheel.backend.engine.embed.EnvironmentStorage;
|
||||||
@ -90,17 +91,27 @@ public class EngineImpl implements Engine {
|
|||||||
Uniforms.update(context);
|
Uniforms.update(context);
|
||||||
environmentStorage.flush();
|
environmentStorage.flush();
|
||||||
drawManager.flush(lightStorage, environmentStorage);
|
drawManager.flush(lightStorage, environmentStorage);
|
||||||
|
} catch (ShaderException e) {
|
||||||
|
triggerFallback();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render(RenderContext context, VisualType visualType) {
|
public void render(RenderContext context, VisualType visualType) {
|
||||||
drawManager.render(visualType);
|
try (var state = GlStateTracker.getRestoreState()) {
|
||||||
|
drawManager.render(visualType);
|
||||||
|
} catch (ShaderException e) {
|
||||||
|
triggerFallback();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void renderCrumbling(RenderContext context, List<CrumblingBlock> crumblingBlocks) {
|
public void renderCrumbling(RenderContext context, List<CrumblingBlock> crumblingBlocks) {
|
||||||
drawManager.renderCrumbling(crumblingBlocks);
|
try (var state = GlStateTracker.getRestoreState()) {
|
||||||
|
drawManager.renderCrumbling(crumblingBlocks);
|
||||||
|
} catch (ShaderException e) {
|
||||||
|
triggerFallback();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -110,6 +121,10 @@ public class EngineImpl implements Engine {
|
|||||||
environmentStorage.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) {
|
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);
|
return drawManager.getInstancer(environment, type, model, visualType, bias);
|
||||||
}
|
}
|
||||||
|
@ -1,56 +1,93 @@
|
|||||||
package dev.engine_room.flywheel.backend.engine;
|
package dev.engine_room.flywheel.backend.engine;
|
||||||
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.UnknownNullability;
|
|
||||||
|
|
||||||
import dev.engine_room.flywheel.api.instance.Instance;
|
import dev.engine_room.flywheel.api.instance.Instance;
|
||||||
import dev.engine_room.flywheel.api.instance.InstanceHandle;
|
import dev.engine_room.flywheel.api.instance.InstanceHandle;
|
||||||
|
|
||||||
public class InstanceHandleImpl<I extends Instance> implements InstanceHandle {
|
public class InstanceHandleImpl<I extends Instance> implements InstanceHandle {
|
||||||
@UnknownNullability
|
public State<I> state;
|
||||||
public AbstractInstancer<I> instancer;
|
|
||||||
@UnknownNullability
|
|
||||||
public I instance;
|
|
||||||
@UnknownNullability
|
|
||||||
public Supplier<AbstractInstancer<I>> recreate;
|
|
||||||
public boolean visible = true;
|
|
||||||
public int index;
|
public int index;
|
||||||
|
|
||||||
|
public InstanceHandleImpl(State<I> state) {
|
||||||
|
this.state = state;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setChanged() {
|
public void setChanged() {
|
||||||
instancer.notifyDirty(index);
|
state = state.setChanged(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setDeleted() {
|
public void setDeleted() {
|
||||||
instancer.notifyRemoval(index);
|
state = state.setDeleted(index);
|
||||||
// invalidate ourselves
|
// invalidate ourselves
|
||||||
clear();
|
clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setVisible(boolean visible) {
|
public void setVisible(boolean visible) {
|
||||||
if (this.visible == visible) {
|
state = state.setVisible(this, index, visible);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.visible = visible;
|
|
||||||
|
|
||||||
if (visible) {
|
|
||||||
recreate.get().stealInstance(instance);
|
|
||||||
} else {
|
|
||||||
instancer.notifyRemoval(index);
|
|
||||||
clear();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isVisible() {
|
public boolean isVisible() {
|
||||||
return visible;
|
return state instanceof AbstractInstancer<?>;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clear() {
|
public void clear() {
|
||||||
index = -1;
|
index = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface State<I extends Instance> {
|
||||||
|
State<I> setChanged(int index);
|
||||||
|
|
||||||
|
State<I> setDeleted(int index);
|
||||||
|
|
||||||
|
State<I> setVisible(InstanceHandleImpl<I> handle, int index, boolean visible);
|
||||||
|
}
|
||||||
|
|
||||||
|
public record Hidden<I extends Instance>(AbstractInstancer.Recreate<I> recreate, I instance) implements State<I> {
|
||||||
|
@Override
|
||||||
|
public State<I> setChanged(int index) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public State<I> setDeleted(int index) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public State<I> setVisible(InstanceHandleImpl<I> handle, int index, boolean visible) {
|
||||||
|
if (!visible) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
var instancer = recreate.recreate();
|
||||||
|
instancer.revealInstance(handle, instance);
|
||||||
|
return instancer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public record Deleted<I extends Instance>() implements State<I> {
|
||||||
|
private static final Deleted<?> INSTANCE = new Deleted<>();
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <I extends Instance> Deleted<I> instance() {
|
||||||
|
return (Deleted<I>) INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public State<I> setChanged(int index) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public State<I> setDeleted(int index) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public State<I> setVisible(InstanceHandleImpl<I> handle, int index, boolean visible) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,12 @@ import net.minecraft.client.renderer.texture.AbstractTexture;
|
|||||||
public final class MaterialRenderState {
|
public final class MaterialRenderState {
|
||||||
public static final Comparator<Material> COMPARATOR = Comparator.comparing((Material m) -> m.light()
|
public static final Comparator<Material> COMPARATOR = Comparator.comparing((Material m) -> m.light()
|
||||||
.source())
|
.source())
|
||||||
|
.thenComparing(material -> material.cutout()
|
||||||
|
.source())
|
||||||
|
.thenComparing(material -> material.shaders()
|
||||||
|
.vertexSource())
|
||||||
|
.thenComparing(material -> material.shaders()
|
||||||
|
.fragmentSource())
|
||||||
.thenComparing(Material::texture)
|
.thenComparing(Material::texture)
|
||||||
.thenComparing(Material::blur)
|
.thenComparing(Material::blur)
|
||||||
.thenComparing(Material::mipmap)
|
.thenComparing(Material::mipmap)
|
||||||
@ -185,12 +191,21 @@ public final class MaterialRenderState {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not here because ubershader: useLight, useOverlay, diffuse, shaders, fog shader, and cutout shader
|
// Not here because ubershader: useLight, useOverlay, diffuse, fog shader
|
||||||
// Everything in the comparator should be here.
|
// Everything in the comparator should be here.
|
||||||
return lhs.blur() == rhs.blur() && lhs.mipmap() == rhs.mipmap() && lhs.backfaceCulling() == rhs.backfaceCulling() && lhs.polygonOffset() == rhs.polygonOffset() && lhs.light()
|
// @formatter:off
|
||||||
.source()
|
return lhs.blur() == rhs.blur()
|
||||||
.equals(rhs.light()
|
&& lhs.mipmap() == rhs.mipmap()
|
||||||
.source()) && lhs.texture()
|
&& lhs.backfaceCulling() == rhs.backfaceCulling()
|
||||||
.equals(rhs.texture()) && lhs.depthTest() == rhs.depthTest() && lhs.transparency() == rhs.transparency() && lhs.writeMask() == rhs.writeMask();
|
&& lhs.polygonOffset() == rhs.polygonOffset()
|
||||||
|
&& lhs.depthTest() == rhs.depthTest()
|
||||||
|
&& lhs.transparency() == rhs.transparency()
|
||||||
|
&& lhs.writeMask() == rhs.writeMask()
|
||||||
|
&& lhs.light().source().equals(rhs.light().source())
|
||||||
|
&& lhs.texture().equals(rhs.texture())
|
||||||
|
&& lhs.cutout().source().equals(rhs.cutout().source())
|
||||||
|
&& lhs.shaders().fragmentSource().equals(rhs.shaders().fragmentSource())
|
||||||
|
&& lhs.shaders().vertexSource().equals(rhs.shaders().vertexSource());
|
||||||
|
// @formatter:on
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -113,7 +113,7 @@ public class IndirectBuffers {
|
|||||||
* Bind all buffers except the draw command buffer.
|
* Bind all buffers except the draw command buffer.
|
||||||
*/
|
*/
|
||||||
public void bindForCrumbling() {
|
public void bindForCrumbling() {
|
||||||
multiBind(3, 3);
|
multiBind(1, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void multiBind(int base, int count) {
|
private void multiBind(int base, int count) {
|
||||||
|
@ -26,7 +26,6 @@ import dev.engine_room.flywheel.backend.engine.MeshPool;
|
|||||||
import dev.engine_room.flywheel.backend.engine.uniform.Uniforms;
|
import dev.engine_room.flywheel.backend.engine.uniform.Uniforms;
|
||||||
import dev.engine_room.flywheel.backend.gl.GlCompat;
|
import dev.engine_room.flywheel.backend.gl.GlCompat;
|
||||||
import dev.engine_room.flywheel.backend.gl.shader.GlProgram;
|
import dev.engine_room.flywheel.backend.gl.shader.GlProgram;
|
||||||
import dev.engine_room.flywheel.lib.material.LightShaders;
|
|
||||||
import dev.engine_room.flywheel.lib.math.MoreMath;
|
import dev.engine_room.flywheel.lib.math.MoreMath;
|
||||||
|
|
||||||
public class IndirectCullingGroup<I extends Instance> {
|
public class IndirectCullingGroup<I extends Instance> {
|
||||||
@ -204,7 +203,7 @@ public class IndirectCullingGroup<I extends Instance> {
|
|||||||
int baseDrawUniformLoc = -1;
|
int baseDrawUniformLoc = -1;
|
||||||
|
|
||||||
for (var multiDraw : multiDraws.get(visualType)) {
|
for (var multiDraw : multiDraws.get(visualType)) {
|
||||||
var drawProgram = programs.getIndirectProgram(instanceType, multiDraw.embedded ? ContextShader.EMBEDDED : ContextShader.DEFAULT, multiDraw.material.light());
|
var drawProgram = programs.getIndirectProgram(instanceType, multiDraw.embedded ? ContextShader.EMBEDDED : ContextShader.DEFAULT, multiDraw.material.light(), multiDraw.material.cutout(), multiDraw.material.shaders());
|
||||||
if (drawProgram != lastProgram) {
|
if (drawProgram != lastProgram) {
|
||||||
lastProgram = drawProgram;
|
lastProgram = drawProgram;
|
||||||
|
|
||||||
@ -221,8 +220,8 @@ public class IndirectCullingGroup<I extends Instance> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void bindWithContextShader(ContextShader override) {
|
public void bindWithContextShader(ContextShader override, Material material) {
|
||||||
var program = programs.getIndirectProgram(instanceType, override, LightShaders.SMOOTH_WHEN_EMBEDDED);
|
var program = programs.getIndirectProgram(instanceType, override, material.light(), material.cutout(), material.shaders());
|
||||||
|
|
||||||
program.bind();
|
program.bind();
|
||||||
|
|
||||||
|
@ -89,7 +89,7 @@ public class IndirectDraw {
|
|||||||
MemoryUtil.memPutInt(ptr + 4, 1); // instanceCount - only drawing one instance
|
MemoryUtil.memPutInt(ptr + 4, 1); // instanceCount - only drawing one instance
|
||||||
MemoryUtil.memPutInt(ptr + 8, mesh.firstIndex()); // firstIndex
|
MemoryUtil.memPutInt(ptr + 8, mesh.firstIndex()); // firstIndex
|
||||||
MemoryUtil.memPutInt(ptr + 12, mesh.baseVertex()); // baseVertex
|
MemoryUtil.memPutInt(ptr + 12, mesh.baseVertex()); // baseVertex
|
||||||
MemoryUtil.memPutInt(ptr + 16, instancer.baseInstance() + instanceIndex); // baseInstance
|
MemoryUtil.memPutInt(ptr + 16, instancer.local2GlobalInstanceIndex(instanceIndex)); // baseInstance
|
||||||
|
|
||||||
MemoryUtil.memPutInt(ptr + 20, instancer.modelIndex()); // modelIndex
|
MemoryUtil.memPutInt(ptr + 20, instancer.modelIndex()); // modelIndex
|
||||||
|
|
||||||
|
@ -19,8 +19,10 @@ import dev.engine_room.flywheel.api.visualization.VisualType;
|
|||||||
import dev.engine_room.flywheel.backend.Samplers;
|
import dev.engine_room.flywheel.backend.Samplers;
|
||||||
import dev.engine_room.flywheel.backend.compile.ContextShader;
|
import dev.engine_room.flywheel.backend.compile.ContextShader;
|
||||||
import dev.engine_room.flywheel.backend.compile.IndirectPrograms;
|
import dev.engine_room.flywheel.backend.compile.IndirectPrograms;
|
||||||
|
import dev.engine_room.flywheel.backend.engine.AbstractInstancer;
|
||||||
import dev.engine_room.flywheel.backend.engine.CommonCrumbling;
|
import dev.engine_room.flywheel.backend.engine.CommonCrumbling;
|
||||||
import dev.engine_room.flywheel.backend.engine.DrawManager;
|
import dev.engine_room.flywheel.backend.engine.DrawManager;
|
||||||
|
import dev.engine_room.flywheel.backend.engine.GroupKey;
|
||||||
import dev.engine_room.flywheel.backend.engine.InstancerKey;
|
import dev.engine_room.flywheel.backend.engine.InstancerKey;
|
||||||
import dev.engine_room.flywheel.backend.engine.LightStorage;
|
import dev.engine_room.flywheel.backend.engine.LightStorage;
|
||||||
import dev.engine_room.flywheel.backend.engine.MaterialRenderState;
|
import dev.engine_room.flywheel.backend.engine.MaterialRenderState;
|
||||||
@ -28,12 +30,12 @@ import dev.engine_room.flywheel.backend.engine.MeshPool;
|
|||||||
import dev.engine_room.flywheel.backend.engine.TextureBinder;
|
import dev.engine_room.flywheel.backend.engine.TextureBinder;
|
||||||
import dev.engine_room.flywheel.backend.engine.embed.EnvironmentStorage;
|
import dev.engine_room.flywheel.backend.engine.embed.EnvironmentStorage;
|
||||||
import dev.engine_room.flywheel.backend.engine.uniform.Uniforms;
|
import dev.engine_room.flywheel.backend.engine.uniform.Uniforms;
|
||||||
import dev.engine_room.flywheel.backend.gl.GlStateTracker;
|
|
||||||
import dev.engine_room.flywheel.backend.gl.array.GlVertexArray;
|
import dev.engine_room.flywheel.backend.gl.array.GlVertexArray;
|
||||||
import dev.engine_room.flywheel.backend.gl.buffer.GlBuffer;
|
import dev.engine_room.flywheel.backend.gl.buffer.GlBuffer;
|
||||||
import dev.engine_room.flywheel.backend.gl.buffer.GlBufferType;
|
import dev.engine_room.flywheel.backend.gl.buffer.GlBufferType;
|
||||||
import dev.engine_room.flywheel.lib.material.SimpleMaterial;
|
import dev.engine_room.flywheel.lib.material.SimpleMaterial;
|
||||||
import dev.engine_room.flywheel.lib.memory.MemoryBlock;
|
import dev.engine_room.flywheel.lib.memory.MemoryBlock;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.resources.model.ModelBakery;
|
import net.minecraft.client.resources.model.ModelBakery;
|
||||||
|
|
||||||
public class IndirectDrawManager extends DrawManager<IndirectInstancer<?>> {
|
public class IndirectDrawManager extends DrawManager<IndirectInstancer<?>> {
|
||||||
@ -66,7 +68,7 @@ public class IndirectDrawManager extends DrawManager<IndirectInstancer<?>> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected <I extends Instance> IndirectInstancer<?> create(InstancerKey<I> key) {
|
protected <I extends Instance> IndirectInstancer<?> create(InstancerKey<I> key) {
|
||||||
return new IndirectInstancer<>(key, () -> getInstancer(key));
|
return new IndirectInstancer<>(key, new AbstractInstancer.Recreate<>(key, this));
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@ -90,26 +92,24 @@ public class IndirectDrawManager extends DrawManager<IndirectInstancer<?>> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try (var state = GlStateTracker.getRestoreState()) {
|
TextureBinder.bindLightAndOverlay();
|
||||||
TextureBinder.bindLightAndOverlay();
|
|
||||||
|
|
||||||
vertexArray.bindForDraw();
|
vertexArray.bindForDraw();
|
||||||
lightBuffers.bind();
|
lightBuffers.bind();
|
||||||
matrixBuffer.bind();
|
matrixBuffer.bind();
|
||||||
Uniforms.bindAll();
|
Uniforms.bindAll();
|
||||||
|
|
||||||
if (needsBarrier) {
|
if (needsBarrier) {
|
||||||
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
|
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
|
||||||
needsBarrier = false;
|
needsBarrier = false;
|
||||||
}
|
|
||||||
|
|
||||||
for (var group : cullingGroups.values()) {
|
|
||||||
group.submit(visualType);
|
|
||||||
}
|
|
||||||
|
|
||||||
MaterialRenderState.reset();
|
|
||||||
TextureBinder.resetLightAndOverlay();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (var group : cullingGroups.values()) {
|
||||||
|
group.submit(visualType);
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialRenderState.reset();
|
||||||
|
TextureBinder.resetLightAndOverlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -193,57 +193,67 @@ public class IndirectDrawManager extends DrawManager<IndirectInstancer<?>> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try (var state = GlStateTracker.getRestoreState()) {
|
TextureBinder.bindLightAndOverlay();
|
||||||
TextureBinder.bindLightAndOverlay();
|
|
||||||
|
|
||||||
vertexArray.bindForDraw();
|
vertexArray.bindForDraw();
|
||||||
Uniforms.bindAll();
|
Uniforms.bindAll();
|
||||||
|
|
||||||
var crumblingMaterial = SimpleMaterial.builder();
|
var crumblingMaterial = SimpleMaterial.builder();
|
||||||
|
|
||||||
// Scratch memory for writing draw commands.
|
// Scratch memory for writing draw commands.
|
||||||
var block = MemoryBlock.malloc(IndirectBuffers.DRAW_COMMAND_STRIDE);
|
var block = MemoryBlock.malloc(IndirectBuffers.DRAW_COMMAND_STRIDE);
|
||||||
|
|
||||||
GlBufferType.DRAW_INDIRECT_BUFFER.bind(crumblingDrawBuffer.handle());
|
// Set up the crumbling program buffers. Nothing changes here between draws.
|
||||||
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, BufferBindings.DRAW, crumblingDrawBuffer.handle(), 0, IndirectBuffers.DRAW_COMMAND_STRIDE);
|
GlBufferType.DRAW_INDIRECT_BUFFER.bind(crumblingDrawBuffer.handle());
|
||||||
|
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, BufferBindings.DRAW, crumblingDrawBuffer.handle(), 0, IndirectBuffers.DRAW_COMMAND_STRIDE);
|
||||||
|
|
||||||
for (var groupEntry : byType.entrySet()) {
|
for (var groupEntry : byType.entrySet()) {
|
||||||
var byProgress = groupEntry.getValue();
|
var byProgress = groupEntry.getValue();
|
||||||
|
|
||||||
// Set up the crumbling program buffers. Nothing changes here between draws.
|
GroupKey<?> groupKey = groupEntry.getKey();
|
||||||
cullingGroups.get(groupEntry.getKey())
|
IndirectCullingGroup<?> cullingGroup = cullingGroups.get(groupKey.instanceType());
|
||||||
.bindWithContextShader(ContextShader.CRUMBLING);
|
|
||||||
|
|
||||||
for (var progressEntry : byProgress.int2ObjectEntrySet()) {
|
if (cullingGroup == null) {
|
||||||
Samplers.CRUMBLING.makeActive();
|
continue;
|
||||||
TextureBinder.bind(ModelBakery.BREAKING_LOCATIONS.get(progressEntry.getIntKey()));
|
|
||||||
|
|
||||||
for (var instanceHandlePair : progressEntry.getValue()) {
|
|
||||||
IndirectInstancer<?> instancer = instanceHandlePair.getFirst();
|
|
||||||
int instanceIndex = instanceHandlePair.getSecond().index;
|
|
||||||
|
|
||||||
for (IndirectDraw draw : instancer.draws()) {
|
|
||||||
// Transform the material to be suited for crumbling.
|
|
||||||
CommonCrumbling.applyCrumblingProperties(crumblingMaterial, draw.material());
|
|
||||||
|
|
||||||
MaterialRenderState.setup(crumblingMaterial);
|
|
||||||
|
|
||||||
// Upload the draw command.
|
|
||||||
draw.writeWithOverrides(block.ptr(), instanceIndex, crumblingMaterial);
|
|
||||||
crumblingDrawBuffer.upload(block);
|
|
||||||
|
|
||||||
// Submit! Everything is already bound by here.
|
|
||||||
glDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MaterialRenderState.reset();
|
for (var progressEntry : byProgress.int2ObjectEntrySet()) {
|
||||||
TextureBinder.resetLightAndOverlay();
|
Samplers.CRUMBLING.makeActive();
|
||||||
|
TextureBinder.bind(ModelBakery.BREAKING_LOCATIONS.get(progressEntry.getIntKey()));
|
||||||
|
|
||||||
block.free();
|
for (var instanceHandlePair : progressEntry.getValue()) {
|
||||||
|
IndirectInstancer<?> instancer = instanceHandlePair.getFirst();
|
||||||
|
int instanceIndex = instanceHandlePair.getSecond().index;
|
||||||
|
|
||||||
|
for (IndirectDraw draw : instancer.draws()) {
|
||||||
|
// Transform the material to be suited for crumbling.
|
||||||
|
CommonCrumbling.applyCrumblingProperties(crumblingMaterial, draw.material());
|
||||||
|
|
||||||
|
cullingGroup.bindWithContextShader(ContextShader.CRUMBLING, crumblingMaterial);
|
||||||
|
|
||||||
|
MaterialRenderState.setup(crumblingMaterial);
|
||||||
|
|
||||||
|
// Upload the draw command.
|
||||||
|
draw.writeWithOverrides(block.ptr(), instanceIndex, crumblingMaterial);
|
||||||
|
crumblingDrawBuffer.upload(block);
|
||||||
|
|
||||||
|
// Submit! Everything is already bound by here.
|
||||||
|
glDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MaterialRenderState.reset();
|
||||||
|
TextureBinder.resetLightAndOverlay();
|
||||||
|
|
||||||
|
block.free();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void triggerFallback() {
|
||||||
|
IndirectPrograms.kill();
|
||||||
|
Minecraft.getInstance().levelRenderer.allChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ package dev.engine_room.flywheel.backend.engine.indirect;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.UnknownNullability;
|
import org.jetbrains.annotations.UnknownNullability;
|
||||||
import org.joml.Vector4fc;
|
import org.joml.Vector4fc;
|
||||||
@ -28,7 +27,7 @@ public class IndirectInstancer<I extends Instance> extends AbstractInstancer<I>
|
|||||||
private int modelIndex = -1;
|
private int modelIndex = -1;
|
||||||
private int baseInstance = -1;
|
private int baseInstance = -1;
|
||||||
|
|
||||||
public IndirectInstancer(InstancerKey<I> key, Supplier<AbstractInstancer<I>> recreate) {
|
public IndirectInstancer(InstancerKey<I> key, Recreate<I> recreate) {
|
||||||
super(key, recreate);
|
super(key, recreate);
|
||||||
instanceStride = MoreMath.align4(type.layout()
|
instanceStride = MoreMath.align4(type.layout()
|
||||||
.byteSize());
|
.byteSize());
|
||||||
@ -37,10 +36,7 @@ public class IndirectInstancer<I extends Instance> extends AbstractInstancer<I>
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void notifyDirty(int index) {
|
public void setIndexChanged(int index) {
|
||||||
if (index < 0 || index >= instanceCount()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
changedPages.set(ObjectStorage.objectIndex2PageIndex(index));
|
changedPages.set(ObjectStorage.objectIndex2PageIndex(index));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,4 +143,8 @@ public class IndirectInstancer<I extends Instance> extends AbstractInstancer<I>
|
|||||||
public int baseInstance() {
|
public int baseInstance() {
|
||||||
return baseInstance;
|
return baseInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int local2GlobalInstanceIndex(int instanceIndex) {
|
||||||
|
return mapping.objectIndex2GlobalIndex(instanceIndex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -216,5 +216,9 @@ public class ObjectStorage extends AbstractArena {
|
|||||||
|
|
||||||
pages = Arrays.copyOf(pages, neededPages);
|
pages = Arrays.copyOf(pages, neededPages);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int objectIndex2GlobalIndex(int objectIndex) {
|
||||||
|
return (pages[objectIndex2PageIndex(objectIndex)] << LOG_2_PAGE_SIZE) + (objectIndex & PAGE_MASK);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import dev.engine_room.flywheel.backend.MaterialShaderIndices;
|
|||||||
import dev.engine_room.flywheel.backend.Samplers;
|
import dev.engine_room.flywheel.backend.Samplers;
|
||||||
import dev.engine_room.flywheel.backend.compile.ContextShader;
|
import dev.engine_room.flywheel.backend.compile.ContextShader;
|
||||||
import dev.engine_room.flywheel.backend.compile.InstancingPrograms;
|
import dev.engine_room.flywheel.backend.compile.InstancingPrograms;
|
||||||
|
import dev.engine_room.flywheel.backend.engine.AbstractInstancer;
|
||||||
import dev.engine_room.flywheel.backend.engine.CommonCrumbling;
|
import dev.engine_room.flywheel.backend.engine.CommonCrumbling;
|
||||||
import dev.engine_room.flywheel.backend.engine.DrawManager;
|
import dev.engine_room.flywheel.backend.engine.DrawManager;
|
||||||
import dev.engine_room.flywheel.backend.engine.GroupKey;
|
import dev.engine_room.flywheel.backend.engine.GroupKey;
|
||||||
@ -25,12 +26,11 @@ import dev.engine_room.flywheel.backend.engine.MeshPool;
|
|||||||
import dev.engine_room.flywheel.backend.engine.TextureBinder;
|
import dev.engine_room.flywheel.backend.engine.TextureBinder;
|
||||||
import dev.engine_room.flywheel.backend.engine.embed.EnvironmentStorage;
|
import dev.engine_room.flywheel.backend.engine.embed.EnvironmentStorage;
|
||||||
import dev.engine_room.flywheel.backend.engine.uniform.Uniforms;
|
import dev.engine_room.flywheel.backend.engine.uniform.Uniforms;
|
||||||
import dev.engine_room.flywheel.backend.gl.GlStateTracker;
|
|
||||||
import dev.engine_room.flywheel.backend.gl.TextureBuffer;
|
import dev.engine_room.flywheel.backend.gl.TextureBuffer;
|
||||||
import dev.engine_room.flywheel.backend.gl.array.GlVertexArray;
|
import dev.engine_room.flywheel.backend.gl.array.GlVertexArray;
|
||||||
import dev.engine_room.flywheel.backend.gl.shader.GlProgram;
|
import dev.engine_room.flywheel.backend.gl.shader.GlProgram;
|
||||||
import dev.engine_room.flywheel.lib.material.LightShaders;
|
|
||||||
import dev.engine_room.flywheel.lib.material.SimpleMaterial;
|
import dev.engine_room.flywheel.lib.material.SimpleMaterial;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.resources.model.ModelBakery;
|
import net.minecraft.client.resources.model.ModelBakery;
|
||||||
|
|
||||||
public class InstancedDrawManager extends DrawManager<InstancedInstancer<?>> {
|
public class InstancedDrawManager extends DrawManager<InstancedInstancer<?>> {
|
||||||
@ -94,17 +94,15 @@ public class InstancedDrawManager extends DrawManager<InstancedInstancer<?>> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try (var state = GlStateTracker.getRestoreState()) {
|
Uniforms.bindAll();
|
||||||
Uniforms.bindAll();
|
vao.bindForDraw();
|
||||||
vao.bindForDraw();
|
TextureBinder.bindLightAndOverlay();
|
||||||
TextureBinder.bindLightAndOverlay();
|
light.bind();
|
||||||
light.bind();
|
|
||||||
|
|
||||||
stage.draw(instanceTexture, programs);
|
stage.draw(instanceTexture, programs);
|
||||||
|
|
||||||
MaterialRenderState.reset();
|
MaterialRenderState.reset();
|
||||||
TextureBinder.resetLightAndOverlay();
|
TextureBinder.resetLightAndOverlay();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -128,7 +126,7 @@ public class InstancedDrawManager extends DrawManager<InstancedInstancer<?>> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected <I extends Instance> InstancedInstancer<I> create(InstancerKey<I> key) {
|
protected <I extends Instance> InstancedInstancer<I> create(InstancerKey<I> key) {
|
||||||
return new InstancedInstancer<>(key, () -> getInstancer(key));
|
return new InstancedInstancer<>(key, new AbstractInstancer.Recreate<>(key, this));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -162,46 +160,48 @@ public class InstancedDrawManager extends DrawManager<InstancedInstancer<?>> {
|
|||||||
|
|
||||||
var crumblingMaterial = SimpleMaterial.builder();
|
var crumblingMaterial = SimpleMaterial.builder();
|
||||||
|
|
||||||
try (var state = GlStateTracker.getRestoreState()) {
|
Uniforms.bindAll();
|
||||||
Uniforms.bindAll();
|
vao.bindForDraw();
|
||||||
vao.bindForDraw();
|
TextureBinder.bindLightAndOverlay();
|
||||||
TextureBinder.bindLightAndOverlay();
|
|
||||||
|
|
||||||
for (var groupEntry : byType.entrySet()) {
|
for (var groupEntry : byType.entrySet()) {
|
||||||
var byProgress = groupEntry.getValue();
|
var byProgress = groupEntry.getValue();
|
||||||
|
|
||||||
GroupKey<?> shader = groupEntry.getKey();
|
GroupKey<?> shader = groupEntry.getKey();
|
||||||
|
|
||||||
var program = programs.get(shader.instanceType(), ContextShader.CRUMBLING, LightShaders.SMOOTH_WHEN_EMBEDDED);
|
for (var progressEntry : byProgress.int2ObjectEntrySet()) {
|
||||||
program.bind();
|
Samplers.CRUMBLING.makeActive();
|
||||||
|
TextureBinder.bind(ModelBakery.BREAKING_LOCATIONS.get(progressEntry.getIntKey()));
|
||||||
|
|
||||||
for (var progressEntry : byProgress.int2ObjectEntrySet()) {
|
for (var instanceHandlePair : progressEntry.getValue()) {
|
||||||
Samplers.CRUMBLING.makeActive();
|
InstancedInstancer<?> instancer = instanceHandlePair.getFirst();
|
||||||
TextureBinder.bind(ModelBakery.BREAKING_LOCATIONS.get(progressEntry.getIntKey()));
|
var index = instanceHandlePair.getSecond().index;
|
||||||
|
|
||||||
for (var instanceHandlePair : progressEntry.getValue()) {
|
|
||||||
InstancedInstancer<?> instancer = instanceHandlePair.getFirst();
|
|
||||||
var index = instanceHandlePair.getSecond().index;
|
|
||||||
|
|
||||||
|
for (InstancedDraw draw : instancer.draws()) {
|
||||||
|
CommonCrumbling.applyCrumblingProperties(crumblingMaterial, draw.material());
|
||||||
|
var program = programs.get(shader.instanceType(), ContextShader.CRUMBLING, crumblingMaterial.light(), crumblingMaterial.cutout(), crumblingMaterial.shaders());
|
||||||
|
program.bind();
|
||||||
program.setInt("_flw_baseInstance", index);
|
program.setInt("_flw_baseInstance", index);
|
||||||
|
uploadMaterialUniform(program, crumblingMaterial);
|
||||||
|
|
||||||
for (InstancedDraw draw : instancer.draws()) {
|
MaterialRenderState.setup(crumblingMaterial);
|
||||||
CommonCrumbling.applyCrumblingProperties(crumblingMaterial, draw.material());
|
|
||||||
uploadMaterialUniform(program, crumblingMaterial);
|
|
||||||
|
|
||||||
MaterialRenderState.setup(crumblingMaterial);
|
Samplers.INSTANCE_BUFFER.makeActive();
|
||||||
|
|
||||||
Samplers.INSTANCE_BUFFER.makeActive();
|
draw.renderOne(instanceTexture);
|
||||||
|
|
||||||
draw.renderOne(instanceTexture);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MaterialRenderState.reset();
|
|
||||||
TextureBinder.resetLightAndOverlay();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MaterialRenderState.reset();
|
||||||
|
TextureBinder.resetLightAndOverlay();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void triggerFallback() {
|
||||||
|
InstancingPrograms.kill();
|
||||||
|
Minecraft.getInstance().levelRenderer.allChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void uploadMaterialUniform(GlProgram program, Material material) {
|
public static void uploadMaterialUniform(GlProgram program, Material material) {
|
||||||
|
@ -2,7 +2,6 @@ package dev.engine_room.flywheel.backend.engine.instancing;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
@ -25,7 +24,7 @@ public class InstancedInstancer<I extends Instance> extends AbstractInstancer<I>
|
|||||||
|
|
||||||
private final List<InstancedDraw> draws = new ArrayList<>();
|
private final List<InstancedDraw> draws = new ArrayList<>();
|
||||||
|
|
||||||
public InstancedInstancer(InstancerKey<I> key, Supplier<AbstractInstancer<I>> recreate) {
|
public InstancedInstancer(InstancerKey<I> key, Recreate<I> recreate) {
|
||||||
super(key, recreate);
|
super(key, recreate);
|
||||||
var layout = type.layout();
|
var layout = type.layout();
|
||||||
// Align to one texel in the texture buffer
|
// Align to one texel in the texture buffer
|
||||||
|
@ -55,14 +55,13 @@ public class InstancedRenderStage {
|
|||||||
var environment = shader.environment();
|
var environment = shader.environment();
|
||||||
|
|
||||||
for (var drawCall : drawCalls.draws) {
|
for (var drawCall : drawCalls.draws) {
|
||||||
var program = programs.get(shader.instanceType(), environment.contextShader(), drawCall.material()
|
var material = drawCall.material();
|
||||||
.light());
|
|
||||||
|
var program = programs.get(shader.instanceType(), environment.contextShader(), material.light(), material.cutout(), material.shaders());
|
||||||
program.bind();
|
program.bind();
|
||||||
|
|
||||||
environment.setupDraw(program);
|
environment.setupDraw(program);
|
||||||
|
|
||||||
var material = drawCall.material();
|
|
||||||
|
|
||||||
uploadMaterialUniform(program, material);
|
uploadMaterialUniform(program, material);
|
||||||
|
|
||||||
MaterialRenderState.setup(material);
|
MaterialRenderState.setup(material);
|
||||||
|
@ -11,7 +11,10 @@ import java.util.Map;
|
|||||||
|
|
||||||
import org.jetbrains.annotations.VisibleForTesting;
|
import org.jetbrains.annotations.VisibleForTesting;
|
||||||
|
|
||||||
|
import dev.engine_room.flywheel.backend.compile.FlwPrograms;
|
||||||
|
import dev.engine_room.flywheel.lib.util.StringUtil;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.server.packs.resources.Resource;
|
||||||
import net.minecraft.server.packs.resources.ResourceManager;
|
import net.minecraft.server.packs.resources.ResourceManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -20,62 +23,96 @@ import net.minecraft.server.packs.resources.ResourceManager;
|
|||||||
public class ShaderSources {
|
public class ShaderSources {
|
||||||
public static final String SHADER_DIR = "flywheel/";
|
public static final String SHADER_DIR = "flywheel/";
|
||||||
|
|
||||||
private final ResourceManager manager;
|
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
protected final Map<ResourceLocation, LoadResult> cache = new HashMap<>();
|
protected final Map<ResourceLocation, LoadResult> cache;
|
||||||
|
|
||||||
/**
|
|
||||||
* Tracks where we are in the mutual recursion to detect circular imports.
|
|
||||||
*/
|
|
||||||
private final Deque<ResourceLocation> findStack = new ArrayDeque<>();
|
|
||||||
|
|
||||||
public ShaderSources(ResourceManager manager) {
|
public ShaderSources(ResourceManager manager) {
|
||||||
this.manager = manager;
|
var sourceFinder = new SourceFinder(manager);
|
||||||
|
|
||||||
|
long loadStart = System.nanoTime();
|
||||||
|
manager.listResources("flywheel", ShaderSources::isShader)
|
||||||
|
.forEach(sourceFinder::rootLoad);
|
||||||
|
|
||||||
|
long loadEnd = System.nanoTime();
|
||||||
|
|
||||||
|
FlwPrograms.LOGGER.info("Loaded {} shader sources in {}", sourceFinder.results.size(), StringUtil.formatTime(loadEnd - loadStart));
|
||||||
|
|
||||||
|
this.cache = sourceFinder.results;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ShaderSources(ResourceManager manager, Map<ResourceLocation, LoadResult> preloadCache) {
|
private static ResourceLocation locationWithoutFlywheelPrefix(ResourceLocation loc) {
|
||||||
this.manager = manager;
|
return new ResourceLocation(loc.getNamespace(), loc.getPath()
|
||||||
cache.putAll(preloadCache);
|
.substring(SHADER_DIR.length()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public LoadResult find(ResourceLocation location) {
|
public LoadResult find(ResourceLocation location) {
|
||||||
if (findStack.contains(location)) {
|
return cache.computeIfAbsent(location, loc -> new LoadResult.Failure(new LoadError.ResourceError(loc)));
|
||||||
// Make a copy of the find stack with the offending location added on top to show the full path.
|
}
|
||||||
|
|
||||||
|
private static boolean isShader(ResourceLocation loc) {
|
||||||
|
var path = loc.getPath();
|
||||||
|
return path.endsWith(".glsl") || path.endsWith(".vert") || path.endsWith(".frag") || path.endsWith(".comp");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SourceFinder {
|
||||||
|
private final Deque<ResourceLocation> findStack = new ArrayDeque<>();
|
||||||
|
private final Map<ResourceLocation, LoadResult> results = new HashMap<>();
|
||||||
|
private final ResourceManager manager;
|
||||||
|
|
||||||
|
public SourceFinder(ResourceManager manager) {
|
||||||
|
this.manager = manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void rootLoad(ResourceLocation loc, Resource resource) {
|
||||||
|
var strippedLoc = locationWithoutFlywheelPrefix(loc);
|
||||||
|
|
||||||
|
if (results.containsKey(strippedLoc)) {
|
||||||
|
// Some other source already #included this one.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.results.put(strippedLoc, readResource(strippedLoc, resource));
|
||||||
|
}
|
||||||
|
|
||||||
|
public LoadResult recursiveLoad(ResourceLocation location) {
|
||||||
|
if (findStack.contains(location)) {
|
||||||
|
// Make a copy of the find stack with the offending location added on top to show the full path.
|
||||||
|
findStack.addLast(location);
|
||||||
|
var copy = List.copyOf(findStack);
|
||||||
|
findStack.removeLast();
|
||||||
|
return new LoadResult.Failure(new LoadError.CircularDependency(location, copy));
|
||||||
|
}
|
||||||
findStack.addLast(location);
|
findStack.addLast(location);
|
||||||
var copy = List.copyOf(findStack);
|
|
||||||
|
LoadResult out = _find(location);
|
||||||
|
|
||||||
findStack.removeLast();
|
findStack.removeLast();
|
||||||
return new LoadResult.Failure(new LoadError.CircularDependency(location, copy));
|
return out;
|
||||||
}
|
}
|
||||||
findStack.addLast(location);
|
|
||||||
|
|
||||||
LoadResult out = _find(location);
|
private LoadResult _find(ResourceLocation location) {
|
||||||
|
// Can't use computeIfAbsent because mutual recursion causes ConcurrentModificationExceptions
|
||||||
findStack.removeLast();
|
var out = results.get(location);
|
||||||
return out;
|
if (out == null) {
|
||||||
}
|
out = load(location);
|
||||||
|
results.put(location, out);
|
||||||
private LoadResult _find(ResourceLocation location) {
|
}
|
||||||
// Can't use computeIfAbsent because mutual recursion causes ConcurrentModificationExceptions
|
return out;
|
||||||
var out = cache.get(location);
|
|
||||||
if (out == null) {
|
|
||||||
out = load(location);
|
|
||||||
cache.put(location, out);
|
|
||||||
}
|
}
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
private LoadResult load(ResourceLocation loc) {
|
||||||
protected LoadResult load(ResourceLocation loc) {
|
return manager.getResource(loc.withPrefix(SHADER_DIR))
|
||||||
return manager.getResource(loc.withPrefix(SHADER_DIR))
|
.map(resource -> readResource(loc, resource))
|
||||||
.map(resource -> {
|
.orElseGet(() -> new LoadResult.Failure(new LoadError.ResourceError(loc)));
|
||||||
try (InputStream stream = resource.open()) {
|
}
|
||||||
String sourceString = new String(stream.readAllBytes(), StandardCharsets.UTF_8);
|
|
||||||
return SourceFile.parse(this, loc, sourceString);
|
private LoadResult readResource(ResourceLocation loc, Resource resource) {
|
||||||
} catch (IOException e) {
|
try (InputStream stream = resource.open()) {
|
||||||
return new LoadResult.Failure(new LoadError.IOError(loc, e));
|
String sourceString = new String(stream.readAllBytes(), StandardCharsets.UTF_8);
|
||||||
}
|
return SourceFile.parse(this::recursiveLoad, loc, sourceString);
|
||||||
})
|
} catch (IOException e) {
|
||||||
.orElseGet(() -> new LoadResult.Failure(new LoadError.ResourceError(loc)));
|
return new LoadResult.Failure(new LoadError.IOError(loc, e));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import java.util.Collection;
|
|||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
@ -69,7 +70,7 @@ public class SourceFile implements SourceComponent {
|
|||||||
return new LoadResult.Success(new SourceFile(name, new SourceLines(name, ""), ImmutableMap.of(), ImmutableMap.of(), ImmutableList.of(), ImmutableMap.of(), ImmutableList.of(), ""));
|
return new LoadResult.Success(new SourceFile(name, new SourceLines(name, ""), ImmutableMap.of(), ImmutableMap.of(), ImmutableList.of(), ImmutableMap.of(), ImmutableList.of(), ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoadResult parse(ShaderSources sourceFinder, ResourceLocation name, String stringSource) {
|
public static LoadResult parse(Function<ResourceLocation, LoadResult> sourceFinder, ResourceLocation name, String stringSource) {
|
||||||
var source = new SourceLines(name, stringSource);
|
var source = new SourceLines(name, stringSource);
|
||||||
|
|
||||||
var imports = Import.parseImports(source);
|
var imports = Import.parseImports(source);
|
||||||
@ -93,7 +94,7 @@ public class SourceFile implements SourceComponent {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = sourceFinder.find(location);
|
var result = sourceFinder.apply(location);
|
||||||
|
|
||||||
if (result instanceof LoadResult.Success s) {
|
if (result instanceof LoadResult.Success s) {
|
||||||
included.add(s.unwrap());
|
included.add(s.unwrap());
|
||||||
|
@ -100,6 +100,10 @@ bool _flw_isVisible(uint instanceIndex, uint modelIndex) {
|
|||||||
|
|
||||||
ivec4 bounds = ivec4(aabb * vec4(levelSizePair));
|
ivec4 bounds = ivec4(aabb * vec4(levelSizePair));
|
||||||
|
|
||||||
|
// Clamp to the texture bounds.
|
||||||
|
// Since we're not going through a sampler out of bounds texel fetches will return 0.
|
||||||
|
bounds = clamp(bounds, ivec4(0), levelSizePair);
|
||||||
|
|
||||||
float depth01 = texelFetch(_flw_depthPyramid, bounds.xw, level).r;
|
float depth01 = texelFetch(_flw_depthPyramid, bounds.xw, level).r;
|
||||||
float depth11 = texelFetch(_flw_depthPyramid, bounds.zw, level).r;
|
float depth11 = texelFetch(_flw_depthPyramid, bounds.zw, level).r;
|
||||||
float depth10 = texelFetch(_flw_depthPyramid, bounds.zy, level).r;
|
float depth10 = texelFetch(_flw_depthPyramid, bounds.zy, level).r;
|
||||||
|
@ -23,12 +23,16 @@ uniform uint _flw_baseDraw;
|
|||||||
|
|
||||||
flat out uvec3 _flw_packedMaterial;
|
flat out uvec3 _flw_packedMaterial;
|
||||||
|
|
||||||
void main() {
|
|
||||||
#if __VERSION__ < 460
|
#if __VERSION__ < 460
|
||||||
uint drawIndex = gl_DrawIDARB + _flw_baseDraw;
|
#define flw_baseInstance gl_BaseInstanceARB
|
||||||
|
#define flw_drawId gl_DrawIDARB
|
||||||
#else
|
#else
|
||||||
uint drawIndex = gl_DrawID + _flw_baseDraw;
|
#define flw_baseInstance gl_BaseInstance
|
||||||
|
#define flw_drawId gl_DrawID
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
uint drawIndex = flw_drawId + _flw_baseDraw;
|
||||||
MeshDrawCommand draw = _flw_drawCommands[drawIndex];
|
MeshDrawCommand draw = _flw_drawCommands[drawIndex];
|
||||||
|
|
||||||
_flw_uberMaterialVertexIndex = draw.materialVertexIndex;
|
_flw_uberMaterialVertexIndex = draw.materialVertexIndex;
|
||||||
@ -42,11 +46,12 @@ void main() {
|
|||||||
// _flw_normalMatrix = mat3(1.);
|
// _flw_normalMatrix = mat3(1.);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if __VERSION__ < 460
|
#ifdef _FLW_CRUMBLING
|
||||||
uint instanceIndex = _flw_instanceIndices[gl_BaseInstanceARB + gl_InstanceID];
|
uint instanceIndex = flw_baseInstance;
|
||||||
#else
|
#else
|
||||||
uint instanceIndex = _flw_instanceIndices[gl_BaseInstance + gl_InstanceID];
|
uint instanceIndex = _flw_instanceIndices[flw_baseInstance + gl_InstanceID];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
FlwInstance instance = _flw_unpackInstance(instanceIndex);
|
FlwInstance instance = _flw_unpackInstance(instanceIndex);
|
||||||
|
|
||||||
_flw_main(instance, instanceIndex);
|
_flw_main(instance, instanceIndex);
|
||||||
|
@ -34,4 +34,10 @@ public abstract class AbstractInstance implements Instance {
|
|||||||
// Override to mark final.
|
// Override to mark final.
|
||||||
handle.setDeleted();
|
handle.setDeleted();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void setVisible(boolean visible) {
|
||||||
|
// Override to mark final.
|
||||||
|
handle.setVisible(visible);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,19 @@ public class TransformedInstance extends ColoredLitInstance implements Affine<Tr
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TransformedInstance mul(Matrix4fc other) {
|
||||||
|
pose.mul(other);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TransformedInstance mul(PoseStack.Pose other) {
|
||||||
|
return mul(other.pose());
|
||||||
|
}
|
||||||
|
|
||||||
|
public TransformedInstance mul(PoseStack stack) {
|
||||||
|
return mul(stack.last());
|
||||||
|
}
|
||||||
|
|
||||||
public TransformedInstance setTransform(Matrix4fc pose) {
|
public TransformedInstance setTransform(Matrix4fc pose) {
|
||||||
this.pose.set(pose);
|
this.pose.set(pose);
|
||||||
return this;
|
return this;
|
||||||
|
@ -39,10 +39,8 @@ public final class InstanceTree {
|
|||||||
private float xScale;
|
private float xScale;
|
||||||
private float yScale;
|
private float yScale;
|
||||||
private float zScale;
|
private float zScale;
|
||||||
@ApiStatus.Experimental
|
private boolean visible = true;
|
||||||
public boolean visible = true;
|
private boolean skipDraw = false;
|
||||||
@ApiStatus.Experimental
|
|
||||||
public boolean skipDraw;
|
|
||||||
|
|
||||||
private boolean changed;
|
private boolean changed;
|
||||||
|
|
||||||
@ -235,6 +233,48 @@ public final class InstanceTree {
|
|||||||
changed = false;
|
changed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the visibility of this tree and all its children, recursively.
|
||||||
|
*
|
||||||
|
* @param visible Whether to make this tree visible.
|
||||||
|
*/
|
||||||
|
public void visible(boolean visible) {
|
||||||
|
this.visible = visible;
|
||||||
|
|
||||||
|
updateVisible();
|
||||||
|
|
||||||
|
// I think you'll get weird behavior if you mark a parent invisible and then mark its child visible.
|
||||||
|
// Not sure if there's a good way to solve that, though.
|
||||||
|
for (InstanceTree child : children) {
|
||||||
|
child.visible(visible);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the visibility of this specific node in the tree.
|
||||||
|
*
|
||||||
|
* @param skipDraw Whether this node should skip rendering.
|
||||||
|
*/
|
||||||
|
public void skipDraw(boolean skipDraw) {
|
||||||
|
this.skipDraw = skipDraw;
|
||||||
|
|
||||||
|
updateVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateVisible() {
|
||||||
|
if (instance != null) {
|
||||||
|
instance.setVisible(visible && !skipDraw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean visible() {
|
||||||
|
return visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean skipDraw() {
|
||||||
|
return skipDraw;
|
||||||
|
}
|
||||||
|
|
||||||
public float xPos() {
|
public float xPos() {
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,6 @@ public class MinecartVisual<T extends AbstractMinecart> extends ComponentEntityV
|
|||||||
private final Matrix4fStack stack = new Matrix4fStack(2);
|
private final Matrix4fStack stack = new Matrix4fStack(2);
|
||||||
|
|
||||||
private BlockState blockState;
|
private BlockState blockState;
|
||||||
private boolean active;
|
|
||||||
|
|
||||||
public MinecartVisual(VisualizationContext ctx, T entity, float partialTick, ModelLayerLocation layerLocation) {
|
public MinecartVisual(VisualizationContext ctx, T entity, float partialTick, ModelLayerLocation layerLocation) {
|
||||||
super(ctx, entity, partialTick);
|
super(ctx, entity, partialTick);
|
||||||
@ -65,11 +64,9 @@ public class MinecartVisual<T extends AbstractMinecart> extends ComponentEntityV
|
|||||||
RenderShape shape = blockState.getRenderShape();
|
RenderShape shape = blockState.getRenderShape();
|
||||||
|
|
||||||
if (shape == RenderShape.ENTITYBLOCK_ANIMATED) {
|
if (shape == RenderShape.ENTITYBLOCK_ANIMATED) {
|
||||||
instances.traverse(instance -> instance.setZeroTransform().setChanged());
|
instances.visible(false);
|
||||||
active = false;
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
active = true;
|
|
||||||
|
|
||||||
if (shape == RenderShape.INVISIBLE) {
|
if (shape == RenderShape.INVISIBLE) {
|
||||||
return null;
|
return null;
|
||||||
@ -100,8 +97,7 @@ public class MinecartVisual<T extends AbstractMinecart> extends ComponentEntityV
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: add proper way to temporarily disable rendering a specific instance
|
if (!instances.visible()) {
|
||||||
if (!active) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,36 +1,62 @@
|
|||||||
package dev.engine_room.flywheel.backend.glsl;
|
package dev.engine_room.flywheel.backend.glsl;
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.Deque;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Assertions;
|
|
||||||
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.server.packs.resources.ResourceManager;
|
|
||||||
|
|
||||||
public class MockShaderSources extends ShaderSources {
|
public class MockShaderSources {
|
||||||
private final Map<ResourceLocation, String> sources = new HashMap<>();
|
private final Map<ResourceLocation, String> sources = new HashMap<>();
|
||||||
|
private final Map<ResourceLocation, LoadResult> cache = new HashMap<>();
|
||||||
|
private final Deque<ResourceLocation> findStack = new ArrayDeque<>();
|
||||||
|
|
||||||
|
|
||||||
public MockShaderSources() {
|
public MockShaderSources() {
|
||||||
super(ResourceManager.Empty.INSTANCE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void add(ResourceLocation loc, String source) {
|
public void add(ResourceLocation loc, String source) {
|
||||||
sources.put(loc, source);
|
sources.put(loc, source);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public LoadResult find(ResourceLocation location) {
|
||||||
protected LoadResult load(ResourceLocation loc) {
|
if (findStack.contains(location)) {
|
||||||
|
// Make a copy of the find stack with the offending location added on top to show the full path.
|
||||||
|
findStack.addLast(location);
|
||||||
|
var copy = List.copyOf(findStack);
|
||||||
|
findStack.removeLast();
|
||||||
|
return new LoadResult.Failure(new LoadError.CircularDependency(location, copy));
|
||||||
|
}
|
||||||
|
findStack.addLast(location);
|
||||||
|
|
||||||
|
LoadResult out = load(location);
|
||||||
|
|
||||||
|
findStack.removeLast();
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
private LoadResult load(ResourceLocation loc) {
|
||||||
|
var out = cache.get(loc);
|
||||||
|
if (out != null) {
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
var loadResult = _load(loc);
|
||||||
|
|
||||||
|
cache.put(loc, loadResult);
|
||||||
|
|
||||||
|
return loadResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
private LoadResult _load(ResourceLocation loc) {
|
||||||
var maybeFound = sources.get(loc);
|
var maybeFound = sources.get(loc);
|
||||||
if (maybeFound == null) {
|
if (maybeFound == null) {
|
||||||
return new LoadResult.Failure(new LoadError.IOError(loc, new FileNotFoundException(loc.toString())));
|
return new LoadResult.Failure(new LoadError.IOError(loc, new FileNotFoundException(loc.toString())));
|
||||||
}
|
}
|
||||||
return SourceFile.parse(this, loc, maybeFound);
|
return SourceFile.parse(this::find, loc, maybeFound);
|
||||||
}
|
|
||||||
|
|
||||||
public LoadResult assertLoaded(ResourceLocation loc) {
|
|
||||||
Assertions.assertTrue(cache.containsKey(loc), "Expected " + loc + " to be cached");
|
|
||||||
return cache.get(loc);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,6 @@ public class TestShaderSourceLoading extends TestBase {
|
|||||||
sources.add(FLW_B, "");
|
sources.add(FLW_B, "");
|
||||||
|
|
||||||
findAndAssertSuccess(sources, FLW_A);
|
findAndAssertSuccess(sources, FLW_A);
|
||||||
sources.assertLoaded(FLW_B);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -78,7 +77,6 @@ public class TestShaderSourceLoading extends TestBase {
|
|||||||
sources.add(FLW_B, "");
|
sources.add(FLW_B, "");
|
||||||
|
|
||||||
SourceFile a = findAndAssertSuccess(sources, FLW_A);
|
SourceFile a = findAndAssertSuccess(sources, FLW_A);
|
||||||
sources.assertLoaded(FLW_B);
|
|
||||||
|
|
||||||
var includeB = assertSingletonList(a.imports);
|
var includeB = assertSingletonList(a.imports);
|
||||||
assertEquals(FLW_B.toString(), includeB.file()
|
assertEquals(FLW_B.toString(), includeB.file()
|
||||||
@ -99,7 +97,6 @@ public class TestShaderSourceLoading extends TestBase {
|
|||||||
sources.add(FLW_B, "");
|
sources.add(FLW_B, "");
|
||||||
|
|
||||||
SourceFile a = findAndAssertSuccess(sources, FLW_A);
|
SourceFile a = findAndAssertSuccess(sources, FLW_A);
|
||||||
sources.assertLoaded(FLW_B);
|
|
||||||
|
|
||||||
assertEquals(2, a.imports.size());
|
assertEquals(2, a.imports.size());
|
||||||
for (Import include : a.imports) {
|
for (Import include : a.imports) {
|
||||||
@ -112,7 +109,7 @@ public class TestShaderSourceLoading extends TestBase {
|
|||||||
|
|
||||||
""", a.finalSource, "Both include statements should be elided.");
|
""", a.finalSource, "Both include statements should be elided.");
|
||||||
|
|
||||||
LoadResult bResult = sources.assertLoaded(FLW_B);
|
LoadResult bResult = sources.find(FLW_B);
|
||||||
SourceFile b = assertSuccessAndUnwrap(FLW_B, bResult);
|
SourceFile b = assertSuccessAndUnwrap(FLW_B, bResult);
|
||||||
|
|
||||||
assertEquals(ImmutableList.of(b), a.included);
|
assertEquals(ImmutableList.of(b), a.included);
|
||||||
@ -148,7 +145,6 @@ public class TestShaderSourceLoading extends TestBase {
|
|||||||
""");
|
""");
|
||||||
|
|
||||||
var aErr = findAndAssertError(LoadError.IncludeError.class, sources, FLW_A);
|
var aErr = findAndAssertError(LoadError.IncludeError.class, sources, FLW_A);
|
||||||
sources.assertLoaded(FLW_B);
|
|
||||||
|
|
||||||
var recursiveInclude = assertSimpleNestedErrorsToDepth(LoadError.CircularDependency.class, aErr, 2);
|
var recursiveInclude = assertSimpleNestedErrorsToDepth(LoadError.CircularDependency.class, aErr, 2);
|
||||||
assertEquals(ImmutableList.of(FLW_A, FLW_B, FLW_A), recursiveInclude.stack());
|
assertEquals(ImmutableList.of(FLW_A, FLW_B, FLW_A), recursiveInclude.stack());
|
||||||
@ -168,8 +164,6 @@ public class TestShaderSourceLoading extends TestBase {
|
|||||||
""");
|
""");
|
||||||
|
|
||||||
var aErr = findAndAssertError(LoadError.IncludeError.class, sources, FLW_A);
|
var aErr = findAndAssertError(LoadError.IncludeError.class, sources, FLW_A);
|
||||||
sources.assertLoaded(FLW_B);
|
|
||||||
sources.assertLoaded(FLW_C);
|
|
||||||
|
|
||||||
var recursiveInclude = assertSimpleNestedErrorsToDepth(LoadError.CircularDependency.class, aErr, 3);
|
var recursiveInclude = assertSimpleNestedErrorsToDepth(LoadError.CircularDependency.class, aErr, 3);
|
||||||
assertEquals(ImmutableList.of(FLW_A, FLW_B, FLW_C, FLW_A), recursiveInclude.stack());
|
assertEquals(ImmutableList.of(FLW_A, FLW_B, FLW_C, FLW_A), recursiveInclude.stack());
|
||||||
|
Loading…
Reference in New Issue
Block a user