godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
90.33k stars 21.06k forks source link

Tearing when using UV to access an array of sampler2d's #98288

Open WaveyGames opened 2 days ago

WaveyGames commented 2 days ago

Tested versions

Reproducible in: 4.3stable and 4.4dev3

System information

Godot v4.3.stable - Windows 10.0.26100 - Vulkan (Forward+) - dedicated AMD Radeon RX 6600 (Advanced Micro Devices, Inc.; 32.0.12011.1036) - 12th Gen Intel(R) Core(TM) i3-12100F (8 Threads)

Issue description

I wrote a short shader to have a mesh sample one image when UV.x < 0.5, and another image otherwise. Here it is:

shader_type spatial;
uniform sampler2D[2] images : source_color;

void fragment() {
    ALBEDO = texture(images[int(step(0.5, UV.x))], UV).rgb;
}

But when you put it on a mesh and fill the array with two different images it looks something like this: tearing_showcase

I expected a clean line in the middle separating the two images, like this: image

The code for the above:

shader_type spatial;
uniform sampler2D[2] images : source_color;

vec3 get_color(int index, vec2 uv) {
    if(index == 1) {
        return texture(images[1], uv).rgb;
    }
    return texture(images[0], uv).rgb;
}
void fragment() {
    ALBEDO = get_color(int(step(0.5, UV.x)), UV);
}

Steps to reproduce

After you open the MRP, the issue should be visible immediately.

Minimal reproduction project (MRP)

MRP_bug.zip

tetrapod00 commented 2 days ago

I can't reproduce it on 4.3. Possibly hardware or driver specific?

Godot_v4 3-stable_win64_42RzYf4qGx

Godot v4.3.stable - Windows 10.0.19045 - Vulkan (Forward+) - dedicated NVIDIA GeForce GTX 1660 Ti with Max-Q Design (NVIDIA; 32.0.15.5612) - AMD Ryzen 7 3750H with Radeon Vega Mobile Gfx (8 Threads)

DarioSamo commented 2 days ago

This is a pretty typical artifact from not indicating the access is not uniform explicitly in the shader.

https://anki3d.org/resource-uniformity-bindless-access-in-vulkan/

One additional piece to the puzzle is the NonUniform SPIR-V decoration which is exposed via nonuniformEXT in GLSL and NonUniformResourceIndex() in HLSL.

I don't believe access to this modifier is exposed in GDShader. It might take a while to get a feature like that added and it's a pretty highly technical modifier to expect people to know about it. I'd argue this is a bit more of a documentation issue.

My suggestion would be you attempt some other way to do this. Try just sampling both of the images and mixing based on the step UV value. That should guarantee the access to both samplers is uniform.

DarioSamo commented 2 days ago

I can't reproduce it on 4.3. Possibly hardware or driver specific?

Yes, AMD is more prone to showing artifacts when non uniform access is not specified.

stuartcarnie commented 1 day ago

Is there a downside to having the Godot shader compiler always insert the nonuniformEXT decorator for array indexing of textures? According to the article:

If an implementation doesn’t care then it will simply ignore it [nonuniformEXT].

WaveyGames commented 1 day ago

My suggestion would be you attempt some other way to do this. Try just sampling both of the images and mixing based on the step UV value. That should guarantee the access to both samplers is uniform.

I found a way around the issue using a bunch of step functions, since the original problem is sampling from a couple more textures, and the result looks great! Thanks!