From 29d861c2ca4c00cf74731c69131d39ed6200e78a Mon Sep 17 00:00:00 2001
From: Daniel Ansorregui <mailszeros@gmail.com>
Date: Sat, 19 Jan 2019 15:32:55 +0000
Subject: [PATCH 5/5] WIP mod for matching AMD results

---
 dlls/d3d9/tests/visual.c       | 238 ++++++++++++++++++++++-----------
 dlls/wined3d/glsl_shader.c     | 172 ++++++++++++------------
 dlls/wined3d/shader.c          |   3 +-
 dlls/wined3d/utils.c           |   8 +-
 dlls/wined3d/wined3d_private.h |   2 +-
 5 files changed, 247 insertions(+), 176 deletions(-)

diff --git a/dlls/d3d9/tests/visual.c b/dlls/d3d9/tests/visual.c
index a3eb30725e..ad6bd19181 100644
--- a/dlls/d3d9/tests/visual.c
+++ b/dlls/d3d9/tests/visual.c
@@ -15129,7 +15129,7 @@ static void fetch4_test(void)
     };
     static const DWORD ps_code_texldp[] =
     {
-        /* Test texldp : It should not apply any projection with Fetch4. Same result as texld */
+        /* Test texldp : AMD and Wine uses the projection on Fetch4, Intel UHD 620 does not apply it */
         0xffff0300,                                                             /* ps_3_0                     */
         0x0200001f, 0x80000005, 0x900f0000,                                     /* dcl_texcoord v0            */
         0x0200001f, 0x90000000, 0xa00f0800,                                     /* dcl_2d s0                  */
@@ -15143,6 +15143,7 @@ static void fetch4_test(void)
     {
         /* Test texldd : Fetch4 uses the same D3D state as LOD bias, therefore disables LOD.
          *               Sampling LOD gradient should be ignored. Same result as texld */
+        /* NOTE: Radeon HD 5700 driver 8.17.10.1404 disables Fetch4 on texldb */
         0xffff0300,                                                             /* ps_3_0                     */
         0x0200001f, 0x80000005, 0x900f0000,                                     /* dcl_texcoord v0            */
         0x0200001f, 0x90000000, 0xa00f0800,                                     /* dcl_2d s0                  */
@@ -15157,6 +15158,7 @@ static void fetch4_test(void)
     {
         /* Test texldb : Fetch4 uses the same D3D state as LOD bias, therefore disables LOD.
          *               Same result as texld */
+        /* NOTE: Radeon HD 5700 driver 8.17.10.1404 disables Fetch4 on texldb */
         0xffff0300,                                                             /* ps_3_0                     */
         0x0200001f, 0x80000005, 0x900f0000,                                     /* dcl_texcoord v0            */
         0x0200001f, 0x90000000, 0xa00f0800,                                     /* dcl_2d s0                  */
@@ -15170,6 +15172,7 @@ static void fetch4_test(void)
     {
         /* Test texldl : Fetch4 uses the same D3D state as LOD bias, therefore disables LOD.
          *               The explicit LOD level is then ignored. Same result as texld */
+        /* NOTE: Radeon HD 5700 driver 8.17.10.1404 disables Fetch4 on texldl */
         0xffff0300,                                                             /* ps_3_0                     */
         0x0200001f, 0x80000005, 0x900f0000,                                     /* dcl_texcoord v0            */
         0x0200001f, 0x90000000, 0xa00f0800,                                     /* dcl_2d s0                  */
@@ -15206,24 +15209,44 @@ static void fetch4_test(void)
 
     static const struct
     {
-        UINT x, y;
-        D3DCOLOR color;
-    }
-    expected_colors[] =
-    {
-        { 40, 30, 0x23102013},{160, 30, 0x22132312},{320, 30, 0x21122211},
-        {480, 30, 0x20112110},{600, 30, 0x23102013},
-        { 40,120, 0x13011002},{160,120, 0x120213f2},{320,120, 0x11f212f1},
-        {480,120, 0x10f11101},{600,120, 0x13011002},
-        { 40,240, 0x02030104},{160,240, 0xf20402f4},{320,240, 0xf1f4f2f3},
-        {480,240, 0x01f3f103},{600,240, 0x02030104},
-        { 40,360, 0x04200323},{160,360, 0xf4230422},{320,360, 0xf322f421},
-        {480,360, 0x0321f320},{600,360, 0x04200323},
-        { 40,450, 0x23102013},{160,450, 0x22132312},{320,450, 0x21122211},
-        {480,450, 0x20112110},{600,450, 0x23102013},
-    };
-
-    static const DWORD fetch4_data[] = {0x10111213, 0x01f1f202, 0x03f3f404, 0x20212223};
+        UINT x[4], y[4]; /* Matrix Sampling positions */
+        D3DCOLOR color_amd[16];   /* AMD original implementation swizzle with -0.5 texel coord */
+        D3DCOLOR color_intel[16]; /* Intel UHD 620 implementation swizzle with no texel coord correction */
+        /* Wine follows the AMD immplementation, and consider an error the Intel one results
+         * However, the test will accept as valid the intel ones when running on windows */
+        D3DCOLOR color_3d_fetch4_off[16];
+        D3DCOLOR color_fetch4_off[16];
+    }
+    expected_colors =
+    {
+        { 40, 200, 360, 520},
+        { 30, 150, 270, 390},
+        /* AMD implementation - Wine implementation */
+        {0x131202f2, 0x1211f2f1, 0x1110f101, 0x10130102,
+         0x02f204f4, 0xf2f1f4f3, 0xf101f303, 0x01020304,
+         0x04f42322, 0xf4f32221, 0xf3032120, 0x03042023,
+         0x23221312, 0x22211211, 0x21201110, 0x20231013},
+        /* Intel UHD 620 implementation */
+        {0x23102013, 0x22132312, 0x21122211, 0x20112110,
+         0x13011002, 0x120213f2, 0x11f212f1, 0x10f11101,
+         0x02030104, 0xf20402f4, 0xf1f4f2f3, 0x01f3f103,
+         0x04200323, 0xf4230422, 0xf322f421, 0x0321f320},
+        /* Fetch4 off on 3D textures */
+        {0xff0b0b0b, 0xff696969, 0xff888888, 0xff292929,
+         0xff050505, 0xffababab, 0xffe1e1e1, 0xff3b3b3b,
+         0xff141414, 0xff696969, 0xff858585, 0xff2f2f2f,
+         0xff181818, 0xff252525, 0xff292929, 0xff1a1a1a},
+        /* Fetch4 off on 2D texture */
+        {0x13131313, 0x12121212, 0x11111111, 0x10101010,
+         0x02020202, 0xf2f2f2f2, 0xf1f1f1f1, 0x01010101,
+         0x04040404, 0xf4f4f4f4, 0xf3f3f3f3, 0x03030303,
+         0x23232323, 0x22222222, 0x21212121, 0x20202020}
+    };
+
+    static const DWORD fetch4_data[] = {0x10111213,
+                                        0x01f1f202,
+                                        0x03f3f404,
+                                        0x20212223};
 
     static struct
     {
@@ -15231,65 +15254,89 @@ static void fetch4_test(void)
         IDirect3DPixelShader9 *ps;
         const DWORD *ps_code;
         const char *name;
+        BOOL projection;            /* The results should be projected (zoomed by 2) */
+        BOOL allow_off;             /* Do not enforce Fetch4 enabled on this one on Windows */
     }
     shaders[] =
     {
-        {NULL, NULL, NULL, "Fixed Function Pipeline"},
-        {NULL, NULL, ps_code_texld, "texld"},
-        {NULL, NULL, ps_code_texldp, "texldp"},
-        {NULL, NULL, ps_code_texldd, "texldd"},
-        {NULL, NULL, ps_code_texldb, "texldb"},
-        {NULL, NULL, ps_code_texldl, "texldl"},
+        {NULL, NULL, NULL,           "FFP",    FALSE, FALSE},
+        {NULL, NULL, ps_code_texld,  "texld",  FALSE, FALSE},
+        {NULL, NULL, ps_code_texldp, "texldp",  TRUE, FALSE},
+        {NULL, NULL, ps_code_texldd, "texldd", FALSE,  TRUE},
+        {NULL, NULL, ps_code_texldb, "texldb", FALSE,  TRUE},
+        {NULL, NULL, ps_code_texldl, "texldl", FALSE,  TRUE},
     };
 
     static const struct
     {
         D3DFORMAT format;           /* The format of the texture */
         DWORD data;                 /* The data we will write to the first line */
-        D3DCOLOR expected_color[3]; /* Test FFP, texld and texldp as in "shaders" struct */
         UINT x, y;                  /* Where we expect the color to be */
-        BOOL disable_alpha_wine;    /* Do not check alpha color on wine (A8 is known to be broken) */
+        BOOL broken_wine;           /* Do not check it on wine because is known ot be broken */
+        D3DCOLOR color_amd[3];      /* Wine results. Results on AMD swizzle + texture offset */
+        D3DCOLOR color_intel[3];    /* Results with intel UHD 620, intel swizzle + no texel offset */
     }
     format_tests[] =
     {
         /* Enabled formats */
-        {D3DFMT_L8,        0xff804010, {0x00400010, 0x00400010, 0x00400010}, 40, 30, FALSE},
-        {D3DFMT_L16,       0xff804010, {0x00ff0040, 0x00ff0040, 0x00ff0040}, 40, 30, FALSE},
-        {D3DFMT_R16F,      0x38003c00, {0x008000ff, 0x008000ff, 0x008000ff}, 40, 30, FALSE},
-        {D3DFMT_R32F,      0x3f000000, {0x00000080, 0x00000080, 0x00000080}, 40, 30, FALSE},
+        {D3DFMT_L8,        0xff804010, 40, 30, FALSE,
+            {0x10400000, 0x10400000, 0x10400000},
+            {0x00400010, 0x00400010, 0x00400010}
+        },
+        {D3DFMT_L16,       0xff804010, 40, 30, FALSE,
+            {0x40ff0000, 0x40ff0000, 0x40ff0000},
+            {0x00ff0040, 0x00ff0040, 0x00ff0040}
+        },
+        {D3DFMT_R16F,      0x38003c00, 40, 30, FALSE,
+            {0xff800000, 0xff800000, 0xff800000},
+            {0x008000ff, 0x008000ff, 0x008000ff}
+        },
+        {D3DFMT_R32F,      0x3f000000, 40, 30, FALSE,
+            {0x80000000, 0x80000000, 0x80000000},
+            {0x00000080, 0x00000080, 0x00000080}
+        },
+
+        /* Disabled format on Intel, enabled on AMD, broken on wine
+         * since it is implemented with GL_ALPHA, and fetch4 will fetch RED value */
+        {D3DFMT_A8,        0xff804010, 40, 30,  TRUE,
+            {0x10400000, 0x10400000, 0x10400000},
+            {0x00000000, 0x00000000, 0x00000000}
+        },
 
-        /* Disabled formats */
-        {D3DFMT_A8,        0xff804010, {0x00000000, 0x00000000, 0x00000000}, 40, 30,  TRUE},
-        {D3DFMT_A8R8G8B8,  0xff804010, {0x64321906, 0x64321906, 0x562b1605}, 40, 30, FALSE},
+        /* Disabled format */
+        {D3DFMT_A8R8G8B8,  0xff804010, 40, 30, FALSE,
+            {0x64321906, 0x64321906, 0x562b1605},
+            {0x64321906, 0x64321906, 0x562b1605}
+        },
     };
 
     static const struct
     {
-        D3DCOLOR color[2]; /* FETCH4 off and on */
+        D3DCOLOR color_off, color_amd, color_intel;
         UINT x, y;
     }
     expected_depth[][4] =
     {
         {
         /* This is the expected result for shadow samplers */
-            {{0x8f8f8f8f,0x8f8f8f8f}, 20, 15},
-            {{0xbfbfbfbf,0xbfbfbfbf},240, 15},
-            {{0x60606060,0x60606060}, 20,240},
-            {{0x40404040,0x40404040},240,120},
+            {0x8f8f8f8f,0x8f8f8f8f,0x8f8f8f8f, 20, 15},
+            {0xbfbfbfbf,0xbfbfbfbf,0xbfbfbfbf,240, 15},
+            {0x60606060,0x60606060,0x60606060, 20,240},
+            {0x40404040,0x40404040,0x40404040,240,120},
         },
         {
         /* This is the expected result with DF16 */
-            {{0xff9b00ff,0x202000ff}, 20, 15},
-            {{0xff8300ff,0x00bf009f},240, 15},
-            {{0xff6c00ff,0x9f000080}, 20,240},
-            {{0xff8000ff,0x80809f60},240,120},
+            {0xff9b00ff,0xfedfdfbf,0x202000ff, 20, 15},
+            {0xff8300ff,0x9f7f7f5f,0x00bf009f,240, 15},
+            {0xff6c00ff,0x7f5f5f3f,0x9f000080, 20,240},
+            {0xff8000ff,0x5f3f3f1f,0x80809f60,240,120},
         },
         {
         /* This is the expected result with DF24 */
-            {{0xff9bffff,0x202000ff}, 20, 15},
-            {{0xff83ffff,0x00bf009f},240, 15},
-            {{0xff6cffff,0x9f000080}, 20,240},
-            {{0xff80ffff,0x80809f60},240,120},
+            {0xff9bffff,0xffdfdfbf,0x202000ff, 20, 15},
+            {0xff83ffff,0x9f7f7f5f,0x00bf009f,240, 15},
+            {0xff6cffff,0x7f5f5f3f,0x9f000080, 20,240},
+            {0xff80ffff,0x5f3f3f1f,0x80809f60,240,120},
         }
     };
 
@@ -15314,6 +15361,8 @@ static void fetch4_test(void)
         {MAKEFOURCC('D','F','2','4'),   "DF24",          2},
     };
 
+    const BOOL isWin = strcmp(winetest_platform, "wine");
+
     IDirect3DSurface9 *original_ds, *original_rt, *rt;
     IDirect3DVolumeTexture9 *texture3D;
     IDirect3DPixelShader9 *ps_3d;
@@ -15435,17 +15484,24 @@ static void fetch4_test(void)
         ok(SUCCEEDED(hr), "EndScene failed, hr %#x.\n", hr);
 
         get_rt_readback(original_rt, &rb);
-        for (j = 0; j < ARRAY_SIZE(expected_colors); ++j)
+        for (j = 0; j < ARRAY_SIZE(expected_colors.color_amd); ++j)
         {
-            D3DCOLOR color = get_readback_color(&rb, expected_colors[j].x, expected_colors[j].y);
-            ok(color_match(color, expected_colors[j].color, 1),
+            UINT x = expected_colors.x[j % 4];
+            UINT y = expected_colors.y[j / 4];
+            D3DCOLOR color = get_readback_color(&rb, x, y);
+            D3DCOLOR color_amd = expected_colors.color_amd[shaders[i].projection ? (j/4/2*4 + (j%4)/2) : j];
+            D3DCOLOR color_intel = expected_colors.color_intel[j];
+            ok(color_match(color, color_amd, 1)
+                    || (isWin && (color_match(color, color_intel, 1) || shaders[i].allow_off)),
                     "Test %s Expected color 0x%08x at (%u, %u), got 0x%08x.\n", shaders[i].name,
-                    expected_colors[j].color, expected_colors[j].x, expected_colors[j].y, color);
+                    color_amd, x, y, color);
         }
         release_surface_readback(&rb);
 
         hr = IDirect3DDevice9_Present(device, NULL, NULL, NULL, NULL);
         ok(SUCCEEDED(hr), "Present failed, hr %#x.\n", hr);
+
+        sleep(1);
     }
 
     /***************************************************************************
@@ -15468,7 +15524,7 @@ static void fetch4_test(void)
         ok(hr == D3D_OK, "Failed to set texture, hr %#x.\n", hr);
 
         /* Test if FETCH4 is enabled/disabled when different textures are used with FFP/texld/texldp */
-        for (j = 0; j < ARRAY_SIZE(format_tests[i].expected_color); ++j)
+        for (j = 0; j < ARRAY_SIZE(format_tests[i].color_amd); ++j)
         {
             hr = IDirect3DDevice9_SetVertexShader(device, shaders[j].vs);
             ok(SUCCEEDED(hr), "Failed to set vertex shader, hr %#x.\n", hr);
@@ -15485,21 +15541,19 @@ static void fetch4_test(void)
             ok(SUCCEEDED(hr), "EndScene failed, hr %#x.\n", hr);
 
             get_rt_readback(original_rt, &rb);
-            D3DCOLOR color = get_readback_color(&rb, format_tests[i].x, format_tests[i].y);
-            D3DCOLOR expected_color = format_tests[i].expected_color[j];
-            /* FIXME: A8 gives different results, therefore we do not test alpha channel
-             * Fetch4 affects all 4 channels, and the test will catch errors anyway */
-            ok(color_match(color & 0x00ffffff, expected_color & 0x00ffffff, 1),
-                    "Test %d,%s expected color 0x%08x at (%u, %u), got 0x%08x.\n", i, shaders[j].name,
-                    expected_color & 0x00ffffff, format_tests[i].x, format_tests[i].y, color & 0x00ffffff);
-            /* If the format gives proper alpha result, test the full color */
-            todo_wine_if(format_tests[i].disable_alpha_wine) ok(color_match(color, expected_color, 1),
+            D3DCOLOR color = get_readback_color(&rb, format_tests[i].x, format_tests[i].y);            
+            D3DCOLOR color_amd = format_tests[i].color_amd[j];
+            D3DCOLOR color_intel = format_tests[i].color_intel[j];
+            todo_wine_if(format_tests[i].broken_wine) ok(color_match(color, color_amd, 1)
+                    || (isWin && color_match(color, color_intel, 1)),
                     "Test %d,%s expected color 0x%08x at (%u, %u), got 0x%08x.\n", i, shaders[j].name,
-                    expected_color, format_tests[i].x, format_tests[i].y, color);
+                    color_amd, format_tests[i].x, format_tests[i].y, color);
             release_surface_readback(&rb);
 
             hr = IDirect3DDevice9_Present(device, NULL, NULL, NULL, NULL);
             ok(SUCCEEDED(hr), "Present failed, hr %#x.\n", hr);
+
+            sleep(1);
         }
         IDirect3DTexture9_Release(tex);
     }
@@ -15541,15 +15595,30 @@ static void fetch4_test(void)
         hr = IDirect3DDevice9_EndScene(device);
         ok(SUCCEEDED(hr), "EndScene failed, hr %#x.\n", hr);
         get_rt_readback(original_rt, &rb);
-        for (j = 0; j < ARRAY_SIZE(expected_colors); ++j)
+        for (j = 0; j < ARRAY_SIZE(expected_colors.color_amd); ++j)
         {
-            D3DCOLOR color = get_readback_color(&rb, expected_colors[j].x, expected_colors[j].y);
-            /* FIXME: Fetch4 on 3D texture is like sampling a 2DArray at .xy0 with Fetch4 enabled
+            UINT x = expected_colors.x[j % 4];
+            UINT y = expected_colors.y[j / 4];
+            D3DCOLOR color = get_readback_color(&rb, x, y);
+            D3DCOLOR color_intel = expected_colors.color_intel[j];
+            D3DCOLOR color_off = expected_colors.color_3d_fetch4_off[j];
+            D3DCOLOR color_zround = expected_colors.color_amd[(j+4) % ARRAY_SIZE(expected_colors.color_amd)];
+            /* FIXME: Fetch4 on 3D textures have different results based on the vendor/driver
+             *  - AMD "HD 5700" rounds to nearest "z" texel, and does fetch4 normally on .xy
+             *  - AMD "R500" has fetch4 disabled
+             *  - Intel UHD 620 sample with fetch4 at .xy0
              * Currently unimplemented on wine due to lack of GL functionality to cast 3D->2DArray
-             * Wine produces same results as if fetch4 is not enabled. Passes on w10 */
-            todo_wine ok(color_match(color, expected_colors[j].color, 1),
-                    "Expected color 0x%08x at (%u, %u), got 0x%08x.\n",
-                    expected_colors[j].color, expected_colors[j].x, expected_colors[j].y, color);
+             * Wine produces same results as if fetch4 is not enabled
+             * Test will pass on windows if either one of the allowed results is returned */
+            if(isWin)
+                ok(color_match(color, color_zround, 1) || color_match(color, color_off, 1)
+                        || color_match(color, color_intel, 1),
+                        "Test 3D %s Expected colors 0x%08x || 0x%08x || 0x%08x at (%u, %u), got 0x%08x.\n",
+                        shaders[i].name, color_zround, color_off, color_intel, x, y, color);
+            else
+                ok(color_match(color, color_off, 1),
+                        "Test 3D %s Expected color 0x%08x at (%u, %u), got 0x%08x.\n", shaders[i].name,
+                        color_off, x, y, color);
         }
         release_surface_readback(&rb);
         hr = IDirect3DDevice9_Present(device, NULL, NULL, NULL, NULL);
@@ -15633,20 +15702,27 @@ static void fetch4_test(void)
             {
                 UINT x = expected_depth[depth_tests[i].index][k].x;
                 UINT y = expected_depth[depth_tests[i].index][k].y;
-                D3DCOLOR expected_color = expected_depth[depth_tests[i].index][k].color[j];
+                D3DCOLOR color_off = expected_depth[depth_tests[i].index][k].color_off;
+                D3DCOLOR color_amd = expected_depth[depth_tests[i].index][k].color_amd;
+                D3DCOLOR color_intel = expected_depth[depth_tests[i].index][k].color_intel;
                 D3DCOLOR color = get_readback_color(&rb, x, y);
-                /* Geforce 7 on Windows returns 1.0 in alpha when the depth format is D24S8 or D24X8,
-                 * whereas other GPUs (all AMD, newer Nvidia) return the same value they return in .rgb.
-                 * Accept alpha mismatches as broken but make sure to check the color channels. */
-                ok(color_match(color, expected_color, 2)
-                        || broken(color_match(color & 0x00ffffff, expected_color & 0x00ffffff, 0)),
-                        "Expected color 0x%08x at (%u, %u) for format %s, got 0x%08x.\n",
-                        expected_color, x, y, depth_tests[i].name, color);
+                /*  */
+                if(j == 0)
+                    ok(color_match(color, color_off, 1),
+                            "Test %s Expected color 0x%08x at (%u, %u) for format %s, got 0x%08x.\n",
+                            j ? "ON" : "OFF", color_amd, x, y, depth_tests[i].name, color);
+                else
+                    ok(color_match(color, color_amd, 1)
+                            || (isWin && color_match(color, color_intel, 1)),
+                            "Test %s Expected color 0x%08x at (%u, %u) for format %s, got 0x%08x.\n",
+                            j ? "ON" : "OFF", color_amd, x, y, depth_tests[i].name, color);
             }
             release_surface_readback(&rb);
 
             hr = IDirect3DDevice9_Present(device, NULL, NULL, NULL, NULL);
             ok(SUCCEEDED(hr), "Present failed, hr %#x.\n", hr);
+
+            sleep(1);
         }
 
         hr = IDirect3DDevice9_SetTexture(device, 0, NULL);
@@ -24980,7 +25056,7 @@ START_TEST(visual)
 
     IDirect3D9_Release(d3d);
 
-    test_sanity();
+    /*test_sanity();
     depth_clamp_test();
     stretchrect_test();
     test_multisample_stretch_rect();
@@ -25046,9 +25122,9 @@ START_TEST(visual)
     depth_buffer_test();
     depth_buffer2_test();
     depth_blit_test();
-    intz_test();
+    intz_test();*/
     fetch4_test();
-    shadow_test();
+    /*shadow_test();
     fp_special_test();
     depth_bounds_test();
     srgbwrite_format_test();
@@ -25092,5 +25168,5 @@ START_TEST(visual)
     test_null_format();
     test_map_synchronisation();
     test_color_vertex();
-    test_sysmem_draw();
+    test_sysmem_draw();*/
 }
diff --git a/dlls/wined3d/glsl_shader.c b/dlls/wined3d/glsl_shader.c
index 1bd94c8271..339178c870 100644
--- a/dlls/wined3d/glsl_shader.c
+++ b/dlls/wined3d/glsl_shader.c
@@ -3801,12 +3801,13 @@ static void shader_glsl_color_correction(const struct wined3d_shader_instruction
     string_buffer_release(priv->string_buffers, reg_name);
 }
 
-static void PRINTF_ATTR(9, 10) shader_glsl_gen_sample_code(const struct wined3d_shader_instruction *ins,
-        unsigned int sampler_bind_idx, const struct glsl_sample_function *sample_function, DWORD swizzle,
-        const char *dx, const char *dy, const char *bias, const struct wined3d_shader_texel_offset *offset,
-        const char *coord_reg_fmt, ...)
+static void PRINTF_ATTR(10, 11) shader_glsl_gen_sample_code(const struct wined3d_shader_instruction *ins,
+        unsigned int sampler_bind_idx, const struct glsl_sample_function *sample_function,
+        const DWORD swizzle, const DWORD sample_flags, const char *dx, const char *dy, const char *bias,
+        const struct wined3d_shader_texel_offset *offset, const char *coord_reg_fmt, ...)
 {
     const struct wined3d_shader_version *version = &ins->ctx->reg_maps->shader_version;
+    const struct shader_glsl_ctx_priv *priv = ins->ctx->backend_data;
     char dst_swizzle[6];
     struct color_fixup_desc fixup;
     BOOL np2_fixup = FALSE;
@@ -3848,10 +3849,8 @@ static void PRINTF_ATTR(9, 10) shader_glsl_gen_sample_code(const struct wined3d_
         if (!string_buffer_resize(ins->ctx->buffer, ret))
             break;
     }
-
     if (np2_fixup)
     {
-        const struct shader_glsl_ctx_priv *priv = ins->ctx->backend_data;
         const unsigned char idx = priv->cur_np2fixup_info->idx[sampler_bind_idx];
 
         switch (shader_glsl_get_write_mask_size(sample_function->coord_mask))
@@ -3874,6 +3873,20 @@ static void PRINTF_ATTR(9, 10) shader_glsl_gen_sample_code(const struct wined3d_
                 break;
         }
     }
+    if(priv->cur_ps_args->fetch4)
+    {
+        if (sample_flags & WINED3D_GLSL_SAMPLE_PROJECTED)
+        {
+            struct wined3d_string_buffer *reg_name = string_buffer_get(priv->string_buffers);
+            shader_glsl_get_register_name(&ins->src[0].reg, ins->src[0].reg.data_type, reg_name, NULL, ins->ctx);
+            shader_addline(ins->ctx->buffer, " / %s.w", reg_name->buffer);
+            string_buffer_release(priv->string_buffers, reg_name);
+        }
+
+        /* Correct the fetch4 0.5 texel offset */
+        shader_addline(ins->ctx->buffer, " + (vec2(0.5) / textureSize(%s_sampler%u, 0).xy)",
+                shader_glsl_get_prefix(version->type), sampler_bind_idx);
+    }
     if (dx && dy)
         shader_addline(ins->ctx->buffer, ", %s, %s", dx, dy);
     else if (bias)
@@ -5413,16 +5426,10 @@ static void shader_glsl_tex(const struct wined3d_shader_instruction *ins)
     }
 
     /* Fetch4 overwrites the other texture flags */
-    if (priv->cur_ps_args->fetch4 & (1u << resource_idx)){
-        /* 3D + Fetch4 should return textureGather(sampler2DArray, t.xy0)
-           unfortunately, we cant convert 3D to 2DArray */
-        if (ins->ctx->reg_maps->resource_info[resource_idx].type == WINED3D_SHADER_RESOURCE_TEXTURE_3D)
-            FIXME("Unsupported Fetch4 and texture3D sampling");
-        else
-        {
-            sample_flags = WINED3D_GLSL_SAMPLE_GATHER;
-            mask = 0;
-        }
+    if (priv->cur_ps_args->fetch4 & (1u << resource_idx))
+    {
+        sample_flags |= WINED3D_GLSL_SAMPLE_GATHER;
+        mask = 0;
     }
 
     shader_glsl_get_sample_function(ins->ctx, resource_idx, resource_idx, sample_flags, &sample_function);
@@ -5430,7 +5437,7 @@ static void shader_glsl_tex(const struct wined3d_shader_instruction *ins)
     sample_function.coord_mask = mask;
 
     if (shader_version < WINED3D_SHADER_VERSION(2,0)) swizzle = WINED3DSP_NOSWIZZLE;
-    else if (sample_flags == WINED3D_GLSL_SAMPLE_GATHER) swizzle = WINED3DSP_FETCH4_SWIZZLE;
+    else if (sample_flags & WINED3D_GLSL_SAMPLE_GATHER) swizzle = WINED3DSP_FETCH4_SWIZZLE;
     else swizzle = ins->src[1].swizzle;
 
     /* 1.0-1.3: Use destination register as coordinate source.
@@ -5439,8 +5446,8 @@ static void shader_glsl_tex(const struct wined3d_shader_instruction *ins)
     {
         char coord_mask[6];
         shader_glsl_write_mask_to_str(mask, coord_mask);
-        shader_glsl_gen_sample_code(ins, resource_idx, &sample_function, swizzle, NULL, NULL, NULL, NULL,
-                "T%u%s", resource_idx, coord_mask);
+        shader_glsl_gen_sample_code(ins, resource_idx, &sample_function, swizzle, sample_flags, NULL, NULL,
+                NULL, NULL, "T%u%s", resource_idx, coord_mask);
     }
     else
     {
@@ -5450,11 +5457,11 @@ static void shader_glsl_tex(const struct wined3d_shader_instruction *ins)
         {
             struct glsl_src_param bias;
             shader_glsl_add_src_param(ins, &ins->src[0], WINED3DSP_WRITEMASK_3, &bias);
-            shader_glsl_gen_sample_code(ins, resource_idx, &sample_function, swizzle, NULL, NULL, bias.param_str,
-                    NULL, "%s", coord_param.param_str);
+            shader_glsl_gen_sample_code(ins, resource_idx, &sample_function, swizzle, sample_flags,
+                    NULL, NULL, bias.param_str, NULL, "%s", coord_param.param_str);
         } else {
-            shader_glsl_gen_sample_code(ins, resource_idx, &sample_function, swizzle, NULL, NULL, NULL, NULL,
-                    "%s", coord_param.param_str);
+            shader_glsl_gen_sample_code(ins, resource_idx, &sample_function, swizzle, sample_flags,
+                    NULL, NULL, NULL, NULL, "%s", coord_param.param_str);
         }
     }
     shader_glsl_release_sample_function(ins->ctx, &sample_function);
@@ -5477,22 +5484,15 @@ static void shader_glsl_texldd(const struct wined3d_shader_instruction *ins)
     }
 
     /* Fetch4 overwrites the other texture flags */
-    if (priv->cur_ps_args->fetch4 & (1u << sampler_idx)){
-        /* 3D + Fetch4 should return textureGather(sampler2DArray, t.xy0)
-           unfortunately, we cant convert 3D to 2DArray */
-        if (ins->ctx->reg_maps->resource_info[sampler_idx].type != WINED3D_SHADER_RESOURCE_TEXTURE_3D)
-        {
-
-            shader_glsl_get_sample_function(ins->ctx, sampler_idx, sampler_idx, WINED3D_GLSL_SAMPLE_GATHER, &sample_function);
-            swizzle = WINED3DSP_FETCH4_SWIZZLE;
-            shader_glsl_add_src_param(ins, &ins->src[0], sample_function.coord_mask, &coord_param);
-            shader_glsl_gen_sample_code(ins, sampler_idx, &sample_function, swizzle, NULL, NULL, NULL, NULL,
-                    "%s", coord_param.param_str);
-            shader_glsl_release_sample_function(ins->ctx, &sample_function);
-            return;
-        }
-        else
-            FIXME("Unsupported Fetch4 and texture3D sampling");
+    if (priv->cur_ps_args->fetch4 & (1u << sampler_idx))
+    {
+        shader_glsl_get_sample_function(ins->ctx, sampler_idx, sampler_idx, WINED3D_GLSL_SAMPLE_GATHER, &sample_function);
+        swizzle = WINED3DSP_FETCH4_SWIZZLE;
+        shader_glsl_add_src_param(ins, &ins->src[0], sample_function.coord_mask, &coord_param);
+        shader_glsl_gen_sample_code(ins, sampler_idx, &sample_function, swizzle, 0, NULL, NULL,
+                NULL, NULL, "%s", coord_param.param_str);
+        shader_glsl_release_sample_function(ins->ctx, &sample_function);
+        return;
     }
 
     shader_glsl_get_sample_function(ins->ctx, sampler_idx, sampler_idx, WINED3D_GLSL_SAMPLE_GRAD, &sample_function);
@@ -5500,8 +5500,8 @@ static void shader_glsl_texldd(const struct wined3d_shader_instruction *ins)
     shader_glsl_add_src_param(ins, &ins->src[2], sample_function.deriv_mask, &dx_param);
     shader_glsl_add_src_param(ins, &ins->src[3], sample_function.deriv_mask, &dy_param);
 
-    shader_glsl_gen_sample_code(ins, sampler_idx, &sample_function, swizzle, dx_param.param_str, dy_param.param_str,
-            NULL, NULL, "%s", coord_param.param_str);
+    shader_glsl_gen_sample_code(ins, sampler_idx, &sample_function, swizzle, 0,
+            dx_param.param_str, dy_param.param_str, NULL, NULL, "%s", coord_param.param_str);
     shader_glsl_release_sample_function(ins->ctx, &sample_function);
 }
 
@@ -5519,12 +5519,7 @@ static void shader_glsl_texldl(const struct wined3d_shader_instruction *ins)
     /* This call can be used in vertex shader, without cur_ps_args */
     if(priv->cur_ps_args && priv->cur_ps_args->fetch4 & (1u << sampler_idx))
     {
-        /* 3D + Fetch4 should return textureGather(sampler2DArray, t.xy0)
-           unfortunately, we cant convert 3D to 2DArray */
-        if (ins->ctx->reg_maps->resource_info[sampler_idx].type == WINED3D_SHADER_RESOURCE_TEXTURE_3D)
-            FIXME("Unsupported Fetch4 and texture3D sampling");
-        else
-            flags = WINED3D_GLSL_SAMPLE_GATHER;
+        flags = WINED3D_GLSL_SAMPLE_GATHER;
     }
 
     shader_glsl_get_sample_function(ins->ctx, sampler_idx, sampler_idx, flags, &sample_function);
@@ -5541,8 +5536,8 @@ static void shader_glsl_texldl(const struct wined3d_shader_instruction *ins)
 
     if (flags == WINED3D_GLSL_SAMPLE_GATHER){
         swizzle = WINED3DSP_FETCH4_SWIZZLE;
-        shader_glsl_gen_sample_code(ins, sampler_idx, &sample_function, swizzle, NULL, NULL, NULL, NULL,
-                "%s", coord_param.param_str);
+        shader_glsl_gen_sample_code(ins, sampler_idx, &sample_function, swizzle, 0,
+                NULL, NULL, NULL, NULL, "%s", coord_param.param_str);
         shader_glsl_release_sample_function(ins->ctx, &sample_function);
         return;
     }
@@ -5550,7 +5545,7 @@ static void shader_glsl_texldl(const struct wined3d_shader_instruction *ins)
     shader_glsl_get_sample_function(ins->ctx, sampler_idx, sampler_idx, WINED3D_GLSL_SAMPLE_LOD, &sample_function);
     shader_glsl_add_src_param(ins, &ins->src[0], sample_function.coord_mask, &coord_param);
     shader_glsl_add_src_param(ins, &ins->src[0], WINED3DSP_WRITEMASK_3, &lod_param);
-    shader_glsl_gen_sample_code(ins, sampler_idx, &sample_function, swizzle, NULL, NULL, lod_param.param_str, NULL,
+    shader_glsl_gen_sample_code(ins, sampler_idx, &sample_function, swizzle, 0, NULL, NULL, lod_param.param_str, NULL,
             "%s", coord_param.param_str);
     shader_glsl_release_sample_function(ins->ctx, &sample_function);
 }
@@ -6269,12 +6264,12 @@ static void shader_glsl_ld(const struct wined3d_shader_instruction *ins)
     if (is_multisampled(reg_maps->resource_info[resource_idx].type))
     {
         shader_glsl_add_src_param(ins, &ins->src[2], WINED3DSP_WRITEMASK_0, &sample_param);
-        shader_glsl_gen_sample_code(ins, sampler_bind_idx, &sample_function, ins->src[1].swizzle,
+        shader_glsl_gen_sample_code(ins, sampler_bind_idx, &sample_function, ins->src[1].swizzle, 0,
                 NULL, NULL, NULL, &ins->texel_offset, "%s, %s", coord_param.param_str, sample_param.param_str);
     }
     else
     {
-        shader_glsl_gen_sample_code(ins, sampler_bind_idx, &sample_function, ins->src[1].swizzle,
+        shader_glsl_gen_sample_code(ins, sampler_bind_idx, &sample_function, ins->src[1].swizzle, 0,
                 NULL, NULL, has_lod_param ? lod_param.param_str : NULL, &ins->texel_offset,
                 "%s", coord_param.param_str);
     }
@@ -6302,15 +6297,8 @@ static void shader_glsl_sample(const struct wined3d_shader_instruction *ins)
         flags |= WINED3D_GLSL_SAMPLE_OFFSET;
     if (priv->cur_ps_args->fetch4 & (1u << sampler_idx))
     {
-        /* 3D + Fetch4 should return textureGather(sampler2DArray, t.xy0)
-           unfortunately, we cant convert 3D to 2DArray */
-        if (ins->ctx->reg_maps->resource_info[sampler_idx].type == WINED3D_SHADER_RESOURCE_TEXTURE_3D)
-            FIXME("Unsupported Fetch4 and texture3D sampling");
-        else
-        {
-            flags = WINED3D_GLSL_SAMPLE_GATHER;
-            swizzle = WINED3DSP_FETCH4_SWIZZLE;
-        }
+        flags = WINED3D_GLSL_SAMPLE_GATHER;
+        swizzle = WINED3DSP_FETCH4_SWIZZLE;
     }
 
     shader_glsl_get_sample_function(ins->ctx, resource_idx, sampler_idx, flags, &sample_function);
@@ -6344,7 +6332,7 @@ static void shader_glsl_sample(const struct wined3d_shader_instruction *ins)
     }
 
     sampler_bind_idx = shader_glsl_find_sampler(&ins->ctx->reg_maps->sampler_map, resource_idx, sampler_idx);
-    shader_glsl_gen_sample_code(ins, sampler_bind_idx, &sample_function, swizzle,
+    shader_glsl_gen_sample_code(ins, sampler_bind_idx, &sample_function, swizzle, 0,
             dx_param_str, dy_param_str, lod_param_str, &ins->texel_offset, "%s", coord_param.param_str);
     shader_glsl_release_sample_function(ins->ctx, &sample_function);
 }
@@ -6419,7 +6407,7 @@ static void shader_glsl_sample_c(const struct wined3d_shader_instruction *ins)
     }
     else
     {
-        shader_glsl_gen_sample_code(ins, sampler_bind_idx, &sample_function, WINED3DSP_NOSWIZZLE,
+        shader_glsl_gen_sample_code(ins, sampler_bind_idx, &sample_function, WINED3DSP_NOSWIZZLE, 0,
                 NULL, NULL, lod_param, &ins->texel_offset, "vec%u(%s, %s)",
                 coord_size, coord_param.param_str, compare_param.param_str);
     }
@@ -6561,18 +6549,18 @@ static void shader_glsl_texdp3tex(const struct wined3d_shader_instruction *ins)
     switch(mask_size)
     {
         case 1:
-            shader_glsl_gen_sample_code(ins, sampler_idx, &sample_function, WINED3DSP_NOSWIZZLE, NULL, NULL, NULL,
-                    NULL, "dot(ffp_texcoord[%u].xyz, %s)", sampler_idx, src0_param.param_str);
+            shader_glsl_gen_sample_code(ins, sampler_idx, &sample_function, WINED3DSP_NOSWIZZLE, 0, NULL, NULL,
+                    NULL, NULL, "dot(ffp_texcoord[%u].xyz, %s)", sampler_idx, src0_param.param_str);
             break;
 
         case 2:
-            shader_glsl_gen_sample_code(ins, sampler_idx, &sample_function, WINED3DSP_NOSWIZZLE, NULL, NULL, NULL,
-                    NULL, "vec2(dot(ffp_texcoord[%u].xyz, %s), 0.0)", sampler_idx, src0_param.param_str);
+            shader_glsl_gen_sample_code(ins, sampler_idx, &sample_function, WINED3DSP_NOSWIZZLE, 0, NULL, NULL,
+                    NULL, NULL, "vec2(dot(ffp_texcoord[%u].xyz, %s), 0.0)", sampler_idx, src0_param.param_str);
             break;
 
         case 3:
-            shader_glsl_gen_sample_code(ins, sampler_idx, &sample_function, WINED3DSP_NOSWIZZLE, NULL, NULL, NULL,
-                    NULL, "vec3(dot(ffp_texcoord[%u].xyz, %s), 0.0, 0.0)", sampler_idx, src0_param.param_str);
+            shader_glsl_gen_sample_code(ins, sampler_idx, &sample_function, WINED3DSP_NOSWIZZLE, 0, NULL, NULL,
+                    NULL, NULL, "vec3(dot(ffp_texcoord[%u].xyz, %s), 0.0, 0.0)", sampler_idx, src0_param.param_str);
             break;
 
         default:
@@ -6680,7 +6668,7 @@ static void shader_glsl_texm3x2tex(const struct wined3d_shader_instruction *ins)
     shader_glsl_get_sample_function(ins->ctx, reg, reg, 0, &sample_function);
 
     /* Sample the texture using the calculated coordinates */
-    shader_glsl_gen_sample_code(ins, reg, &sample_function, WINED3DSP_NOSWIZZLE, NULL, NULL, NULL, NULL, "tmp0.xy");
+    shader_glsl_gen_sample_code(ins, reg, &sample_function, WINED3DSP_NOSWIZZLE, 0, NULL, NULL, NULL, NULL, "tmp0.xy");
     shader_glsl_release_sample_function(ins->ctx, &sample_function);
 }
 
@@ -6701,7 +6689,7 @@ static void shader_glsl_texm3x3tex(const struct wined3d_shader_instruction *ins)
     shader_glsl_get_sample_function(ins->ctx, reg, reg, 0, &sample_function);
 
     /* Sample the texture using the calculated coordinates */
-    shader_glsl_gen_sample_code(ins, reg, &sample_function, WINED3DSP_NOSWIZZLE, NULL, NULL, NULL, NULL, "tmp0.xyz");
+    shader_glsl_gen_sample_code(ins, reg, &sample_function, WINED3DSP_NOSWIZZLE, 0, NULL, NULL, NULL, NULL, "tmp0.xyz");
     shader_glsl_release_sample_function(ins->ctx, &sample_function);
 
     tex_mx->current_row = 0;
@@ -6752,7 +6740,7 @@ static void shader_glsl_texm3x3spec(const struct wined3d_shader_instruction *ins
     shader_glsl_write_mask_to_str(sample_function.coord_mask, coord_mask);
 
     /* Sample the texture */
-    shader_glsl_gen_sample_code(ins, reg, &sample_function, WINED3DSP_NOSWIZZLE,
+    shader_glsl_gen_sample_code(ins, reg, &sample_function, WINED3DSP_NOSWIZZLE, 0,
             NULL, NULL, NULL, NULL, "tmp0%s", coord_mask);
     shader_glsl_release_sample_function(ins->ctx, &sample_function);
 
@@ -6786,7 +6774,7 @@ static void shader_glsl_texm3x3vspec(const struct wined3d_shader_instruction *in
     shader_glsl_write_mask_to_str(sample_function.coord_mask, coord_mask);
 
     /* Sample the texture using the calculated coordinates */
-    shader_glsl_gen_sample_code(ins, reg, &sample_function, WINED3DSP_NOSWIZZLE,
+    shader_glsl_gen_sample_code(ins, reg, &sample_function, WINED3DSP_NOSWIZZLE, 0,
             NULL, NULL, NULL, NULL, "tmp0%s", coord_mask);
     shader_glsl_release_sample_function(ins->ctx, &sample_function);
 
@@ -6845,7 +6833,7 @@ static void shader_glsl_texbem(const struct wined3d_shader_instruction *ins)
 
     shader_glsl_add_src_param(ins, &ins->src[0], WINED3DSP_WRITEMASK_0 | WINED3DSP_WRITEMASK_1, &coord_param);
 
-    shader_glsl_gen_sample_code(ins, sampler_idx, &sample_function, WINED3DSP_NOSWIZZLE, NULL, NULL, NULL, NULL,
+    shader_glsl_gen_sample_code(ins, sampler_idx, &sample_function, WINED3DSP_NOSWIZZLE, 0, NULL, NULL, NULL, NULL,
             "T%u%s + vec4(bumpenv_mat%u * %s, 0.0, 0.0)%s", sampler_idx, coord_mask, sampler_idx,
             coord_param.param_str, coord_mask);
 
@@ -6890,7 +6878,7 @@ static void shader_glsl_texreg2ar(const struct wined3d_shader_instruction *ins)
     shader_glsl_get_register_name(&ins->src[0].reg, ins->src[0].reg.data_type, reg_name, NULL, ins->ctx);
 
     shader_glsl_get_sample_function(ins->ctx, sampler_idx, sampler_idx, 0, &sample_function);
-    shader_glsl_gen_sample_code(ins, sampler_idx, &sample_function, WINED3DSP_NOSWIZZLE, NULL, NULL, NULL, NULL,
+    shader_glsl_gen_sample_code(ins, sampler_idx, &sample_function, WINED3DSP_NOSWIZZLE, 0, NULL, NULL, NULL, NULL,
             "%s.wx", reg_name->buffer);
     shader_glsl_release_sample_function(ins->ctx, &sample_function);
 
@@ -6910,7 +6898,7 @@ static void shader_glsl_texreg2gb(const struct wined3d_shader_instruction *ins)
     shader_glsl_get_register_name(&ins->src[0].reg, ins->src[0].reg.data_type, reg_name, NULL, ins->ctx);
 
     shader_glsl_get_sample_function(ins->ctx, sampler_idx, sampler_idx, 0, &sample_function);
-    shader_glsl_gen_sample_code(ins, sampler_idx, &sample_function, WINED3DSP_NOSWIZZLE, NULL, NULL, NULL, NULL,
+    shader_glsl_gen_sample_code(ins, sampler_idx, &sample_function, WINED3DSP_NOSWIZZLE, 0, NULL, NULL, NULL, NULL,
             "%s.yz", reg_name->buffer);
     shader_glsl_release_sample_function(ins->ctx, &sample_function);
 
@@ -6929,7 +6917,7 @@ static void shader_glsl_texreg2rgb(const struct wined3d_shader_instruction *ins)
     shader_glsl_get_sample_function(ins->ctx, sampler_idx, sampler_idx, 0, &sample_function);
     shader_glsl_add_src_param(ins, &ins->src[0], sample_function.coord_mask, &src0_param);
 
-    shader_glsl_gen_sample_code(ins, sampler_idx, &sample_function, WINED3DSP_NOSWIZZLE, NULL, NULL, NULL, NULL,
+    shader_glsl_gen_sample_code(ins, sampler_idx, &sample_function, WINED3DSP_NOSWIZZLE, 0, NULL, NULL, NULL, NULL,
             "%s", src0_param.param_str);
     shader_glsl_release_sample_function(ins->ctx, &sample_function);
 }
@@ -9937,6 +9925,7 @@ static GLuint shader_glsl_generate_ffp_fragment_shader(struct shader_glsl_priv *
     for (stage = 0; stage < MAX_TEXTURES && settings->op[stage].cop != WINED3D_TOP_DISABLE; ++stage)
     {
         const char *texture_function, *coord_mask;
+        struct wined3d_string_buffer coord;
         BOOL fetch4 = settings->op[stage].fetch4;
         BOOL proj;
 
@@ -9974,8 +9963,6 @@ static GLuint shader_glsl_generate_ffp_fragment_shader(struct shader_glsl_priv *
             case WINED3D_GL_RES_TYPE_TEX_3D:
                 texture_function = "texture3D";
                 coord_mask = "xyz";
-                /* 3D + Fetch4 should return textureGather(sampler2DArray, t.xy0)
-                   unfortunately, we cant convert 3D to 2DArray */
                 if (fetch4)
                     FIXME("Unsupported Fetch4 and texture3D sampling");
                 fetch4 = FALSE;
@@ -9987,9 +9974,6 @@ static GLuint shader_glsl_generate_ffp_fragment_shader(struct shader_glsl_priv *
             case WINED3D_GL_RES_TYPE_TEX_RECT:
                 texture_function = "texture2DRect";
                 coord_mask = "xy";
-                if (fetch4)
-                    FIXME("Unsupported Fetch4 and texture2DRect sampling");
-                fetch4 = FALSE;
                 break;
             default:
                 FIXME("Unhandled texture type %#x.\n", settings->op[stage].tex_type);
@@ -10002,10 +9986,17 @@ static GLuint shader_glsl_generate_ffp_fragment_shader(struct shader_glsl_priv *
         if (!legacy_syntax)
             texture_function = "texture";
 
+        string_buffer_init(&coord);
         if (fetch4)
         {
             texture_function = "textureGather";
-            /* Tested on W10+Intel, fetch4 enabled disables projection */
+            /* Apply a 0.5 texel offset as in AMD implementation */
+            if(2 == strlen(coord_mask))
+                shader_addline(&coord, " + (vec2(0.5) / textureSize(ps_sampler%u, 0).xy)", stage);
+            else
+                shader_addline(&coord, " + vec3(vec2(0.5) / textureSize(ps_sampler%u, 0).xy, 0)", stage);
+
+            /* TODO PROJECTION */
             proj = FALSE;
         }
 
@@ -10039,8 +10030,8 @@ static GLuint shader_glsl_generate_ffp_fragment_shader(struct shader_glsl_priv *
                 shader_addline(buffer, "ret = ffp_texcoord[%u] + ret.xyxy;\n", stage);
             }
 
-            shader_addline(buffer, "tex%u = %s%s(ps_sampler%u, ret.%s%s);\n",
-                    stage, texture_function, proj ? "Proj" : "", stage, coord_mask, proj ? "w" : "");
+            shader_addline(buffer, "tex%u = %s%s(ps_sampler%u, ret.%s%s%s);\n", stage, texture_function,
+                    proj ? "Proj" : "", stage, coord_mask, proj ? "w" : "", coord.buffer);
 
             if (settings->op[stage - 1].cop == WINED3D_TOP_BUMPENVMAP_LUMINANCE)
                 shader_addline(buffer, "tex%u *= clamp(tex%u.z * bumpenv_lum_scale%u + bumpenv_lum_offset%u, 0.0, 1.0);\n",
@@ -10048,18 +10039,19 @@ static GLuint shader_glsl_generate_ffp_fragment_shader(struct shader_glsl_priv *
         }
         else if (settings->op[stage].projected == WINED3D_PROJECTION_COUNT3)
         {
-            shader_addline(buffer, "tex%u = %s%s(ps_sampler%u, ffp_texcoord[%u].xyz);\n",
-                    stage, texture_function, proj ? "Proj" : "", stage, stage);
+            shader_addline(buffer, "tex%u = %s%s(ps_sampler%u, ffp_texcoord[%u].xyz%s);\n", stage,
+                    texture_function, proj ? "Proj" : "", stage, stage, coord.buffer);
         }
         else
         {
-            shader_addline(buffer, "tex%u = %s%s(ps_sampler%u, ffp_texcoord[%u].%s%s);\n",
-                    stage, texture_function, proj ? "Proj" : "", stage, stage, coord_mask, proj ? "w" : "");
+            shader_addline(buffer, "tex%u = %s%s(ps_sampler%u, ffp_texcoord[%u].%s%s%s);\n", stage,
+                    texture_function, proj ? "Proj" : "", stage, stage, coord_mask, proj ? "w" : "", coord.buffer);
         }
+        string_buffer_clear(&coord);
 
         /* Match FETCH4 swizzle with textureGather swizzle */
         if (fetch4)
-            shader_addline(buffer, "tex%u = tex%u.xwyz;\n", stage, stage);
+            shader_addline(buffer, "tex%u = tex%u.zxyw;\n", stage, stage);
 
         string_buffer_sprintf(tex_reg_name, "tex%u", stage);
         shader_glsl_color_correction_ext(buffer, tex_reg_name->buffer, WINED3DSP_WRITEMASK_ALL,
diff --git a/dlls/wined3d/shader.c b/dlls/wined3d/shader.c
index f07804de63..6e21444f8c 100644
--- a/dlls/wined3d/shader.c
+++ b/dlls/wined3d/shader.c
@@ -4032,7 +4032,8 @@ void find_ps_compile_args(const struct wined3d_state *state, const struct wined3
         for (i = 0; i < MAX_FRAGMENT_SAMPLERS; ++i)
         {
             if (state->sampler_states[i][WINED3D_SAMP_MIPMAP_LOD_BIAS] == MAKEFOURCC('G','E','T','4')
-                    && state->textures[i]->resource.format_flags & WINED3DFMT_FLAG_ALLOW_FETCH4)
+                    && state->textures[i]->resource.format_flags & WINED3DFMT_FLAG_ALLOW_FETCH4
+                    && shader->reg_maps.resource_info[i].type == WINED3D_SHADER_RESOURCE_TEXTURE_2D)
                 args->fetch4 |= 1 << i;
             else
                 args->fetch4 &= ~(1 << i);
diff --git a/dlls/wined3d/utils.c b/dlls/wined3d/utils.c
index 02667870cb..8a6e3c2975 100644
--- a/dlls/wined3d/utils.c
+++ b/dlls/wined3d/utils.c
@@ -349,6 +349,7 @@ static const struct wined3d_format_base_flags format_base_flags[] =
     {WINED3DFMT_R16_FLOAT,            WINED3DFMT_FLAG_ALLOW_FETCH4},
     {WINED3DFMT_R16,                  WINED3DFMT_FLAG_ALLOW_FETCH4},
     {WINED3DFMT_R32_FLOAT,            WINED3DFMT_FLAG_ALLOW_FETCH4},
+    {WINED3DFMT_A8_UNORM,             WINED3DFMT_FLAG_ALLOW_FETCH4},
     {WINED3DFMT_DF16,                 WINED3DFMT_FLAG_ALLOW_FETCH4},
     {WINED3DFMT_DF24,                 WINED3DFMT_FLAG_ALLOW_FETCH4},
 };
@@ -5962,9 +5963,10 @@ void gen_ffp_frag_op(const struct wined3d_context *context, const struct wined3d
         settings->op[i].aarg1 = aarg1;
         settings->op[i].aarg2 = aarg2;
         settings->op[i].tmp_dst = state->texture_states[i][WINED3D_TSS_RESULT_ARG] == WINED3DTA_TEMP;
-        settings->op[i].fetch4 = (state->textures[i] && gl_info->supported[ARB_TEXTURE_GATHER] &&
-                state->textures[i]->resource.format_flags & WINED3DFMT_FLAG_ALLOW_FETCH4 &&
-                state->sampler_states[i][WINED3D_SAMP_MIPMAP_LOD_BIAS] == MAKEFOURCC('G','E','T','4'));
+        settings->op[i].fetch4 = (state->textures[i] && gl_info->supported[ARB_TEXTURE_GATHER]
+                && state->textures[i]->resource.format_flags & WINED3DFMT_FLAG_ALLOW_FETCH4
+                && state->sampler_states[i][WINED3D_SAMP_MIPMAP_LOD_BIAS] == MAKEFOURCC('G','E','T','4')
+                && settings->op[i].tex_type & (WINED3D_GL_RES_TYPE_TEX_2D | WINED3D_GL_RES_TYPE_TEX_RECT));
     }
 
     /* Clear unsupported stages */
diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
index 79cc638ae6..8eed3c19eb 100644
--- a/dlls/wined3d/wined3d_private.h
+++ b/dlls/wined3d/wined3d_private.h
@@ -522,7 +522,7 @@ enum wined3d_immconst_type
 };
 
 #define WINED3DSP_NOSWIZZLE      (0u | (1u << 2) | (2u << 4) | (3u << 6))
-#define WINED3DSP_FETCH4_SWIZZLE (0u | (3u << 2) | (1u << 4) | (2u << 6)) /* xwyz */
+#define WINED3DSP_FETCH4_SWIZZLE (2u | (0u << 2) | (1u << 4) | (3u << 6)) /* zxyw */
 
 enum wined3d_shader_src_modifier
 {
-- 
2.17.1

