veldrid-spirv icon indicating copy to clipboard operation
veldrid-spirv copied to clipboard

ShaderDescription entryPoint different to "main"

Open pzgulyas opened this issue 6 years ago • 5 comments

When I try to load a shader with an entryPoint of the ShaderDescription set to something else than "main", I get a null reference exception in CreateFromSpirv, no matter if that function actually exists in shader or not.

(Since includes are quite mystical to mee, how they are supposed to work in glsl and also here, my idea was to just shovel everything into a single file, and generating different actual shaders by setting different entry points, but it doesn't work with this parameter.)

pzgulyas avatar Jan 21 '19 08:01 pzgulyas

@pzgulyas EDIT: Oops, I see now that you're describing a different issue than I initially thought. There is a parallel issue in the Vulkan backend that prevents it from working with different entry point names at the moment.

Could you provide more info about how you are producing the SPIR-V that causes this issue?

mellinoe avatar Jan 21 '19 08:01 mellinoe

I have my glsl sources, and do a ReadAllBytes on them. So it is the same way how you describe it here in the README.md Usage section.

EDIT: Oh, sorry, not exactly. I have the glsl sources in the files, not the compiled bytecode. Hmmm, maybe that was the problem? But it still works with "main" and ReadAllBytes.

EDIT2: Just to add some code how I thought:

byte[] vertexShader = File.ReadAllBytes("sky.vert");
byte[] fragmentShader = File.ReadAllBytes("sky.frag");
Shader[] SkyShader = factory.CreateFromSpirv(
    new ShaderDescription(ShaderStages.Vertex, vertexShader, "main"),
    new ShaderDescription(ShaderStages.Fragment, fragmentShader, "main"));
Shader[] CloudsShader = factory.CreateFromSpirv(
    new ShaderDescription(ShaderStages.Vertex, vertexShader, "main"),
    new ShaderDescription(ShaderStages.Fragment, fragmentShader, "clouds"));

or something similar.

pzgulyas avatar Jan 21 '19 08:01 pzgulyas

As far as I know, there's no way to use an entrypoint other than "main()" in GLSL. So unless I'm missing something, you'll need to specify "main" in the ShaderDescription since that's what the code will have. This isn't true for HLSL (it lets you specify any function name you want), so that's what I initially thought you were referring to. Could you provide a sample GLSL shader that would exhibit this problem?

mellinoe avatar Jan 22 '19 06:01 mellinoe

Below is a sample of my technical experiment. Not that this particular one could not be easily exploded into multiple files, but I also have some more difficult ones. :-)

Sky.frag
#version 450

#extension GL_ARB_separate_shader_objects : enable
#extension GL_ARB_shading_language_420pack : enable

layout(location = 0) in vec3 InNormal;
layout(location = 1) in vec2 InTexCoords;

layout(set = 2, binding = 0) uniform PerBatchFragment {
    vec4 lightVector; // Direction vector to sun, w = 1/length of vector
    vec4 overcast; // x = alpha, y = contrast, z = brightness, w = !Overcast.y && !Overcast.z
    vec3 skyColor;
    float time; // Used for moving textures across the sky
    vec3 fogColor;
    float cloudColor;
    vec4 fog;
    vec2 windDisplacement;
    vec2 moonColor;
    vec2 moonTexCoord;
    vec2 padding;
};

layout(set = 1, binding = 0) uniform texture2D PrimaryTexture;
layout(set = 1, binding = 1) uniform texture2D SecondaryTexture;
layout(set = 1, binding = 2) uniform sampler SkySampler;

layout(location = 0) out vec4 OutColor;

// This function adjusts brightness, saturation and contrast
// By Romain Dura aka Romz
vec3 ContrastSaturationBrightness(vec3 color, float brt, float sat, float con)
{
    // Increase or decrease theese values to adjust r, g and b color channels separately
    const float AvgLumR = 0.5;
    const float AvgLumG = 0.5;
    const float AvgLumB = 0.5;

    const vec3 LumCoeff = vec3(0.2125, 0.7154, 0.0721);

    vec3 AvgLumin = vec3(AvgLumR, AvgLumG, AvgLumB);
    vec3 brtColor = color * brt;
    float intensityf = dot(brtColor, LumCoeff);
    vec3 intensity = vec3(intensityf, intensityf, intensityf);
    vec3 satColor = mix(intensity, brtColor, sat);
    vec3 conColor = mix(AvgLumin, satColor, con);
    return conColor;
}

void main()
{
    // Get the color information for the current pixel
    OutColor = texture(sampler2D(PrimaryTexture, SkySampler), InTexCoords);
    vec2 texCoords = vec2((1.0 - InTexCoords.x) + time, InTexCoords.y);
    vec4 starColor = texture(sampler2D(SecondaryTexture, SkySampler), texCoords);

    // Adjust sky color brightness for time of day
    OutColor *= skyColor.x;

    // Stars
    OutColor = mix(starColor, OutColor, skyColor.y);

    // Fogging
    OutColor.rgb = mix(OutColor.rgb, fogColor.rgb, clamp((1 - InNormal.y) * fog.x, 0, 1));

    // Calculate angular difference between LightVector and vertex normal, radians
    float dotproduct = dot(lightVector.xyz, InNormal);
    float angleRcp = 1 / acos(dotproduct * lightVector.w / length(InNormal));

    // Sun glow
    // Coefficients selected by the author to achieve the desired appearance - fot limits the effect
    OutColor += angleRcp * fog.y;

    // increase orange at sunset - fog limits the effect
    if (lightVector.x < 0)
    {
        OutColor.r += skyColor.z * angleRcp * fog.z;
        OutColor.g += OutColor.r * fog.w;
    }

    // Keep alpha opaque
    OutColor.a = 1.0;
}

void moon()
{
    // Get the color information for the current pixel
    vec2 texCoords = vec2(moonTexCoord.x + InTexCoords.x * 0.5, moonTexCoord.y + InTexCoords.y * 0.25);
    OutColor = texture(sampler2D(PrimaryTexture, SkySampler), texCoords);
    vec4 moonMask = texture(sampler2D(SecondaryTexture, SkySampler), InTexCoords);

    // Fade moon during daylight
    OutColor.a *= moonColor.x;

    // Fogging
    OutColor.rgb = mix(OutColor.rgb, fogColor.rgb, clamp((1 - InNormal.y) * fog.x, 0, 1));

    // Mask stars behind dark side (mask fades in)
    OutColor.a += moonMask.r * moonColor.y;
}

void clouds()
{
    // Get the color information for the current pixel
    // Cloud map is tiled. Tiling factor: 4
    // Move cloud map to suit wind conditions
    vec2 texCoords = vec2(InTexCoords.x * 4 + windDisplacement.x, InTexCoords.y * 4 + windDisplacement.y);
    OutColor = texture(sampler2D(PrimaryTexture, SkySampler), texCoords);
    float alpha = OutColor.a;

    // Fogging
    OutColor.rgb = mix(OutColor.rgb, fogColor.rgb, clamp((1 - InNormal.y) * fog.x, 0, 1));

    // Adjust amount of overcast by adjusting alpha
    if (overcast.w != 0)
    {
        alpha += overcast.x;
        // Reduce contrast and brightness
        vec3 color = ContrastSaturationBrightness(OutColor.xyz, 1.0, overcast.z, overcast.y); // Brightness and saturation are really need to be exchanged?
        OutColor = vec4(color, alpha);
    }
    else
    {
        alpha *= overcast.x;
    }

    // Adjust cloud color brightness for time of day
    OutColor *= cloudColor;
    OutColor.a = alpha;
}
Anyway, temporarily I am just doing some replacing code, like
var shaderBytes = File.ReadAllText(shaderFile)
    .Replace("void main()", "void origmain()")
    .Replace($"void {shaderEntry}()", "void main()")
    .Select(c => (byte)c).ToArray();

pzgulyas avatar Jan 22 '19 18:01 pzgulyas

Okay, I'm able to compile that shader using the following glslangvalidator command:

glslangvalidator sky.frag -V -S frag -e moon --sep main glslangvalidator sky.frag -V -S frag -e clouds --sep main

The confusing part is this --sep parameter always needs to be "main", as far as I can tell. Nothing else seems to work, and you can't leave the parameter off. However, the actual "main" function needs to be deleted, otherwise it complains about duplicate functions. It feels like this feature is a little half-baked, but it does work. I'm looking at the SPIR-V output and the shader's "OpEntryPoint" correctly matches what the input function's name was, and the function body looks correct. It should be possible to support this same functionality through the shaderc library, instead of just ignoring the entry point as it currently does.

I expect that I will fix this when I finish up the HLSL support, which will hopefully be soon. HLSL has better support for defining multiple shaders in a single file, so being able to control which entry point is used will be more valuable there, anyways.

mellinoe avatar Jan 24 '19 06:01 mellinoe