Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ShaderDescription entryPoint different to "main" #4

Open
pzgulyas opened this issue Jan 21, 2019 · 5 comments
Open

ShaderDescription entryPoint different to "main" #4

pzgulyas opened this issue Jan 21, 2019 · 5 comments

Comments

@pzgulyas
Copy link

pzgulyas commented Jan 21, 2019

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.)

@mellinoe
Copy link
Collaborator

mellinoe commented Jan 21, 2019

@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?

@pzgulyas
Copy link
Author

pzgulyas commented Jan 21, 2019

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.

@mellinoe
Copy link
Collaborator

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?

@pzgulyas
Copy link
Author

pzgulyas commented Jan 22, 2019

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();

@mellinoe
Copy link
Collaborator

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.

frenzibyte pushed a commit to frenzibyte/veldrid-spirv that referenced this issue Sep 10, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants