diff --git a/src/main/java/net/vulkanmod/vulkan/shader/SPIRVUtils.java b/src/main/java/net/vulkanmod/vulkan/shader/SPIRVUtils.java index 8238611c2..259e2eca3 100644 --- a/src/main/java/net/vulkanmod/vulkan/shader/SPIRVUtils.java +++ b/src/main/java/net/vulkanmod/vulkan/shader/SPIRVUtils.java @@ -1,44 +1,56 @@ package net.vulkanmod.vulkan.shader; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import org.lwjgl.system.MemoryStack; import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.NativeResource; +import org.lwjgl.util.shaderc.ShadercIncludeResolveI; +import org.lwjgl.util.shaderc.ShadercIncludeResult; +import org.lwjgl.util.shaderc.ShadercIncludeResultReleaseI; +import org.lwjgl.vulkan.VK12; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.net.URISyntaxException; +import java.net.URL; import java.nio.ByteBuffer; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import static org.lwjgl.system.MemoryUtil.NULL; +import static org.lwjgl.system.MemoryUtil.memASCII; import static org.lwjgl.util.shaderc.Shaderc.*; public class SPIRVUtils { - private static final boolean DEBUG = true; + private static final boolean DEBUG = false; private static final boolean OPTIMIZATIONS = false; private static long compiler; + private static long options; - public static SPIRV compileShaderAbsoluteFile(String shaderFile, ShaderKind shaderKind) { - try { - String source = new String(Files.readAllBytes(Paths.get(new URI(shaderFile)))); - return compileShader(shaderFile, source, shaderKind); - } catch (IOException | URISyntaxException e) { - e.printStackTrace(); - } - return null; - } + //The dedicated Includer and Releaser Inner Classes used to Initialise #include Support for ShaderC + private static final ShaderIncluder SHADER_INCLUDER = new ShaderIncluder(); + private static final ShaderReleaser SHADER_RELEASER = new ShaderReleaser(); + private static final long pUserData = 0; - public static SPIRV compileShader(String filename, String source, ShaderKind shaderKind) { + private static ObjectArrayList includePaths; + + private static float time = 0.0f; - if(compiler == 0) compiler = shaderc_compiler_initialize(); + static { + initCompiler(); + } + + private static void initCompiler() { + compiler = shaderc_compiler_initialize(); if(compiler == NULL) { throw new RuntimeException("Failed to create shader compiler"); } - long options = shaderc_compile_options_initialize(); + options = shaderc_compile_options_initialize(); if(options == NULL) { throw new RuntimeException("Failed to create compiler options"); @@ -50,6 +62,33 @@ public static SPIRV compileShader(String filename, String source, ShaderKind sha if(DEBUG) shaderc_compile_options_set_generate_debug_info(options); + shaderc_compile_options_set_target_env(options, shaderc_env_version_vulkan_1_2, VK12.VK_API_VERSION_1_2); + shaderc_compile_options_set_include_callbacks(options, SHADER_INCLUDER, SHADER_RELEASER, pUserData); + + includePaths = new ObjectArrayList<>(); + addIncludePath("/assets/vulkanmod/shaders/include/"); + } + + public static void addIncludePath(String path) { + URL url = SPIRVUtils.class.getResource(path); + + if(url != null) + includePaths.add(url.toExternalForm()); + } + + public static SPIRV compileShaderAbsoluteFile(String shaderFile, ShaderKind shaderKind) { + try { + String source = new String(Files.readAllBytes(Paths.get(new URI(shaderFile)))); + return compileShader(shaderFile, source, shaderKind); + } catch (IOException | URISyntaxException e) { + e.printStackTrace(); + } + return null; + } + + public static SPIRV compileShader(String filename, String source, ShaderKind shaderKind) { + long startTime = System.nanoTime(); + long result = shaderc_compile_into_spv(compiler, source, shaderKind.kind, filename, "main", options); if(result == NULL) { @@ -60,6 +99,8 @@ public static SPIRV compileShader(String filename, String source, ShaderKind sha throw new RuntimeException("Failed to compile shader " + filename + " into SPIR-V:\n" + shaderc_result_get_error_message(result)); } + time += (System.nanoTime() - startTime) / 1000000.0f; + return new SPIRV(result, shaderc_result_get_bytes(result)); } @@ -90,6 +131,48 @@ public enum ShaderKind { } } + private static class ShaderIncluder implements ShadercIncludeResolveI { + + private static final int MAX_PATH_LENGTH = 4096; //Maximum Linux/Unix Path Length + + @Override + public long invoke(long user_data, long requested_source, int type, long requesting_source, long include_depth) { + var requesting = memASCII(requesting_source); + var requested = memASCII(requested_source); + + try(MemoryStack stack = MemoryStack.stackPush()) { + Path path; + + for(String includePath : includePaths) { + path = Paths.get(new URI(String.format("%s%s", includePath, requested))); + + if(Files.exists(path)) { + byte[] bytes = Files.readAllBytes(path); + + return ShadercIncludeResult.malloc(stack) + .source_name(stack.ASCII(requested)) + .content(stack.bytes(bytes)) + .user_data(user_data).address(); + } + } + } catch (IOException | URISyntaxException e) { + throw new RuntimeException(e); + } + + throw new RuntimeException(String.format("%s: Unable to find %s in include paths", requesting, requested)); + } + } + + //TODO: Don't actually need the Releaser at all, (MemoryStack frees this for us) + //But ShaderC won't let us create the Includer without a corresponding Releaser, (so we need it anyway) + private static class ShaderReleaser implements ShadercIncludeResultReleaseI { + + @Override + public void invoke(long user_data, long include_result) { + //TODO:Maybe dump Shader Compiled Binaries here to a .Misc Diretcory to allow easy caching.recompilation... + } + } + public static final class SPIRV implements NativeResource { private final long handle; diff --git a/src/main/resources/assets/vulkanmod/shaders/basic/terrain_direct/terrain_direct.fsh b/src/main/resources/assets/vulkanmod/shaders/basic/terrain_direct/terrain_direct.fsh index ae9df2593..81a508fd6 100644 --- a/src/main/resources/assets/vulkanmod/shaders/basic/terrain_direct/terrain_direct.fsh +++ b/src/main/resources/assets/vulkanmod/shaders/basic/terrain_direct/terrain_direct.fsh @@ -1,13 +1,6 @@ #version 450 -vec4 linear_fog(vec4 inColor, float vertexDistance, float fogStart, float fogEnd, vec4 fogColor) { - if (vertexDistance <= fogStart) { - return inColor; - } - - float fogValue = vertexDistance < fogEnd ? smoothstep(fogStart, fogEnd, vertexDistance) : 1.0; - return vec4(mix(inColor.rgb, fogColor.rgb, fogValue * fogColor.a), inColor.a); -} +#include "light.glsl" layout(binding = 2) uniform sampler2D Sampler0; diff --git a/src/main/resources/assets/vulkanmod/shaders/basic/terrain_direct/terrain_direct.vsh b/src/main/resources/assets/vulkanmod/shaders/basic/terrain_direct/terrain_direct.vsh index aaa913505..a08f80d2f 100644 --- a/src/main/resources/assets/vulkanmod/shaders/basic/terrain_direct/terrain_direct.vsh +++ b/src/main/resources/assets/vulkanmod/shaders/basic/terrain_direct/terrain_direct.vsh @@ -1,12 +1,6 @@ #version 460 -//light.glsl -#define MINECRAFT_LIGHT_POWER 0.6 -#define MINECRAFT_AMBIENT_LIGHT 0.4 - -vec4 minecraft_sample_lightmap(sampler2D lightMap, ivec2 uv) { - return texelFetch(lightMap, (uv & 255) >> 4, 0); -} +#include "light.glsl" layout(binding = 0) uniform UniformBufferObject { mat4 MVP; @@ -24,9 +18,7 @@ layout(location = 1) out vec4 vertexColor; layout(location = 2) out vec2 texCoord0; //layout(location = 3) out vec4 normal; -#define COMPRESSED_VERTEX -#ifdef COMPRESSED_VERTEX - +//Compressed Vertex layout(location = 0) in ivec3 Position; layout(location = 1) in vec4 Color; layout(location = 2) in uvec2 UV0; @@ -41,26 +33,23 @@ void main() { gl_Position = MVP * vec4(pos + ChunkOffset, 1.0); vertexDistance = length((ModelViewMat * vec4(pos + ChunkOffset, 1.0)).xyz); - vertexColor = Color * minecraft_sample_lightmap(Sampler2, UV2); + vertexColor = Color * sample_lightmap(Sampler2, UV2); texCoord0 = UV0 * UV_INV; // normal = MVP * vec4(Normal, 0.0); } -#else - -layout(location = 0) in vec3 Position; -layout(location = 1) in vec4 Color; -layout(location = 2) in vec2 UV0; -layout(location = 3) in ivec2 UV2; -layout(location = 4) in vec3 Normal; - -void main() { - gl_Position = MVP * vec4(Position + ChunkOffset, 1.0); - - vertexDistance = length((ModelViewMat * vec4(Position + ChunkOffset, 1.0)).xyz); - vertexColor = Color * minecraft_sample_lightmap(Sampler2, UV2); - texCoord0 = UV0; - // normal = MVP * vec4(Normal, 0.0); -} +//Default Vertex +//layout(location = 0) in vec3 Position; +//layout(location = 1) in vec4 Color; +//layout(location = 2) in vec2 UV0; +//layout(location = 3) in ivec2 UV2; +//layout(location = 4) in vec3 Normal; -#endif \ No newline at end of file +//void main() { +// gl_Position = MVP * vec4(Position + ChunkOffset, 1.0); +// +// vertexDistance = length((ModelViewMat * vec4(Position + ChunkOffset, 1.0)).xyz); +// vertexColor = Color * minecraft_sample_lightmap(Sampler2, UV2); +// texCoord0 = UV0; +// // normal = MVP * vec4(Normal, 0.0); +//} \ No newline at end of file diff --git a/src/main/resources/assets/vulkanmod/shaders/basic/terrain_indirect/terrain_indirect.fsh b/src/main/resources/assets/vulkanmod/shaders/basic/terrain_indirect/terrain_indirect.fsh index ce60b3fb9..6880c1e94 100644 --- a/src/main/resources/assets/vulkanmod/shaders/basic/terrain_indirect/terrain_indirect.fsh +++ b/src/main/resources/assets/vulkanmod/shaders/basic/terrain_indirect/terrain_indirect.fsh @@ -1,13 +1,6 @@ #version 450 -vec4 linear_fog(vec4 inColor, float vertexDistance, float fogStart, float fogEnd, vec4 fogColor) { - if (vertexDistance <= fogStart) { - return inColor; - } - - float fogValue = vertexDistance < fogEnd ? smoothstep(fogStart, fogEnd, vertexDistance) : 1.0; - return vec4(mix(inColor.rgb, fogColor.rgb, fogValue * fogColor.a), inColor.a); -} +#include "light.glsl" layout(binding = 3) uniform sampler2D Sampler0; diff --git a/src/main/resources/assets/vulkanmod/shaders/basic/terrain_indirect/terrain_indirect.vsh b/src/main/resources/assets/vulkanmod/shaders/basic/terrain_indirect/terrain_indirect.vsh index 17b12036d..b6bfcd991 100644 --- a/src/main/resources/assets/vulkanmod/shaders/basic/terrain_indirect/terrain_indirect.vsh +++ b/src/main/resources/assets/vulkanmod/shaders/basic/terrain_indirect/terrain_indirect.vsh @@ -2,9 +2,7 @@ #define MAX_OFFSET_COUNT 512 -vec4 minecraft_sample_lightmap(sampler2D lightMap, ivec2 uv) { - return texelFetch(lightMap, (uv & 255) >> 4, 0); -} +#include "light.glsl" layout(binding = 0) uniform UniformBufferObject { mat4 MVP; @@ -38,7 +36,7 @@ void main() { gl_Position = MVP * vec4(pos + ChunkOffset[gl_DrawID], 1.0); vertexDistance = length((ModelViewMat * vec4(pos + ChunkOffset[gl_DrawID], 1.0)).xyz); - vertexColor = Color * minecraft_sample_lightmap(Sampler2, UV2); + vertexColor = Color * sample_lightmap(Sampler2, UV2); texCoord0 = UV0 * UV_INV; // normal = MVP * vec4(Normal, 0.0); } diff --git a/src/main/resources/assets/vulkanmod/shaders/include/light.glsl b/src/main/resources/assets/vulkanmod/shaders/include/light.glsl new file mode 100644 index 000000000..d7b6b19c5 --- /dev/null +++ b/src/main/resources/assets/vulkanmod/shaders/include/light.glsl @@ -0,0 +1,29 @@ +//light.glsl +#define MINECRAFT_LIGHT_POWER (0.6) +#define MINECRAFT_AMBIENT_LIGHT (0.4) + +vec4 minecraft_mix_light(vec3 lightDir0, vec3 lightDir1, vec3 normal, vec4 color) { + lightDir0 = normalize(lightDir0); + lightDir1 = normalize(lightDir1); + float light0 = max(0.0, dot(lightDir0, normal)); + float light1 = max(0.0, dot(lightDir1, normal)); + float lightAccum = min(1.0, (light0 + light1) * MINECRAFT_LIGHT_POWER + MINECRAFT_AMBIENT_LIGHT); + return vec4(color.rgb * lightAccum, color.a); +} + +//vec4 minecraft_sample_lightmap(sampler2D lightMap, ivec2 uv) { +// return texture(lightMap, clamp(uv / 256.0, vec2(0.5 / 16.0), vec2(15.5 / 16.0))); +//} + +vec4 sample_lightmap(sampler2D lightMap, ivec2 uv) { + return texelFetch(lightMap, (uv & 255) >> 4, 0); +} + +vec4 linear_fog(vec4 inColor, float vertexDistance, float fogStart, float fogEnd, vec4 fogColor) { + if (vertexDistance <= fogStart) { + return inColor; + } + + float fogValue = vertexDistance < fogEnd ? smoothstep(fogStart, fogEnd, vertexDistance) : 1.0; + return vec4(mix(inColor.rgb, fogColor.rgb, fogValue * fogColor.a), inColor.a); +} \ No newline at end of file