Spaces:
Build error
Build error
package net.minecraft.client.renderer; | |
import com.google.common.collect.ImmutableMap; | |
import com.google.common.collect.ImmutableMap.Builder; | |
import com.google.gson.JsonElement; | |
import com.google.gson.JsonParseException; | |
import com.google.gson.JsonParser; | |
import com.google.gson.JsonSyntaxException; | |
import com.mojang.blaze3d.preprocessor.GlslPreprocessor; | |
import com.mojang.blaze3d.shaders.CompiledShader; | |
import com.mojang.blaze3d.systems.RenderSystem; | |
import com.mojang.logging.LogUtils; | |
import com.mojang.serialization.JsonOps; | |
import it.unimi.dsi.fastutil.objects.ObjectArraySet; | |
import java.io.IOException; | |
import java.io.Reader; | |
import java.util.HashMap; | |
import java.util.HashSet; | |
import java.util.Map; | |
import java.util.Optional; | |
import java.util.Set; | |
import java.util.Map.Entry; | |
import java.util.function.Consumer; | |
import java.util.stream.Collectors; | |
import javax.annotation.Nullable; | |
import net.minecraft.FileUtil; | |
import net.minecraft.ResourceLocationException; | |
import net.minecraft.client.renderer.texture.TextureManager; | |
import net.minecraft.resources.FileToIdConverter; | |
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.ResourceProvider; | |
import net.minecraft.server.packs.resources.SimplePreparableReloadListener; | |
import net.minecraft.util.profiling.ProfilerFiller; | |
import net.minecraftforge.api.distmarker.Dist; | |
import net.minecraftforge.api.distmarker.OnlyIn; | |
import org.apache.commons.io.IOUtils; | |
import org.slf4j.Logger; | |
public class ShaderManager extends SimplePreparableReloadListener<ShaderManager.Configs> implements AutoCloseable { | |
static final Logger LOGGER = LogUtils.getLogger(); | |
public static final String SHADER_PATH = "shaders"; | |
public static final String SHADER_INCLUDE_PATH = "shaders/include/"; | |
private static final FileToIdConverter PROGRAM_ID_CONVERTER = FileToIdConverter.json("shaders"); | |
private static final FileToIdConverter POST_CHAIN_ID_CONVERTER = FileToIdConverter.json("post_effect"); | |
public static final int MAX_LOG_LENGTH = 32768; | |
final TextureManager textureManager; | |
private final Consumer<Exception> recoveryHandler; | |
private ShaderManager.CompilationCache compilationCache = new ShaderManager.CompilationCache(ShaderManager.Configs.EMPTY); | |
public ShaderManager(TextureManager p_360733_, Consumer<Exception> p_367243_) { | |
this.textureManager = p_360733_; | |
this.recoveryHandler = p_367243_; | |
} | |
protected ShaderManager.Configs prepare(ResourceManager p_363890_, ProfilerFiller p_362646_) { | |
Builder<ResourceLocation, ShaderProgramConfig> builder = ImmutableMap.builder(); | |
Builder<ShaderManager.ShaderSourceKey, String> builder1 = ImmutableMap.builder(); | |
Map<ResourceLocation, Resource> map = p_363890_.listResources("shaders", p_362430_ -> isProgram(p_362430_) || isShader(p_362430_)); | |
for (Entry<ResourceLocation, Resource> entry : map.entrySet()) { | |
ResourceLocation resourcelocation = entry.getKey(); | |
CompiledShader.Type compiledshader$type = CompiledShader.Type.byLocation(resourcelocation); | |
if (compiledshader$type != null) { | |
loadShader(resourcelocation, entry.getValue(), compiledshader$type, map, builder1); | |
} else if (isProgram(resourcelocation)) { | |
loadProgram(resourcelocation, entry.getValue(), builder); | |
} | |
} | |
Builder<ResourceLocation, PostChainConfig> builder2 = ImmutableMap.builder(); | |
for (Entry<ResourceLocation, Resource> entry1 : POST_CHAIN_ID_CONVERTER.listMatchingResources(p_363890_).entrySet()) { | |
loadPostChain(entry1.getKey(), entry1.getValue(), builder2); | |
} | |
return new ShaderManager.Configs(builder.build(), builder1.build(), builder2.build()); | |
} | |
private static void loadShader( | |
ResourceLocation p_369261_, | |
Resource p_361062_, | |
CompiledShader.Type p_365455_, | |
Map<ResourceLocation, Resource> p_367069_, | |
Builder<ShaderManager.ShaderSourceKey, String> p_365134_ | |
) { | |
ResourceLocation resourcelocation = p_365455_.idConverter().fileToId(p_369261_); | |
GlslPreprocessor glslpreprocessor = createPreprocessor(p_367069_, p_369261_); | |
try (Reader reader = p_361062_.openAsReader()) { | |
String s = IOUtils.toString(reader); | |
p_365134_.put(new ShaderManager.ShaderSourceKey(resourcelocation, p_365455_), String.join("", glslpreprocessor.process(s))); | |
} catch (IOException ioexception) { | |
LOGGER.error("Failed to load shader source at {}", p_369261_, ioexception); | |
} | |
} | |
private static GlslPreprocessor createPreprocessor(final Map<ResourceLocation, Resource> p_367930_, ResourceLocation p_369394_) { | |
final ResourceLocation resourcelocation = p_369394_.withPath(FileUtil::getFullResourcePath); | |
return new GlslPreprocessor() { | |
private final Set<ResourceLocation> importedLocations = new ObjectArraySet<>(); | |
public String applyImport(boolean p_365562_, String p_361440_) { | |
ResourceLocation resourcelocation1; | |
try { | |
if (p_365562_) { | |
resourcelocation1 = resourcelocation.withPath(p_366909_ -> FileUtil.normalizeResourcePath(p_366909_ + p_361440_)); | |
} else { | |
resourcelocation1 = ResourceLocation.parse(p_361440_).withPrefix("shaders/include/"); | |
} | |
} catch (ResourceLocationException resourcelocationexception) { | |
ShaderManager.LOGGER.error("Malformed GLSL import {}: {}", p_361440_, resourcelocationexception.getMessage()); | |
return "#error " + resourcelocationexception.getMessage(); | |
} | |
if (!this.importedLocations.add(resourcelocation1)) { | |
return null; | |
} else { | |
try { | |
String s; | |
try (Reader reader = p_367930_.get(resourcelocation1).openAsReader()) { | |
s = IOUtils.toString(reader); | |
} | |
return s; | |
} catch (IOException ioexception) { | |
ShaderManager.LOGGER.error("Could not open GLSL import {}: {}", resourcelocation1, ioexception.getMessage()); | |
return "#error " + ioexception.getMessage(); | |
} | |
} | |
} | |
}; | |
} | |
private static void loadProgram(ResourceLocation p_365990_, Resource p_366934_, Builder<ResourceLocation, ShaderProgramConfig> p_366842_) { | |
ResourceLocation resourcelocation = PROGRAM_ID_CONVERTER.fileToId(p_365990_); | |
try (Reader reader = p_366934_.openAsReader()) { | |
JsonElement jsonelement = JsonParser.parseReader(reader); | |
ShaderProgramConfig shaderprogramconfig = ShaderProgramConfig.CODEC.parse(JsonOps.INSTANCE, jsonelement).getOrThrow(JsonSyntaxException::new); | |
p_366842_.put(resourcelocation, shaderprogramconfig); | |
} catch (JsonParseException | IOException ioexception) { | |
LOGGER.error("Failed to parse shader config at {}", p_365990_, ioexception); | |
} | |
} | |
private static void loadPostChain(ResourceLocation p_365599_, Resource p_365135_, Builder<ResourceLocation, PostChainConfig> p_362996_) { | |
ResourceLocation resourcelocation = POST_CHAIN_ID_CONVERTER.fileToId(p_365599_); | |
try (Reader reader = p_365135_.openAsReader()) { | |
JsonElement jsonelement = JsonParser.parseReader(reader); | |
p_362996_.put(resourcelocation, PostChainConfig.CODEC.parse(JsonOps.INSTANCE, jsonelement).getOrThrow(JsonSyntaxException::new)); | |
} catch (JsonParseException | IOException ioexception) { | |
LOGGER.error("Failed to parse post chain at {}", p_365599_, ioexception); | |
} | |
} | |
private static boolean isProgram(ResourceLocation p_368414_) { | |
return p_368414_.getPath().endsWith(".json"); | |
} | |
private static boolean isShader(ResourceLocation p_368473_) { | |
return CompiledShader.Type.byLocation(p_368473_) != null || p_368473_.getPath().endsWith(".glsl"); | |
} | |
protected void apply(ShaderManager.Configs p_360858_, ResourceManager p_369986_, ProfilerFiller p_364135_) { | |
ShaderManager.CompilationCache shadermanager$compilationcache = new ShaderManager.CompilationCache(p_360858_); | |
Map<ShaderProgram, ShaderManager.CompilationException> map = new HashMap<>(); | |
Set<ShaderProgram> set = new HashSet<>(CoreShaders.getProgramsToPreload()); | |
for (PostChainConfig postchainconfig : p_360858_.postChains.values()) { | |
for (PostChainConfig.Pass postchainconfig$pass : postchainconfig.passes()) { | |
set.add(postchainconfig$pass.program()); | |
} | |
} | |
for (ShaderProgram shaderprogram : set) { | |
try { | |
shadermanager$compilationcache.programs.put(shaderprogram, Optional.of(shadermanager$compilationcache.compileProgram(shaderprogram))); | |
} catch (ShaderManager.CompilationException shadermanager$compilationexception) { | |
map.put(shaderprogram, shadermanager$compilationexception); | |
} | |
} | |
if (!map.isEmpty()) { | |
shadermanager$compilationcache.close(); | |
throw new RuntimeException( | |
"Failed to load required shader programs:\n" | |
+ map.entrySet() | |
.stream() | |
.map(p_366321_ -> " - " + p_366321_.getKey() + ": " + p_366321_.getValue().getMessage()) | |
.collect(Collectors.joining("\n")) | |
); | |
} else { | |
this.compilationCache.close(); | |
this.compilationCache = shadermanager$compilationcache; | |
} | |
} | |
public String getName() { | |
return "Shader Loader"; | |
} | |
private void tryTriggerRecovery(Exception p_378248_) { | |
if (!this.compilationCache.triggeredRecovery) { | |
this.recoveryHandler.accept(p_378248_); | |
this.compilationCache.triggeredRecovery = true; | |
} | |
} | |
public void preloadForStartup(ResourceProvider p_367540_, ShaderProgram... p_362777_) throws IOException, ShaderManager.CompilationException { | |
for (ShaderProgram shaderprogram : p_362777_) { | |
Resource resource = p_367540_.getResourceOrThrow(PROGRAM_ID_CONVERTER.idToFile(shaderprogram.configId())); | |
try (Reader reader = resource.openAsReader()) { | |
JsonElement jsonelement = JsonParser.parseReader(reader); | |
ShaderProgramConfig shaderprogramconfig = ShaderProgramConfig.CODEC | |
.parse(JsonOps.INSTANCE, jsonelement) | |
.getOrThrow(JsonSyntaxException::new); | |
ShaderDefines shaderdefines = shaderprogramconfig.defines().withOverrides(shaderprogram.defines()); | |
CompiledShader compiledshader = this.preloadShader(p_367540_, shaderprogramconfig.vertex(), CompiledShader.Type.VERTEX, shaderdefines); | |
CompiledShader compiledshader1 = this.preloadShader(p_367540_, shaderprogramconfig.fragment(), CompiledShader.Type.FRAGMENT, shaderdefines); | |
CompiledShaderProgram compiledshaderprogram = linkProgram(shaderprogram, shaderprogramconfig, compiledshader, compiledshader1); | |
this.compilationCache.programs.put(shaderprogram, Optional.of(compiledshaderprogram)); | |
} | |
} | |
} | |
private CompiledShader preloadShader(ResourceProvider p_363994_, ResourceLocation p_360916_, CompiledShader.Type p_362265_, ShaderDefines p_368503_) throws IOException, ShaderManager.CompilationException { | |
ResourceLocation resourcelocation = p_362265_.idConverter().idToFile(p_360916_); | |
CompiledShader compiledshader1; | |
try (Reader reader = p_363994_.getResourceOrThrow(resourcelocation).openAsReader()) { | |
String s = IOUtils.toString(reader); | |
String s1 = GlslPreprocessor.injectDefines(s, p_368503_); | |
CompiledShader compiledshader = CompiledShader.compile(p_360916_, p_362265_, s1); | |
this.compilationCache.shaders.put(new ShaderManager.ShaderCompilationKey(p_360916_, p_362265_, p_368503_), compiledshader); | |
compiledshader1 = compiledshader; | |
} | |
return compiledshader1; | |
} | |
public CompiledShaderProgram getProgram(ShaderProgram p_362106_) { | |
try { | |
return this.compilationCache.getOrCompileProgram(p_362106_); | |
} catch (ShaderManager.CompilationException shadermanager$compilationexception) { | |
LOGGER.error("Failed to load shader program: {}", p_362106_, shadermanager$compilationexception); | |
this.compilationCache.programs.put(p_362106_, Optional.empty()); | |
this.tryTriggerRecovery(shadermanager$compilationexception); | |
return null; | |
} | |
} | |
public CompiledShaderProgram getProgramForLoading(ShaderProgram p_365077_) throws ShaderManager.CompilationException { | |
CompiledShaderProgram compiledshaderprogram = this.compilationCache.getOrCompileProgram(p_365077_); | |
if (compiledshaderprogram == null) { | |
throw new ShaderManager.CompilationException("Shader '" + p_365077_ + "' could not be found"); | |
} else { | |
return compiledshaderprogram; | |
} | |
} | |
static CompiledShaderProgram linkProgram(ShaderProgram p_368435_, ShaderProgramConfig p_369334_, CompiledShader p_361097_, CompiledShader p_364151_) throws ShaderManager.CompilationException { | |
CompiledShaderProgram compiledshaderprogram = CompiledShaderProgram.link(p_361097_, p_364151_, p_368435_.vertexFormat()); | |
compiledshaderprogram.setupUniforms(p_369334_.uniforms(), p_369334_.samplers()); | |
return compiledshaderprogram; | |
} | |
public PostChain getPostChain(ResourceLocation p_370004_, Set<ResourceLocation> p_362698_) { | |
try { | |
return this.compilationCache.getOrLoadPostChain(p_370004_, p_362698_); | |
} catch (ShaderManager.CompilationException shadermanager$compilationexception) { | |
LOGGER.error("Failed to load post chain: {}", p_370004_, shadermanager$compilationexception); | |
this.compilationCache.postChains.put(p_370004_, Optional.empty()); | |
this.tryTriggerRecovery(shadermanager$compilationexception); | |
return null; | |
} | |
} | |
public void close() { | |
this.compilationCache.close(); | |
} | |
class CompilationCache implements AutoCloseable { | |
private final ShaderManager.Configs configs; | |
final Map<ShaderProgram, Optional<CompiledShaderProgram>> programs = new HashMap<>(); | |
final Map<ShaderManager.ShaderCompilationKey, CompiledShader> shaders = new HashMap<>(); | |
final Map<ResourceLocation, Optional<PostChain>> postChains = new HashMap<>(); | |
boolean triggeredRecovery; | |
CompilationCache(final ShaderManager.Configs p_369367_) { | |
this.configs = p_369367_; | |
} | |
public CompiledShaderProgram getOrCompileProgram(ShaderProgram p_362727_) throws ShaderManager.CompilationException { | |
Optional<CompiledShaderProgram> optional = this.programs.get(p_362727_); | |
if (optional != null) { | |
return optional.orElse(null); | |
} else { | |
CompiledShaderProgram compiledshaderprogram = this.compileProgram(p_362727_); | |
this.programs.put(p_362727_, Optional.of(compiledshaderprogram)); | |
return compiledshaderprogram; | |
} | |
} | |
CompiledShaderProgram compileProgram(ShaderProgram p_362724_) throws ShaderManager.CompilationException { | |
ShaderProgramConfig shaderprogramconfig = this.configs.programs.get(p_362724_.configId()); | |
if (shaderprogramconfig == null) { | |
throw new ShaderManager.CompilationException("Could not find program with id: " + p_362724_.configId()); | |
} else { | |
ShaderDefines shaderdefines = shaderprogramconfig.defines().withOverrides(p_362724_.defines()); | |
CompiledShader compiledshader = this.getOrCompileShader(shaderprogramconfig.vertex(), CompiledShader.Type.VERTEX, shaderdefines); | |
CompiledShader compiledshader1 = this.getOrCompileShader(shaderprogramconfig.fragment(), CompiledShader.Type.FRAGMENT, shaderdefines); | |
return ShaderManager.linkProgram(p_362724_, shaderprogramconfig, compiledshader, compiledshader1); | |
} | |
} | |
private CompiledShader getOrCompileShader(ResourceLocation p_362898_, CompiledShader.Type p_364365_, ShaderDefines p_369295_) throws ShaderManager.CompilationException { | |
ShaderManager.ShaderCompilationKey shadermanager$shadercompilationkey = new ShaderManager.ShaderCompilationKey(p_362898_, p_364365_, p_369295_); | |
CompiledShader compiledshader = this.shaders.get(shadermanager$shadercompilationkey); | |
if (compiledshader == null) { | |
compiledshader = this.compileShader(shadermanager$shadercompilationkey); | |
this.shaders.put(shadermanager$shadercompilationkey, compiledshader); | |
} | |
return compiledshader; | |
} | |
private CompiledShader compileShader(ShaderManager.ShaderCompilationKey p_369853_) throws ShaderManager.CompilationException { | |
String s = this.configs.shaderSources.get(new ShaderManager.ShaderSourceKey(p_369853_.id, p_369853_.type)); | |
if (s == null) { | |
throw new ShaderManager.CompilationException("Could not find shader: " + p_369853_); | |
} else { | |
String s1 = GlslPreprocessor.injectDefines(s, p_369853_.defines); | |
return CompiledShader.compile(p_369853_.id, p_369853_.type, s1); | |
} | |
} | |
public PostChain getOrLoadPostChain(ResourceLocation p_362197_, Set<ResourceLocation> p_368742_) throws ShaderManager.CompilationException { | |
Optional<PostChain> optional = this.postChains.get(p_362197_); | |
if (optional != null) { | |
return optional.orElse(null); | |
} else { | |
PostChain postchain = this.loadPostChain(p_362197_, p_368742_); | |
this.postChains.put(p_362197_, Optional.of(postchain)); | |
return postchain; | |
} | |
} | |
private PostChain loadPostChain(ResourceLocation p_366740_, Set<ResourceLocation> p_366419_) throws ShaderManager.CompilationException { | |
PostChainConfig postchainconfig = this.configs.postChains.get(p_366740_); | |
if (postchainconfig == null) { | |
throw new ShaderManager.CompilationException("Could not find post chain with id: " + p_366740_); | |
} else { | |
return PostChain.load(postchainconfig, ShaderManager.this.textureManager, ShaderManager.this, p_366419_); | |
} | |
} | |
public void close() { | |
RenderSystem.assertOnRenderThread(); | |
this.programs.values().forEach(p_365427_ -> p_365427_.ifPresent(CompiledShaderProgram::close)); | |
this.shaders.values().forEach(CompiledShader::close); | |
this.programs.clear(); | |
this.shaders.clear(); | |
this.postChains.clear(); | |
} | |
} | |
public static class CompilationException extends Exception { | |
public CompilationException(String p_366142_) { | |
super(p_366142_); | |
} | |
} | |
public static record Configs( | |
Map<ResourceLocation, ShaderProgramConfig> programs, | |
Map<ShaderManager.ShaderSourceKey, String> shaderSources, | |
Map<ResourceLocation, PostChainConfig> postChains | |
) { | |
public static final ShaderManager.Configs EMPTY = new ShaderManager.Configs(Map.of(), Map.of(), Map.of()); | |
} | |
static record ShaderCompilationKey(ResourceLocation id, CompiledShader.Type type, ShaderDefines defines) { | |
public String toString() { | |
String s = this.id + " (" + this.type + ")"; | |
return !this.defines.isEmpty() ? s + " with " + this.defines : s; | |
} | |
} | |
static record ShaderSourceKey(ResourceLocation id, CompiledShader.Type type) { | |
public String toString() { | |
return this.id + " (" + this.type + ")"; | |
} | |
} | |
} |