floooh / sokol-tools

Command line tools for use with sokol headers
MIT License
219 stars 54 forks source link

warns/ignores precision qualifier #108

Open danielchasehooper opened 11 months ago

danielchasehooper commented 11 months ago

compiling the shader below warns: warning: '' : all default precisions are highp; use precision statements to quiet warning, e.g.:

which seems wrong because we are using precision statements

compiled with sokol-shdc -l glsl330:metal_macos:glsl300es -i precision.glsl -o out.h


@vs blur_vs
in vec2 aVertexPosition;
uniform vs_uniforms {
    mat4 uProjectionMatrix;
    vec4 uv_window;
    vec2 offset1, offset2;
};
out vec2 uv_l2, uv_l1, uv_c, uv_h1, uv_h2;

void main(void) {
  vec2 uv = mix(uv_window.xy, uv_window.zw, aVertexPosition);
  uv_l2 = uv - offset2;
  uv_l1 = uv - offset1;
  uv_c  = uv;
  uv_h1 = uv + offset1;
  uv_h2 = uv + offset2;

  gl_Position =  vec4((uProjectionMatrix*vec4(aVertexPosition,0,1.0)).xy, 0,1.0);
}
@end

@fs blur_fs
precision highp float;
uniform texture2D tex;
uniform sampler blur_sampler;
uniform fs_uniforms {
    vec3 weight;
    vec4 uv_window;
};
in vec2 uv_l2, uv_l1, uv_c, uv_h1, uv_h2;
out vec4 FragColor;

void main(void) {
  lowp float s_l2,s_l1,s_c,s_h1,s_h2; // warning: '' : all default precisions are highp; use precision statements to quiet warning, e.g.:
  s_l2 = texture(sampler2D(tex, blur_sampler), uv_l2).a;
  s_l1 = texture(sampler2D(tex, blur_sampler), uv_l1).a;
  s_c  = texture(sampler2D(tex, blur_sampler), uv_c).a;
  s_h1 = texture(sampler2D(tex, blur_sampler), uv_h1).a;
  s_h2 = texture(sampler2D(tex, blur_sampler), uv_h2).a;
  float col;
  col =  s_c * weight.x;
  col += (s_l1 + s_h1) * weight.y;
  col += (s_l2 + s_h2) * weight.z;
  FragColor = vec4(col,col,col,col);
}

@end

@program blur blur_vs blur_fs
floooh commented 11 months ago

Hmm ok, I actually need to check how SPIRVCross assigns precision qualifiers and how much control we actually have over the process, e.g. in the regular texcube-sapp shader (which doesn't have any manual precision qualifiers) it looks like this (e.g. a global specifier which sets floats to mediump, but then specific highp specifiers in various places).

    #version 300 es
    precision mediump float;
    precision highp int;

    uniform highp sampler2D tex_smp;

    layout(location = 0) out highp vec4 frag_color;
    in highp vec2 uv;
    in highp vec4 color;

PS: apart from that, SPIRVCross allows to configure the default precision, but sokol-shdc currently doesn't expose that and uses the default settings (we could do this by extending the existing @glsl_options I guess:

https://github.com/KhronosGroup/SPIRV-Cross/blob/2de1265fca722929785d9acdec4ab728c47a0254/spirv_glsl.hpp#L184-L190

floooh commented 11 months ago

Btw, here's a fragment shader code snippet from the mandelbulb sample (sdf-sapp, input shader code: https://github.com/floooh/sokol-samples/blob/master/sapp/sdf-sapp.glsl), I looked at this specifically, because the mediump precision would be too low for that shader.

highp float sd_mandelbulb(highp vec3 p, inout highp vec4 res_color)
    {
        highp vec3 w = p;
        highp float _49 = dot(p, p);
        highp float m = _49;
        highp vec4 trap = vec4(abs(p), _49);
        highp float dz = 1.0;
        for (int i = 0; i < 4; i++)
        {
            highp float _76 = m * m;
            dz = (8.0 * sqrt(((_76 * _76) * _76) * m)) * dz + 1.0;
            highp vec3 _653 = w;
            highp float _100 = _653.x * _653.x;
            highp float _104 = _100 * _100;
            highp float _112 = _653.y * _653.y;
            highp float _124 = _653.z * _653.z;
            highp float _128 = _124 * _124;
            highp float _132 = _653.x * _653.x + _124;

...looks like SPIRVCross forces anything to highp precision anyway, so putting any precision qualifiers into the input source code will probably be ignored.

Also, SPIRVCross actually allows to configure the global precision qualifier, but sokol-shdc currently doesn't expose that and instead uses the default settings (we could extend the existing @glsl_options tag for that though):

https://github.com/KhronosGroup/SPIRV-Cross/blob/2de1265fca722929785d9acdec4ab728c47a0254/spirv_glsl.hpp#L184-L190

Not sure how much the global precision mediump float; statement matters though if everything is explicitely forced to highp anyway.

danielchasehooper commented 10 months ago

Being able to specify a reduced global precision to sokol-shdc wouldn't be that useful, because there are usually only some variables in a shader that can fit their values in a reduced precision range.

Are you saying that SPIRVCross doesn't support tracking per-variable precision qualifiers?

floooh commented 10 months ago

Are you saying that SPIRVCross doesn't support tracking per-variable precision qualifiers?

I'm not sure actually, what it does seem to do is slap a highp to every local variable despite the default global specifier being mediump.

I'll need to check what happens when explicitly setting a local variable to mediump in the input source code (e.g. will SPIRVCross preserve that, or overwrite it to highp)

floooh commented 10 months ago

I'm currently tinkering around with this, and the behaviour is "interesting".

First, that warning isn't actually coming out of SPIRVCross, but is from the first compile pass from GLSL to SPIRV which is handled by glslang (https://github.com/KhronosGroup/glslang), and I wasn't able to silence this warning in any way, it shows up as soon as local precision qualifiers on variables are used.

SPIRVCross on the other hand seems to behave correctly, and the rule seems to be like this:

Meaning:

I suspect that the SPIRV spec doesn't have the concept of "global precision qualifiers" and only carries that information for each variable, and that's why SPIRVCross has to use a config option to assume a "default precision", and then annotates all variables that differ from the default precision.

...so apart from that warning in glslangValidator everything appears to work as expected (e.g. you can set a global precision qualifier at the top of an @fs shader snippet (the default is highp), and then annotate individual variables that differ from that precision. So in the default case (no global precision in the input GLSL) it only makes sense to annoate variables with mediump.

Next I'll need to check what to do about that warning (it's not only confusing, but also clipped)

danielchasehooper commented 7 months ago

Apple says that 16 bit floats are twice as fast as 32 bit floats - I'd love to optimize some of our shaders with them but I'm unable to get sokol-shdc to emit the half type in its metal output. Am I missing something, or is there in fact a problem related to the interesting behavior you found?

for example, if you compile the below code with sokol-shdc -l metal_macos -i test.glsl -o out.h none of the variables will be of type half or half2


@vs layer_vs

in mat2 aRotateScale;
in vec4 aColor; // premultiplied alpha
in vec2 aVertexPosition;
in vec2 aTranslate;
in vec2 aSize;
in float aCornerRadius;

out lowp vec4 color;
out lowp vec2 coord;
out lowp vec2 half_size;
out lowp float cornerRadius;

void main(void) {
    color = aColor;
    half_size = aSize/2.0;
    cornerRadius = aCornerRadius;
    gl_Position =  vec4(aRotateScale * aVertexPosition + aTranslate, 1, 1);
    coord = (aVertexPosition - 0.5) * aSize;
}
@end

@fs layer_fs

in lowp vec4 color; // must be premultiplied alpha
in lowp vec2 coord;
in lowp vec2 half_size;
in lowp float cornerRadius;

out vec4 FragColor;

lowp float round_rect_sdf(lowp vec2 p,lowp  vec2 rect_half_size,lowp  float radius){
    lowp vec2 d = abs(p) - rect_half_size + radius;
    return min(max(d.x, d.y), 0.0) + length(max(d, 0.0)) - radius;
}

void main(void) {

    lowp float b = round_rect_sdf( coord, half_size, cornerRadius );

    lowp float alpha = (1.-clamp(b+1.5,0.,1.));
    FragColor = color * alpha;
}
@end

@program layer layer_vs layer_fs
danielchasehooper commented 7 months ago

Found this: https://gist.github.com/zeux/c83001968e06fe0b789fa4bd513860c6

Unfortunately, SPIRV-Cross doesn't support RelaxedPrecision for Metal Shading Language. To get around that, you need to enable 16-bit type support when building SPIRV for use with SPIRV-Cross, which will result in the actual half-precision types being emitted

floooh commented 7 months ago

Right. As far as I'm aware I'm not doing anything in sokol-shdc to prevent half precision. I would have recommended to check the SPIRVCross issue tracker and documentation though, such limitations are usually mentioned there.

The SPIRV compilation happens here:

https://github.com/floooh/sokol-tools/blob/7337f8acad05e875c9b60a749b4274a31a69517d/src/shdc/spirv.cc#L159-L225

...maybe there's an option on glslang::TShader to enable this 16-bit type support for GLSL input?

Hmm, on the other hand: https://gist.github.com/zeux/c83001968e06fe0b789fa4bd513860c6?permalink_comment_id=2942898#gistcomment-2942898

floooh commented 7 months ago

(PS: I'm actually surprised that Vulkan-style GLSL didn't add a proper half type)

staminajim commented 6 months ago

I've found that putting:

#extension GL_EXT_shader_explicit_arithmetic_types_float16 : enable

in your shader code, and use any of the 16 bit types mentioned here: https://github.com/KhronosGroup/GLSL/blob/main/extensions/ext/GL_EXT_shader_explicit_arithmetic_types.txt

(for example f16vec4 instead of vec4), then the emitted metal shader code uses half precision