floooh / sokol

minimal cross-platform standalone C headers
https://floooh.github.io/sokol-html5
zlib License
6.92k stars 487 forks source link

Can't Set Float Uniform Variable #991

Closed Voxi0 closed 7 months ago

Voxi0 commented 7 months ago

This is a .glsl shader file that I'm compiling using sokol-shdc I grabbed from the sokol-tools-bin repo. You can see in the fragment shader that I have a uniform block with a float variable called time which I set to '(float)glfwGetTime()'. I am using this variable to just play with the colors. This shader works perfectly fine when embedding this code in the shader desc, but compiling it with sokol-shdc into a header file just does not work. Setting the uniform variable says that the block size does not match and after going through the generated header file, the float variable in the fragment shader is getting converted into a vec4 for whatever reason. And sure enough, it works perfectly when I set a vec4, no block size errors.

This is the first time I've ever reported an issue on GitHub so excuse me if it's not formatted nicely. I'm really loving sokol by the way, it's great. Also I think it would be kinda cool if there was a discord server or something no?

#version 330 core

@ctype mat4 glm::mat4
@ctype vec3 glm::vec3
@ctype vec4 glm::vec4

@vs vs
// Vertex Attributes
layout(location = 0) in vec3 vertexPos;

// Uniform Variables
uniform vsParams {
    mat4 pvm;
};

// Main
void main() {
    gl_Position = pvm * vec4(vertexPos, 1.0f);
}
@end

@fs fs
// Uniform Variables
uniform fsParams {
    float time;
};

// Output
out vec4 fragColor;

// Main
void main() {
    // Set Final Fragment Color
    vec3 color = vec3(abs(sin(time)), 0.0f, 0.0f);
    fragColor = vec4(color, 1.0f);
}
@end

@program testShaders vs fs
floooh commented 7 months ago

Do you use the code-generated struct or your own (in the sg_apply_uniforms call)? E.g. in the sokol-shdc output there's the following struct generated:

#pragma pack(push,1)
SOKOL_SHDC_ALIGN(16) typedef struct fsParams_t {
    float time;
    uint8_t _pad_4[12];
} fsParams_t;
#pragma pack(pop

Note how this is padded to a multiple of 16 bytes. This is a side effect of 'flattening' uniform blocks into a vec4 array via SPIRVCross, which allows to update uniforms with a single glUniform4fv() call per uniform-block in the GL backend (instead of one individual glUniform call per uniform block member which is otherwise used).

PS: the #version 330 core at the top of your shader isn't necessary (and might even confuse sokol-shdc in some cases).

PPS: in general, and if possible, you should use the code-generated structs for passing uniforms into sokol-gfx, since the alignment and padding rules for uniform blocks are non-trivial, and very different from vanilla C structs.

PPPS: code example

const fsParams_t fs_params = { .time = (float)glfwGetTime() };
sg_apply_uniforms(SG_SHADERSTAGE_FS, SLOT_fsParams, &SG_RANGE(fs_params));
Voxi0 commented 7 months ago

I know the #version 330 core isn't necessary but I was pretty desperate to get it working. I am using the generated struct yes, I'd rather not make my own or mess with the generated one. And, oh, the code example you provided actually works. I guess that's it, thanks.

By the way, using the '&' before 'SG_RANGE' gives me an error. I've seen it being used every example though. The error is 'Taking address of rvalue [-fpermissive]', the program runs fine regardless of this error. I have all warnings and errors enabled so that must be the case.

floooh commented 7 months ago

By the way, using the '&' before 'SG_RANGE' gives me an error.

Ah right, that's a C vs C++ thing. In C++ you can do this instead (just drop the '&'):

sg_apply_uniforms(SG_SHADERSTAGE_FS, SLOT_fsParams, SG_RANGE(fs_params));

...or use the SG_RANGE_REF() macro which does the right thing both in C and C++:

sg_apply_uniforms(SG_SHADERSTAGE_FS, SLOT_fsParams, SG_RANGE_REF(fs_params));