Repository: godot
Issue: Vulkan Validation Errors in Canvas2D on launch
Labels: bug,topic:rendering,topic:2d

### Tested versions

4.4-master

### System information

Godot v4.4.dev (44fa552343722bb048e2d7c6d3661174a95a8a3c) - Ubuntu 24.04.1 LTS 24.04 on X11 - X11 display driver, Multi-window, 2 monitors - Vulkan (Mobile) - dedicated AMD Radeon RX 6800 XT (RADV NAVI21) - AMD Ryzen 9 5900X 12-Core Processor (24 threads)

### Issue description

Launching the editor with an empty project with Vulkan validation enabled (`--gpu-validation`) results in the following message spammed:

```
VUID-vkCmdDrawIndexed-None-06479(ERROR / SPEC): msgNum: 1397510317 - Validation Error: [ VUID-vkCmdDrawIndexed-None-06479 ] Object 0: handle = 0xf9a57300000043d5, name = RID:694113959674081, type = VK_OBJECT_TYPE_DESCRIPTOR_SET; Object 1: handle = 0x4baaca000000019b, name = RID:665719930899 View, type = VK_OBJECT_TYPE_IMAGE_VIEW; | MessageID = 0x534c50ad | vkCmdDrawIndexed():  the descriptor (VkDescriptorSet 0xf9a57300000043d5[RID:694113959674081], binding 4, index 0) has VkImageView 0x4baaca000000019b[RID:665719930899 View] with format of VK_FORMAT_R32_SFLOAT which doesn't support VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_DEPTH_COMPARISON_BIT in.
i(supported features: VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_BIT|VK_FORMAT_FEATURE_2_STORAGE_IMAGE_BIT|VK_FORMAT_FEATURE_2_STORAGE_IMAGE_ATOMIC_BIT|VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BIT|VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BLEND_BIT|VK_FORMAT_FEATURE_2_BLIT_SRC_BIT|VK_FORMAT_FEATURE_2_BLIT_DST_BIT|VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_FILTER_LINEAR_BIT|VK_FORMAT_FEATURE_2_TRANSFER_SRC_BIT|VK_FORMAT_FEATURE_2_TRANSFER_DST_BIT|VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_FILTER_MINMAX_BIT|VK_FORMAT_FEATURE_2_STORAGE_READ_WITHOUT_FORMAT_BIT|VK_FORMAT_FEATURE_2_STORAGE_WRITE_WITHOUT_FORMAT_BIT). The Vulkan spec states: If a VkImageView is sampled with depth comparison, the image view's format features must contain VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_DEPTH_COMPARISON_BIT (https://vulkan.lunarg.com/doc/view/1.3.283.0/linux/1.3-extensions/vkspec.html#VUID-vkCmdDrawIndexed-None-06479)
```

Upon closer inspection the error is caused from:

1. canvas.glsl.gen.h
2. `layout(set = 0, binding = 4) uniform texture2D shadow_atlas_texture;`
3. This shader is used when rendering the dark-grey-blueish background of Godot's Editor.

I am not familiar enough with the Canvas2D renderer to fix it, but I gathered the following and I can suggest several solutions:

renderer_canvas_render_rd.cpp creates 2 textures for shadow rendering in `RendererCanvasRenderRD::_update_shadow_atlas`:

1. `state.shadow_texture` which is a "**colour**" R32_FLOAT texture (technically monochrome).
2. `state.shadow_depth_texture` which is a **depth** D32_FLOAT texture.
3. Both textures are combined as an FBO for rendering into it.
4. `state.shadow_texture` is the texture bound to `shadow_atlas_texture` for use during rendering. `state.shadow_depth_texture` is never bound.

If this were 3D rendering, most of the time the colour texture would be pointless (unless it's doing an advanced algorithm) and say there's a mistake and it should be the `D32_FLOAT` texture the one to be alone. But since I don't know how to even trigger 2D shadows in Godot, perhaps the colour texture is the one that should be bound.

## Possible Solution 1

Just get rid of the colour texture, leave the depth texture and bind that.
It is specially suspicious that the colour texture appears to contain depth information.

## Possible Solution 2

OK so let's assume the colour R32_FLOAT is needed.

The problem happens because Godot is relying on HW support to do bilinear PCF. This issue [affects D3D too](https://computergraphics.stackexchange.com/questions/8417/r16-float-samplecmp-and-checkformatsupport):

```glsl
vec4 light_shadow_compute(uint light_base, vec4 light_color, vec4 shadow_uv /*, ...*/ )
{
// ..
shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size * 2.0, 0.0).x;
// ..
}
```

We cannot do "sampler2DShadow(shadow_atlas_texture, shadow_sampler)" if unsupported for that pixel format.

The solution is to emulate the PCF:

```glsl
float emulatedPcf2x2( texture2D t, float4 uv, float2 uv_resolution )
{
   float r00 = texture2D( sampler2D( t, SAMPLER_NEAREST_CLAMP ), uv.xy ).x;
   float r10 = texture2D( sampler2D( t, SAMPLER_NEAREST_CLAMP ), uv.xy + float( 1/uv_resolution.x, 0 ) ).x;
   float r01 = texture2D( sampler2D( t, SAMPLER_NEAREST_CLAMP ), uv.xy + float( 0, 1/uv_resolution.y ) ).x;
   float r11 = texture2D( sampler2D( t, SAMPLER_NEAREST_CLAMP ), uv.xy + float( 1/uv_resolution ) ).x;

    // Perform depth comparisons.
    r00 = r00 > uv.w;
    r10 = r10 > uv.w;
    r01 = r01 > uv.w;
    r11 = r11 > uv.w;

   // Apply bilinear filtering to depth comparisons.
   // WARNING: fract() will give the wrong value for negative values. This example code is not accounting for that. Or maybe the "wrong" value is the correct one we need. I haven't given thought about it.
   float xyFract = fract( uv.xy * uv_resolution.xy );
   float t = lerp( r00, r10, xyFract.x );
   float s = lerp( r01, r11, xyFract.x );

   return lerp( t, s, xyFract.y );
}
```

I wrote this out of memory, so it may have one or two syntax errors.

Of course this emulation requires 4 fetches, so it's far from ideal.

It could be optimized using Gather4 (which can fetch 4 texels in one sample). Last time I tried that (10 years ago!) I could not get the Gather4 version to run reliably because some drivers would be buggy or would scramble the order of the pixels (e.g. r10 and r01 were swapped, stuff like that).

## Possible Solution 3

Just perform a blit or compute copy from the R32_FLOAT to a D32_FLOAT texture and bind the D32_FLOAT instead.

### Steps to reproduce

1. Setup Vulkan Validation Layers
2. Launch an empty project with `-e --gpu-validation`

### Minimal reproduction project (MRP)

N / A