godotengine/godot

Vulkan canvas_item shader different to GLS3

Opened this issue ยท 20 comments

Godot version

v4.0.beta.custom_build [380fba6]

System information

Windows 10, Vulkan

Issue description

Porting projects with complex shaders from 3.x GLS3 to 4.x Vulkan, trying to fix what appears broken.
I've tried to strip it down to simpler parts that still showcase the issue. A ShaderMaterial applied to a ColorRect. None of the 'texture_filter' options seem to have any impact, nor 'texture_repeat' options in the node (which makes sense to me when directly modifying 'COLOR' in a canvas_item).
Godot 3.5:
image

Godot 4 [380fba6]:
image

I've tried different render modes to no avail, maybe there's a project setting I'm missing. I know 'MODULATE' is gone and 'COLOR' has new behaviour but it doesn't seem related.

Steps to reproduce

Same shader code on both projects:

shader_type canvas_item;

float hash12(vec2 p) {
	return fract(sin(dot(p, vec2(12.9898,12.1414))) * 83758.5453);
}

float noise(vec2 n) {
	vec2 v2_floor = floor(n);
	vec2 v2_fract = fract(n);
	float m1 = mix(hash12(v2_floor), hash12(v2_floor + vec2(1.0, 0.0)), v2_fract.x);
	float m2 = mix(hash12(v2_floor + vec2(0.0, 1.0)), hash12(v2_floor + vec2(1.0)), v2_fract.x);
	float m3 = mix(m1, m2, v2_fract.y);
	return m3;
}

void fragment() {
	vec2 v_uv = UV*5.0;
	float n = noise(v_uv);
	COLOR = vec4(n);
}

Minimal reproduction project

Shader Issue 4.x.zip
Shader Issue 3.x.zip

Likely related to the fact that COLOR behavior is different in 4.0

Present in v4.0.beta.custom_build [204715a]

I don't think it's related to COLOR, but I could be wrong. It's as if the UV is being degraded over time quickly, which is a problem with shaders that use TIME. I've updated the MRP to use a better hash by default and show the problem in a different way (also removed the .godot folders, sorry about that).

Does Vulkan use different float precision on shaders by default? I've tried enforcing highp but the result is the same... don't know what else to try.

4.x:
image

3.x:
image

New MRPs (also edited in OP):
Shader Issue 4.x.zip
Shader Issue 3.x.zip

I can't reproduce the issue on an intel integrated GPU with PopOS 22.04 and running current master f692b47

The result looks fine:
Screenshot from 2022-12-08 12-13-37

I can't reproduce the issue on an intel integrated GPU with PopOS 22.04 and running current master [f692b47]

Can you post a pic from the new MRP, please? I see the issues on a GTX 970.

Thank you, I'll try updating my drivers and followup later.

It could be an issue from using sin in the hash function see e.g. https://www.shadertoy.com/view/4djSRW

It could be an issue from using sin in the hash function see e.g. https://www.shadertoy.com/view/4djSRW

If you notice the new MRP, it's using a different hash for that reason, without sin. I left the older hash for archive reasons, but it's not in use.

Had a friend test on his GTX 1070, Windows 10:
image

Also broken for them.

@Lippanon Can you test the same shader in shadertoy to see if you get the issue there as well?

here is the code converted to shadertoy:

float hash12(vec2 p) {
	return fract(sin(dot(p, vec2(12.9898,12.1414))) * 83758.5453);
}

float noise(vec2 n) {
	vec2 v2_floor = floor(n);
	vec2 v2_fract = fract(n);
	float m1 = mix(hash12(v2_floor), hash12(v2_floor + vec2(1.0, 0.0)), v2_fract.x);
	float m2 = mix(hash12(v2_floor + vec2(0.0, 1.0)), hash12(v2_floor + vec2(1.0)), v2_fract.x);
	float m3 = mix(m1, m2, v2_fract.y);
	return m3;
}

vec4 fragment(vec2 UV) {
	vec2 v_uv = UV*15.0;
	float n = noise(v_uv);
	return vec4(n);
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    // Normalized pixel coordinates (from 0 to 1)
    vec2 uv = fragCoord/iResolution.xy;

    // Output to screen
    fragColor = fragment(uv);
}

@clayjohn Here's my output on shadertoy with that code:
image

It does appear correct in shadertoy.
I also tested other variants that appear broken for me in 4.x, and they also seem correct in shadertoy.

celyk commented

This happens to me. Tested in Godot 3.5.1 (GLES3), Godot 4.0 (Vulkan, GLES3), even with a GLSL shader compiled via RenderingDevice. Same result.

Adding a non-constant term to the hash function makes it work

float hash12(vec2 p) {
	p += min(TIME,0.0);
	return fract(sin(dot(p, vec2(12.9898,12.1414))) * 83758.5453);
}

This happens to me. Tested in Godot 3.5.1 (GLES3), Godot 4.0 (Vulkan, GLES3), even with a GLSL shader compiled via RenderingDevice. Same result.

Adding a non-constant term to the hash function makes it work

float hash12(vec2 p) {
	p += min(TIME,0.0);
	return fract(sin(dot(p, vec2(12.9898,12.1414))) * 83758.5453);
}

Thank you for the temp fix! I confirm that does work for me also, it fixes the artifacts. Pinging @clayjohn to let you know.

Has anyone tested this on newer Nvidia GPUs? (16 series, 20 series, 30 series and 40 series)

It seems to be be happening on newer Nvidia GPUs.
We are working on a sky_shader for Godot 4 Stable with another developper, he's on a 10 series and have no glitches. The same fresh project with his shader glitches the same way and i have a RTX 3080.

What driver version is he using?

also @Lippanon what driver is the 1070 on?

image
Not the latest (It's actually 531.18), but one of the latest. I'll try update my drivers to see if it change anything.

Just to update everyone on this issue. This is an NVidia Vulkan driver bug. I'm glad to here users have found a workaround, because there isn't really anything we can do. We just pass the shader code off to the GPU driver to compile.

Other people experience this same bug and NVidia hasnt done anything about it https://github.com/danilw/GPU-my-list-of-bugs and https://www.shadertoy.com/view/stK3WG

Just a note about the workaround, if used in a sky shader another value other than TIME should be used as using TIME will force sky shaders to update reflections each frame which is a costly operation.

addmix commented

The issue here originates from the line: fract(sin(dot(p, vec2(12.9898,12.1414))) * 83758.5453);. From what I have read, each manufacturer, and even each version of driver has different ways to handle sin(), and is generally inaccurate, causing these issues. If you are experiencing this issue on compute shaders, you can try p += min(float(gl_LocalInvocationID), 0.0);, in my case with a 1070, it solved the problem.

I recently upgraded to a Radeon card, and had similar issues, which were not resolved by the aforementioned workaround. Instead, I had to use a different hash function that omits the use of sin() entirely.

Here is the function I used:

float hash_fract(vec2 p) {
    p  = fract( p*0.3183099+.1 );
    p *= 17.0;
    return -1.+2.*fract( p.x*p.y*(p.x+p.y) );
}

As already stated in #67150 (comment), it also happens when using sinless hashes. The MRP is using a sinless hash.
Celyk's non-constant term work-around works perfectly with any hash I've tested aswell.

addmix commented

I only mention it because despite using the p += min(float(gl_LocalInvocationID), 0.0);, I still had the same issue while using a 7900XTX, where the issue was resolved by that workaround on my 1070.