/*
 * Copyright 2016-2017 Józef Kucia for CodeWeavers
 * Copyright 2020-2021 Philip Rebohle for Valve Corporation
 * Copyright 2020-2021 Joshua Ashton for Valve Corporation
 * Copyright 2020-2021 Hans-Kristian Arntzen for Valve Corporation
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#define VKD3D_DBG_CHANNEL VKD3D_DBG_CHANNEL_API
#include "d3d12_crosstest.h"

#define TILE_SIZE 65536

static uint32_t compute_tile_count(uint32_t resource_size, uint32_t mip, uint32_t tile_size)
{
    uint32_t mip_size = max(resource_size >> mip, 1u);
    return (mip_size / tile_size) + (mip_size % tile_size ? 1 : 0);
}

void test_get_resource_tiling(void)
{
    D3D12_FEATURE_DATA_D3D12_OPTIONS options;
    D3D12_SUBRESOURCE_TILING tilings_alt[64];
    D3D12_PACKED_MIP_INFO packed_mip_info;
    D3D12_SUBRESOURCE_TILING tilings[64];
    UINT num_resource_tiles, num_tilings;
    D3D12_RESOURCE_DESC resource_desc;
    struct test_context_desc desc;
    struct test_context context;
    D3D12_TILE_SHAPE tile_shape;
    ID3D12Resource *resource;
    unsigned int i, j;
    HRESULT hr;

    static const struct
    {
        D3D12_RESOURCE_DIMENSION dim;
        DXGI_FORMAT format;
        UINT width;
        UINT height;
        UINT depth_or_array_layers;
        UINT mip_levels;
        UINT expected_tile_count;
        UINT expected_tiling_count;
        UINT expected_standard_mips;
        UINT tile_shape_w;
        UINT tile_shape_h;
        UINT tile_shape_d;
        D3D12_TILED_RESOURCES_TIER min_tier;
        bool todo_radv;
    }
    tests[] =
    {
        /* Test buffers */
        { D3D12_RESOURCE_DIMENSION_BUFFER,    DXGI_FORMAT_UNKNOWN,            1024,    1,  1,  1,  1,  1,  0, 65536,   1,   1, D3D12_TILED_RESOURCES_TIER_1 },
        { D3D12_RESOURCE_DIMENSION_BUFFER,    DXGI_FORMAT_UNKNOWN,        16*65536,    1,  1,  1, 16,  1,  0, 65536,   1,   1, D3D12_TILED_RESOURCES_TIER_1 },
        /* Test small resource behavior */
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_R8_UNORM,              1,    1,  1,  1,  1,  1,  0,   256, 256,   1, D3D12_TILED_RESOURCES_TIER_1 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_R8_UNORM,              2,    2,  1,  2,  1,  2,  0,   256, 256,   1, D3D12_TILED_RESOURCES_TIER_1 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_R8_UNORM,              4,    4,  1,  3,  1,  3,  0,   256, 256,   1, D3D12_TILED_RESOURCES_TIER_1 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_R8_UNORM,              8,    8,  1,  4,  1,  4,  0,   256, 256,   1, D3D12_TILED_RESOURCES_TIER_1 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_R8_UNORM,             16,   16,  1,  5,  1,  5,  0,   256, 256,   1, D3D12_TILED_RESOURCES_TIER_1 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_R8_UNORM,             32,   32,  1,  6,  1,  6,  0,   256, 256,   1, D3D12_TILED_RESOURCES_TIER_1 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_R8_UNORM,             64,   64,  1,  7,  1,  7,  0,   256, 256,   1, D3D12_TILED_RESOURCES_TIER_1 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_R8_UNORM,            128,  128,  1,  8,  1,  8,  0,   256, 256,   1, D3D12_TILED_RESOURCES_TIER_1 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_R8_UNORM,            256,  256,  1,  9,  2,  9,  1,   256, 256,   1, D3D12_TILED_RESOURCES_TIER_1 },
        /* Test various image formats */
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_R8_UNORM,	           512,  512,  1,  1,  4,  1,  1,   256, 256,   1, D3D12_TILED_RESOURCES_TIER_1 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_R8G8_UNORM,          512,  512,  1,  1,  8,  1,  1,   256, 128,   1, D3D12_TILED_RESOURCES_TIER_1 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_R8G8B8A8_UNORM,      512,  512,  1,  1, 16,  1,  1,   128, 128,   1, D3D12_TILED_RESOURCES_TIER_1 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_R16G16B16A16_UNORM,  512,  512,  1,  1, 32,  1,  1,   128,  64,   1, D3D12_TILED_RESOURCES_TIER_1 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_R32G32B32A32_FLOAT,  512,  512,  1,  1, 64,  1,  1,    64,  64,   1, D3D12_TILED_RESOURCES_TIER_1 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_D16_UNORM,           512,  512,  1,  1,  8,  1,  1,   256, 128,   1, D3D12_TILED_RESOURCES_TIER_1, true },
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_D32_FLOAT,           512,  512,  1,  1, 16,  1,  1,   128, 128,   1, D3D12_TILED_RESOURCES_TIER_1, true },

        /* Test rectangular textures */
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_R8G8B8A8_UNORM,     1024,  256,  1,  1, 16,  1,  1,   128, 128,   1, D3D12_TILED_RESOURCES_TIER_1 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_R8G8B8A8_UNORM,      256, 1024,  1,  1, 16,  1,  1,   128, 128,   1, D3D12_TILED_RESOURCES_TIER_1 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_R8G8B8A8_UNORM,      192,  128,  1,  1,  2,  1,  1,   128, 128,   1, D3D12_TILED_RESOURCES_TIER_2 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_R8G8B8A8_UNORM,      128,  192,  1,  1,  2,  1,  1,   128, 128,   1, D3D12_TILED_RESOURCES_TIER_2 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_R8G8B8A8_UNORM,      320,  192,  1,  1,  6,  1,  1,   128, 128,   1, D3D12_TILED_RESOURCES_TIER_2 },
        /* Test array layers and packed mip levels */
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_R8G8B8A8_UNORM,      128,  128, 16,  1, 16, 16,  1,   128, 128,   1, D3D12_TILED_RESOURCES_TIER_1 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_R8G8B8A8_UNORM,      128,  128,  1,  8,  1,  8,  1,   128, 128,   1, D3D12_TILED_RESOURCES_TIER_1 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_R8G8B8A8_UNORM,      512,  512,  1, 10, 21, 10,  3,   128, 128,   1, D3D12_TILED_RESOURCES_TIER_1 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_R8G8B8A8_UNORM,      512,  512,  4,  3, 84, 12,  3,   128, 128,   1, D3D12_TILED_RESOURCES_TIER_1 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_R8G8B8A8_UNORM,       64,   64,  1,  1,  1,  1,  0,   128, 128,   1, D3D12_TILED_RESOURCES_TIER_1 },
        /* Test 3D textures */
        { D3D12_RESOURCE_DIMENSION_TEXTURE3D, DXGI_FORMAT_R8_UNORM,             64,   64, 64,  1,  4,  1,  1,    64,  32,  32, D3D12_TILED_RESOURCES_TIER_3 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE3D, DXGI_FORMAT_R8G8_UNORM,           64,   64, 64,  1,  8,  1,  1,    32,  32,  32, D3D12_TILED_RESOURCES_TIER_3 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE3D, DXGI_FORMAT_R8G8B8A8_UNORM,       64,   64, 64,  1, 16,  1,  1,    32,  32,  16, D3D12_TILED_RESOURCES_TIER_3 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE3D, DXGI_FORMAT_R32G32B32A32_FLOAT,   64,   64, 64,  3, 73,  3,  3,    16,  16,  16, D3D12_TILED_RESOURCES_TIER_3 },

        /* Basic BC configurations. */
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_BC1_UNORM,           512, 512,  1,  1,  2,  1,  1,   512, 256,   1, D3D12_TILED_RESOURCES_TIER_1 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_BC2_UNORM,           512, 512,  1,  1,  4,  1,  1,   256, 256,   1, D3D12_TILED_RESOURCES_TIER_1 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_BC3_UNORM,           512, 512,  1,  1,  4,  1,  1,   256, 256,   1, D3D12_TILED_RESOURCES_TIER_1 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_BC4_UNORM,           512, 512,  1,  1,  2,  1,  1,   512, 256,   1, D3D12_TILED_RESOURCES_TIER_1 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_BC5_UNORM,           512, 512,  1,  1,  4,  1,  1,   256, 256,   1, D3D12_TILED_RESOURCES_TIER_1 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_BC6H_UF16,           512, 512,  1,  1,  4,  1,  1,   256, 256,   1, D3D12_TILED_RESOURCES_TIER_1 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_BC6H_SF16,           512, 512,  1,  1,  4,  1,  1,   256, 256,   1, D3D12_TILED_RESOURCES_TIER_1 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_BC7_UNORM,           512, 512,  1,  1,  4,  1,  1,   256, 256,   1, D3D12_TILED_RESOURCES_TIER_1 },

        /* Basic mipmapping with obvious tiling layouts. */
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_BC1_UNORM,           512,  256,  1,  10,  2, 10,  1,   512, 256,   1, D3D12_TILED_RESOURCES_TIER_1 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_BC1_UNORM,          1024,  512,  1,  10,  6, 10,  2,   512, 256,   1, D3D12_TILED_RESOURCES_TIER_1 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_BC1_UNORM,          2048, 1024,  1,  10, 22, 10,  3,   512, 256,   1, D3D12_TILED_RESOURCES_TIER_1 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_BC7_UNORM,           256,  256,  1,   9,  2,  9,  1,   256, 256,   1, D3D12_TILED_RESOURCES_TIER_1 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_BC7_UNORM,           512,  512,  1,   9,  6,  9,  2,   256, 256,   1, D3D12_TILED_RESOURCES_TIER_1 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_BC7_UNORM,          1024, 1024,  1,   9, 22,  9,  3,   256, 256,   1, D3D12_TILED_RESOURCES_TIER_1 },

        /* Wide shapes. On AMD, we keep observing standard mips even when the smallest dimension dips below the tile size.
         * This is not the case on NV however. */
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_BC1_UNORM,          1024,  256,  1, 10,  3, 10,  1,   512, 256,   1, D3D12_TILED_RESOURCES_TIER_1 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_BC1_UNORM,          2048,  256,  1, 10,  6, 10,  1,   512, 256,   1, D3D12_TILED_RESOURCES_TIER_1 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_BC1_UNORM,          4096,  256,  1, 10, 11, 10,  1,   512, 256,   1, D3D12_TILED_RESOURCES_TIER_1 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_BC7_UNORM,           512,  256,  1,  9,  3,  9,  1,   256, 256,   1, D3D12_TILED_RESOURCES_TIER_1 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_BC7_UNORM,          1024,  256,  1,  9,  6,  9,  1,   256, 256,   1, D3D12_TILED_RESOURCES_TIER_1 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_BC7_UNORM,          2048,  256,  1,  9, 11,  9,  1,   256, 256,   1, D3D12_TILED_RESOURCES_TIER_1 },

        /* Tall shapes. Similar to wide tests. */
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_BC1_UNORM,           512,  512,  1, 10,  3, 10,  1,   512, 256,   1, D3D12_TILED_RESOURCES_TIER_1 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_BC1_UNORM,           512, 1024,  1, 10,  6, 10,  1,   512, 256,   1, D3D12_TILED_RESOURCES_TIER_1 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_BC1_UNORM,           512, 2048,  1, 10, 11, 10,  1,   512, 256,   1, D3D12_TILED_RESOURCES_TIER_1 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_BC7_UNORM,           256,  512,  1,  9,  3,  9,  1,   256, 256,   1, D3D12_TILED_RESOURCES_TIER_1 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_BC7_UNORM,           256, 1024,  1,  9,  6,  9,  1,   256, 256,   1, D3D12_TILED_RESOURCES_TIER_1 },
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_BC7_UNORM,           256, 2048,  1,  9, 11,  9,  1,   256, 256,   1, D3D12_TILED_RESOURCES_TIER_1 },

        /* Array images with full mip chain; now allowed with tier 4 */
        { D3D12_RESOURCE_DIMENSION_TEXTURE2D, DXGI_FORMAT_R8G8B8A8_UNORM,      512,  512,  4, 10, 88, 40,  3,   128, 128,   1, D3D12_TILED_RESOURCES_TIER_4 },
    };

    memset(&desc, 0, sizeof(desc));
    desc.rt_width = 640;
    desc.rt_height = 480;
    desc.rt_format = DXGI_FORMAT_R8G8B8A8_UNORM;
    if (!init_test_context(&context, &desc))
        return;

    hr = ID3D12Device_CheckFeatureSupport(context.device, D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(options));
    ok(hr == S_OK, "Failed to check feature support, hr %#x.\n", hr);

    if (!options.TiledResourcesTier)
    {
        skip("Tiled resources not supported by device.\n");
        destroy_test_context(&context);
        return;
    }

    /* Test behaviour with various parameter combinations */
    resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
    resource_desc.Alignment = 0;
    resource_desc.Width = 512;
    resource_desc.Height = 512;
    resource_desc.DepthOrArraySize = 1;
    resource_desc.MipLevels = 10;
    resource_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    resource_desc.SampleDesc.Count = 1;
    resource_desc.SampleDesc.Quality = 0;
    resource_desc.Layout = D3D12_TEXTURE_LAYOUT_64KB_UNDEFINED_SWIZZLE;
    resource_desc.Flags = D3D12_RESOURCE_FLAG_NONE;

    hr = ID3D12Device_CreateReservedResource(context.device, &resource_desc,
        D3D12_RESOURCE_STATE_GENERIC_READ, NULL, &IID_ID3D12Resource, (void **)&resource);
    ok(hr == S_OK, "Failed to create reserved resource, hr %#x.\n", hr);

    /* This is nonsense, but it doesn't crash or generate errors. */
    ID3D12Device_GetResourceTiling(context.device, resource, NULL, NULL, NULL, NULL, 0, NULL);

    /* If num_tilings is NULL, tilings_alt is ignored. */
    memset(tilings, 0, sizeof(tilings));
    memset(tilings_alt, 0, sizeof(tilings_alt));
    ID3D12Device_GetResourceTiling(context.device, resource, NULL, NULL, NULL, NULL, 0, tilings_alt);
    ok(memcmp(tilings, tilings_alt, sizeof(tilings_alt)) == 0, "Mismatch.\n");

    num_tilings = 0;
    ID3D12Device_GetResourceTiling(context.device, resource, NULL, NULL, NULL, &num_tilings, 0, NULL);
    ok(num_tilings == 0, "Unexpected tiling count %u.\n", num_tilings);

    num_tilings = ARRAY_SIZE(tilings);
    ID3D12Device_GetResourceTiling(context.device, resource, NULL, NULL, NULL, &num_tilings, 10, tilings);
    ok(num_tilings == 0, "Unexpected tiling count %u.\n", num_tilings);

    num_tilings = ARRAY_SIZE(tilings);
    ID3D12Device_GetResourceTiling(context.device, resource, NULL, NULL, NULL, &num_tilings, 2, tilings);
    ok(num_tilings == 8, "Unexpected tiling count %u.\n", num_tilings);
    ok(tilings[0].StartTileIndexInOverallResource == 20, "Unexpected start tile index %u.\n", tilings[0].StartTileIndexInOverallResource);

    num_tilings = 1;
    memset(&tilings, 0xaa, sizeof(tilings));
    ID3D12Device_GetResourceTiling(context.device, resource, NULL, NULL, NULL, &num_tilings, 2, tilings);
    ok(num_tilings == 1, "Unexpected tiling count %u.\n", num_tilings);
    ok(tilings[0].StartTileIndexInOverallResource == 20, "Unexpected start tile index %u.\n", tilings[0].StartTileIndexInOverallResource);
    ok(tilings[1].StartTileIndexInOverallResource == 0xaaaaaaaa, "Tiling array got modified.\n");

    ID3D12Resource_Release(resource);

    /* Test actual tiling properties */
    for (i = 0; i < ARRAY_SIZE(tests); i++)
    {
        unsigned int tile_index = 0;
        vkd3d_test_set_context("test %u", i);

        if (tests[i].min_tier > options.TiledResourcesTier)
        {
            skip("Tiled resources tier %u not supported.\n", tests[i].min_tier);
            continue;
        }

        memset(&packed_mip_info, 0xaa, sizeof(packed_mip_info));
        memset(&tile_shape, 0xaa, sizeof(tile_shape));
        memset(&tilings, 0xaa, sizeof(tilings));

        num_resource_tiles = 0xdeadbeef;
        num_tilings = ARRAY_SIZE(tilings);

        resource_desc.Dimension = tests[i].dim;
        resource_desc.Alignment = 0;
        resource_desc.Width = tests[i].width;
        resource_desc.Height = tests[i].height;
        resource_desc.DepthOrArraySize = tests[i].depth_or_array_layers;
        resource_desc.MipLevels = tests[i].mip_levels;
        resource_desc.Format = tests[i].format;
        resource_desc.SampleDesc.Count = 1;
        resource_desc.SampleDesc.Quality = 0;
        resource_desc.Layout = D3D12_TEXTURE_LAYOUT_64KB_UNDEFINED_SWIZZLE;
        resource_desc.Flags = D3D12_RESOURCE_FLAG_NONE;

        if (tests[i].dim == D3D12_RESOURCE_DIMENSION_BUFFER)
            resource_desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;

        hr = ID3D12Device_CreateReservedResource(context.device, &resource_desc,
            D3D12_RESOURCE_STATE_GENERIC_READ, NULL, &IID_ID3D12Resource, (void **)&resource);
        todo_if(is_radv_device(context.device) && tests[i].todo_radv)
        ok(hr == S_OK, "Failed to create reserved resource, hr %#x.\n", hr);

        if (hr != S_OK)
            continue;

        ID3D12Device_GetResourceTiling(context.device, resource, &num_resource_tiles, &packed_mip_info, &tile_shape, &num_tilings, 0, tilings);

        ok(num_resource_tiles >= tests[i].expected_tile_count, "Unexpected resource tile count %u.\n", num_resource_tiles);
        ok(num_tilings == tests[i].expected_tiling_count, "Unexpected subresource tiling count %u.\n", num_tilings);

        ok(packed_mip_info.NumStandardMips >= tests[i].expected_standard_mips, "Unexpected standard mip count %u.\n", packed_mip_info.NumStandardMips);
        ok(packed_mip_info.NumPackedMips == (tests[i].dim == D3D12_RESOURCE_DIMENSION_BUFFER
                ? 0 : tests[i].mip_levels - packed_mip_info.NumStandardMips),
                "Unexpected packed mip count %u.\n", packed_mip_info.NumPackedMips);
        ok((packed_mip_info.NumTilesForPackedMips == 0) == (packed_mip_info.NumPackedMips == 0),
                "Unexpected packed tile count %u.\n", packed_mip_info.NumTilesForPackedMips);

        /* Docs say that tile shape should be cleared to zero if there is no standard mip, but drivers don't seem to care about this. */
        ok(tile_shape.WidthInTexels == tests[i].tile_shape_w, "Unexpected tile width %u.\n", tile_shape.WidthInTexels);
        ok(tile_shape.HeightInTexels == tests[i].tile_shape_h, "Unexpected tile height %u.\n", tile_shape.HeightInTexels);
        ok(tile_shape.DepthInTexels == tests[i].tile_shape_d, "Unexpected tile depth %u.\n", tile_shape.DepthInTexels);

        for (j = 0; j < tests[i].expected_tiling_count; j++)
        {
            uint32_t mip = j % tests[i].mip_levels;

            if (mip < packed_mip_info.NumStandardMips || !packed_mip_info.NumPackedMips)
            {
                uint32_t expected_w = compute_tile_count(tests[i].width, mip, tests[i].tile_shape_w);
                uint32_t expected_h = compute_tile_count(tests[i].height, mip, tests[i].tile_shape_h);
                uint32_t expected_d = 1;

                if (tests[i].dim == D3D12_RESOURCE_DIMENSION_TEXTURE3D)
                    expected_d = compute_tile_count(tests[i].depth_or_array_layers, mip, tests[i].tile_shape_d);

                ok(tilings[j].WidthInTiles == expected_w, "Unexpected width %u for subresource %u.\n", tilings[j].WidthInTiles, j);
                ok(tilings[j].HeightInTiles == expected_h, "Unexpected width %u for subresource %u.\n", tilings[j].HeightInTiles, j);
                ok(tilings[j].DepthInTiles == expected_d, "Unexpected width %u for subresource %u.\n", tilings[j].DepthInTiles, j);

                ok(tilings[j].StartTileIndexInOverallResource == tile_index, "Unexpected start tile index %u for subresource %u.\n",
                        tilings[j].StartTileIndexInOverallResource, j);

                tile_index += tilings[j].WidthInTiles * tilings[j].HeightInTiles * tilings[j].DepthInTiles;
            }
            else
            {
                ok(!tilings[j].WidthInTiles && !tilings[j].HeightInTiles && !tilings[j].DepthInTiles,
                        "Unexpected tile count (%u,%u,%u) for packed subresource %u.\n",
                        tilings[j].WidthInTiles, tilings[j].HeightInTiles, tilings[j].DepthInTiles, j);
                ok(tilings[j].StartTileIndexInOverallResource == 0xffffffff, "Unexpected start tile index %u for packed subresource %u.\n",
                        tilings[j].StartTileIndexInOverallResource, j);
            }
        }

        ok(num_resource_tiles == tile_index + packed_mip_info.NumTilesForPackedMips,
                "Unexpected resource tile count %u.\n", num_resource_tiles);
        ok(packed_mip_info.StartTileIndexInOverallResource == (packed_mip_info.NumPackedMips ? tile_index : 0),
                "Unexpected mip tail start tile index %u.\n", packed_mip_info.StartTileIndexInOverallResource);

        ID3D12Resource_Release(resource);
    }
    vkd3d_test_set_context(NULL);

    destroy_test_context(&context);
}

static void set_region_offset(D3D12_TILED_RESOURCE_COORDINATE *region, uint32_t x, uint32_t y, uint32_t z, uint32_t subresource)
{
    region->X = x;
    region->Y = y;
    region->Z = z;
    region->Subresource = subresource;
}

static void set_region_size(D3D12_TILE_REGION_SIZE *region, uint32_t num_tiles, bool use_box, uint32_t w, uint32_t h, uint32_t d)
{
    region->NumTiles = num_tiles;
    region->UseBox = use_box;
    region->Width = w;
    region->Height = h;
    region->Depth = d;
}

void test_update_tile_mappings_remap_stress(void)
{
    D3D12_FEATURE_DATA_D3D12_OPTIONS options;
    D3D12_ROOT_SIGNATURE_DESC rs_desc;
    D3D12_ROOT_PARAMETER rs_params[2];
    D3D12_RESOURCE_DESC resource_desc;
    D3D12_HEAP_PROPERTIES heap_props;
    ID3D12Resource *output_resource;
    struct test_context_desc desc;
    struct test_context context;
    struct resource_readback rb;
    D3D12_HEAP_DESC heap_desc;
    ID3D12Resource *resource;
    unsigned int i, j, iter;
    ID3D12Heap *heap = NULL;
    HRESULT hr;

#include "shaders/sparse/headers/update_tile_mappings.h"

    memset(&desc, 0, sizeof(desc));
    desc.no_render_target = true;
    desc.no_pipeline = true;
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;

    hr = ID3D12Device_CheckFeatureSupport(context.device, D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(options));
    ok(hr == S_OK, "Failed to check feature support, hr %#x.\n", hr);

    if (!options.TiledResourcesTier)
    {
        skip("Tiled resources not supported by device.\n");
        destroy_test_context(&context);
        return;
    }

    memset(&heap_props, 0, sizeof(heap_props));
    heap_props.Type = D3D12_HEAP_TYPE_DEFAULT;

    heap_desc.Properties = heap_props;
    heap_desc.Alignment = 0;
    heap_desc.SizeInBytes = 64 * TILE_SIZE;
    heap_desc.Flags = D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS;

    resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
    resource_desc.Alignment = 0;
    resource_desc.Width = 64 * TILE_SIZE;
    resource_desc.Height = 1;
    resource_desc.DepthOrArraySize = 1;
    resource_desc.MipLevels = 1;
    resource_desc.Format = DXGI_FORMAT_UNKNOWN;
    resource_desc.SampleDesc.Count = 1;
    resource_desc.SampleDesc.Quality = 0;
    resource_desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
    resource_desc.Flags = 0;

    hr = ID3D12Device_CreateReservedResource(context.device, &resource_desc,
            D3D12_RESOURCE_STATE_UNORDERED_ACCESS, NULL, &IID_ID3D12Resource, (void **)&resource);
    ok(hr == S_OK, "Failed to create reserved buffer, hr %#x.\n", hr);

    output_resource = create_default_buffer(context.device, 64 * sizeof(uint32_t),
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

    memset(rs_params, 0, sizeof(rs_params));
    memset(&rs_desc, 0, sizeof(rs_desc));
    rs_desc.NumParameters = ARRAY_SIZE(rs_params);
    rs_desc.pParameters = rs_params;
    rs_params[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_SRV;
    rs_params[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV;
    create_root_signature(context.device, &rs_desc, &context.root_signature);

    context.pipeline_state = create_compute_pipeline_state(
            context.device, context.root_signature, update_tile_mappings_dxbc);

    for (iter = 0; iter < 100; iter++)
    {
        D3D12_TILE_REGION_SIZE resource_tile_region_size;
        D3D12_TILED_RESOURCE_COORDINATE resource_coord;
        D3D12_TILE_RANGE_FLAGS heap_range_flags;
        ID3D12Resource *placed_resource;
        ID3D12Resource *upload;
        UINT heap_tile_offset;
        uint32_t *upload_data;
        UINT heap_tile_count;
        ID3D12Heap *new_heap;

        const struct mappings
        {
            UINT resource_tile;
            UINT heap_tile;
            UINT count;
            D3D12_TILE_RANGE_FLAGS flags;
        } mappings[] = {
            { 0, 0, 64, D3D12_TILE_RANGE_FLAG_NULL },
            { 4 + (iter & 31), 8 + (iter & 1), 3, D3D12_TILE_RANGE_FLAG_NONE },
            { 1 + (iter & 14), 2 + (iter & 4), 40, D3D12_TILE_RANGE_FLAG_NONE },
            { 13 + (iter & 9), 0, 9, D3D12_TILE_RANGE_FLAG_NULL },
            { 13 + (iter & 7), 19 + (iter & 4), 8, D3D12_TILE_RANGE_FLAG_REUSE_SINGLE_TILE },
            { 30 + (iter & 5), 0, 7, D3D12_TILE_RANGE_FLAG_NULL },
            { 1 + (iter & 3), 0, 7, D3D12_TILE_RANGE_FLAG_NULL },
        };

        hr = ID3D12Device_CreateHeap(context.device, &heap_desc, &IID_ID3D12Heap, (void **)&new_heap);
        ok(hr == S_OK, "Failed to create heap, hr %#x.\n", hr);

        hr = ID3D12Device_CreatePlacedResource(context.device, new_heap, 0, &resource_desc,
                D3D12_RESOURCE_STATE_COMMON, NULL, &IID_ID3D12Resource, (void **)&placed_resource);
        ok(SUCCEEDED(hr), "Failed to create resource, hr #%x.\n", hr);

        /* Destroy the old heap while it has mappings to the heap, then rebind those pages. Should not explode. */
        if (heap)
            ID3D12Heap_Release(heap);
        heap = new_heap;

        upload_data = malloc(resource_desc.Width);
        for (i = 0; i < resource_desc.Width / 4; i++)
            upload_data[i] = i + 1;
        upload = create_upload_buffer(context.device, resource_desc.Width, upload_data);
        ID3D12GraphicsCommandList_CopyResource(context.list, placed_resource, upload);
        ID3D12GraphicsCommandList_Close(context.list);
        exec_command_list(context.queue, context.list);
        ID3D12GraphicsCommandList_Reset(context.list, context.allocator, NULL);
        free(upload_data);

        for (i = 0; i < ARRAY_SIZE(mappings); i++)
        {
            memset(&resource_coord, 0, sizeof(resource_coord));
            memset(&resource_tile_region_size, 0, sizeof(resource_tile_region_size));
            resource_coord.X = mappings[i].resource_tile;
            resource_tile_region_size.NumTiles = mappings[i].count;
            resource_tile_region_size.UseBox = FALSE;
            heap_range_flags = mappings[i].flags;
            heap_tile_offset = mappings[i].heap_tile;
            heap_tile_count = mappings[i].count;

            ID3D12CommandQueue_UpdateTileMappings(context.queue, resource,
                    1, &resource_coord, &resource_tile_region_size, heap, 1,
                    &heap_range_flags, &heap_tile_offset, &heap_tile_count, D3D12_TILE_MAPPING_FLAG_NONE);
        }

#define OFFSET_INTO_PAGE 50000

        ID3D12GraphicsCommandList_SetComputeRootSignature(context.list, context.root_signature);
        ID3D12GraphicsCommandList_SetPipelineState(context.list, context.pipeline_state);
        ID3D12GraphicsCommandList_SetComputeRootShaderResourceView(context.list, 0,
                ID3D12Resource_GetGPUVirtualAddress(resource) + OFFSET_INTO_PAGE);
        ID3D12GraphicsCommandList_SetComputeRootUnorderedAccessView(context.list, 1,
                ID3D12Resource_GetGPUVirtualAddress(output_resource));
        ID3D12GraphicsCommandList_Dispatch(context.list, 1, 1, 1);
        transition_resource_state(context.list, output_resource, D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
                D3D12_RESOURCE_STATE_COPY_SOURCE);
        get_buffer_readback_with_command_list(output_resource, DXGI_FORMAT_R32_UINT, &rb, context.queue, context.list);
        reset_command_list(context.list, context.allocator);
        for (i = 0; i < 64; i++)
        {
            uint32_t expected = 0;
            for (j = 0; j < ARRAY_SIZE(mappings); j++)
            {
                if (i >= mappings[j].resource_tile && i < mappings[j].resource_tile + mappings[j].count)
                {
                    if (mappings[j].flags == D3D12_TILE_RANGE_FLAG_NULL)
                        expected = 0;
                    else if (mappings[j].flags == D3D12_TILE_RANGE_FLAG_REUSE_SINGLE_TILE)
                        expected = mappings[j].heap_tile * TILE_SIZE / 4 + OFFSET_INTO_PAGE / 4 + 1;
                    else
                        expected = (mappings[j].heap_tile + (i - mappings[j].resource_tile)) * TILE_SIZE / 4 + OFFSET_INTO_PAGE / 4 + 1;
                }
            }
            ok(expected == get_readback_uint(&rb, i, 0, 0),
                    "Iter %u: value %u: Expected %u, got %u.\n", iter, i, expected, get_readback_uint(&rb, i, 0, 0));
        }
        release_resource_readback(&rb);
        ID3D12Resource_Release(placed_resource);
        ID3D12Resource_Release(upload);
    }

    ID3D12Resource_Release(resource);
    ID3D12Resource_Release(output_resource);
    ID3D12Heap_Release(heap);
    destroy_test_context(&context);
}

void test_update_tile_mappings(void)
{
    D3D12_TILED_RESOURCE_COORDINATE region_offsets[8];
    ID3D12PipelineState *check_texture_array_pipeline;
    ID3D12PipelineState *check_texture_3d_pipeline;
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    ID3D12PipelineState *clear_texture_pipeline;
    ID3D12PipelineState *check_texture_pipeline;
    ID3D12PipelineState *check_buffer_pipeline;
    ID3D12Resource *resource, *readback_buffer;
    D3D12_UNORDERED_ACCESS_VIEW_DESC uav_desc;
    ID3D12DescriptorHeap *cpu_heap, *gpu_heap;
    ID3D12RootSignature *clear_root_signature;
    D3D12_FEATURE_DATA_D3D12_OPTIONS options;
    D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc;
    D3D12_DESCRIPTOR_RANGE descriptor_range;
    D3D12_ROOT_PARAMETER root_parameters[2];
    D3D12_TILE_REGION_SIZE region_sizes[8];
    D3D12_GPU_VIRTUAL_ADDRESS readback_va;
    D3D12_PACKED_MIP_INFO packed_mip_info;
    D3D12_HEAP_PROPERTIES heap_properties;
    D3D12_SUBRESOURCE_TILING tilings[10];
    D3D12_TILE_RANGE_FLAGS tile_flags[8];
    ID3D12RootSignature *root_signature;
    D3D12_RESOURCE_DESC resource_desc;
    struct test_context_desc desc;
    struct resource_readback rb;
    struct test_context context;
    D3D12_TILE_SHAPE tile_shape;
    unsigned int i, j, x, y, z;
    D3D12_HEAP_DESC heap_desc;
    UINT tile_offsets[8];
    UINT tile_counts[8];
    ID3D12Heap *heap;
    UINT num_tilings;
    D3D12_BOX box;
    HRESULT hr;

#include "shaders/sparse/headers/update_tile_mappings.h"
#include "shaders/sparse/headers/update_tile_mappings_texture.h"
#include "shaders/sparse/headers/update_tile_mappings_texture_array.h"
#include "shaders/sparse/headers/update_tile_mappings_texture_3d.h"
#include "shaders/sparse/headers/update_tile_mappings_cs_clear.h"

    static const uint32_t buffer_region_tiles[] =
    {
    /*     0   1   2   3   4   5   6   7   8   9 */
    /*0*/ 33, 34, 35, 36, 37,  6,  7,  8,  9, 10,
    /*1*/ 11, 12, 38, 39, 40, 41,  1, 18,  2, 20,
    /*2*/ 21, 22, 23,  3,  4,  4,  4,  0,  0, 25,
    /*3*/ 26, 27, 28, 29, 30, 36, 37, 38, 39, 40,
    /*4*/  9, 11, 43, 44, 45, 46, 45, 46, 49, 50,
    /*5*/  0,  0, 17, 18, 19, 20, 21, 22, 23, 24,
    /*6*/ 61, 62, 63, 12,
    };

    static const uint32_t texture_region_tiles[] =
    {
        1, 2, 4, 5, 6, 7, 1, 1, 9, 1, 17, 14, 8, 14, 3, 0,
        17, 18, 19, 18, 19, 22, 23, 24, 25, 26, 27, 28,
    };

    static const uint32_t texture_3d_region_tiles[] =
    {
        3, 2, 0, 7, 8, 2, 4, 5, 6,
    };

    memset(&desc, 0, sizeof(desc));
    desc.rt_width = 640;
    desc.rt_height = 480;
    desc.rt_format = DXGI_FORMAT_R8G8B8A8_UNORM;
    if (!init_test_context(&context, &desc))
        return;

    hr = ID3D12Device_CheckFeatureSupport(context.device, D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(options));
    ok(hr == S_OK, "Failed to check feature support, hr %#x.\n", hr);

    if (!options.TiledResourcesTier)
    {
        skip("Tiled resources not supported by device.\n");
        destroy_test_context(&context);
        return;
    }

    descriptor_range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
    descriptor_range.NumDescriptors = 1;
    descriptor_range.BaseShaderRegister = 0;
    descriptor_range.RegisterSpace = 0;
    descriptor_range.OffsetInDescriptorsFromTableStart = 0;
    root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameters[0].DescriptorTable.NumDescriptorRanges = 1;
    root_parameters[0].DescriptorTable.pDescriptorRanges = &descriptor_range;
    root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    root_parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV;
    root_parameters[1].Descriptor.ShaderRegister = 0;
    root_parameters[1].Descriptor.RegisterSpace = 0;
    root_parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    root_signature_desc.NumParameters = ARRAY_SIZE(root_parameters);
    root_signature_desc.pParameters = root_parameters;
    root_signature_desc.NumStaticSamplers = 0;
    root_signature_desc.pStaticSamplers = NULL;
    root_signature_desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_NONE;
    hr = create_root_signature(context.device, &root_signature_desc, &root_signature);
    ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr);

    descriptor_range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
    root_parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
    root_parameters[1].Constants.ShaderRegister = 0;
    root_parameters[1].Constants.RegisterSpace = 0;
    root_parameters[1].Constants.Num32BitValues = 4;
    root_parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    hr = create_root_signature(context.device, &root_signature_desc, &clear_root_signature);
    ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr);

    clear_texture_pipeline = create_compute_pipeline_state(context.device, clear_root_signature, update_tile_mappings_cs_clear_dxbc);
    check_texture_pipeline = create_compute_pipeline_state(context.device, root_signature, update_tile_mappings_texture_dxbc);
    check_texture_array_pipeline = create_compute_pipeline_state(context.device, root_signature, update_tile_mappings_texture_array_dxbc);
    check_texture_3d_pipeline = create_compute_pipeline_state(context.device, root_signature, update_tile_mappings_texture_3d_dxbc);
    check_buffer_pipeline = create_compute_pipeline_state(context.device, root_signature, update_tile_mappings_dxbc);

    cpu_heap = create_cpu_descriptor_heap(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 64);
    gpu_heap = create_gpu_descriptor_heap(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 64);

    memset(&heap_properties, 0, sizeof(heap_properties));
    heap_properties.Type = D3D12_HEAP_TYPE_DEFAULT;

    resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
    resource_desc.Alignment = 0;
    resource_desc.Width = 64 * sizeof(uint32_t);
    resource_desc.Height = 1;
    resource_desc.DepthOrArraySize = 1;
    resource_desc.MipLevels = 1;
    resource_desc.Format = DXGI_FORMAT_UNKNOWN;
    resource_desc.SampleDesc.Count = 1;
    resource_desc.SampleDesc.Quality = 0;
    resource_desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
    resource_desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
    hr = ID3D12Device_CreateCommittedResource(context.device, &heap_properties, D3D12_HEAP_FLAG_NONE,
            &resource_desc, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, NULL, &IID_ID3D12Resource, (void **)&readback_buffer);
    ok(hr == S_OK, "Failed to create readback buffer, hr %#x.\n", hr);

    readback_va = ID3D12Resource_GetGPUVirtualAddress(readback_buffer);

    /* Test buffer tile mappings */
    heap_desc.Properties = heap_properties;
    heap_desc.Alignment = 0;
    heap_desc.SizeInBytes = 64 * 65536;
    heap_desc.Flags = D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS;
    hr = ID3D12Device_CreateHeap(context.device, &heap_desc, &IID_ID3D12Heap, (void **)&heap);
    ok(hr == S_OK, "Failed to create heap, hr %#x.\n", hr);

    resource_desc.Width = 64 * 65536;
    hr = ID3D12Device_CreateReservedResource(context.device, &resource_desc,
        D3D12_RESOURCE_STATE_UNORDERED_ACCESS, NULL, &IID_ID3D12Resource, (void **)&resource);
    ok(hr == S_OK, "Failed to create reserved buffer, hr %#x.\n", hr);

    srv_desc.Format = DXGI_FORMAT_UNKNOWN;
    srv_desc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER;
    srv_desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
    srv_desc.Buffer.FirstElement = 0;
    srv_desc.Buffer.NumElements = resource_desc.Width / sizeof(uint32_t);
    srv_desc.Buffer.StructureByteStride = sizeof(uint32_t);
    srv_desc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_NONE;
    ID3D12Device_CreateShaderResourceView(context.device, resource, &srv_desc, get_cpu_descriptor_handle(&context, gpu_heap, 0));

    uav_desc.Format = DXGI_FORMAT_R32_UINT;
    uav_desc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
    uav_desc.Buffer.FirstElement = 0;
    uav_desc.Buffer.NumElements = resource_desc.Width / sizeof(uint32_t);
    uav_desc.Buffer.StructureByteStride = 0;
    uav_desc.Buffer.CounterOffsetInBytes = 0;
    uav_desc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_NONE;
    ID3D12Device_CreateUnorderedAccessView(context.device, resource, NULL, &uav_desc, get_cpu_descriptor_handle(&context, cpu_heap, 1));
    ID3D12Device_CreateUnorderedAccessView(context.device, resource, NULL, &uav_desc, get_cpu_descriptor_handle(&context, gpu_heap, 1));

    /* Map entire buffer, linearly, and initialize tile data */
    tile_offsets[0] = 0;
    ID3D12CommandQueue_UpdateTileMappings(context.queue, resource,
        1, NULL, NULL, heap, 1, NULL, tile_offsets, NULL, D3D12_TILE_MAPPING_FLAG_NONE);
    ID3D12GraphicsCommandList_SetDescriptorHeaps(context.list, 1, &gpu_heap);

    for (i = 0; i < 64; i++)
    {
        UINT clear_value[4] = { 0, 0, 0, 0 };
        D3D12_RECT clear_rect;

        set_rect(&clear_rect, 16384 * i, 0, 16384 * (i + 1), 1);
        clear_value[0] = i + 1;

        ID3D12GraphicsCommandList_ClearUnorderedAccessViewUint(context.list,
                get_gpu_descriptor_handle(&context, gpu_heap, 1),
                get_cpu_descriptor_handle(&context, cpu_heap, 1),
                resource, clear_value, 1, &clear_rect);
    }

    transition_resource_state(context.list, resource, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);
    ID3D12GraphicsCommandList_SetComputeRootSignature(context.list, root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(context.list, check_buffer_pipeline);
    ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(context.list, 0, get_gpu_descriptor_handle(&context, gpu_heap, 0));
    ID3D12GraphicsCommandList_SetComputeRootUnorderedAccessView(context.list, 1, readback_va);
    ID3D12GraphicsCommandList_Dispatch(context.list, 1, 1, 1);
    transition_resource_state(context.list, readback_buffer, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);

    get_buffer_readback_with_command_list(readback_buffer, DXGI_FORMAT_R32_UINT, &rb, context.queue, context.list);

    for (i = 0; i < 64; i++)
    {
        set_box(&box, i, 0, 0, i + 1, 1, 1);
        check_readback_data_uint(&rb, &box, i + 1, 0);
    }

    release_resource_readback(&rb);

    /* Test arbitrary tile mappings */
    set_region_offset(&region_offsets[0], 16, 0, 0, 0);
    set_region_offset(&region_offsets[1], 18, 0, 0, 0);
    set_region_offset(&region_offsets[2], 23, 0, 0, 0);
    set_region_offset(&region_offsets[3], 40, 0, 0, 0);
    set_region_offset(&region_offsets[4], 41, 0, 0, 0);
    set_region_offset(&region_offsets[5], 63, 0, 0, 0);

    tile_offsets[0] = 0;
    tile_offsets[1] = 8;
    tile_offsets[2] = 10;

    tile_counts[0] = 3;
    tile_counts[1] = 1;
    tile_counts[2] = 2;

    ID3D12CommandQueue_UpdateTileMappings(context.queue, resource,
            6, region_offsets, NULL, heap, 3, NULL, tile_offsets, tile_counts,
            D3D12_TILE_MAPPING_FLAG_NONE);

    set_region_offset(&region_offsets[0], 24, 0, 0, 0);
    set_region_offset(&region_offsets[1], 50, 0, 0, 0);
    set_region_offset(&region_offsets[2], 0, 0, 0, 0);
    set_region_offset(&region_offsets[3], 52, 0, 0, 0);
    set_region_offset(&region_offsets[4], 29, 0, 0, 0);

    set_region_size(&region_sizes[0], 5, false, 0, 0, 0);
    set_region_size(&region_sizes[1], 2, false, 0, 0, 0);
    set_region_size(&region_sizes[2], 16, false, 0, 0, 0);
    set_region_size(&region_sizes[3], 8, false, 0, 0, 0);
    set_region_size(&region_sizes[4], 6, false, 0, 0, 0);

    tile_flags[0] = D3D12_TILE_RANGE_FLAG_REUSE_SINGLE_TILE;
    tile_flags[1] = D3D12_TILE_RANGE_FLAG_NULL;
    tile_flags[2] = D3D12_TILE_RANGE_FLAG_NONE;
    tile_flags[3] = D3D12_TILE_RANGE_FLAG_SKIP;
    tile_flags[4] = D3D12_TILE_RANGE_FLAG_NONE;
    tile_flags[5] = D3D12_TILE_RANGE_FLAG_NONE;

    tile_offsets[0] = 3;
    tile_offsets[1] = 0;
    tile_offsets[2] = 32;
    tile_offsets[3] = 0;
    tile_offsets[4] = 37;
    tile_offsets[5] = 16;

    tile_counts[0] = 3;
    tile_counts[1] = 4;
    tile_counts[2] = 5;
    tile_counts[3] = 7;
    tile_counts[4] = 4;
    tile_counts[5] = 14;

    ID3D12CommandQueue_UpdateTileMappings(context.queue, resource,
        5, region_offsets, region_sizes, heap, 6, tile_flags, tile_offsets, tile_counts,
        D3D12_TILE_MAPPING_FLAG_NONE);

    set_region_offset(&region_offsets[0], 46, 0, 0, 0);
    set_region_offset(&region_offsets[1], 44, 0, 0, 0);
    set_region_size(&region_sizes[0], 2, false, 0, 0, 0);

    ID3D12CommandQueue_CopyTileMappings(context.queue,
        resource, &region_offsets[0], resource, &region_offsets[1],
        &region_sizes[0], D3D12_TILE_MAPPING_FLAG_NONE);

    reset_command_list(context.list, context.allocator);

    transition_resource_state(context.list, readback_buffer, D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
    ID3D12GraphicsCommandList_SetDescriptorHeaps(context.list, 1, &gpu_heap);
    ID3D12GraphicsCommandList_SetComputeRootSignature(context.list, root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(context.list, check_buffer_pipeline);
    ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(context.list, 0, get_gpu_descriptor_handle(&context, gpu_heap, 0));
    ID3D12GraphicsCommandList_SetComputeRootUnorderedAccessView(context.list, 1, readback_va);
    ID3D12GraphicsCommandList_Dispatch(context.list, 1, 1, 1);
    transition_resource_state(context.list, readback_buffer, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);

    get_buffer_readback_with_command_list(readback_buffer, DXGI_FORMAT_R32_UINT, &rb, context.queue, context.list);

    for (i = 0; i < ARRAY_SIZE(buffer_region_tiles); i++)
    {
        if (options.TiledResourcesTier > D3D12_TILED_RESOURCES_TIER_2 || buffer_region_tiles[i])
        {
            set_box(&box, i, 0, 0, i + 1, 1, 1);
            check_readback_data_uint(&rb, &box, buffer_region_tiles[i], 0);
        }
    }

    release_resource_readback(&rb);

    /* Test mapping a single tile twice in one call */
    set_region_offset(&region_offsets[0], 0, 0, 0, 0);
    set_region_offset(&region_offsets[1], 15, 0, 0, 0);

    set_region_size(&region_sizes[0], 64, false, 0, 0, 0);
    set_region_size(&region_sizes[1], 3, false, 0, 0, 0);

    tile_flags[0] = D3D12_TILE_RANGE_FLAG_NULL;
    tile_flags[1] = D3D12_TILE_RANGE_FLAG_NONE;

    tile_offsets[0] = 0;
    tile_offsets[1] = 8;

    tile_counts[0] = 64;
    tile_counts[1] = 4;

    ID3D12CommandQueue_UpdateTileMappings(context.queue, resource,
        2, region_offsets, region_sizes, heap, 2, tile_flags, tile_offsets, tile_counts,
        D3D12_TILE_MAPPING_FLAG_NONE);

    reset_command_list(context.list, context.allocator);

    transition_resource_state(context.list, readback_buffer, D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
    ID3D12GraphicsCommandList_SetDescriptorHeaps(context.list, 1, &gpu_heap);
    ID3D12GraphicsCommandList_SetComputeRootSignature(context.list, root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(context.list, check_buffer_pipeline);
    ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(context.list, 0, get_gpu_descriptor_handle(&context, gpu_heap, 0));
    ID3D12GraphicsCommandList_SetComputeRootUnorderedAccessView(context.list, 1, readback_va);
    ID3D12GraphicsCommandList_Dispatch(context.list, 1, 1, 1);
    transition_resource_state(context.list, readback_buffer, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);

    get_buffer_readback_with_command_list(readback_buffer, DXGI_FORMAT_R32_UINT, &rb, context.queue, context.list);

    for (i = 0; i < ARRAY_SIZE(buffer_region_tiles); i++)
    {
        uint32_t expected = 0u;

        if (i >= region_offsets[1].X && i < region_offsets[1].X + region_sizes[1].NumTiles)
            expected = tile_offsets[1] + i + 1 - region_offsets[1].X;

        set_box(&box, i, 0, 0, i + 1, 1, 1);

        /* WARP binds everything to NULL */
        bug_if(use_warp_device)
        check_readback_data_uint(&rb, &box, expected, 0);
    }

    release_resource_readback(&rb);

    ID3D12Resource_Release(resource);
    ID3D12Heap_Release(heap);

    /* Test 2D image tile mappings */
    heap_desc.Properties = heap_properties;
    heap_desc.Alignment = 0;
    heap_desc.SizeInBytes = 64 * 65536;
    heap_desc.Flags = D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES;
    hr = ID3D12Device_CreateHeap(context.device, &heap_desc, &IID_ID3D12Heap, (void **)&heap);
    ok(hr == S_OK, "Failed to create heap, hr %#x.\n", hr);

    resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
    resource_desc.Alignment = 0;
    resource_desc.Width = 512;
    resource_desc.Height = 512;
    resource_desc.DepthOrArraySize = 1;
    resource_desc.MipLevels = 10;
    resource_desc.Format = DXGI_FORMAT_R32_UINT;
    resource_desc.SampleDesc.Count = 1;
    resource_desc.SampleDesc.Quality = 0;
    resource_desc.Layout = D3D12_TEXTURE_LAYOUT_64KB_UNDEFINED_SWIZZLE;
    resource_desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;

    hr = ID3D12Device_CreateReservedResource(context.device, &resource_desc,
        D3D12_RESOURCE_STATE_UNORDERED_ACCESS, NULL, &IID_ID3D12Resource, (void **)&resource);
    ok(hr == S_OK, "Failed to create reserved texture, hr %#x.\n", hr);

    num_tilings = resource_desc.MipLevels;
    ID3D12Device_GetResourceTiling(context.device, resource, NULL, &packed_mip_info, &tile_shape, &num_tilings, 0, tilings);
    ok(packed_mip_info.NumStandardMips >= 3, "Unexpected number of standard mips %u.\n", packed_mip_info.NumStandardMips);

    srv_desc.Format = DXGI_FORMAT_R32_UINT;
    srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
    srv_desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
    srv_desc.Texture2D.MostDetailedMip = 0;
    srv_desc.Texture2D.MipLevels = resource_desc.MipLevels;
    srv_desc.Texture2D.PlaneSlice = 0;
    srv_desc.Texture2D.ResourceMinLODClamp = 0.0f;
    ID3D12Device_CreateShaderResourceView(context.device, resource, &srv_desc, get_cpu_descriptor_handle(&context, gpu_heap, 0));

    /* Map entire image */
    tile_offsets[0] = 0;
    ID3D12CommandQueue_UpdateTileMappings(context.queue, resource,
        1, NULL, NULL, heap, 1, NULL, tile_offsets, NULL, D3D12_TILE_MAPPING_FLAG_NONE);

    reset_command_list(context.list, context.allocator);
    ID3D12GraphicsCommandList_SetDescriptorHeaps(context.list, 1, &gpu_heap);

    for (i = 0, j = 0; i < resource_desc.MipLevels; i++)
    {
        uav_desc.Format = DXGI_FORMAT_R32_UINT;
        uav_desc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D;
        uav_desc.Texture2D.MipSlice = i;
        uav_desc.Texture2D.PlaneSlice = 0;
        ID3D12Device_CreateUnorderedAccessView(context.device, resource, NULL, &uav_desc, get_cpu_descriptor_handle(&context, cpu_heap, 1 + i));
        ID3D12Device_CreateUnorderedAccessView(context.device, resource, NULL, &uav_desc, get_cpu_descriptor_handle(&context, gpu_heap, 1 + i));

        for (y = 0; y < max(1u, tilings[i].HeightInTiles); y++)
        {
            for (x = 0; x < max(1u, tilings[i].WidthInTiles); x++)
            {
                UINT clear_value[4] = { 0, 0, 0, 0 };
                D3D12_RECT clear_rect;

                clear_value[0] = ++j;
                set_rect(&clear_rect,
                    x * tile_shape.WidthInTexels, y * tile_shape.HeightInTexels,
                    min(resource_desc.Width >> i, (x + 1) * tile_shape.WidthInTexels),
                    min(resource_desc.Height >> i, (y + 1) * tile_shape.HeightInTexels));

                ID3D12GraphicsCommandList_ClearUnorderedAccessViewUint(context.list,
                        get_gpu_descriptor_handle(&context, gpu_heap, 1 + i),
                        get_cpu_descriptor_handle(&context, cpu_heap, 1 + i),
                        resource, clear_value, 1, &clear_rect);
            }
        }
    }

    transition_resource_state(context.list, resource, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);
    transition_resource_state(context.list, readback_buffer, D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
    ID3D12GraphicsCommandList_SetComputeRootSignature(context.list, root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(context.list, check_texture_pipeline);
    ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(context.list, 0, get_gpu_descriptor_handle(&context, gpu_heap, 0));
    ID3D12GraphicsCommandList_SetComputeRootUnorderedAccessView(context.list, 1, readback_va);
    ID3D12GraphicsCommandList_Dispatch(context.list, 1, 1, 1);
    transition_resource_state(context.list, readback_buffer, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);

    get_buffer_readback_with_command_list(readback_buffer, DXGI_FORMAT_R32_UINT, &rb, context.queue, context.list);

    for (i = 0; i < j; i++)
    {
        set_box(&box, i, 0, 0, i + 1, 1, 1);
        check_readback_data_uint(&rb, &box, i + 1, 0);
    }

    release_resource_readback(&rb);

    set_region_offset(&region_offsets[0], 2, 0, 0, 0);
    set_region_offset(&region_offsets[1], 1, 1, 0, 0);
    set_region_offset(&region_offsets[2], 1, 1, 0, 1);
    set_region_offset(&region_offsets[3], 0, 3, 0, 0);

    set_region_size(&region_sizes[0], 3, false, 0, 0, 0);
    set_region_size(&region_sizes[1], 4, true, 2, 2, 1);
    set_region_size(&region_sizes[2], 2, false, 0, 0, 0);
    set_region_size(&region_sizes[3], 4, true, 4, 1, 1);

    tile_flags[0] = D3D12_TILE_RANGE_FLAG_NONE;
    tile_flags[1] = D3D12_TILE_RANGE_FLAG_REUSE_SINGLE_TILE;
    tile_flags[2] = D3D12_TILE_RANGE_FLAG_NONE;
    tile_flags[3] = D3D12_TILE_RANGE_FLAG_NONE;
    tile_flags[4] = D3D12_TILE_RANGE_FLAG_SKIP;
    tile_flags[5] = D3D12_TILE_RANGE_FLAG_NONE;
    tile_flags[6] = D3D12_TILE_RANGE_FLAG_NULL;

    tile_offsets[0] = 3;
    tile_offsets[1] = 0;
    tile_offsets[2] = 16;
    tile_offsets[3] = 7;
    tile_offsets[4] = 0;
    tile_offsets[5] = 2;
    tile_offsets[6] = 0;

    tile_counts[0] = 4;
    tile_counts[1] = 2;
    tile_counts[2] = 3;
    tile_counts[3] = 1;
    tile_counts[4] = 1;
    tile_counts[5] = 1;
    tile_counts[6] = 1;

    ID3D12CommandQueue_UpdateTileMappings(context.queue, resource,
        4, region_offsets, region_sizes, heap, 7, tile_flags, tile_offsets, tile_counts,
        D3D12_TILE_MAPPING_FLAG_NONE);

    set_region_offset(&region_offsets[0], 3, 1, 0, 0);
    set_region_offset(&region_offsets[1], 1, 2, 0, 0);
    set_region_size(&region_sizes[0], 2, true, 1, 2, 1);

    ID3D12CommandQueue_CopyTileMappings(context.queue,
        resource, &region_offsets[0], resource, &region_offsets[1],
        &region_sizes[0], D3D12_TILE_MAPPING_FLAG_NONE);

    reset_command_list(context.list, context.allocator);

    transition_resource_state(context.list, readback_buffer, D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
    ID3D12GraphicsCommandList_SetDescriptorHeaps(context.list, 1, &gpu_heap);
    ID3D12GraphicsCommandList_SetComputeRootSignature(context.list, root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(context.list, check_texture_pipeline);
    ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(context.list, 0, get_gpu_descriptor_handle(&context, gpu_heap, 0));
    ID3D12GraphicsCommandList_SetComputeRootUnorderedAccessView(context.list, 1, readback_va);
    ID3D12GraphicsCommandList_Dispatch(context.list, 1, 1, 1);
    transition_resource_state(context.list, readback_buffer, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);

    get_buffer_readback_with_command_list(readback_buffer, DXGI_FORMAT_R32_UINT, &rb, context.queue, context.list);

    for (i = 0; i < j; i++)
    {
        if (options.TiledResourcesTier > D3D12_TILED_RESOURCES_TIER_2 || texture_region_tiles[i])
        {
            set_box(&box, i, 0, 0, i + 1, 1, 1);
            check_readback_data_uint(&rb, &box, texture_region_tiles[i], 0);
        }
    }

    release_resource_readback(&rb);
    ID3D12Resource_Release(resource);

    if (options.TiledResourcesTier >= D3D12_TILED_RESOURCES_TIER_3)
    {
        /* Test 3D image tile mappings */
        resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE3D;
        resource_desc.Alignment = 0;
        resource_desc.Width = 64;
        resource_desc.Height = 64;
        resource_desc.DepthOrArraySize = 32;
        resource_desc.MipLevels = 2;
        resource_desc.Format = DXGI_FORMAT_R32_UINT;
        resource_desc.SampleDesc.Count = 1;
        resource_desc.SampleDesc.Quality = 0;
        resource_desc.Layout = D3D12_TEXTURE_LAYOUT_64KB_UNDEFINED_SWIZZLE;
        resource_desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;

        hr = ID3D12Device_CreateReservedResource(context.device, &resource_desc,
            D3D12_RESOURCE_STATE_UNORDERED_ACCESS, NULL, &IID_ID3D12Resource, (void **)&resource);
        ok(hr == S_OK, "Failed to create reserved texture, hr %#x.\n", hr);

        num_tilings = resource_desc.MipLevels;
        ID3D12Device_GetResourceTiling(context.device, resource, NULL, &packed_mip_info, &tile_shape, &num_tilings, 0, tilings);
        ok(packed_mip_info.NumStandardMips == 2, "Unexpected number of standard mips %u.\n", packed_mip_info.NumStandardMips);

        srv_desc.Format = DXGI_FORMAT_R32_UINT;
        srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE3D;
        srv_desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
        srv_desc.Texture3D.MostDetailedMip = 0;
        srv_desc.Texture3D.MipLevels = resource_desc.MipLevels;
        srv_desc.Texture3D.ResourceMinLODClamp = 0.0f;
        ID3D12Device_CreateShaderResourceView(context.device, resource, &srv_desc, get_cpu_descriptor_handle(&context, gpu_heap, 0));

        /* Map entire image */
        tile_offsets[0] = 0;
        ID3D12CommandQueue_UpdateTileMappings(context.queue, resource,
            1, NULL, NULL, heap, 1, NULL, tile_offsets, NULL, D3D12_TILE_MAPPING_FLAG_NONE);

        reset_command_list(context.list, context.allocator);

        for (i = 0, j = 0; i < resource_desc.MipLevels; i++)
        {
            uav_desc.Format = DXGI_FORMAT_R32_UINT;
            uav_desc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE3D;
            uav_desc.Texture3D.MipSlice = i;
            uav_desc.Texture3D.FirstWSlice = 0;
            uav_desc.Texture3D.WSize = resource_desc.DepthOrArraySize >> i;
            ID3D12Device_CreateUnorderedAccessView(context.device, resource, NULL, &uav_desc, get_cpu_descriptor_handle(&context, cpu_heap, 1 + i));
            ID3D12Device_CreateUnorderedAccessView(context.device, resource, NULL, &uav_desc, get_cpu_descriptor_handle(&context, gpu_heap, 1 + i));

            /* ClearUnorderedAccessView only takes 2D coordinates so we have to
             * bring our own shader to initialize portions of a 3D image */
            ID3D12GraphicsCommandList_SetDescriptorHeaps(context.list, 1, &gpu_heap);
            ID3D12GraphicsCommandList_SetComputeRootSignature(context.list, clear_root_signature);
            ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(context.list, 0, get_gpu_descriptor_handle(&context, gpu_heap, 1 + i));
            ID3D12GraphicsCommandList_SetPipelineState(context.list, clear_texture_pipeline);

            for (z = 0; z < max(1u, tilings[i].DepthInTiles); z++)
            {
                for (y = 0; y < max(1u, tilings[i].HeightInTiles); y++)
                {
                    for (x = 0; x < max(1u, tilings[i].WidthInTiles); x++)
                    {
                        UINT shader_args[4];
                        shader_args[0] = tile_shape.WidthInTexels * x;
                        shader_args[1] = tile_shape.HeightInTexels * y;
                        shader_args[2] = tile_shape.DepthInTexels * z;
                        shader_args[3] = ++j;

                        ID3D12GraphicsCommandList_SetComputeRoot32BitConstants(context.list,
                                1, ARRAY_SIZE(shader_args), shader_args, 0);
                        ID3D12GraphicsCommandList_Dispatch(context.list,
                                tile_shape.WidthInTexels / 4,
                                tile_shape.HeightInTexels / 4,
                                tile_shape.DepthInTexels / 4);
                    }
                }
            }
        }

        transition_resource_state(context.list, resource, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);
        transition_resource_state(context.list, readback_buffer, D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
        ID3D12GraphicsCommandList_SetDescriptorHeaps(context.list, 1, &gpu_heap);
        ID3D12GraphicsCommandList_SetComputeRootSignature(context.list, root_signature);
        ID3D12GraphicsCommandList_SetPipelineState(context.list, check_texture_3d_pipeline);
        ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(context.list, 0, get_gpu_descriptor_handle(&context, gpu_heap, 0));
        ID3D12GraphicsCommandList_SetComputeRootUnorderedAccessView(context.list, 1, readback_va);
        ID3D12GraphicsCommandList_Dispatch(context.list, 1, 1, 1);
        transition_resource_state(context.list, readback_buffer, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);

        get_buffer_readback_with_command_list(readback_buffer, DXGI_FORMAT_R32_UINT, &rb, context.queue, context.list);

        for (i = 0; i < j; i++)
        {
            set_box(&box, i, 0, 0, i + 1, 1, 1);
            check_readback_data_uint(&rb, &box, i + 1, 0);
        }

        release_resource_readback(&rb);

        set_region_offset(&region_offsets[0], 0, 0, 0, 0);
        set_region_offset(&region_offsets[1], 0, 1, 1, 0);
        set_region_offset(&region_offsets[2], 1, 1, 0, 0);
        set_region_offset(&region_offsets[3], 1, 0, 0, 0);
        set_region_offset(&region_offsets[4], 0, 1, 0, 0);

        set_region_size(&region_sizes[0], 1, false, 0, 0, 0);
        set_region_size(&region_sizes[1], 3, false, 0, 0, 0);
        set_region_size(&region_sizes[2], 2, false, 0, 0, 0);
        set_region_size(&region_sizes[3], 2, true,  1, 1, 2);
        set_region_size(&region_sizes[4], 1, true,  1, 1, 1);

        tile_flags[0] = D3D12_TILE_RANGE_FLAG_NONE;
        tile_flags[1] = D3D12_TILE_RANGE_FLAG_REUSE_SINGLE_TILE;
        tile_flags[2] = D3D12_TILE_RANGE_FLAG_NULL;

        tile_offsets[0] = 2;
        tile_offsets[1] = 1;
        tile_offsets[2] = 0;

        tile_counts[0] = 6;
        tile_counts[1] = 2;
        tile_counts[2] = 1;

        ID3D12CommandQueue_UpdateTileMappings(context.queue, resource,
            5, region_offsets, region_sizes, heap, 3, tile_flags, tile_offsets, tile_counts,
            D3D12_TILE_MAPPING_FLAG_NONE);

        reset_command_list(context.list, context.allocator);

        transition_resource_state(context.list, readback_buffer, D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
        ID3D12GraphicsCommandList_SetDescriptorHeaps(context.list, 1, &gpu_heap);
        ID3D12GraphicsCommandList_SetComputeRootSignature(context.list, root_signature);
        ID3D12GraphicsCommandList_SetPipelineState(context.list, check_texture_3d_pipeline);
        ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(context.list, 0, get_gpu_descriptor_handle(&context, gpu_heap, 0));
        ID3D12GraphicsCommandList_SetComputeRootUnorderedAccessView(context.list, 1, readback_va);
        ID3D12GraphicsCommandList_Dispatch(context.list, 1, 1, 1);
        transition_resource_state(context.list, readback_buffer, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);

        get_buffer_readback_with_command_list(readback_buffer, DXGI_FORMAT_R32_UINT, &rb, context.queue, context.list);

        for (i = 0; i < j; i++)
        {
            set_box(&box, i, 0, 0, i + 1, 1, 1);
            check_readback_data_uint(&rb, &box, texture_3d_region_tiles[i], 0);
        }

        release_resource_readback(&rb);
        ID3D12Resource_Release(resource);
    }
    else
    {
        skip("Tiled resources tier 3 not supported.\n");
    }

    if (options.TiledResourcesTier >= D3D12_TILED_RESOURCES_TIER_4)
    {
        /* Test 2D array image with mip tail */
        resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
        resource_desc.Alignment = 0;
        resource_desc.Width = 256;
        resource_desc.Height = 256;
        resource_desc.DepthOrArraySize = 4;
        resource_desc.MipLevels = 9;
        resource_desc.Format = DXGI_FORMAT_R32_UINT;
        resource_desc.SampleDesc.Count = 1;
        resource_desc.SampleDesc.Quality = 0;
        resource_desc.Layout = D3D12_TEXTURE_LAYOUT_64KB_UNDEFINED_SWIZZLE;
        resource_desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;

        hr = ID3D12Device_CreateReservedResource(context.device, &resource_desc,
            D3D12_RESOURCE_STATE_UNORDERED_ACCESS, NULL, &IID_ID3D12Resource, (void **)&resource);
        ok(hr == S_OK, "Failed to create reserved texture, hr %#x.\n", hr);

        /* Map entire image */
        tile_offsets[0] = 0;
        ID3D12CommandQueue_UpdateTileMappings(context.queue, resource,
            1, NULL, NULL, heap, 1, NULL, tile_offsets, NULL, D3D12_TILE_MAPPING_FLAG_NONE);

        srv_desc.Format = DXGI_FORMAT_R32_UINT;
        srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY;
        srv_desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
        srv_desc.Texture2DArray.MostDetailedMip = 0;
        srv_desc.Texture2DArray.MipLevels = resource_desc.MipLevels;
        srv_desc.Texture2DArray.FirstArraySlice = 0;
        srv_desc.Texture2DArray.ArraySize = resource_desc.DepthOrArraySize;
        srv_desc.Texture2DArray.ResourceMinLODClamp = 0.0f;
        srv_desc.Texture2DArray.PlaneSlice = 0;
        ID3D12Device_CreateShaderResourceView(context.device, resource, &srv_desc, get_cpu_descriptor_handle(&context, gpu_heap, 0));

        reset_command_list(context.list, context.allocator);
        ID3D12GraphicsCommandList_SetDescriptorHeaps(context.list, 1, &gpu_heap);

        for (i = 0; i < (unsigned int)(resource_desc.MipLevels * resource_desc.DepthOrArraySize); i++)
        {
            UINT clear_value[4] = { 0, 0, 0, 0 };
            clear_value[0] = i;

            uav_desc.Format = DXGI_FORMAT_R32_UINT;
            uav_desc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2DARRAY;
            uav_desc.Texture2DArray.MipSlice = i % resource_desc.MipLevels;
            uav_desc.Texture2DArray.FirstArraySlice = i / resource_desc.MipLevels;
            uav_desc.Texture2DArray.ArraySize = 1;
            uav_desc.Texture2DArray.PlaneSlice = 0;
            ID3D12Device_CreateUnorderedAccessView(context.device, resource, NULL, &uav_desc, get_cpu_descriptor_handle(&context, cpu_heap, 1 + i));
            ID3D12Device_CreateUnorderedAccessView(context.device, resource, NULL, &uav_desc, get_cpu_descriptor_handle(&context, gpu_heap, 1 + i));

            ID3D12GraphicsCommandList_ClearUnorderedAccessViewUint(context.list,
                    get_gpu_descriptor_handle(&context, gpu_heap, 1 + i),
                    get_cpu_descriptor_handle(&context, cpu_heap, 1 + i),
                    resource, clear_value, 0, NULL);
        }

        transition_resource_state(context.list, readback_buffer, D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
        ID3D12GraphicsCommandList_SetDescriptorHeaps(context.list, 1, &gpu_heap);
        ID3D12GraphicsCommandList_SetComputeRootSignature(context.list, root_signature);
        ID3D12GraphicsCommandList_SetPipelineState(context.list, check_texture_array_pipeline);
        ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(context.list, 0, get_gpu_descriptor_handle(&context, gpu_heap, 0));
        ID3D12GraphicsCommandList_SetComputeRootUnorderedAccessView(context.list, 1, readback_va);
        ID3D12GraphicsCommandList_Dispatch(context.list, 1, 1, 1);
        transition_resource_state(context.list, readback_buffer, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);

        get_buffer_readback_with_command_list(readback_buffer, DXGI_FORMAT_R32_UINT, &rb, context.queue, context.list);

        for (i = 0; i < (unsigned int)(resource_desc.MipLevels * resource_desc.DepthOrArraySize); i++)
        {
            set_box(&box, i, 0, 0, i + 1, 1, 1);
            check_readback_data_uint(&rb, &box, i, 0);
        }

        release_resource_readback(&rb);
        ID3D12Resource_Release(resource);
    }
    else
    {
        skip("Tiled resources tier 4 not supported.\n");
    }

    ID3D12Heap_Release(heap);

    ID3D12DescriptorHeap_Release(gpu_heap);
    ID3D12DescriptorHeap_Release(cpu_heap);
    ID3D12Resource_Release(readback_buffer);
    ID3D12PipelineState_Release(clear_texture_pipeline);
    ID3D12PipelineState_Release(check_texture_3d_pipeline);
    ID3D12PipelineState_Release(check_texture_array_pipeline);
    ID3D12PipelineState_Release(check_texture_pipeline);
    ID3D12PipelineState_Release(check_buffer_pipeline);
    ID3D12RootSignature_Release(clear_root_signature);
    ID3D12RootSignature_Release(root_signature);
    destroy_test_context(&context);
}

void test_copy_tiles(void)
{
    ID3D12Resource *tiled_resource, *dst_buffer, *src_buffer;
    D3D12_TILED_RESOURCE_COORDINATE region_offset;
    D3D12_FEATURE_DATA_D3D12_OPTIONS options;
    uint32_t tile_offset, buffer_offset;
    D3D12_TILE_REGION_SIZE region_size;
    D3D12_RESOURCE_DESC resource_desc;
    struct test_context_desc desc;
    struct resource_readback rb;
    struct test_context context;
    D3D12_HEAP_DESC heap_desc;
    uint32_t *buffer_data;
    unsigned int i, x, y;
    ID3D12Heap *heap;
    D3D12_BOX box;
    HRESULT hr;

    static const struct
    {
        uint32_t x;
        uint32_t y;
        uint32_t tile_idx;
    }
    image_tiles[] =
    {
        {1, 0, 0}, {2, 0, 1}, {1, 1, 2}, {2, 1, 3},
        {3, 1, 4}, {0, 2, 5}, {1, 2, 6},
    };

    memset(&desc, 0, sizeof(desc));
    desc.rt_width = 640;
    desc.rt_height = 480;
    desc.rt_format = DXGI_FORMAT_R8G8B8A8_UNORM;
    if (!init_test_context(&context, &desc))
        return;

    hr = ID3D12Device_CheckFeatureSupport(context.device, D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(options));
    ok(hr == S_OK, "Failed to check feature support, hr %#x.\n", hr);

    if (!options.TiledResourcesTier)
    {
        skip("Tiled resources not supported by device.\n");
        destroy_test_context(&context);
        return;
    }

    memset(&heap_desc, 0, sizeof(heap_desc));
    heap_desc.Properties.Type = D3D12_HEAP_TYPE_DEFAULT;
    heap_desc.SizeInBytes = TILE_SIZE * 16;

    resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
    resource_desc.Alignment = 0;
    resource_desc.Width = heap_desc.SizeInBytes;
    resource_desc.Height = 1;
    resource_desc.DepthOrArraySize = 1;
    resource_desc.MipLevels = 1;
    resource_desc.Format = DXGI_FORMAT_UNKNOWN;
    resource_desc.SampleDesc.Count = 1;
    resource_desc.SampleDesc.Quality = 0;
    resource_desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
    resource_desc.Flags = D3D12_RESOURCE_FLAG_NONE;
    hr = ID3D12Device_CreateCommittedResource(context.device, &heap_desc.Properties, D3D12_HEAP_FLAG_NONE,
            &resource_desc, D3D12_RESOURCE_STATE_COPY_DEST, NULL, &IID_ID3D12Resource, (void **)&src_buffer);
    ok(hr == S_OK, "Failed to create buffer, hr %#x.\n", hr);
    hr = ID3D12Device_CreateCommittedResource(context.device, &heap_desc.Properties, D3D12_HEAP_FLAG_NONE,
            &resource_desc, D3D12_RESOURCE_STATE_COPY_DEST, NULL, &IID_ID3D12Resource, (void **)&dst_buffer);
    ok(hr == S_OK, "Failed to create buffer, hr %#x.\n", hr);

    buffer_data = malloc(resource_desc.Width);
    for (i = 0; i < resource_desc.Width / sizeof(*buffer_data); i++)
        buffer_data[i] = i;
    upload_buffer_data(src_buffer, 0, resource_desc.Width, buffer_data, context.queue, context.list);

    reset_command_list(context.list, context.allocator);
    transition_resource_state(context.list, src_buffer,
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COPY_SOURCE);

    /* Test buffer */
    hr = ID3D12Device_CreateReservedResource(context.device, &resource_desc,
            D3D12_RESOURCE_STATE_COPY_DEST, NULL, &IID_ID3D12Resource, (void **)&tiled_resource);
    ok(hr == S_OK, "Failed to create tiled buffer, hr %#x.\n", hr);

    heap_desc.Flags = D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS;
    hr = ID3D12Device_CreateHeap(context.device, &heap_desc, &IID_ID3D12Heap, (void **)&heap);
    ok(hr == S_OK, "Failed to create heap, hr %#x.\n", hr);

    tile_offset = 0;
    ID3D12CommandQueue_UpdateTileMappings(context.queue, tiled_resource,
            1, NULL, NULL, heap, 1, NULL, &tile_offset, NULL, D3D12_TILE_MAPPING_FLAG_NONE);

    /* Copy source tiles 0-2 with a 32-byte offset to buffer tiles 4-6 */
    set_region_offset(&region_offset, 4, 0, 0, 0);
    set_region_size(&region_size, 3, false, 0, 0, 0);

    buffer_offset = 32;

    ID3D12GraphicsCommandList_CopyTiles(context.list, tiled_resource,
            &region_offset, &region_size, src_buffer, buffer_offset,
            D3D12_TILE_COPY_FLAG_LINEAR_BUFFER_TO_SWIZZLED_TILED_RESOURCE);

    transition_resource_state(context.list, tiled_resource,
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COPY_SOURCE);

    get_buffer_readback_with_command_list(tiled_resource, DXGI_FORMAT_R32_UINT, &rb, context.queue, context.list);

    for (i = 0; i < 3 * TILE_SIZE / sizeof(*buffer_data); i += 1024)
    {
        uint32_t offset = i + 4 * TILE_SIZE / sizeof(*buffer_data);
        set_box(&box, offset, 0, 0, offset + 1, 1, 1);
        check_readback_data_uint(&rb, &box, buffer_data[i + buffer_offset / sizeof(*buffer_data)], 0);
    }

    release_resource_readback(&rb);

    reset_command_list(context.list, context.allocator);

    /* Read tiles 5-6 from the tiled resource */
    set_region_offset(&region_offset, 5, 0, 0, 0);
    set_region_size(&region_size, 1, false, 0, 0, 0);

    ID3D12GraphicsCommandList_CopyTiles(context.list, tiled_resource,
            &region_offset, &region_size, dst_buffer, 0,
            D3D12_TILE_COPY_FLAG_SWIZZLED_TILED_RESOURCE_TO_LINEAR_BUFFER);

    /* NONE behaves the same as SWIZZLED_TILED_RESOURCE_TO_LINEAR_BUFFER */
    set_region_offset(&region_offset, 6, 0, 0, 0);

    ID3D12GraphicsCommandList_CopyTiles(context.list, tiled_resource,
            &region_offset, &region_size, dst_buffer, TILE_SIZE,
            D3D12_TILE_COPY_FLAG_NONE);

    transition_resource_state(context.list, dst_buffer,
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COPY_SOURCE);

    get_buffer_readback_with_command_list(dst_buffer, DXGI_FORMAT_R32_UINT, &rb, context.queue, context.list);

    for (i = 0; i < 2 * TILE_SIZE / sizeof(*buffer_data); i += 1024)
    {
        uint32_t offset = i + (TILE_SIZE + buffer_offset) / sizeof(*buffer_data);
        set_box(&box, i, 0, 0, i + 1, 1, 1);
        check_readback_data_uint(&rb, &box, buffer_data[offset], 0);
    }

    release_resource_readback(&rb);

    ID3D12Resource_Release(tiled_resource);
    ID3D12Heap_Release(heap);

    /* Test image */
    resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
    resource_desc.Alignment = 0;
    resource_desc.Width = 512;
    resource_desc.Height = 512;
    resource_desc.DepthOrArraySize = 1;
    resource_desc.MipLevels = 1;
    resource_desc.Format = DXGI_FORMAT_R32_UINT;
    resource_desc.SampleDesc.Count = 1;
    resource_desc.SampleDesc.Quality = 0;
    resource_desc.Layout = D3D12_TEXTURE_LAYOUT_64KB_UNDEFINED_SWIZZLE;
    resource_desc.Flags = D3D12_RESOURCE_FLAG_NONE;

    hr = ID3D12Device_CreateReservedResource(context.device, &resource_desc,
            D3D12_RESOURCE_STATE_COPY_DEST, NULL, &IID_ID3D12Resource, (void **)&tiled_resource);
    ok(hr == S_OK, "Failed to create tiled buffer, hr %#x.\n", hr);

    heap_desc.Flags = D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES;
    hr = ID3D12Device_CreateHeap(context.device, &heap_desc, &IID_ID3D12Heap, (void **)&heap);
    ok(hr == S_OK, "Failed to create heap, hr %#x.\n", hr);

    tile_offset = 0;
    ID3D12CommandQueue_UpdateTileMappings(context.queue, tiled_resource,
            1, NULL, NULL, heap, 1, NULL, &tile_offset, NULL, D3D12_TILE_MAPPING_FLAG_NONE);

    reset_command_list(context.list, context.allocator);

    /* Copy source tiles 0-3 to 2x2 region at (1,0) */
    set_region_offset(&region_offset, 1, 0, 0, 0);
    set_region_size(&region_size, 4, true, 2, 2, 1);

    ID3D12GraphicsCommandList_CopyTiles(context.list, tiled_resource,
            &region_offset, &region_size, src_buffer, 0,
            D3D12_TILE_COPY_FLAG_LINEAR_BUFFER_TO_SWIZZLED_TILED_RESOURCE);

    /* Copy source tiles 4-6 to (3,1), (0,2) and (1,2) */
    set_region_offset(&region_offset, 3, 1, 0, 0);
    set_region_size(&region_size, 3, false, 0, 0, 0);

    ID3D12GraphicsCommandList_CopyTiles(context.list, tiled_resource,
            &region_offset, &region_size, src_buffer, 4 * TILE_SIZE,
            D3D12_TILE_COPY_FLAG_LINEAR_BUFFER_TO_SWIZZLED_TILED_RESOURCE);

    transition_resource_state(context.list, tiled_resource,
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COPY_SOURCE);

    get_texture_readback_with_command_list(tiled_resource, 0, &rb, context.queue, context.list);

    for (i = 0; i < ARRAY_SIZE(image_tiles); i++)
    {
        for (y = 0; y < 128; y += 32)
        {
            for (x = 0; x < 128; x += 32)
            {
                uint32_t offset = image_tiles[i].tile_idx * TILE_SIZE / sizeof(*buffer_data) + 128 * y + x;
                set_box(&box, 128 * image_tiles[i].x + x, 128 * image_tiles[i].y + y, 0,
                        128 * image_tiles[i].x + x + 1, 128 * image_tiles[i].y + y + 1, 1);
                check_readback_data_uint(&rb, &box, buffer_data[offset], 0);
            }
        }
    }

    release_resource_readback(&rb);

    reset_command_list(context.list, context.allocator);

    transition_resource_state(context.list, dst_buffer,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_COPY_DEST);

    /* Read 0-3 to 2x2 region at (1,0) */
    set_region_offset(&region_offset, 1, 0, 0, 0);
    set_region_size(&region_size, 4, true, 2, 2, 1);

    ID3D12GraphicsCommandList_CopyTiles(context.list, tiled_resource,
            &region_offset, &region_size, dst_buffer, 0,
            D3D12_TILE_COPY_FLAG_SWIZZLED_TILED_RESOURCE_TO_LINEAR_BUFFER);

    /* Read tiles (3,1), (0,2) and (1,2) */
    set_region_offset(&region_offset, 3, 1, 0, 0);
    set_region_size(&region_size, 3, false, 0, 0, 0);

    ID3D12GraphicsCommandList_CopyTiles(context.list, tiled_resource,
            &region_offset, &region_size, dst_buffer, 4 * TILE_SIZE,
            D3D12_TILE_COPY_FLAG_NONE);

    transition_resource_state(context.list, dst_buffer,
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COPY_SOURCE);

    get_buffer_readback_with_command_list(dst_buffer, DXGI_FORMAT_R32_UINT, &rb, context.queue, context.list);

    for (i = 0; i < ARRAY_SIZE(image_tiles); i++)
    {
        for (x = 0; x < TILE_SIZE / sizeof(uint32_t); x += 1024)
        {
            uint32_t offset = image_tiles[i].tile_idx * TILE_SIZE / sizeof(uint32_t) + x;
            set_box(&box, offset, 0, 0, offset + 1, 1, 1);
            check_readback_data_uint(&rb, &box, buffer_data[offset], 0);
        }
    }

    release_resource_readback(&rb);

    ID3D12Resource_Release(tiled_resource);
    ID3D12Heap_Release(heap);

    ID3D12Resource_Release(src_buffer);
    ID3D12Resource_Release(dst_buffer);

    free(buffer_data);
    destroy_test_context(&context);
#undef TILE_SIZE
}

static void test_buffer_feedback_instructions(bool use_dxil)
{
#define TILE_SIZE 65536
    D3D12_TILED_RESOURCE_COORDINATE tile_regions[2];
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    D3D12_DESCRIPTOR_RANGE descriptor_ranges[2];
    ID3D12DescriptorHeap *cpu_heap, *gpu_heap;
    ID3D12Resource *tiled_buffer, *out_buffer;
    D3D12_UNORDERED_ACCESS_VIEW_DESC uav_desc;
    D3D12_FEATURE_DATA_D3D12_OPTIONS options;
    D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc;
    D3D12_ROOT_PARAMETER root_parameters[3];
    ID3D12PipelineState *pipeline_state;
    ID3D12RootSignature *root_signature;
    D3D12_RESOURCE_DESC resource_desc;
    struct test_context_desc desc;
    struct resource_readback rb;
    struct test_context context;
    D3D12_HEAP_DESC heap_desc;
    unsigned int i, j;
    ID3D12Heap *heap;
    UINT tile_offset;
    bool test_is_raw;
    HRESULT hr;

#include "shaders/sparse/headers/buffer_feedback_ld_typed.h"
#include "shaders/sparse/headers/buffer_feedback_ld_raw.h"
#include "shaders/sparse/headers/buffer_feedback_ld_structured.h"
#include "shaders/sparse/headers/buffer_feedback_ld_typed_uav.h"

    const struct
    {
        D3D12_SHADER_BYTECODE cs_dxbc;
        D3D12_SHADER_BYTECODE cs_dxil;
        bool is_structured;
        bool is_raw;
        bool is_uav;
    }
    tests[] =
    {
        { buffer_feedback_ld_typed_dxbc, buffer_feedback_ld_typed_dxil, false, false, false },
        { buffer_feedback_ld_typed_uav_dxbc, buffer_feedback_ld_typed_uav_dxil, false, false, true },
        { buffer_feedback_ld_raw_dxbc, buffer_feedback_ld_raw_dxil, false, true, false },
        { buffer_feedback_ld_structured_dxbc, buffer_feedback_ld_structured_dxil, true, false, false },
    };

    struct shader_args
    {
        uint32_t stride;
    }
    shader_args;

    memset(&desc, 0, sizeof(desc));
    desc.no_render_target = true;
    if (!init_test_context(&context, &desc))
        return;

    if (use_dxil && !context_supports_dxil(&context))
    {
        destroy_test_context(&context);
        return;
    }

    hr = ID3D12Device_CheckFeatureSupport(context.device, D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(options));
    ok(hr == S_OK, "Failed to check feature support, hr %#x.\n", hr);

    if (options.TiledResourcesTier < D3D12_TILED_RESOURCES_TIER_2)
    {
        skip("Tiled resources Tier 2 not supported by device.\n");
        destroy_test_context(&context);
        return;
    }

    descriptor_ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
    descriptor_ranges[0].NumDescriptors = 1;
    descriptor_ranges[0].BaseShaderRegister = 0;
    descriptor_ranges[0].RegisterSpace = 0;
    descriptor_ranges[0].OffsetInDescriptorsFromTableStart = 0;
    descriptor_ranges[1].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
    descriptor_ranges[1].NumDescriptors = 1;
    descriptor_ranges[1].BaseShaderRegister = 1;
    descriptor_ranges[1].RegisterSpace = 0;
    descriptor_ranges[1].OffsetInDescriptorsFromTableStart = 1;
    root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameters[0].DescriptorTable.NumDescriptorRanges = ARRAY_SIZE(descriptor_ranges);
    root_parameters[0].DescriptorTable.pDescriptorRanges = descriptor_ranges;
    root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    root_parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV;
    root_parameters[1].Descriptor.ShaderRegister = 0;
    root_parameters[1].Descriptor.RegisterSpace = 0;
    root_parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    root_parameters[2].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
    root_parameters[2].Constants.Num32BitValues = sizeof(shader_args) / sizeof(uint32_t);
    root_parameters[2].Constants.ShaderRegister = 0;
    root_parameters[2].Constants.RegisterSpace = 0;
    root_parameters[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    root_signature_desc.NumParameters = ARRAY_SIZE(root_parameters);
    root_signature_desc.pParameters = root_parameters;
    root_signature_desc.NumStaticSamplers = 0;
    root_signature_desc.pStaticSamplers = NULL;
    root_signature_desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_NONE;
    hr = create_root_signature(context.device, &root_signature_desc, &root_signature);
    ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr);

    memset(&heap_desc, 0, sizeof(heap_desc));
    heap_desc.Properties.Type = D3D12_HEAP_TYPE_DEFAULT;
    heap_desc.SizeInBytes = TILE_SIZE * 2;

    resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
    resource_desc.Alignment = 0;
    resource_desc.Width = 128 * sizeof(uint32_t);
    resource_desc.Height = 1;
    resource_desc.DepthOrArraySize = 1;
    resource_desc.MipLevels = 1;
    resource_desc.Format = DXGI_FORMAT_UNKNOWN;
    resource_desc.SampleDesc.Count = 1;
    resource_desc.SampleDesc.Quality = 0;
    resource_desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
    resource_desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
    hr = ID3D12Device_CreateCommittedResource(context.device, &heap_desc.Properties, D3D12_HEAP_FLAG_NONE,
            &resource_desc, D3D12_RESOURCE_STATE_COPY_SOURCE, NULL, &IID_ID3D12Resource, (void **)&out_buffer);
    ok(hr == S_OK, "Failed to create committed resource, hr %#x.\n", hr);

    resource_desc.Width = 4 * TILE_SIZE;
    hr = ID3D12Device_CreateReservedResource(context.device, &resource_desc,
            D3D12_RESOURCE_STATE_UNORDERED_ACCESS, NULL, &IID_ID3D12Resource, (void **)&tiled_buffer);
    ok(hr == S_OK, "Failed to create reserved resource, hr %#x.\n", hr);

    hr = ID3D12Device_CreateHeap(context.device, &heap_desc, &IID_ID3D12Heap, (void **)&heap);
    ok(hr == S_OK, "Failed to create heap, hr %#x.\n", hr);

    /* Map the 0k-64k range and the 128k-192k range, leave the rest unmapped */
    set_region_offset(&tile_regions[0], 0, 0, 0, 0);
    set_region_offset(&tile_regions[1], 2, 0, 0, 0);
    tile_offset = 0;

    ID3D12CommandQueue_UpdateTileMappings(context.queue, tiled_buffer,
            ARRAY_SIZE(tile_regions), tile_regions, NULL, heap, 1, NULL,
            &tile_offset, NULL, D3D12_TILE_MAPPING_FLAG_NONE);

    gpu_heap = create_gpu_descriptor_heap(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 2);
    cpu_heap = create_cpu_descriptor_heap(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1);

    uav_desc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
    uav_desc.Format = DXGI_FORMAT_R32_UINT;
    uav_desc.Buffer.FirstElement = 0;
    uav_desc.Buffer.NumElements = resource_desc.Width / sizeof(uint32_t);
    uav_desc.Buffer.StructureByteStride = 0;
    uav_desc.Buffer.CounterOffsetInBytes = 0;
    uav_desc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_NONE;

    ID3D12Device_CreateUnorderedAccessView(context.device, tiled_buffer, NULL, &uav_desc, get_cpu_descriptor_handle(&context, cpu_heap, 0));
    ID3D12Device_CreateUnorderedAccessView(context.device, tiled_buffer, NULL, &uav_desc, get_cpu_descriptor_handle(&context, gpu_heap, 1));

    for (i = 0; i < resource_desc.Width / TILE_SIZE; i++)
    {
        UINT clear_values[] = { i + 1, 0, 0, 0 };
        D3D12_RECT rect;
        set_rect(&rect, i * TILE_SIZE / sizeof(uint32_t), 0, (i + 1) * TILE_SIZE / sizeof(uint32_t), 1);

        ID3D12GraphicsCommandList_ClearUnorderedAccessViewUint(context.list,
                get_gpu_descriptor_handle(&context, gpu_heap, 1),
                get_cpu_descriptor_handle(&context, cpu_heap, 0),
                tiled_buffer, clear_values, 1, &rect);
    };

    transition_resource_state(context.list, tiled_buffer,
            D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);

    for (i = 0; i < ARRAY_SIZE(tests); i++)
    {
        vkd3d_test_set_context("Test %u", i);
        test_is_raw = (i == 2) || (i == 3);

        todo_if(use_dxil && test_is_raw)
        pipeline_state = create_compute_pipeline_state(context.device,
                root_signature, use_dxil ? tests[i].cs_dxil : tests[i].cs_dxbc);

        /* This will fail for SSBO buffer feedback case on DXIL. */
        todo_if(use_dxil && test_is_raw)
        ok(!!pipeline_state, "Failed to create pipeline state.\n");
        if (!pipeline_state)
            continue;

        srv_desc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER;
        srv_desc.Format = tests[i].is_structured ? DXGI_FORMAT_UNKNOWN :
            (tests[i].is_raw ? DXGI_FORMAT_R32_TYPELESS : DXGI_FORMAT_R32_UINT);
        srv_desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
        srv_desc.Buffer.FirstElement = 0;
        srv_desc.Buffer.NumElements = resource_desc.Width / sizeof(uint32_t);
        srv_desc.Buffer.StructureByteStride = tests[i].is_structured ? sizeof(uint32_t) : 0;
        srv_desc.Buffer.Flags = tests[i].is_raw ? D3D12_BUFFER_SRV_FLAG_RAW : D3D12_BUFFER_SRV_FLAG_NONE;

        uav_desc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
        uav_desc.Format = srv_desc.Format;
        uav_desc.Buffer.FirstElement = srv_desc.Buffer.FirstElement;
        uav_desc.Buffer.NumElements = srv_desc.Buffer.NumElements;
        uav_desc.Buffer.StructureByteStride = srv_desc.Buffer.StructureByteStride;
        uav_desc.Buffer.CounterOffsetInBytes = 0;
        uav_desc.Buffer.Flags = tests[i].is_raw ? D3D12_BUFFER_UAV_FLAG_RAW : D3D12_BUFFER_UAV_FLAG_NONE;

        if (tests[i].is_uav)
        {
            ID3D12Device_CreateUnorderedAccessView(context.device, tiled_buffer, NULL, &uav_desc, get_cpu_descriptor_handle(&context, gpu_heap, 1));
            transition_resource_state(context.list, tiled_buffer, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
        }
        else
            ID3D12Device_CreateShaderResourceView(context.device, tiled_buffer, &srv_desc, get_cpu_descriptor_handle(&context, gpu_heap, 0));

        transition_resource_state(context.list, out_buffer,
        D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

        shader_args.stride = resource_desc.Width / 64;

        if (!tests[i].is_raw)
            shader_args.stride /= sizeof(uint32_t);

        ID3D12GraphicsCommandList_SetDescriptorHeaps(context.list, 1, &gpu_heap);
        ID3D12GraphicsCommandList_SetComputeRootSignature(context.list, root_signature);
        ID3D12GraphicsCommandList_SetPipelineState(context.list, pipeline_state);
        ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(context.list, 0, get_gpu_descriptor_handle(&context, gpu_heap, 0));
        ID3D12GraphicsCommandList_SetComputeRootUnorderedAccessView(context.list, 1, ID3D12Resource_GetGPUVirtualAddress(out_buffer));
        ID3D12GraphicsCommandList_SetComputeRoot32BitConstants(context.list, 2, sizeof(shader_args) / sizeof(uint32_t), &shader_args, 0);
        ID3D12GraphicsCommandList_Dispatch(context.list, 1, 1, 1);

        transition_resource_state(context.list, out_buffer,
                D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);

        if (tests[i].is_uav)
        {
            transition_resource_state(context.list, tiled_buffer,
                    D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);
        }

        get_buffer_readback_with_command_list(out_buffer, DXGI_FORMAT_R32_UINT, &rb, context.queue, context.list);

        for (j = 0; j < 64; j++)
        {
            D3D12_BOX box;
            UINT tile_index = j / 16;

            set_box(&box, 2 * j, 0, 0, 2 * j + 1, 1, 1);
            todo_if(test_is_raw) check_readback_data_uint(&rb, &box, (tile_index & 1) ? 0 : (tile_index + 1), 0);
            set_box(&box, 2 * j + 1, 0, 0, 2 * j + 2, 1, 1);
            todo_if(test_is_raw) check_readback_data_uint(&rb, &box, (tile_index & 1) ? 0 : 1, 0);
        }

        release_resource_readback(&rb);

        reset_command_list(context.list, context.allocator);

        ID3D12PipelineState_Release(pipeline_state);
    }

    ID3D12DescriptorHeap_Release(cpu_heap);
    ID3D12DescriptorHeap_Release(gpu_heap);
    ID3D12Heap_Release(heap);
    ID3D12Resource_Release(tiled_buffer);
    ID3D12Resource_Release(out_buffer);
    ID3D12RootSignature_Release(root_signature);
    destroy_test_context(&context);
#undef TILE_SIZE
}

void test_buffer_feedback_instructions_sm51(void)
{
    test_buffer_feedback_instructions(false);
}

void test_buffer_feedback_instructions_dxil(void)
{
    test_buffer_feedback_instructions(true);
}

static void test_texture_feedback_instructions(bool use_dxil)
{
#define TILE_SIZE 65536
    ID3D12DescriptorHeap *gpu_heap, *sampler_heap, *rtv_heap;
    ID3D12Resource *tiled_image, *color_rt, *residency_rt;
    D3D12_GRAPHICS_PIPELINE_STATE_DESC pipeline_desc;
    D3D12_TILED_RESOURCE_COORDINATE tile_regions[3];
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    D3D12_DESCRIPTOR_RANGE descriptor_ranges[3];
    D3D12_UNORDERED_ACCESS_VIEW_DESC uav_desc;
    D3D12_FEATURE_DATA_D3D12_OPTIONS options;
    D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc;
    D3D12_ROOT_PARAMETER root_parameters[3];
    D3D12_RENDER_TARGET_VIEW_DESC rtv_desc;
    ID3D12PipelineState *pipeline_state;
    ID3D12RootSignature *root_signature;
    D3D12_RESOURCE_DESC resource_desc;
    D3D12_SAMPLER_DESC sampler_desc;
    struct test_context_desc desc;
    struct resource_readback rb;
    struct test_context context;
    D3D12_HEAP_DESC heap_desc;
    D3D12_VIEWPORT viewport;
    D3D12_RECT scissor;
    ID3D12Heap *heap;
    UINT tile_offset;
    unsigned int i;
    HRESULT hr;

#include "shaders/sparse/headers/texture_feedback_vs.h"
#include "shaders/sparse/headers/texture_feedback_sample.h"
#include "shaders/sparse/headers/texture_feedback_sample_bias.h"
#include "shaders/sparse/headers/texture_feedback_sample_grad.h"
#include "shaders/sparse/headers/texture_feedback_sample_lod.h"
#include "shaders/sparse/headers/texture_feedback_gather.h"
#include "shaders/sparse/headers/texture_feedback_gather_po.h"
#include "shaders/sparse/headers/texture_feedback_ld.h"
#include "shaders/sparse/headers/texture_feedback_ld_uav.h"

    struct shader_args
    {
        float args[4];
    };

    const struct
    {
        D3D12_SHADER_BYTECODE ps_dxbc;
        D3D12_SHADER_BYTECODE ps_dxil;
        uint32_t expected_mip;
        struct shader_args args;
        bool is_gather;
    }
    tests[] =
    {
        { texture_feedback_sample_dxbc, texture_feedback_sample_dxil,             0, {{0.0f}} },
        { texture_feedback_sample_dxbc, texture_feedback_sample_dxil,             1, {{1.0f}} },
        { texture_feedback_sample_bias_dxbc, texture_feedback_sample_bias_dxil,   0, {{0.0f, 0.0f}} },
        { texture_feedback_sample_bias_dxbc, texture_feedback_sample_bias_dxil,   1, {{1.0f, 0.0f}} },
        { texture_feedback_sample_bias_dxbc, texture_feedback_sample_bias_dxil,   1, {{0.0f, 1.0f}} },
        { texture_feedback_sample_grad_dxbc, texture_feedback_sample_grad_dxil,   0, {{0.0f, 0.0f, 0.0f}} },
        { texture_feedback_sample_grad_dxbc, texture_feedback_sample_grad_dxil,   1, {{0.0f, 1.0f, 1.0f}} },
        { texture_feedback_sample_lod_dxbc, texture_feedback_sample_lod_dxil,     0, {{0.0f}} },
        { texture_feedback_sample_lod_dxbc, texture_feedback_sample_lod_dxil,     1, {{1.0f}} },
        { texture_feedback_gather_dxbc, texture_feedback_gather_dxil,             0, {{0.0f}}, true },
        { texture_feedback_gather_po_dxbc, texture_feedback_gather_po_dxil,       0, {{0.0f, 0.0f}}, true },
        { texture_feedback_ld_dxbc, texture_feedback_ld_dxil,                     0, {{0.0f}} },
        { texture_feedback_ld_uav_dxbc, texture_feedback_ld_uav_dxil,             0, {{0.0f}} },
    };

    static const FLOAT clear_colors[2][4] = {
        { 0.2f, 0.4f, 0.6f, 0.8f },
        { 0.5f, 0.5f, 0.5f, 0.5f },
    };

    memset(&desc, 0, sizeof(desc));
    desc.no_render_target = true;
    if (!init_test_context(&context, &desc))
        return;

    if (use_dxil && !context_supports_dxil(&context))
    {
        destroy_test_context(&context);
        return;
    }

    hr = ID3D12Device_CheckFeatureSupport(context.device, D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(options));
    ok(hr == S_OK, "Failed to check feature support, hr %#x.\n", hr);

    if (options.TiledResourcesTier < D3D12_TILED_RESOURCES_TIER_2)
    {
        skip("Tiled resources Tier 2 not supported by device.\n");
        destroy_test_context(&context);
        return;
    }

    descriptor_ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
    descriptor_ranges[0].NumDescriptors = 1;
    descriptor_ranges[0].BaseShaderRegister = 0;
    descriptor_ranges[0].RegisterSpace = 0;
    descriptor_ranges[0].OffsetInDescriptorsFromTableStart = 0;
    descriptor_ranges[1].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
    descriptor_ranges[1].NumDescriptors = 1;
    descriptor_ranges[1].BaseShaderRegister = 2;
    descriptor_ranges[1].RegisterSpace = 0;
    descriptor_ranges[1].OffsetInDescriptorsFromTableStart = 1;
    descriptor_ranges[2].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER;
    descriptor_ranges[2].NumDescriptors = 1;
    descriptor_ranges[2].BaseShaderRegister = 0;
    descriptor_ranges[2].RegisterSpace = 0;
    descriptor_ranges[2].OffsetInDescriptorsFromTableStart = 0;
    root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameters[0].DescriptorTable.NumDescriptorRanges = 2;
    root_parameters[0].DescriptorTable.pDescriptorRanges = &descriptor_ranges[0];
    root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
    root_parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameters[1].DescriptorTable.NumDescriptorRanges = 1;
    root_parameters[1].DescriptorTable.pDescriptorRanges = &descriptor_ranges[2];
    root_parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
    root_parameters[2].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
    root_parameters[2].Constants.Num32BitValues = sizeof(struct shader_args) / sizeof(uint32_t);
    root_parameters[2].Constants.ShaderRegister = 0;
    root_parameters[2].Constants.RegisterSpace = 0;
    root_parameters[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
    root_signature_desc.NumParameters = ARRAY_SIZE(root_parameters);
    root_signature_desc.pParameters = root_parameters;
    root_signature_desc.NumStaticSamplers = 0;
    root_signature_desc.pStaticSamplers = NULL;
    root_signature_desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_NONE;
    hr = create_root_signature(context.device, &root_signature_desc, &root_signature);
    ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr);

    memset(&heap_desc, 0, sizeof(heap_desc));
    heap_desc.Properties.Type = D3D12_HEAP_TYPE_DEFAULT;
    heap_desc.SizeInBytes = TILE_SIZE * 3;

    resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
    resource_desc.Alignment = 0;
    resource_desc.Width = 256;
    resource_desc.Height = 256;
    resource_desc.DepthOrArraySize = 1;
    resource_desc.MipLevels = 1;
    resource_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    resource_desc.SampleDesc.Count = 1;
    resource_desc.SampleDesc.Quality = 0;
    resource_desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
    resource_desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
    hr = ID3D12Device_CreateCommittedResource(context.device, &heap_desc.Properties, D3D12_HEAP_FLAG_NONE,
            &resource_desc, D3D12_RESOURCE_STATE_RENDER_TARGET, NULL, &IID_ID3D12Resource, (void **)&color_rt);
    ok(hr == S_OK, "Failed to create committed resource, hr %#x.\n", hr);

    resource_desc.Format = DXGI_FORMAT_R32_UINT;
    hr = ID3D12Device_CreateCommittedResource(context.device, &heap_desc.Properties, D3D12_HEAP_FLAG_NONE,
            &resource_desc, D3D12_RESOURCE_STATE_RENDER_TARGET, NULL, &IID_ID3D12Resource, (void **)&residency_rt);
    ok(hr == S_OK, "Failed to create committed resource, hr %#x.\n", hr);

    resource_desc.MipLevels = 2;
    resource_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    resource_desc.Layout = D3D12_TEXTURE_LAYOUT_64KB_UNDEFINED_SWIZZLE;
    resource_desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
    hr = ID3D12Device_CreateReservedResource(context.device, &resource_desc,
            D3D12_RESOURCE_STATE_RENDER_TARGET, NULL, &IID_ID3D12Resource, (void **)&tiled_image);
    ok(hr == S_OK, "Failed to create reserved resource, hr %#x.\n", hr);

    hr = ID3D12Device_CreateHeap(context.device, &heap_desc, &IID_ID3D12Heap, (void **)&heap);
    ok(hr == S_OK, "Failed to create heap, hr %#x.\n", hr);

    /* Map top-left + bottom-right of mip 0 + entire mip 1 */
    set_region_offset(&tile_regions[0], 0, 0, 0, 0);
    set_region_offset(&tile_regions[1], 1, 1, 0, 0);
    set_region_offset(&tile_regions[2], 0, 0, 0, 1);
    tile_offset = 0;

    ID3D12CommandQueue_UpdateTileMappings(context.queue, tiled_image,
            ARRAY_SIZE(tile_regions), tile_regions, NULL, heap, 1, NULL,
            &tile_offset, NULL, D3D12_TILE_MAPPING_FLAG_NONE);

    rtv_heap = create_cpu_descriptor_heap(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_RTV, 4);
    gpu_heap = create_gpu_descriptor_heap(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 2);
    sampler_heap = create_gpu_descriptor_heap(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, 1);

    rtv_desc.Format = DXGI_FORMAT_UNKNOWN;
    rtv_desc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D;
    rtv_desc.Texture2D.MipSlice = 0;
    rtv_desc.Texture2D.PlaneSlice = 0;

    ID3D12Device_CreateRenderTargetView(context.device, color_rt, &rtv_desc, get_cpu_rtv_handle(&context, rtv_heap, 0));
    ID3D12Device_CreateRenderTargetView(context.device, residency_rt, &rtv_desc, get_cpu_rtv_handle(&context, rtv_heap, 1));
    ID3D12Device_CreateRenderTargetView(context.device, tiled_image, &rtv_desc, get_cpu_rtv_handle(&context, rtv_heap, 2));

    rtv_desc.Texture2D.MipSlice = 1;
    ID3D12Device_CreateRenderTargetView(context.device, tiled_image, &rtv_desc, get_cpu_rtv_handle(&context, rtv_heap, 3));

    ID3D12GraphicsCommandList_ClearRenderTargetView(context.list, get_cpu_rtv_handle(&context, rtv_heap, 2), clear_colors[0], 0, NULL);
    ID3D12GraphicsCommandList_ClearRenderTargetView(context.list, get_cpu_rtv_handle(&context, rtv_heap, 3), clear_colors[1], 0, NULL);
    transition_resource_state(context.list, tiled_image, D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);

    srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
    srv_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    srv_desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
    srv_desc.Texture2D.MostDetailedMip = 0;
    srv_desc.Texture2D.MipLevels = ~0u;
    srv_desc.Texture2D.PlaneSlice = 0;
    srv_desc.Texture2D.ResourceMinLODClamp = 0;
    ID3D12Device_CreateShaderResourceView(context.device, tiled_image, &srv_desc, get_cpu_descriptor_handle(&context, gpu_heap, 0));

    uav_desc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D;
    uav_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    uav_desc.Texture2D.MipSlice = 0;
    uav_desc.Texture2D.PlaneSlice = 0;
    ID3D12Device_CreateUnorderedAccessView(context.device, tiled_image, NULL, &uav_desc, get_cpu_descriptor_handle(&context, gpu_heap, 1));

    memset(&sampler_desc, 0, sizeof(sampler_desc));
    sampler_desc.Filter = D3D12_FILTER_MIN_MAG_MIP_POINT;
    sampler_desc.AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
    sampler_desc.AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
    sampler_desc.AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
    sampler_desc.MipLODBias = 0.0f;
    sampler_desc.MaxAnisotropy = 1;
    sampler_desc.MinLOD = -16.0f;
    sampler_desc.MaxLOD = 16.0f;
    ID3D12Device_CreateSampler(context.device, &sampler_desc, get_cpu_sampler_handle(&context, sampler_heap, 0));

    memset(&pipeline_desc, 0, sizeof(pipeline_desc));
    pipeline_desc.pRootSignature = root_signature;
    pipeline_desc.VS = use_dxil ? texture_feedback_vs_dxil : texture_feedback_vs_dxbc;
    pipeline_desc.BlendState.RenderTarget[0].RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;
    pipeline_desc.BlendState.RenderTarget[1].RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;
    pipeline_desc.SampleMask = 0xFFFFFFFF;
    pipeline_desc.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID;
    pipeline_desc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE;
    pipeline_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
    pipeline_desc.NumRenderTargets = 2;
    pipeline_desc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
    pipeline_desc.RTVFormats[1] = DXGI_FORMAT_R32_UINT;
    pipeline_desc.SampleDesc.Count = 1;

    set_viewport(&viewport, 0.0f, 0.0f, 256.0f, 256.0f, 0.0f, 1.0f);
    set_rect(&scissor, 0, 0, 256, 256);

    for (i = 0; i < ARRAY_SIZE(tests); i++)
    {
        D3D12_CPU_DESCRIPTOR_HANDLE rt_handle = get_cpu_rtv_handle(&context, rtv_heap, 0);
        ID3D12DescriptorHeap *descriptor_heaps[2] = { gpu_heap, sampler_heap };
        const FLOAT clear_residency_rt[] = { 100.0f, 0.0f, 0.0f, 0.0f };
        const FLOAT clear_color_rt[] = { 1.0f, 1.0f, 1.0f, 1.0f };
        unsigned int color, expected_a, expected_b;
        vkd3d_test_set_context("Test %u", i);

        expected_a = tests[i].expected_mip ? 0x80808080 : (tests[i].is_gather ? 0x33333333 : 0xcc996633);
        expected_b = tests[i].expected_mip ? expected_a : 0;
        
        pipeline_desc.PS = use_dxil ? tests[i].ps_dxil : tests[i].ps_dxbc;

        hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pipeline_desc, &IID_ID3D12PipelineState, (void **)&pipeline_state);
        ok(hr == S_OK, "Failed to compile graphics pipeline, hr %#x.\n", hr);

        ID3D12GraphicsCommandList_OMSetRenderTargets(context.list, 2, &rt_handle, true, NULL);
        ID3D12GraphicsCommandList_ClearRenderTargetView(context.list, get_cpu_rtv_handle(&context, rtv_heap, 1), clear_residency_rt, 0, NULL);
        ID3D12GraphicsCommandList_ClearRenderTargetView(context.list, get_cpu_rtv_handle(&context, rtv_heap, 0), clear_color_rt, 0, NULL);
        ID3D12GraphicsCommandList_IASetPrimitiveTopology(context.list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
        ID3D12GraphicsCommandList_RSSetViewports(context.list, 1, &viewport);
        ID3D12GraphicsCommandList_RSSetScissorRects(context.list, 1, &scissor);
        ID3D12GraphicsCommandList_SetDescriptorHeaps(context.list, ARRAY_SIZE(descriptor_heaps), descriptor_heaps);
        ID3D12GraphicsCommandList_SetGraphicsRootSignature(context.list, root_signature);
        ID3D12GraphicsCommandList_SetPipelineState(context.list, pipeline_state);
        ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(context.list, 0, get_gpu_descriptor_handle(&context, gpu_heap, 0));
        ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(context.list, 1, get_gpu_sampler_handle(&context, sampler_heap, 0));
        ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(context.list, 2, sizeof(tests[i].args) / sizeof(uint32_t), tests[i].args.args, 0);
        ID3D12GraphicsCommandList_DrawInstanced(context.list, 3, 1, 0, 0);

        transition_resource_state(context.list, color_rt, D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
        transition_resource_state(context.list, residency_rt, D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);

        get_texture_readback_with_command_list(color_rt, 0, &rb, context.queue, context.list);

        color = get_readback_uint(&rb, 64, 64, 0);
        ok(compare_color(color, expected_a, 1), "Got color 0x%08x, expected %08x.\n", color, expected_a);
        color = get_readback_uint(&rb, 192, 64, 0);
        ok(compare_color(color, expected_b, 1), "Got color 0x%08x, expected %08x.\n", color, expected_b);
        color = get_readback_uint(&rb, 64, 192, 0);
        ok(compare_color(color, expected_b, 1), "Got color 0x%08x, expected %08x.\n", color, expected_b);
        color = get_readback_uint(&rb, 192, 192, 0);
        ok(compare_color(color, expected_a, 1), "Got color 0x%08x, expected %08x.\n", color, expected_a);

        release_resource_readback(&rb);
        reset_command_list(context.list, context.allocator);

        get_texture_readback_with_command_list(residency_rt, 0, &rb, context.queue, context.list);

        color = get_readback_uint(&rb, 64, 64, 0);
        ok(compare_color(color, !!expected_a, 0), "Got residency %#x, expected %#x.\n", color, !!expected_a);
        color = get_readback_uint(&rb, 192, 64, 0);
        ok(compare_color(color, !!expected_b, 0), "Got residency %#x, expected %#x.\n", color, !!expected_b);
        color = get_readback_uint(&rb, 64, 192, 0);
        ok(compare_color(color, !!expected_b, 0), "Got residency %#x, expected %#x.\n", color, !!expected_b);
        color = get_readback_uint(&rb, 192, 192, 0);
        ok(compare_color(color, !!expected_a, 0), "Got residency %#x, expected %#x.\n", color, !!expected_a);

        release_resource_readback(&rb);
        reset_command_list(context.list, context.allocator);

        transition_resource_state(context.list, color_rt, D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
        transition_resource_state(context.list, residency_rt, D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

        ID3D12PipelineState_Release(pipeline_state);
    }

    ID3D12DescriptorHeap_Release(rtv_heap);
    ID3D12DescriptorHeap_Release(gpu_heap);
    ID3D12DescriptorHeap_Release(sampler_heap);
    ID3D12Heap_Release(heap);
    ID3D12Resource_Release(tiled_image);
    ID3D12Resource_Release(color_rt);
    ID3D12Resource_Release(residency_rt);
    ID3D12RootSignature_Release(root_signature);
    destroy_test_context(&context);
}

void test_texture_feedback_instructions_sm51(void)
{
    test_texture_feedback_instructions(false);
}

void test_texture_feedback_instructions_dxil(void)
{
    test_texture_feedback_instructions(true);
}

void test_sparse_buffer_memory_lifetime(void)
{
    /* Attempt to bind sparse memory, then free the underlying heap, but keep the sparse resource
     * alive. This should confuse drivers that attempt to track BO lifetimes. */
    D3D12_UNORDERED_ACCESS_VIEW_DESC uav_desc;
    D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc;
    D3D12_FEATURE_DATA_D3D12_OPTIONS options;
    const UINT values[] = { 42, 42, 42, 42 };
    D3D12_ROOT_PARAMETER root_parameters[2];
    D3D12_TILE_REGION_SIZE region_size;
    D3D12_GPU_DESCRIPTOR_HANDLE h_gpu;
    D3D12_CPU_DESCRIPTOR_HANDLE h_cpu;
    D3D12_ROOT_SIGNATURE_DESC rs_desc;
    D3D12_DESCRIPTOR_RANGE desc_range;
    struct test_context context;
    struct resource_readback rb;
    ID3D12DescriptorHeap *cpu;
    ID3D12DescriptorHeap *gpu;
    D3D12_HEAP_DESC heap_desc;
    D3D12_RESOURCE_DESC desc;
    ID3D12Resource *sparse;
    ID3D12Resource *buffer;
    ID3D12Heap *heap_live;
    ID3D12Heap *heap;
    unsigned int i;
    HRESULT hr;

#include "shaders/sparse/headers/sparse_query.h"

    if (!init_compute_test_context(&context))
        return;

    hr = ID3D12Device_CheckFeatureSupport(context.device, D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(options));
    ok(hr == S_OK, "Failed to check feature support, hr %#x.\n", hr);

    if (options.TiledResourcesTier < D3D12_TILED_RESOURCES_TIER_2)
    {
        skip("Tiled resources Tier 2 not supported by device.\n");
        destroy_test_context(&context);
        return;
    }

    memset(&rs_desc, 0, sizeof(rs_desc));
    memset(root_parameters, 0, sizeof(root_parameters));
    memset(&desc_range, 0, sizeof(desc_range));
    rs_desc.NumParameters = ARRAY_SIZE(root_parameters);
    rs_desc.pParameters = root_parameters;
    root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV;
    root_parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    root_parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameters[1].DescriptorTable.NumDescriptorRanges = 1;
    root_parameters[1].DescriptorTable.pDescriptorRanges = &desc_range;
    desc_range.NumDescriptors = 1;
    desc_range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
    create_root_signature(context.device, &rs_desc, &context.root_signature);
    context.pipeline_state = create_compute_pipeline_state(context.device, context.root_signature, sparse_query_dxbc);

    memset(&heap_desc, 0, sizeof(heap_desc));
    heap_desc.SizeInBytes = 4 * 1024 * 1024;
    heap_desc.Properties.Type = D3D12_HEAP_TYPE_DEFAULT;
    heap_desc.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
    heap_desc.Flags = D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS;
    hr = ID3D12Device_CreateHeap(context.device, &heap_desc, &IID_ID3D12Heap, (void**)&heap);
    ok(SUCCEEDED(hr), "Failed to create heap, hr #%x.\n", hr);
    hr = ID3D12Device_CreateHeap(context.device, &heap_desc, &IID_ID3D12Heap, (void**)&heap_live);
    ok(SUCCEEDED(hr), "Failed to create heap, hr #%x.\n", hr);

    memset(&desc, 0, sizeof(desc));
    desc.Width = 64 * 1024 * 1024;
    desc.Height = 1;
    desc.DepthOrArraySize = 1;
    desc.SampleDesc.Count = 1;
    desc.Format = DXGI_FORMAT_UNKNOWN;
    desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
    desc.MipLevels = 1;
    desc.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
    desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
    desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
    hr = ID3D12Device_CreateReservedResource(context.device, &desc, D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
            NULL, &IID_ID3D12Resource, (void**)&sparse);
    ok(SUCCEEDED(hr), "Failed to create reserved resource, hr #%x.\n", hr);

    {
        const D3D12_TILED_RESOURCE_COORDINATE region_start_coordinate = { 0 };
        const D3D12_TILE_RANGE_FLAGS range_flag = D3D12_TILE_RANGE_FLAG_NULL;
        const UINT offset = 0;
        const UINT count = desc.Width / (64 * 1024);
        region_size.UseBox = FALSE;
        region_size.NumTiles = desc.Width / (64 * 1024);
        ID3D12CommandQueue_UpdateTileMappings(context.queue, sparse, 1, &region_start_coordinate, &region_size,
                NULL, 1, &range_flag, &offset, &count, D3D12_TILE_MAPPING_FLAG_NONE);
    }

    region_size.UseBox = FALSE;
    region_size.NumTiles = 1;

    for (i = 0; i < 2; i++)
    {
        const D3D12_TILED_RESOURCE_COORDINATE region_start_coordinate = { i, 0, 0, 0 };
        const D3D12_TILE_RANGE_FLAGS range_flag = D3D12_TILE_RANGE_FLAG_NONE;
        const UINT offset = i;
        const UINT count = 1;

        ID3D12CommandQueue_UpdateTileMappings(context.queue, sparse, 1, &region_start_coordinate, &region_size,
                i == 0 ? heap : heap_live, 1, &range_flag, &offset, &count, D3D12_TILE_MAPPING_FLAG_NONE);
    }
    wait_queue_idle(context.device, context.queue);

    buffer = create_default_buffer(context.device, 128 * 1024,
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_DEST);
    cpu = create_cpu_descriptor_heap(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1);
    gpu = create_gpu_descriptor_heap(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 2);
    memset(&uav_desc, 0, sizeof(uav_desc));
    uav_desc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
    uav_desc.Format = DXGI_FORMAT_R32_UINT;
    uav_desc.Buffer.NumElements = 128 * 1024 / 4;
    uav_desc.Buffer.FirstElement = 0;
    ID3D12Device_CreateUnorderedAccessView(context.device, sparse, NULL, &uav_desc,
            ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(cpu));
    ID3D12Device_CreateUnorderedAccessView(context.device, sparse, NULL, &uav_desc,
            ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(gpu));

    memset(&srv_desc, 0, sizeof(srv_desc));
    srv_desc.Buffer.FirstElement = 0;
    srv_desc.Buffer.NumElements = 2 * 1024 * 1024;
    srv_desc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER;
    srv_desc.Format = DXGI_FORMAT_R32_UINT;
    srv_desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;

    h_cpu = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(gpu);
    h_cpu.ptr += ID3D12Device_GetDescriptorHandleIncrementSize(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
    ID3D12Device_CreateShaderResourceView(context.device, sparse, &srv_desc, h_cpu);

    ID3D12GraphicsCommandList_SetDescriptorHeaps(context.list, 1, &gpu);
    ID3D12GraphicsCommandList_ClearUnorderedAccessViewUint(context.list,
            ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(gpu),
            ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(cpu), sparse, values, 0, NULL);
    transition_resource_state(context.list, sparse,
            D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
    ID3D12GraphicsCommandList_CopyBufferRegion(context.list, buffer, 0, sparse, 0, 128 * 1024);
    transition_resource_state(context.list, buffer,
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_buffer_readback_with_command_list(buffer, DXGI_FORMAT_R32_UINT,
            &rb, context.queue, context.list);
    reset_command_list(context.list, context.allocator);
    ok(get_readback_uint(&rb, 0, 0, 0) == 42, "Got #%x, expected 42.\n", get_readback_uint(&rb, 0, 0, 0));
    ok(get_readback_uint(&rb, 64 * 1024 / 4, 0, 0) == 42, "Got #%x, expected 42.\n", get_readback_uint(&rb, 64 * 1024 / 4, 0, 0));
    release_resource_readback(&rb);

    ID3D12Heap_Release(heap);

    /* Access a resource where we can hypothetically access the freed heap memory. */
    /* On AMD Windows native at least, if we read the freed region, we read garbage, which proves it's not required to unbind explicitly.
     * We'd read 0 in that case. */
    ID3D12GraphicsCommandList_CopyBufferRegion(context.list, buffer, 0, sparse, 64 * 1024, 64 * 1024);

#define EXPLORE_UNDEFINED_BEHAVIOR 0

#if EXPLORE_UNDEFINED_BEHAVIOR
    /* This reads unmapped memory. */
    ID3D12GraphicsCommandList_CopyBufferRegion(context.list, buffer, 1024, sparse, 1024, 1024);
#endif

    transition_resource_state(context.list, buffer, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

    h_gpu = ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(gpu);
    h_gpu.ptr += ID3D12Device_GetDescriptorHandleIncrementSize(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
    ID3D12GraphicsCommandList_SetDescriptorHeaps(context.list, 1, &gpu);
    ID3D12GraphicsCommandList_SetComputeRootSignature(context.list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(context.list, context.pipeline_state);
    ID3D12GraphicsCommandList_SetComputeRootUnorderedAccessView(context.list, 0, ID3D12Resource_GetGPUVirtualAddress(buffer));
    ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(context.list, 1, h_gpu);
#if EXPLORE_UNDEFINED_BEHAVIOR
    ID3D12GraphicsCommandList_Dispatch(context.list, 1, 1, 1);
#endif

    transition_resource_state(context.list, buffer, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);

    get_buffer_readback_with_command_list(buffer, DXGI_FORMAT_R32_UINT,
            &rb, context.queue, context.list);

#if EXPLORE_UNDEFINED_BEHAVIOR
    skip("Reading undefined value #%x.\n", get_readback_uint(&rb, 0, 0, 0));
    skip("Reading value #%x (expect 0).\n", get_readback_uint(&rb, 1, 0, 0));
    skip("Reading undefined value #%x.\n", get_readback_uint(&rb, 1024 / 4, 0, 0));
#endif
    ok(get_readback_uint(&rb, 2048 / 4, 0, 0) == 42, "Got #%x, expected 42.\n", get_readback_uint(&rb, 2048 / 4, 0, 0));
    ok(get_readback_uint(&rb, 64 * 1024 / 4, 0, 0) == 42, "Got #%x, expected 42.\n", get_readback_uint(&rb, 64 * 1024 / 4, 0, 0));
    release_resource_readback(&rb);

    ID3D12Resource_Release(buffer);
    ID3D12Resource_Release(sparse);
    ID3D12DescriptorHeap_Release(cpu);
    ID3D12DescriptorHeap_Release(gpu);
    ID3D12Heap_Release(heap_live);
    destroy_test_context(&context);
}

void test_reserved_resource_mapping(void)
{
    struct test_context context;
    D3D12_FEATURE_DATA_D3D12_OPTIONS options;
    D3D12_RESOURCE_DESC desc;
    ID3D12Resource *res;
    HRESULT hr;
    void *ptr;

    if (!init_compute_test_context(&context))
        return;

    hr = ID3D12Device_CheckFeatureSupport(context.device, D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(options));
    ok(hr == S_OK, "Failed to check feature support, hr %#x.\n", hr);

    if (!options.TiledResourcesTier)
    {
        skip("Tiled resources not supported by device.\n");
        destroy_test_context(&context);
        return;
    }


    memset(&desc, 0, sizeof(desc));
    desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
    desc.MipLevels = 1;
    desc.DepthOrArraySize = 1;
    desc.Height = 1;
    desc.Width = 64 * 1024;
    desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
    desc.SampleDesc.Count = 1;
    hr = ID3D12Device_CreateReservedResource(context.device, &desc, D3D12_RESOURCE_STATE_COMMON, NULL, &IID_ID3D12Resource, (void **)&res);
    ok(SUCCEEDED(hr), "Failed to create reserved resource, hr #%x.\n", hr);

    hr = ID3D12Resource_Map(res, 0, NULL, &ptr);
    ok(hr == E_INVALIDARG, "Unexpected return value hr #%x.\n", hr);

    ID3D12Resource_Release(res);

    destroy_test_context(&context);
}

void test_sparse_depth_stencil_rendering(void)
{
    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
    D3D12_FEATURE_DATA_D3D12_OPTIONS options;
    D3D12_FEATURE_DATA_FORMAT_SUPPORT format;
    struct test_context_desc context_desc;
    D3D12_ROOT_SIGNATURE_DESC rs_desc;
    D3D12_ROOT_PARAMETER rs_params[3];
    D3D12_RESOURCE_DESC resource_desc;
    ID3D12DescriptorHeap *dsv_heap;
    ID3D12DescriptorHeap *rtv_heap;
    ID3D12Resource *atomic_buffer;
    struct resource_readback rb;
    struct test_context context;
    D3D12_HEAP_DESC heap_desc;
    unsigned int iter, x, y;
    ID3D12Resource *ds;
    ID3D12Resource *rt;
    ID3D12Heap *heap;
    HRESULT hr;

#include "shaders/sparse/headers/ds_sparse_vs.h"
#include "shaders/sparse/headers/ds_sparse_ps.h"

    memset(&context_desc, 0, sizeof(context_desc));
    context_desc.no_pipeline = true;
    context_desc.no_root_signature = true;
    context_desc.no_render_target = true;
    if (!init_test_context(&context, &context_desc))
        return;

    if (FAILED(ID3D12Device_CheckFeatureSupport(context.device, D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(options))) ||
        options.TiledResourcesTier < D3D12_TILED_RESOURCES_TIER_2)
    {
        skip("Tiled resources TIER_2 not supported.\n");
        destroy_test_context(&context);
        return;
    }

    format.Format = DXGI_FORMAT_D32_FLOAT;
    hr = ID3D12Device_CheckFeatureSupport(context.device, D3D12_FEATURE_FORMAT_SUPPORT, &format, sizeof(format));
    ok(SUCCEEDED(hr), "Failed to query for format support.\n");

    if (!(format.Support2 & D3D12_FORMAT_SUPPORT2_TILED))
    {
        skip("Tiled is not supported for D32.\n");
        destroy_test_context(&context);
        return;
    }

    memset(&rs_desc, 0, sizeof(rs_desc));
    memset(rs_params, 0, sizeof(rs_params));
    rs_desc.NumParameters = ARRAY_SIZE(rs_params);
    rs_desc.pParameters = rs_params;

    rs_params[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;
    rs_params[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
    rs_params[0].Constants.Num32BitValues = 2;
    rs_params[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
    rs_params[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
    rs_params[1].Constants.Num32BitValues = 2;
    rs_params[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
    rs_params[2].ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV;
    rs_params[2].Descriptor.ShaderRegister = 1;

    create_root_signature(context.device, &rs_desc, &context.root_signature);

    init_pipeline_state_desc_shaders(&pso_desc, context.root_signature, DXGI_FORMAT_R8_UNORM, NULL,
        ds_sparse_vs_dxbc.pShaderBytecode, ds_sparse_vs_dxbc.BytecodeLength,
        ds_sparse_ps_dxbc.pShaderBytecode, ds_sparse_ps_dxbc.BytecodeLength);
    pso_desc.DepthStencilState.DepthEnable = TRUE;
    pso_desc.DepthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
    pso_desc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_GREATER;
    pso_desc.DSVFormat = DXGI_FORMAT_D32_FLOAT;
    pso_desc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE;
    hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc, &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
    ok(SUCCEEDED(hr), "Failed to create PSO, hr #%x.\n", hr);

    memset(&resource_desc, 0, sizeof(resource_desc));
    resource_desc.Width = 256;
    resource_desc.Height = 256;
    resource_desc.Format = DXGI_FORMAT_D32_FLOAT;
    resource_desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
    resource_desc.DepthOrArraySize = 1;
    resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
    resource_desc.Layout = D3D12_TEXTURE_LAYOUT_64KB_UNDEFINED_SWIZZLE;
    resource_desc.MipLevels = 1;
    resource_desc.SampleDesc.Count = 1;
    hr = ID3D12Device_CreateReservedResource(context.device, &resource_desc, D3D12_RESOURCE_STATE_COPY_SOURCE, NULL, &IID_ID3D12Resource, (void**)&ds);
    ok(SUCCEEDED(hr), "Failed to create reserved resource, hr #%x\n", hr);

    if (FAILED(hr))
    {
        destroy_test_context(&context);
        return;
    }

    rt = create_default_texture2d(context.device, 256, 256, 1, 1,
        DXGI_FORMAT_R8_UNORM, D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);

    atomic_buffer = create_default_buffer(context.device, 16, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

    memset(&heap_desc, 0, sizeof(heap_desc));
    heap_desc.SizeInBytes = 64 * 1024 * 4;
    heap_desc.Flags = D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES;
    heap_desc.Properties.Type = D3D12_HEAP_TYPE_DEFAULT;
    hr = ID3D12Device_CreateHeap(context.device, &heap_desc, &IID_ID3D12Heap, (void**)&heap);
    ok(SUCCEEDED(hr), "Failed to create heap, hr #%x\n", hr);

    dsv_heap = create_cpu_descriptor_heap(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_DSV, 1);
    rtv_heap = create_cpu_descriptor_heap(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_RTV, 1);
    ID3D12Device_CreateDepthStencilView(context.device, ds, NULL, get_cpu_dsv_handle(&context, dsv_heap, 0));
    ID3D12Device_CreateRenderTargetView(context.device, rt, NULL, get_cpu_dsv_handle(&context, rtv_heap, 0));

    for (iter = 0; iter < 4; iter++)
    {
        D3D12_TILED_RESOURCE_COORDINATE region_start_coordinates;
        D3D12_CPU_DESCRIPTOR_HANDLE dsv_handle;
        D3D12_CPU_DESCRIPTOR_HANDLE rt_handle;
        D3D12_TILE_REGION_SIZE region_sizes;
        D3D12_TILE_RANGE_FLAGS range_flags;
        const FLOAT clear_value[4] = { 0 };
        UINT heap_range_offsets;
        UINT range_tile_counts;
        D3D12_VIEWPORT vp;
        float params[2];
        D3D12_RECT sci;

        region_start_coordinates.Subresource = 0;
        region_start_coordinates.X = iter & 1;
        region_start_coordinates.Y = iter / 2;
        region_start_coordinates.Z = 0;

        memset(&region_sizes, 0, sizeof(region_sizes));
        region_sizes.NumTiles = 1;

        heap_range_offsets = iter;
        range_tile_counts = 1;
        range_flags = D3D12_TILE_RANGE_FLAG_NONE;

        ID3D12CommandQueue_UpdateTileMappings(context.queue, ds, 1, &region_start_coordinates, &region_sizes,
            heap, 1, &range_flags, &heap_range_offsets, &range_tile_counts, D3D12_TILE_MAPPING_FLAG_NONE);

        transition_resource_state(context.list, ds, D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_DEPTH_WRITE);
        transition_resource_state(context.list, rt, D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

        rt_handle = get_cpu_rtv_handle(&context, rtv_heap, 0);
        dsv_handle = get_cpu_dsv_handle(&context, dsv_heap, 0);

        /* https://learn.microsoft.com/en-us/windows/win32/direct3d11/rasterizer-behavior-with-non-mapped-tiles
         * It is allowed to temporarily cache writes to unmapped tiles in TIER 2 apparently.
         * Avoid this situation by forcing a full memory barrier in-between, making such caching impossible.
         * Unmapped tiles won't get cleared. */
        ID3D12GraphicsCommandList_ClearRenderTargetView(context.list, rt_handle, clear_value, 0, NULL);
        ID3D12GraphicsCommandList_ClearDepthStencilView(context.list, dsv_handle, D3D12_CLEAR_FLAG_DEPTH, 0.5f, 0, 0, NULL);
        ID3D12GraphicsCommandList_Close(context.list);
        exec_command_list(context.queue, context.list);
        ID3D12GraphicsCommandList_Reset(context.list, context.allocator, NULL);

        ID3D12GraphicsCommandList_OMSetRenderTargets(context.list, 1, &rt_handle, TRUE, &dsv_handle);

        ID3D12GraphicsCommandList_SetGraphicsRootSignature(context.list, context.root_signature);
        ID3D12GraphicsCommandList_SetPipelineState(context.list, context.pipeline_state);

        /* Unmapped pages should pass the depth test. We enable write in the PSO, but this write should be masked when passing.
         * We draw two full-screen primitives, the first with depth 0.25, the second one with 0.25 - delta.
         * If sparse isn't working as intended, we'll draw 0.25 depth, then 0.25 - delta fails, meaning we end up with 200 in RT.
         * If sparse is working as intended, the depth test will always compare against 0, so we'll end up with 150 in RT. */
        params[0] = 0.25f;
        params[1] = -0.05f;
        ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(context.list, 0, ARRAY_SIZE(params), params, 0);
        params[0] = 200.0f / 255.0f;
        params[1] = -50.0f / 255.0f;
        ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(context.list, 1, ARRAY_SIZE(params), params, 0);
        ID3D12GraphicsCommandList_SetGraphicsRootUnorderedAccessView(context.list, 2, ID3D12Resource_GetGPUVirtualAddress(atomic_buffer) + 4 * iter);

        set_viewport(&vp, 0, 0, 256, 256, 0, 1);
        set_rect(&sci, 0, 0, 256, 256);
        ID3D12GraphicsCommandList_RSSetViewports(context.list, 1, &vp);
        ID3D12GraphicsCommandList_RSSetScissorRects(context.list, 1, &sci);

        ID3D12GraphicsCommandList_IASetPrimitiveTopology(context.list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
        ID3D12GraphicsCommandList_DrawInstanced(context.list, 3, 2, 0, 0);

        transition_resource_state(context.list, rt, D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
        transition_resource_state(context.list, ds, D3D12_RESOURCE_STATE_DEPTH_WRITE, D3D12_RESOURCE_STATE_COPY_SOURCE);

        get_texture_readback_with_command_list(rt, 0, &rb, context.queue, context.list);
        reset_command_list(context.list, context.allocator);

        for (y = 0; y < 2; y++)
        {
            for (x = 0; x < 2; x++)
            {
                uint8_t value, expected;
                value = get_readback_uint8(&rb, x * 128, y * 128);

                /* For mapped pages, we cleared to 0.5, so we don't expect to see FB write. */
                expected = y * 2 + x <= iter ? 0 : 150;

                /* We work around lack of sparse support currently. */
                todo_if(is_radv_device(context.device))
                ok(value == expected, "Iter %u, tile %u, %u: expected %u, got %u.\n", iter, x, y, expected, value);
            }
        }

        release_resource_readback(&rb);
    }

    transition_resource_state(context.list, atomic_buffer, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_buffer_readback_with_command_list(atomic_buffer, DXGI_FORMAT_R32_UINT, &rb, context.queue, context.list);
    reset_command_list(context.list, context.allocator);

    /* Ensure that driver didn't try to mask draws. Pixel invocations must still happen as-is. */
    for (iter = 0; iter < 4; iter++)
    {
        uint32_t value, expected;
        value = get_readback_uint(&rb, iter, 0, 0);
        expected = 256 * 256 * 2;
        ok(value == expected, "Iter %u: expected %u, got %u.\n", iter, expected, value);
    }

    release_resource_readback(&rb);

    ID3D12Resource_Release(ds);
    ID3D12Resource_Release(rt);
    ID3D12Heap_Release(heap);
    ID3D12DescriptorHeap_Release(dsv_heap);
    ID3D12DescriptorHeap_Release(rtv_heap);
    ID3D12Resource_Release(atomic_buffer);

    destroy_test_context(&context);
}

void test_sparse_default_mapping(void)
{
#define TILE_SIZE 65536
    ID3D12Resource *tiled_buffer, *tiled_image, *feedback_buffer;
    D3D12_DESCRIPTOR_HEAP_DESC descriptor_heap_desc;
    D3D12_COMPUTE_PIPELINE_STATE_DESC pso_desc;
    D3D12_UNORDERED_ACCESS_VIEW_DESC uav_desc;
    D3D12_FEATURE_DATA_D3D12_OPTIONS options;
    D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc;
    D3D12_DESCRIPTOR_RANGE rs_descriptors[2];
    struct test_context_desc context_desc;
    D3D12_HEAP_PROPERTIES heap_properties;
    ID3D12DescriptorHeap *descriptor_heap;
    D3D12_RESOURCE_DESC resource_desc;
    D3D12_ROOT_SIGNATURE_DESC rs_desc;
    D3D12_ROOT_PARAMETER rs_args[2];
    struct resource_readback rb;
    struct test_context context;
    ID3D12PipelineState *pso;
    ID3D12RootSignature *rs;
    unsigned int i;
    HRESULT hr;

#include "shaders/sparse/headers/sparse_init_access.h"

    static const struct
    {
        uint32_t buf_fb;
        uint32_t img_fb;
    }
    expected_results[] =
    {
        { 0xffffffffu, 0x0033ffffu },
        { 0xffffffffu, 0x00010001u },
        { 0xffffffffu, 0x00010001u },
        { 0xffffffffu, 0x00010001u },
        { 0xffffffffu, 0x00010001u },
        { 0xffffffffu, 0x00000001u },
        { 0xffffffffu, 0x00000000u },
        { 0xffffffffu, 0x00000000u },
    };

    struct
    {
        uint32_t image_tile_w;
        uint32_t image_tile_h;
        uint32_t image_w;
        uint32_t image_h;
        uint32_t image_mips;
        uint32_t buffer_stride;
    }
    shader_args =
    {
        256, 256, 1024, 1024, 11, TILE_SIZE / 16
    };

    memset(&context_desc, 0, sizeof(context_desc));
    context_desc.no_pipeline = true;
    context_desc.no_root_signature = true;
    context_desc.no_render_target = true;
    if (!init_test_context(&context, &context_desc))
        return;

    if (FAILED(ID3D12Device_CheckFeatureSupport(context.device, D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(options))) ||
        options.TiledResourcesTier < D3D12_TILED_RESOURCES_TIER_2)
    {
        skip("Tiled resources TIER_2 not supported.\n");
        destroy_test_context(&context);
        return;
    }

    if (is_amd_windows_device(context.device))
    {
        /* AMD segfaults when compiling the compute shader */
        skip("Skipping test to avoid crash inside AMD driver.\n");
        destroy_test_context(&context);
        return;
    }

    memset(&heap_properties, 0, sizeof(heap_properties));
    heap_properties.Type = D3D12_HEAP_TYPE_DEFAULT;

    memset(&resource_desc, 0, sizeof(resource_desc));
    resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
    resource_desc.Width = 64u;
    resource_desc.Height = 1u;
    resource_desc.DepthOrArraySize = 1u;
    resource_desc.MipLevels = 1u;
    resource_desc.SampleDesc.Count = 1u;
    resource_desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
    resource_desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;

    hr = ID3D12Device_CreateCommittedResource(context.device, &heap_properties,
            D3D12_HEAP_FLAG_NONE, &resource_desc, D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
            NULL, &IID_ID3D12Resource, (void**)&feedback_buffer);
    ok(hr == S_OK, "Failed to create feedback buffer, hr %#x.\n", hr);

    resource_desc.Width = 256u * TILE_SIZE;
    resource_desc.Flags = D3D12_RESOURCE_FLAG_NONE;

    hr = ID3D12Device_CreateReservedResource(context.device, &resource_desc, D3D12_RESOURCE_STATE_GENERIC_READ,
            NULL, &IID_ID3D12Resource, (void**)&tiled_buffer);
    ok(hr == S_OK, "Failed to create tiled buffer, hr %#x.\n", hr);

    memset(&resource_desc, 0, sizeof(resource_desc));
    resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
    resource_desc.Format = DXGI_FORMAT_R8_UNORM;
    resource_desc.Width = 1024u;
    resource_desc.Height = 1024u;
    resource_desc.DepthOrArraySize = 1u;
    resource_desc.MipLevels = 11u;
    resource_desc.SampleDesc.Count = 1u;
    resource_desc.Layout = D3D12_TEXTURE_LAYOUT_64KB_UNDEFINED_SWIZZLE;

    hr = ID3D12Device_CreateReservedResource(context.device, &resource_desc, D3D12_RESOURCE_STATE_GENERIC_READ,
            NULL, &IID_ID3D12Resource, (void**)&tiled_image);
    ok(hr == S_OK, "Failed to create tiled image, hr %#x.\n", hr);

    memset(rs_descriptors, 0, sizeof(rs_descriptors));
    rs_descriptors[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
    rs_descriptors[0].NumDescriptors = 1u;

    rs_descriptors[1].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
    rs_descriptors[1].NumDescriptors = 2u;
    rs_descriptors[1].OffsetInDescriptorsFromTableStart = 1u;

    memset(rs_args, 0, sizeof(rs_args));
    rs_args[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
    rs_args[0].Constants.Num32BitValues = sizeof(shader_args) / sizeof(uint32_t);
    rs_args[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;

    rs_args[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    rs_args[1].DescriptorTable.NumDescriptorRanges = ARRAY_SIZE(rs_descriptors);
    rs_args[1].DescriptorTable.pDescriptorRanges = rs_descriptors;
    rs_args[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;

    memset(&rs_desc, 0, sizeof(rs_desc));
    rs_desc.NumParameters = ARRAY_SIZE(rs_args);
    rs_desc.pParameters = rs_args;

    hr = create_root_signature(context.device, &rs_desc, &rs);
    ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr);

    memset(&pso_desc, 0, sizeof(pso_desc));
    pso_desc.pRootSignature = rs;
    pso_desc.CS = sparse_init_access_dxil;

    hr = ID3D12Device_CreateComputePipelineState(context.device, &pso_desc, &IID_ID3D12PipelineState, (void**)&pso);
    ok(hr == S_OK, "Failed to create compute pipeline, hr %#x.\n", hr);

    memset(&descriptor_heap_desc, 0, sizeof(descriptor_heap_desc));
    descriptor_heap_desc.NumDescriptors = 3;
    descriptor_heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
    descriptor_heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;

    hr = ID3D12Device_CreateDescriptorHeap(context.device, &descriptor_heap_desc, &IID_ID3D12DescriptorHeap, (void**)&descriptor_heap);
    ok(hr == S_OK, "Failed to create descriptor heap, hr %#x.\n", hr);

    memset(&uav_desc, 0, sizeof(uav_desc));
    uav_desc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
    uav_desc.Buffer.NumElements = ARRAY_SIZE(expected_results);
    uav_desc.Buffer.StructureByteStride = sizeof(expected_results[0]);

    ID3D12Device_CreateUnorderedAccessView(context.device, feedback_buffer,
            NULL, &uav_desc, get_cpu_descriptor_handle(&context, descriptor_heap, 0));

    memset(&srv_desc, 0, sizeof(srv_desc));
    srv_desc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER;
    srv_desc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
    srv_desc.Buffer.NumElements = 256 * TILE_SIZE / 16;
    srv_desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;

    ID3D12Device_CreateShaderResourceView(context.device, tiled_buffer,
            &srv_desc, get_cpu_descriptor_handle(&context, descriptor_heap, 1));

    memset(&srv_desc, 0, sizeof(srv_desc));
    srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
    srv_desc.Format = DXGI_FORMAT_R8_UNORM;
    srv_desc.Texture2D.MipLevels = 11;
    srv_desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;

    ID3D12Device_CreateShaderResourceView(context.device, tiled_image,
            &srv_desc, get_cpu_descriptor_handle(&context, descriptor_heap, 2));

    ID3D12GraphicsCommandList_SetDescriptorHeaps(context.list, 1, &descriptor_heap);
    ID3D12GraphicsCommandList_SetComputeRootSignature(context.list, rs);
    ID3D12GraphicsCommandList_SetComputeRoot32BitConstants(context.list, 0, sizeof(shader_args) / sizeof(uint32_t), &shader_args, 0);
    ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(context.list, 1, get_gpu_descriptor_handle(&context, descriptor_heap, 0));
    ID3D12GraphicsCommandList_SetPipelineState(context.list, pso);
    ID3D12GraphicsCommandList_Dispatch(context.list, 1, 1, 1);

    transition_resource_state(context.list, feedback_buffer, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_buffer_readback_with_command_list(feedback_buffer, DXGI_FORMAT_R32_UINT, &rb, context.queue, context.list);

    for (i = 0; i < ARRAY_SIZE(expected_results); i++)
    {
        uint32_t buf = get_readback_uint(&rb, 2u * i, 0u, 0u);
        uint32_t img = get_readback_uint(&rb, 2u * i + 1u, 0u, 0u);

        ok(buf == expected_results[i].buf_fb, "Got %#x, expected %#x for tiled buffer at %u.\n",
                buf, expected_results[i].buf_fb, i);
        ok(img == expected_results[i].img_fb, "Got %#x, expected %#x for tiled texture at %u.\n",
                img, expected_results[i].img_fb, i);
    }

    release_resource_readback(&rb);

    ID3D12DescriptorHeap_Release(descriptor_heap);

    ID3D12PipelineState_Release(pso);
    ID3D12RootSignature_Release(rs);

    ID3D12Resource_Release(feedback_buffer);
    ID3D12Resource_Release(tiled_buffer);
    ID3D12Resource_Release(tiled_image);

    destroy_test_context(&context);
#undef TILE_SIZE
}
