/*
 * Copyright 2016-2017 Józef Kucia for CodeWeavers
 *
 * 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
 */

#ifdef _MSC_VER
/* Used for M_PI */
#define _USE_MATH_DEFINES
#endif

#include "d3d12_crosstest.h"

VKD3D_AGILITY_SDK_EXPORTS

struct test_options test_options = {0};

static PFN_D3D12_CREATE_VERSIONED_ROOT_SIGNATURE_DESERIALIZER pfn_D3D12CreateVersionedRootSignatureDeserializer;
static PFN_D3D12_SERIALIZE_VERSIONED_ROOT_SIGNATURE pfn_D3D12SerializeVersionedRootSignature;

static ID3D10Blob *compile_shader(const char *source, size_t len, const char *profile)
{
    ID3D10Blob *bytecode = NULL, *errors = NULL;
    HRESULT hr;

    hr = D3DCompile(source, len, NULL, NULL, NULL, "main", profile, 0, 0, &bytecode, &errors);
    ok(hr == S_OK, "Cannot compile shader, hr %#x.\n", hr);
    ok(!!bytecode, "Compilation didn't produce any bytecode.\n");
    if (errors)
        ID3D10Blob_Release(errors);

    return bytecode;
}

static bool compare_uint8(uint8_t a, uint8_t b, unsigned int max_diff)
{
    return abs(a - b) <= max_diff;
}

static bool compare_uint16(uint16_t a, uint16_t b, unsigned int max_diff)
{
    return abs(a - b) <= max_diff;
}

static ULONG get_refcount(void *iface)
{
    IUnknown *unk = iface;
    IUnknown_AddRef(unk);
    return IUnknown_Release(unk);
}

#define check_interface(a, b, c) check_interface_(__FILE__, __LINE__, (IUnknown *)a, b, c)
static void check_interface_(const char *file, unsigned int line, IUnknown *iface, REFIID riid, bool supported)
{
    HRESULT hr, expected_hr;
    IUnknown *unk;

    expected_hr = supported ? S_OK : E_NOINTERFACE;

    hr = IUnknown_QueryInterface(iface, riid, (void **)&unk);
    ok_(file, line)(hr == expected_hr, "Got hr %#x, expected %#x.\n", hr, expected_hr);
    if (SUCCEEDED(hr))
        IUnknown_Release(unk);
}

#define check_heap_properties(a, b) check_heap_properties_(__FILE__, __LINE__, a, b)
static void check_heap_properties_(const char *file, unsigned int line,
        const D3D12_HEAP_PROPERTIES *properties, const D3D12_HEAP_PROPERTIES *expected_properties)
{
    D3D12_HEAP_PROPERTIES expected = *expected_properties;

    if (!expected.CreationNodeMask)
        expected.CreationNodeMask = 0x1;
    if (!expected.VisibleNodeMask)
        expected.VisibleNodeMask = 0x1;

    ok_(file, line)(properties->Type == expected.Type,
            "Got type %#x, expected %#x.\n", properties->Type, expected.Type);
    ok_(file, line)(properties->CPUPageProperty == expected.CPUPageProperty,
            "Got CPU page properties %#x, expected %#x.\n",
            properties->CPUPageProperty, expected.CPUPageProperty);
    ok_(file, line)(properties->MemoryPoolPreference == expected.MemoryPoolPreference,
            "Got memory pool %#x, expected %#x.\n",
            properties->MemoryPoolPreference, expected.MemoryPoolPreference);
    ok_(file, line)(properties->CreationNodeMask == expected.CreationNodeMask,
            "Got creation node mask %#x, expected %#x.\n",
            properties->CreationNodeMask, expected.CreationNodeMask);
    ok_(file, line)(properties->VisibleNodeMask == expected.VisibleNodeMask,
            "Got visible node mask %#x, expected %#x.\n",
            properties->VisibleNodeMask, expected.VisibleNodeMask);
}

#define check_heap_desc(a, b) check_heap_desc_(__FILE__, __LINE__, a, b)
static void check_heap_desc_(const char *file, unsigned int line, const D3D12_HEAP_DESC *desc,
        const D3D12_HEAP_DESC *expected_desc)
{
    D3D12_HEAP_DESC expected = *expected_desc;

    if (!expected.Alignment)
        expected.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;

    ok_(file, line)(desc->SizeInBytes == expected.SizeInBytes,
            "Got size %"PRIu64", expected %"PRIu64".\n",
            desc->SizeInBytes, expected.SizeInBytes);
    check_heap_properties_(file, line, &desc->Properties, &expected.Properties);
    ok_(file, line)(desc->Alignment == expected.Alignment,
            "Got alignment %"PRIu64", expected %"PRIu64".\n",
            desc->Alignment, expected.Alignment);
    ok_(file, line)(desc->Flags == expected.Flags,
            "Got flags %#x, expected %#x.\n", desc->Flags, expected.Flags);
}

#define check_alignment(a, b) check_alignment_(__FILE__, __LINE__, a, b)
static void check_alignment_(const char *file, unsigned int line, uint64_t size, uint64_t alignment)
{
    uint64_t aligned_size = align(size, alignment);
    ok_(file, line)(aligned_size == size, "Got unaligned size %"PRIu64", expected %"PRIu64".\n",
            size, aligned_size);
}

static const DXGI_FORMAT depth_stencil_formats[] =
{
    DXGI_FORMAT_R32G8X24_TYPELESS,
    DXGI_FORMAT_D32_FLOAT_S8X24_UINT,
    DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS,
    DXGI_FORMAT_X32_TYPELESS_G8X24_UINT,
    DXGI_FORMAT_R32_TYPELESS,
    DXGI_FORMAT_D32_FLOAT,
    DXGI_FORMAT_R24G8_TYPELESS,
    DXGI_FORMAT_D24_UNORM_S8_UINT,
    DXGI_FORMAT_R24_UNORM_X8_TYPELESS,
    DXGI_FORMAT_X24_TYPELESS_G8_UINT,
    DXGI_FORMAT_R16_TYPELESS,
    DXGI_FORMAT_D16_UNORM,
};

static void init_readback(struct d3d12_resource_readback *rb, ID3D12Resource *buffer,
        uint64_t buffer_size, uint64_t width, uint64_t height, unsigned int depth, uint64_t row_pitch)
{
    D3D12_RANGE read_range;
    HRESULT hr;

    rb->rb.width = width;
    rb->rb.height = height;
    rb->rb.depth = depth;
    rb->resource = buffer;
    rb->rb.row_pitch = row_pitch;
    rb->rb.data = NULL;

    ID3D12Resource_AddRef(rb->resource);

    read_range.Begin = 0;
    read_range.End = buffer_size;
    hr = ID3D12Resource_Map(rb->resource, 0, &read_range, &rb->rb.data);
    ok(hr == S_OK, "Failed to map readback buffer, hr %#x.\n", hr);
}

static void get_buffer_readback_with_command_list(ID3D12Resource *buffer, DXGI_FORMAT format,
        struct d3d12_resource_readback *rb, ID3D12CommandQueue *queue, ID3D12GraphicsCommandList *command_list)
{
    D3D12_HEAP_PROPERTIES heap_properties;
    D3D12_RESOURCE_DESC resource_desc;
    ID3D12Resource *rb_buffer;
    D3D12_RANGE read_range;
    ID3D12Device *device;
    HRESULT hr;

    hr = ID3D12Resource_GetDevice(buffer, &IID_ID3D12Device, (void **)&device);
    ok(SUCCEEDED(hr), "Failed to get device, hr %#x.\n", hr);

    resource_desc = ID3D12Resource_GetDesc(buffer);
    assert(resource_desc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER);
    resource_desc.Flags = D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE;

    hr = ID3D12Resource_GetHeapProperties(buffer, &heap_properties, NULL);
    ok(SUCCEEDED(hr), "Failed to get heap properties.\n");
    if (heap_properties.Type == D3D12_HEAP_TYPE_READBACK)
    {
        rb_buffer = buffer;
        ID3D12Resource_AddRef(rb_buffer);
    }
    else
    {
        rb_buffer = create_readback_buffer(device, resource_desc.Width);
        ID3D12GraphicsCommandList_CopyBufferRegion(command_list, rb_buffer, 0,
                buffer, 0, resource_desc.Width);
    }

    hr = ID3D12GraphicsCommandList_Close(command_list);
    ok(SUCCEEDED(hr), "Failed to close command list, hr %#x.\n", hr);

    exec_command_list(queue, command_list);
    wait_queue_idle(device, queue);
    ID3D12Device_Release(device);

    rb->rb.width = resource_desc.Width / format_size(format);
    rb->rb.height = 1;
    rb->rb.depth = 1;
    rb->resource = rb_buffer;
    rb->rb.row_pitch = resource_desc.Width;
    rb->rb.data = NULL;

    read_range.Begin = 0;
    read_range.End = resource_desc.Width;
    hr = ID3D12Resource_Map(rb_buffer, 0, &read_range, &rb->rb.data);
    ok(SUCCEEDED(hr), "Failed to map readback buffer, hr %#x.\n", hr);
}

static uint8_t get_readback_uint8(struct resource_readback *rb, unsigned int x, unsigned int y)
{
    return *(uint8_t *)get_readback_data(rb, x, y, 0, sizeof(uint8_t));
}

static uint16_t get_readback_uint16(struct resource_readback *rb, unsigned int x, unsigned int y)
{
    return *(uint16_t *)get_readback_data(rb, x, y, 0, sizeof(uint16_t));
}

#define check_sub_resource_float(a, b, c, d, e, f) check_sub_resource_float_(__FILE__, __LINE__, a, b, c, d, e, f)
static void check_sub_resource_float_(const char *file, unsigned int line, ID3D12Resource *resource,
        unsigned int sub_resource_idx, ID3D12CommandQueue *queue, ID3D12GraphicsCommandList *command_list,
        float expected, unsigned int max_diff)
{
    struct d3d12_resource_readback rb;

    get_resource_readback_with_command_list(resource, sub_resource_idx, &rb, queue, command_list);
    check_readback_data_float_(file, line, &rb.rb, NULL, expected, max_diff);
    release_resource_readback(&rb);
}

#define check_readback_data_uint8(a, b, c, d) check_readback_data_uint8_(__FILE__, __LINE__, a, b, c, d)
static void check_readback_data_uint8_(const char *file, unsigned int line, struct resource_readback *rb,
        const RECT *rect, uint8_t expected, unsigned int max_diff)
{
    RECT r = {0, 0, rb->width, rb->height};
    unsigned int x = 0, y;
    bool all_match = true;
    uint8_t got = 0;

    if (rect)
        r = *rect;

    for (y = r.top; y < r.bottom; ++y)
    {
        for (x = r.left; x < r.right; ++x)
        {
            got = get_readback_uint8(rb, x, y);
            if (!compare_uint8(got, expected, max_diff))
            {
                all_match = false;
                break;
            }
        }
        if (!all_match)
            break;
    }
    ok_(file, line)(all_match, "Got 0x%02x, expected 0x%02x at (%u, %u).\n", got, expected, x, y);
}

#define check_sub_resource_uint8(a, b, c, d, e, f) check_sub_resource_uint8_(__FILE__, __LINE__, a, b, c, d, e, f)
static void check_sub_resource_uint8_(const char *file, unsigned int line, ID3D12Resource *resource,
        unsigned int sub_resource_idx, ID3D12CommandQueue *queue, ID3D12GraphicsCommandList *command_list,
        uint8_t expected, unsigned int max_diff)
{
    struct d3d12_resource_readback rb;

    get_resource_readback_with_command_list(resource, sub_resource_idx, &rb, queue, command_list);
    check_readback_data_uint8_(file, line, &rb.rb, NULL, expected, max_diff);
    release_resource_readback(&rb);
}

#define check_readback_data_uint16(a, b, c, d) check_readback_data_uint16_(__FILE__, __LINE__, a, b, c, d)
static void check_readback_data_uint16_(const char *file, unsigned int line, struct resource_readback *rb,
        const RECT *rect, uint16_t expected, unsigned int max_diff)
{
    RECT r = {0, 0, rb->width, rb->height};
    unsigned int x = 0, y;
    bool all_match = true;
    uint16_t got = 0;

    if (rect)
        r = *rect;

    for (y = r.top; y < r.bottom; ++y)
    {
        for (x = r.left; x < r.right; ++x)
        {
            got = get_readback_uint16(rb, x, y);
            if (!compare_uint16(got, expected, max_diff))
            {
                all_match = false;
                break;
            }
        }
        if (!all_match)
            break;
    }
    ok_(file, line)(all_match, "Got 0x%04x, expected 0x%04x at (%u, %u).\n", got, expected, x, y);
}

#define check_sub_resource_uint16(a, b, c, d, e, f) check_sub_resource_uint16_(__FILE__, __LINE__, a, b, c, d, e, f)
static void check_sub_resource_uint16_(const char *file, unsigned int line, ID3D12Resource *resource,
        unsigned int sub_resource_idx, ID3D12CommandQueue *queue, ID3D12GraphicsCommandList *command_list,
        uint16_t expected, unsigned int max_diff)
{
    struct d3d12_resource_readback rb;

    get_resource_readback_with_command_list(resource, sub_resource_idx, &rb, queue, command_list);
    check_readback_data_uint16_(file, line, &rb.rb, NULL, expected, max_diff);
    release_resource_readback(&rb);
}

#define check_sub_resource_uint64(a, b, c, d, e, f) check_sub_resource_uint64_(__FILE__, __LINE__, a, b, c, d, e, f)
static void check_sub_resource_uint64_(const char *file, unsigned int line, ID3D12Resource *resource,
        unsigned int sub_resource_idx, ID3D12CommandQueue *queue, ID3D12GraphicsCommandList *command_list,
        uint64_t expected, unsigned int max_diff)
{
    struct d3d12_resource_readback rb;

    get_resource_readback_with_command_list(resource, sub_resource_idx, &rb, queue, command_list);
    check_readback_data_uint64_(file, line, &rb.rb, NULL, expected, max_diff);
    release_resource_readback(&rb);
}

#define check_sub_resource_uvec4(a, b, c, d, e) check_sub_resource_uvec4_(__FILE__, __LINE__, a, b, c, d, e)
static void check_sub_resource_uvec4_(const char *file, unsigned int line, ID3D12Resource *resource,
        unsigned int sub_resource_idx, ID3D12CommandQueue *queue, ID3D12GraphicsCommandList *command_list,
        const struct uvec4 *expected_value)
{
    struct d3d12_resource_readback rb;
    struct uvec4 value = {0};
    unsigned int x = 0, y;
    bool all_match = true;

    get_resource_readback_with_command_list(resource, sub_resource_idx, &rb, queue, command_list);
    for (y = 0; y < rb.rb.height; ++y)
    {
        for (x = 0; x < rb.rb.width; ++x)
        {
            value = *get_readback_uvec4(&rb.rb, x, y);
            if (!compare_uvec4(&value, expected_value))
            {
                all_match = false;
                break;
            }
        }
        if (!all_match)
            break;
    }
    release_resource_readback(&rb);

    ok_(file, line)(all_match,
            "Got {0x%08x, 0x%08x, 0x%08x, 0x%08x}, expected {0x%08x, 0x%08x, 0x%08x, 0x%08x} at (%u, %u).\n",
            value.x, value.y, value.z, value.w,
            expected_value->x, expected_value->y, expected_value->z, expected_value->w, x, y);
}

#define check_buffer_uint(a, b, c, d, e) check_buffer_uint_(__FILE__, __LINE__, a, b, c, d, e)
static void check_buffer_uint_(const char *file, unsigned int line, ID3D12Resource *buffer,
        ID3D12CommandQueue *queue, ID3D12GraphicsCommandList *command_list,
        unsigned int expected, unsigned int max_diff)
{
    struct d3d12_resource_readback rb;

    get_buffer_readback_with_command_list(buffer, DXGI_FORMAT_R32_UINT, &rb, queue, command_list);
    check_readback_data_uint_(file, line, &rb.rb, NULL, expected, max_diff);
    release_resource_readback(&rb);
}

static bool broken_on_warp(bool condition)
{
    return broken(test_options.use_warp_device && condition);
}

static bool is_min_max_filtering_supported(ID3D12Device *device)
{
    D3D12_FEATURE_DATA_D3D12_OPTIONS options;
    HRESULT hr;

    if (FAILED(hr = ID3D12Device_CheckFeatureSupport(device,
            D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(options))))
    {
        trace("Failed to check feature support, hr %#x.\n", hr);
        return false;
    }

    /* D3D12 validation layer says tiled resource tier 2+ support implies min/max filtering support. */
    return options.TiledResourcesTier >= D3D12_TILED_RESOURCES_TIER_2;
}

static D3D12_TILED_RESOURCES_TIER get_tiled_resources_tier(ID3D12Device *device)
{
    D3D12_FEATURE_DATA_D3D12_OPTIONS options;
    HRESULT hr;

    if (FAILED(hr = ID3D12Device_CheckFeatureSupport(device,
            D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(options))))
    {
        trace("Failed to check feature support, hr %#x.\n", hr);
        return D3D12_TILED_RESOURCES_TIER_NOT_SUPPORTED;
    }

    return options.TiledResourcesTier;
}

static bool is_standard_swizzle_64kb_supported(ID3D12Device *device)
{
    D3D12_FEATURE_DATA_D3D12_OPTIONS options;
    HRESULT hr;

    if (FAILED(hr = ID3D12Device_CheckFeatureSupport(device,
            D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(options))))
    {
        trace("Failed to check feature support, hr %#x.\n", hr);
        return false;
    }

    return options.StandardSwizzle64KBSupported;
}

static bool is_memory_pool_L1_supported(ID3D12Device *device)
{
    D3D12_FEATURE_DATA_ARCHITECTURE architecture;
    HRESULT hr;

    memset(&architecture, 0, sizeof(architecture));
    if (FAILED(hr = ID3D12Device_CheckFeatureSupport(device, D3D12_FEATURE_ARCHITECTURE,
            &architecture, sizeof(architecture))))
    {
        trace("Failed to check feature support, hr %#x.\n", hr);
        return false;
    }

    return !architecture.UMA;
}

static bool is_gpu_upload_heap_supported(ID3D12Device *device)
{
    D3D12_FEATURE_DATA_D3D12_OPTIONS16 options;
    HRESULT hr;

    memset(&options, 0, sizeof(options));
    if (FAILED(hr = ID3D12Device_CheckFeatureSupport(device,
            D3D12_FEATURE_D3D12_OPTIONS16, &options, sizeof(options))))
    {
        trace("Failed to check feature support, hr %#x.\n", hr);
        return false;
    }

    return options.GPUUploadHeapSupported;
}

static bool is_shader_float64_supported(ID3D12Device *device)
{
    D3D12_FEATURE_DATA_D3D12_OPTIONS options;
    HRESULT hr;

    if (FAILED(hr = ID3D12Device_CheckFeatureSupport(device,
            D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(options))))
    {
        trace("Failed to check feature support, hr %#x.\n", hr);
        return false;
    }

    return options.DoublePrecisionFloatShaderOps;
}

static D3D12_RESOURCE_BINDING_TIER get_resource_binding_tier(ID3D12Device *device)
{
    D3D12_FEATURE_DATA_D3D12_OPTIONS options;
    HRESULT hr;

    if (FAILED(hr = ID3D12Device_CheckFeatureSupport(device,
            D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(options))))
    {
        trace("Failed to check feature support, hr %#x.\n", hr);
        return D3D12_RESOURCE_BINDING_TIER_1;
    }

    return options.ResourceBindingTier;
}

static bool is_output_merger_logic_op_supported(ID3D12Device *device)
{
    D3D12_FEATURE_DATA_D3D12_OPTIONS options;
    HRESULT hr;

    if (FAILED(hr = ID3D12Device_CheckFeatureSupport(device,
            D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(options))))
    {
        trace("Failed to check feature support, hr %#x.\n", hr);
        return false;
    }

    return options.OutputMergerLogicOp;
}

static bool is_stencil_ref_export_supported(ID3D12Device *device)
{
    D3D12_FEATURE_DATA_D3D12_OPTIONS options;
    HRESULT hr;

    if (FAILED(hr = ID3D12Device_CheckFeatureSupport(device,
            D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(options))))
    {
        trace("Failed to check feature support, hr %#x.\n", hr);
        return false;
    }

    return options.PSSpecifiedStencilRefSupported;
}

static bool are_typed_uav_load_additional_formats_supported(ID3D12Device *device)
{
    D3D12_FEATURE_DATA_D3D12_OPTIONS options;
    HRESULT hr;

    if (FAILED(hr = ID3D12Device_CheckFeatureSupport(device,
            D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(options))))
    {
        trace("Failed to check feature support, hr %#x.\n", hr);
        return false;
    }

    return options.TypedUAVLoadAdditionalFormats;
}

static bool is_vs_array_index_supported(ID3D12Device *device)
{
    D3D12_FEATURE_DATA_D3D12_OPTIONS options;
    HRESULT hr;

    if (FAILED(hr = ID3D12Device_CheckFeatureSupport(device,
            D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(options))))
    {
        trace("Failed to check feature support, hr %#x.\n", hr);
        return false;
    }

    return options.VPAndRTArrayIndexFromAnyShaderFeedingRasterizerSupportedWithoutGSEmulation;
}

static bool is_typed_uav_format_supported(ID3D12Device *device, DXGI_FORMAT format)
{
    D3D12_FEATURE_DATA_FORMAT_SUPPORT format_support = {0};
    HRESULT hr;

    format_support.Format = format;
    hr = ID3D12Device_CheckFeatureSupport(device, D3D12_FEATURE_FORMAT_SUPPORT,
            &format_support, sizeof(format_support));
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);

    return format_support.Support1 & D3D12_FORMAT_SUPPORT1_TYPED_UNORDERED_ACCESS_VIEW;
}

static bool is_ds_format_supported(ID3D12Device *device, DXGI_FORMAT format)
{
    D3D12_FEATURE_DATA_FORMAT_SUPPORT format_support = {0};
    HRESULT hr;

    format_support.Format = format;
    hr = ID3D12Device_CheckFeatureSupport(device, D3D12_FEATURE_FORMAT_SUPPORT,
            &format_support, sizeof(format_support));
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);

    return format_support.Support1 & D3D12_FORMAT_SUPPORT1_DEPTH_STENCIL;
}

static bool are_unaligned_block_textures_supported(ID3D12Device *device)
{
    D3D12_FEATURE_DATA_D3D12_OPTIONS8 options;
    HRESULT hr;

    if (FAILED(hr = ID3D12Device_CheckFeatureSupport(device,
            D3D12_FEATURE_D3D12_OPTIONS8, &options, sizeof(options))))
    {
        /* Requires Windows 11 build 10.0.22000.194. */
        return false;
    }

    return options.UnalignedBlockTexturesSupported;
}

#define create_cb_root_signature(a, b, c, e) create_cb_root_signature_(__FILE__, __LINE__, a, b, c, e)
static ID3D12RootSignature *create_cb_root_signature_(const char *file, unsigned int line,
        ID3D12Device *device, unsigned int reg_idx, D3D12_SHADER_VISIBILITY shader_visibility,
        D3D12_ROOT_SIGNATURE_FLAGS flags)
{
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    ID3D12RootSignature *root_signature = NULL;
    D3D12_ROOT_PARAMETER root_parameter;
    HRESULT hr;

    root_parameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
    root_parameter.Descriptor.ShaderRegister = reg_idx;
    root_parameter.Descriptor.RegisterSpace = 0;
    root_parameter.ShaderVisibility = shader_visibility;

    memset(&root_signature_desc, 0, sizeof(root_signature_desc));
    root_signature_desc.NumParameters = 1;
    root_signature_desc.pParameters = &root_parameter;
    root_signature_desc.Flags = flags;
    hr = create_root_signature(device, &root_signature_desc, &root_signature);
    ok_(file, line)(SUCCEEDED(hr), "Failed to create root signature, hr %#x.\n", hr);

    return root_signature;
}

#define create_texture_root_signature(a, b, c, d) create_texture_root_signature_(__FILE__, __LINE__, a, b, c, d, NULL)
static ID3D12RootSignature *create_texture_root_signature_(const char *file, unsigned int line,
        ID3D12Device *device, D3D12_SHADER_VISIBILITY shader_visibility,
        unsigned int constant_count, D3D12_ROOT_SIGNATURE_FLAGS flags,
        const D3D12_STATIC_SAMPLER_DESC *sampler_desc)
{
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    ID3D12RootSignature *root_signature = NULL;
    D3D12_STATIC_SAMPLER_DESC static_sampler;
    D3D12_DESCRIPTOR_RANGE descriptor_range;
    D3D12_ROOT_PARAMETER root_parameters[2];
    HRESULT hr;

    if (sampler_desc)
    {
        static_sampler = *sampler_desc;
    }
    else
    {
        memset(&static_sampler, 0, sizeof(static_sampler));
        static_sampler.Filter = D3D12_FILTER_MIN_MAG_MIP_POINT;
        static_sampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
        static_sampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
        static_sampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
        static_sampler.MaxLOD = D3D12_FLOAT32_MAX;
        static_sampler.ShaderRegister = 0;
        static_sampler.RegisterSpace = 0;
        static_sampler.ShaderVisibility = shader_visibility;
    }

    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 = shader_visibility;

    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 = constant_count;
    root_parameters[1].ShaderVisibility = shader_visibility;

    memset(&root_signature_desc, 0, sizeof(root_signature_desc));
    root_signature_desc.NumParameters = constant_count ? 2 : 1;
    root_signature_desc.pParameters = root_parameters;
    root_signature_desc.NumStaticSamplers = 1;
    root_signature_desc.pStaticSamplers = &static_sampler;
    root_signature_desc.Flags = flags;

    hr = create_root_signature(device, &root_signature_desc, &root_signature);
    ok_(file, line)(SUCCEEDED(hr), "Failed to create root signature, hr %#x.\n", hr);

    return root_signature;
}

#define create_command_signature(a, b) create_command_signature_(__FILE__, __LINE__, a, b)
static ID3D12CommandSignature *create_command_signature_(const char *file, unsigned int line,
        ID3D12Device *device, D3D12_INDIRECT_ARGUMENT_TYPE argument_type)
{
    D3D12_COMMAND_SIGNATURE_DESC signature_desc;
    D3D12_INDIRECT_ARGUMENT_DESC argument_desc;
    ID3D12CommandSignature *command_signature;
    HRESULT hr;

    argument_desc.Type = argument_type;

    switch (argument_type)
    {
        case D3D12_INDIRECT_ARGUMENT_TYPE_DRAW:
            signature_desc.ByteStride = sizeof(D3D12_DRAW_ARGUMENTS);
            break;
        case D3D12_INDIRECT_ARGUMENT_TYPE_DRAW_INDEXED:
            signature_desc.ByteStride = sizeof(D3D12_DRAW_INDEXED_ARGUMENTS);
            break;
        case D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH:
            signature_desc.ByteStride = sizeof(D3D12_DISPATCH_ARGUMENTS);
            break;
        default:
            return NULL;
    }

    signature_desc.NumArgumentDescs = 1;
    signature_desc.pArgumentDescs = &argument_desc;
    signature_desc.NodeMask = 0;
    hr = ID3D12Device_CreateCommandSignature(device, &signature_desc,
            NULL, &IID_ID3D12CommandSignature, (void **)&command_signature);
    ok_(file, line)(hr == S_OK, "Failed to create command signature, hr %#x.\n", hr);

    return command_signature;
}

struct depth_stencil_resource
{
    ID3D12Resource *texture;
    ID3D12DescriptorHeap *heap;
    D3D12_CPU_DESCRIPTOR_HANDLE dsv_handle;
};

#define init_depth_stencil(a, b, c, d, e, f, g, h, i) init_depth_stencil_(__FILE__, __LINE__, a, b, c, d, e, f, g, h, i)
static void init_depth_stencil_(const char *file, unsigned int line, struct depth_stencil_resource *ds,
        ID3D12Device *device, unsigned int width, unsigned int height, unsigned int array_size,
        unsigned int level_count, DXGI_FORMAT format, DXGI_FORMAT view_format, const D3D12_CLEAR_VALUE *clear_value)
{
    D3D12_DEPTH_STENCIL_VIEW_DESC dsv_desc, *view_desc;
    D3D12_HEAP_PROPERTIES heap_properties;
    D3D12_RESOURCE_DESC resource_desc;
    HRESULT hr;

    memset(ds, 0, sizeof(*ds));

    ds->heap = create_cpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_DSV, 1);

    memset(&heap_properties, 0, sizeof(heap_properties));
    heap_properties.Type = D3D12_HEAP_TYPE_DEFAULT;
    resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
    resource_desc.Alignment = 0;
    resource_desc.Width = width;
    resource_desc.Height = height;
    resource_desc.DepthOrArraySize = array_size;
    resource_desc.MipLevels = level_count;
    resource_desc.Format = format;
    resource_desc.SampleDesc.Count = 1;
    resource_desc.SampleDesc.Quality = 0;
    resource_desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
    resource_desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
    hr = ID3D12Device_CreateCommittedResource(device, &heap_properties, D3D12_HEAP_FLAG_NONE,
            &resource_desc, D3D12_RESOURCE_STATE_DEPTH_WRITE, clear_value,
            &IID_ID3D12Resource, (void **)&ds->texture);
    ok_(file, line)(SUCCEEDED(hr), "Failed to create texture, hr %#x.\n", hr);

    view_desc = NULL;
    if (view_format)
    {
        memset(&dsv_desc, 0, sizeof(dsv_desc));
        dsv_desc.Format = view_format;
        dsv_desc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
        view_desc = &dsv_desc;
    }
    ds->dsv_handle = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(ds->heap);
    ID3D12Device_CreateDepthStencilView(device, ds->texture, view_desc, ds->dsv_handle);
}

#define destroy_depth_stencil(depth_stencil) destroy_depth_stencil_(__FILE__, __LINE__, depth_stencil)
static void destroy_depth_stencil_(const char *file, unsigned int line, struct depth_stencil_resource *ds)
{
    ID3D12DescriptorHeap_Release(ds->heap);
    ID3D12Resource_Release(ds->texture);
}

static void test_create_device(void)
{
    ID3D12Device *device;
    ULONG refcount;
    HRESULT hr;

    if (!(device = create_device()))
    {
        skip("Failed to create device.\n");
        return;
    }

    check_interface(device, &IID_ID3D12Object, true);
    check_interface(device, &IID_ID3D12DeviceChild, false);
    check_interface(device, &IID_ID3D12Pageable, false);
    check_interface(device, &IID_ID3D12Device, true);

    refcount = ID3D12Device_Release(device);
    ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount);

    hr = D3D12CreateDevice(NULL, D3D_FEATURE_LEVEL_11_0, &IID_ID3D12Device, (void **)&device);
    ok(hr == S_OK, "Failed to create device, hr %#x.\n", hr);
    ID3D12Device_Release(device);

    hr = D3D12CreateDevice(NULL, D3D_FEATURE_LEVEL_11_0, &IID_ID3D12Device, NULL);
    ok(hr == S_FALSE, "Got unexpected hr %#x.\n", hr);
    hr = D3D12CreateDevice(NULL, D3D_FEATURE_LEVEL_11_0, NULL, NULL);
    ok(hr == S_FALSE, "Got unexpected hr %#x.\n", hr);
    hr = D3D12CreateDevice(NULL, D3D_FEATURE_LEVEL_11_0, &IID_ID3D12DeviceChild, NULL);
    ok(hr == S_FALSE, "Got unexpected hr %#x.\n", hr);

    hr = D3D12CreateDevice(NULL, D3D_FEATURE_LEVEL_9_1, &IID_ID3D12Device, (void **)&device);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    hr = D3D12CreateDevice(NULL, D3D_FEATURE_LEVEL_9_2, &IID_ID3D12Device, (void **)&device);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    hr = D3D12CreateDevice(NULL, D3D_FEATURE_LEVEL_9_3, &IID_ID3D12Device, (void **)&device);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    hr = D3D12CreateDevice(NULL, D3D_FEATURE_LEVEL_10_0, &IID_ID3D12Device, (void **)&device);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    hr = D3D12CreateDevice(NULL, D3D_FEATURE_LEVEL_10_1, &IID_ID3D12Device, (void **)&device);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    hr = D3D12CreateDevice(NULL, 0, &IID_ID3D12Device, (void **)&device);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    hr = D3D12CreateDevice(NULL, ~0u, &IID_ID3D12Device, (void **)&device);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
}

static void test_node_count(void)
{
    ID3D12Device *device;
    UINT node_count;
    ULONG refcount;

    if (!(device = create_device()))
    {
        skip("Failed to create device.\n");
        return;
    }

    node_count = ID3D12Device_GetNodeCount(device);
    trace("Node count: %u.\n", node_count);
    ok(1 <= node_count && node_count <= 32, "Got unexpected node count %u.\n", node_count);

    refcount = ID3D12Device_Release(device);
    ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount);
}

static void test_check_feature_support(void)
{
    D3D12_FEATURE_DATA_GPU_VIRTUAL_ADDRESS_SUPPORT gpu_virtual_address;
    D3D12_FEATURE_DATA_FEATURE_LEVELS feature_levels;
    D3D12_FEATURE_DATA_ROOT_SIGNATURE root_signature;
    D3D_FEATURE_LEVEL max_supported_feature_level;
    D3D12_FEATURE_DATA_ARCHITECTURE architecture;
    D3D12_FEATURE_DATA_SHADER_MODEL shader_model;
    D3D12_FEATURE_DATA_FORMAT_INFO format_info;
    unsigned int expected_plane_count;
    ID3D12Device *device;
    D3D_SHADER_MODEL sm;
    DXGI_FORMAT format;
    ULONG refcount;
    bool is_todo;
    HRESULT hr;

    static const D3D_FEATURE_LEVEL all_feature_levels[] =
    {
        D3D_FEATURE_LEVEL_12_1,
        D3D_FEATURE_LEVEL_12_0,
        D3D_FEATURE_LEVEL_11_1,
        D3D_FEATURE_LEVEL_11_0,
        D3D_FEATURE_LEVEL_10_1,
        D3D_FEATURE_LEVEL_10_0,
        D3D_FEATURE_LEVEL_9_3,
        D3D_FEATURE_LEVEL_9_2,
        D3D_FEATURE_LEVEL_9_1,
    };
    static const D3D_FEATURE_LEVEL d3d12_feature_levels[] =
    {
        D3D_FEATURE_LEVEL_12_1,
        D3D_FEATURE_LEVEL_12_0,
        D3D_FEATURE_LEVEL_11_1,
        D3D_FEATURE_LEVEL_11_0,
    };
    static const D3D_FEATURE_LEVEL d3d_9_x_feature_levels[] =
    {
        D3D_FEATURE_LEVEL_9_3,
        D3D_FEATURE_LEVEL_9_2,
        D3D_FEATURE_LEVEL_9_1,
    };
    static const D3D_FEATURE_LEVEL invalid_feature_levels[] =
    {
        0x0000,
        0x3000,
    };

    if (!(device = create_device()))
    {
        skip("Failed to create device.\n");
        return;
    }

    /* Architecture. */
    memset(&architecture, 0, sizeof(architecture));
    hr = ID3D12Device_CheckFeatureSupport(device, D3D12_FEATURE_ARCHITECTURE,
            &architecture, sizeof(architecture));
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    ok(!architecture.NodeIndex, "Got unexpected node %u.\n", architecture.NodeIndex);
    ok(!architecture.CacheCoherentUMA || architecture.UMA,
            "Got unexpected cache coherent UMA %#x (UMA %#x).\n",
            architecture.CacheCoherentUMA, architecture.UMA);
    trace("UMA %#x, cache coherent UMA %#x, tile based renderer %#x.\n",
            architecture.UMA, architecture.CacheCoherentUMA, architecture.TileBasedRenderer);

    if (ID3D12Device_GetNodeCount(device) == 1)
    {
        memset(&architecture, 0, sizeof(architecture));
        architecture.NodeIndex = 1;
        hr = ID3D12Device_CheckFeatureSupport(device, D3D12_FEATURE_ARCHITECTURE,
                &architecture, sizeof(architecture));
        ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    }

    /* Feature levels */
    memset(&feature_levels, 0, sizeof(feature_levels));
    hr = ID3D12Device_CheckFeatureSupport(device, D3D12_FEATURE_FEATURE_LEVELS,
            &feature_levels, sizeof(feature_levels));
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    feature_levels.NumFeatureLevels = ARRAY_SIZE(all_feature_levels);
    feature_levels.pFeatureLevelsRequested = all_feature_levels;
    feature_levels.MaxSupportedFeatureLevel = 0;
    hr = ID3D12Device_CheckFeatureSupport(device, D3D12_FEATURE_FEATURE_LEVELS,
            &feature_levels, sizeof(feature_levels));
    ok(hr == S_OK, "Failed to check feature support, hr %#x.\n", hr);
    trace("Max supported feature level %#x.\n", feature_levels.MaxSupportedFeatureLevel);
    max_supported_feature_level = feature_levels.MaxSupportedFeatureLevel;

    feature_levels.NumFeatureLevels = ARRAY_SIZE(d3d12_feature_levels);
    feature_levels.pFeatureLevelsRequested = d3d12_feature_levels;
    feature_levels.MaxSupportedFeatureLevel = 0;
    hr = ID3D12Device_CheckFeatureSupport(device, D3D12_FEATURE_FEATURE_LEVELS,
            &feature_levels, sizeof(feature_levels));
    ok(hr == S_OK, "Failed to check feature support, hr %#x.\n", hr);
    ok(feature_levels.MaxSupportedFeatureLevel == max_supported_feature_level,
            "Got unexpected feature level %#x, expected %#x.\n",
            feature_levels.MaxSupportedFeatureLevel, max_supported_feature_level);

    /* Check invalid size. */
    hr = ID3D12Device_CheckFeatureSupport(device, D3D12_FEATURE_FEATURE_LEVELS,
            &feature_levels, sizeof(feature_levels) + 1);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    hr = ID3D12Device_CheckFeatureSupport(device, D3D12_FEATURE_FEATURE_LEVELS,
            &feature_levels, sizeof(feature_levels) - 1);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    feature_levels.NumFeatureLevels = ARRAY_SIZE(d3d_9_x_feature_levels);
    feature_levels.pFeatureLevelsRequested = d3d_9_x_feature_levels;
    feature_levels.MaxSupportedFeatureLevel = 0;
    hr = ID3D12Device_CheckFeatureSupport(device, D3D12_FEATURE_FEATURE_LEVELS,
            &feature_levels, sizeof(feature_levels));
    ok(hr == S_OK, "Failed to check feature support, hr %#x.\n", hr);
    ok(feature_levels.MaxSupportedFeatureLevel == D3D_FEATURE_LEVEL_9_3,
            "Got unexpected max feature level %#x.\n", feature_levels.MaxSupportedFeatureLevel);

    feature_levels.NumFeatureLevels = ARRAY_SIZE(invalid_feature_levels);
    feature_levels.pFeatureLevelsRequested = invalid_feature_levels;
    feature_levels.MaxSupportedFeatureLevel = 0;
    hr = ID3D12Device_CheckFeatureSupport(device, D3D12_FEATURE_FEATURE_LEVELS,
            &feature_levels, sizeof(feature_levels));
    ok(hr == S_OK, "Failed to check feature support, hr %#x.\n", hr);
    ok(feature_levels.MaxSupportedFeatureLevel == 0x3000,
            "Got unexpected max feature level %#x.\n", feature_levels.MaxSupportedFeatureLevel);

    /* Format info. */
    memset(&format_info, 0, sizeof(format_info));
    hr = ID3D12Device_CheckFeatureSupport(device, D3D12_FEATURE_FORMAT_INFO,
            &format_info, sizeof(format_info));
    ok(hr == S_OK, "Failed to get format info, hr %#x.\n", hr);
    ok(format_info.Format == DXGI_FORMAT_UNKNOWN, "Got unexpected format %#x.\n", format_info.Format);
    ok(format_info.PlaneCount == 1, "Got unexpected plane count %u.\n", format_info.PlaneCount);

    for (format = DXGI_FORMAT_UNKNOWN; format <= DXGI_FORMAT_B4G4R4A4_UNORM; ++format)
    {
        vkd3d_test_push_context("format %#x", format);

        expected_plane_count = format_plane_count(format);

        is_todo = format == DXGI_FORMAT_R9G9B9E5_SHAREDEXP
                || format == DXGI_FORMAT_R8G8_B8G8_UNORM
                || format == DXGI_FORMAT_G8R8_G8B8_UNORM
                || format == DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM
                || (DXGI_FORMAT_AYUV <= format && format <= DXGI_FORMAT_A8P8);

        memset(&format_info, 0, sizeof(format_info));
        format_info.Format = format;
        hr = ID3D12Device_CheckFeatureSupport(device, D3D12_FEATURE_FORMAT_INFO,
                &format_info, sizeof(format_info));

        if (format == DXGI_FORMAT_R1_UNORM)
        {
            ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
            vkd3d_test_pop_context();
            continue;
        }

        todo_if(is_todo)
        ok(hr == S_OK, "Failed to get format info, hr %#x.\n", hr);
        ok(format_info.Format == format, "Got unexpected format %#x.\n", format_info.Format);
        todo_if(is_todo)
        ok(format_info.PlaneCount == expected_plane_count,
                "Got plane count %u, expected %u.\n", format_info.PlaneCount, expected_plane_count);

        vkd3d_test_pop_context();
    }

    /* GPU virtual address */
    memset(&gpu_virtual_address, 0, sizeof(gpu_virtual_address));
    hr = ID3D12Device_CheckFeatureSupport(device, D3D12_FEATURE_GPU_VIRTUAL_ADDRESS_SUPPORT,
            &gpu_virtual_address, sizeof(gpu_virtual_address));
    ok(hr == S_OK, "Failed to check GPU virtual address support, hr %#x.\n", hr);
    trace("GPU virtual address bits per resource: %u.\n",
            gpu_virtual_address.MaxGPUVirtualAddressBitsPerResource);
    trace("GPU virtual address bits per process: %u.\n",
            gpu_virtual_address.MaxGPUVirtualAddressBitsPerProcess);

    /* Shader model */
    shader_model.HighestShaderModel = D3D_SHADER_MODEL_5_1;
    hr = ID3D12Device_CheckFeatureSupport(device, D3D12_FEATURE_SHADER_MODEL, &shader_model, sizeof(shader_model));
    ok(hr == S_OK, "Failed to check shader model, hr %#x.\n", hr);
    ok(shader_model.HighestShaderModel <= D3D_SHADER_MODEL_5_1,
            "Got shader model %#x, expected <= %#x.\n", shader_model.HighestShaderModel, D3D_SHADER_MODEL_5_1);
    for (sm = D3D_HIGHEST_SHADER_MODEL; sm >= D3D_SHADER_MODEL_6_0; --sm)
    {
        shader_model.HighestShaderModel = sm;
        hr = ID3D12Device_CheckFeatureSupport(device, D3D12_FEATURE_SHADER_MODEL, &shader_model, sizeof(shader_model));
        ok(hr == S_OK || hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
        if (hr == S_OK)
            break;
    }
    ok(hr == S_OK, "Failed to check shader model, hr %#x.\n", hr);
    trace("Highest shader model %#x.\n", shader_model.HighestShaderModel);
    ok(shader_model.HighestShaderModel <= sm,
            "Got shader model %#x, expected <= %#x.\n", shader_model.HighestShaderModel, sm);
    shader_model.HighestShaderModel = 0x89;
    hr = ID3D12Device_CheckFeatureSupport(device, D3D12_FEATURE_SHADER_MODEL, &shader_model, sizeof(shader_model));
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    shader_model.HighestShaderModel = 0x52;
    hr = ID3D12Device_CheckFeatureSupport(device, D3D12_FEATURE_SHADER_MODEL, &shader_model, sizeof(shader_model));
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    shader_model.HighestShaderModel = 0;
    hr = ID3D12Device_CheckFeatureSupport(device, D3D12_FEATURE_SHADER_MODEL, &shader_model, sizeof(shader_model));
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    /* Root signature */
    root_signature.HighestVersion = D3D_ROOT_SIGNATURE_VERSION_1_0;
    hr = ID3D12Device_CheckFeatureSupport(device, D3D12_FEATURE_ROOT_SIGNATURE,
            &root_signature, sizeof(root_signature));
    ok(hr == S_OK, "Failed to get root signature feature support, hr %#x.\n", hr);
    ok(root_signature.HighestVersion == D3D_ROOT_SIGNATURE_VERSION_1_0,
            "Got unexpected root signature feature version %#x.\n", root_signature.HighestVersion);

    root_signature.HighestVersion = D3D_ROOT_SIGNATURE_VERSION_1_1;
    hr = ID3D12Device_CheckFeatureSupport(device, D3D12_FEATURE_ROOT_SIGNATURE,
            &root_signature, sizeof(root_signature));
    ok(hr == S_OK, "Failed to get root signature feature support, hr %#x.\n", hr);
    ok(root_signature.HighestVersion == D3D_ROOT_SIGNATURE_VERSION_1_0
            || root_signature.HighestVersion == D3D_ROOT_SIGNATURE_VERSION_1_1,
            "Got unexpected root signature feature version %#x.\n", root_signature.HighestVersion);

    refcount = ID3D12Device_Release(device);
    ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount);
}

static void test_format_support(void)
{
    D3D12_FEATURE_DATA_FORMAT_SUPPORT format_support;
    ID3D12Device *device;
    ULONG refcount;
    unsigned int i;
    HRESULT hr;

    static const struct
    {
        D3D12_FEATURE_DATA_FORMAT_SUPPORT f;
        bool broken;
    }
    unsupported_format_features[] =
    {
        /* A recent version of WARP supports B8G8R8A8 UAVs even on D3D_FEATURE_LEVEL_11_0. */
        {{DXGI_FORMAT_B8G8R8A8_TYPELESS, D3D12_FORMAT_SUPPORT1_TYPED_UNORDERED_ACCESS_VIEW,
                D3D12_FORMAT_SUPPORT2_UAV_TYPED_LOAD | D3D12_FORMAT_SUPPORT2_UAV_TYPED_STORE}, true},
    };

    if (!(device = create_device()))
    {
        skip("Failed to create device.\n");
        return;
    }

    memset(&format_support, 0, sizeof(format_support));
    hr = ID3D12Device_CheckFeatureSupport(device, D3D12_FEATURE_FORMAT_SUPPORT,
            &format_support, sizeof(format_support));
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    todo ok(format_support.Support1 == D3D12_FORMAT_SUPPORT1_BUFFER,
            "Got unexpected support1 %#x.\n", format_support.Support1);
    ok(!format_support.Support2 || format_support.Support2 == D3D12_FORMAT_SUPPORT2_TILED,
            "Got unexpected support2 %#x.\n", format_support.Support2);

    for (i = 0; i < ARRAY_SIZE(unsupported_format_features); ++i)
    {
        memset(&format_support, 0, sizeof(format_support));
        format_support.Format = unsupported_format_features[i].f.Format;
        hr = ID3D12Device_CheckFeatureSupport(device, D3D12_FEATURE_FORMAT_SUPPORT,
                &format_support, sizeof(format_support));
        ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
        ok(!(format_support.Support1 & unsupported_format_features[i].f.Support1)
                || broken_on_warp(unsupported_format_features[i].broken),
                "Format %#x supports %#x.\n", unsupported_format_features[i].f.Format,
                format_support.Support1 & unsupported_format_features[i].f.Support1);
        ok(!(format_support.Support2 & unsupported_format_features[i].f.Support2)
                || broken_on_warp(unsupported_format_features[i].broken),
                "Format %#x supports %#x.\n", unsupported_format_features[i].f.Format,
                format_support.Support2 & unsupported_format_features[i].f.Support2);
    }

    for (i = 0; i < ARRAY_SIZE(depth_stencil_formats); ++i)
    {
        memset(&format_support, 0, sizeof(format_support));
        format_support.Format = depth_stencil_formats[i];
        hr = ID3D12Device_CheckFeatureSupport(device, D3D12_FEATURE_FORMAT_SUPPORT,
                &format_support, sizeof(format_support));
        ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    }

    refcount = ID3D12Device_Release(device);
    ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount);
}

static void test_multisample_quality_levels(void)
{
    static const unsigned int sample_counts[] = {1, 2, 4, 8, 16, 32};
    D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS format_support;
    ID3D12Device *device;
    DXGI_FORMAT format;
    unsigned int i, j;
    ULONG refcount;
    HRESULT hr;

    if (!(device = create_device()))
    {
        skip("Failed to create device.\n");
        return;
    }

    memset(&format_support, 0, sizeof(format_support));
    format_support.NumQualityLevels = 0xdeadbeef;
    hr = ID3D12Device_CheckFeatureSupport(device, D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS,
            &format_support, sizeof(format_support));
    ok(hr == E_FAIL, "Got unexpected hr %#x.\n", hr);
    ok(!format_support.Flags, "Got unexpected flags %#x.\n", format_support.Flags);
    ok(!format_support.NumQualityLevels, "Got unexpected quality levels %u.\n", format_support.NumQualityLevels);

    format_support.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    format_support.NumQualityLevels = 0xdeadbeef;
    hr = ID3D12Device_CheckFeatureSupport(device, D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS,
            &format_support, sizeof(format_support));
    ok(hr == E_FAIL, "Got unexpected hr %#x.\n", hr);
    ok(!format_support.Flags, "Got unexpected flags %#x.\n", format_support.Flags);
    ok(!format_support.NumQualityLevels, "Got unexpected quality levels %u.\n", format_support.NumQualityLevels);

    /* 1 sample */
    for (format = DXGI_FORMAT_UNKNOWN; format <= DXGI_FORMAT_B4G4R4A4_UNORM; ++format)
    {
        if (format == DXGI_FORMAT_R1_UNORM)
            continue;

        vkd3d_test_push_context("format %#x", format);

        memset(&format_support, 0, sizeof(format_support));
        format_support.Format = format;
        format_support.SampleCount = 1;
        format_support.NumQualityLevels = 0xdeadbeef;
        hr = ID3D12Device_CheckFeatureSupport(device, D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS,
                &format_support, sizeof(format_support));
        ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
        ok(format_support.NumQualityLevels == 1, "Got unexpected quality levels %u.\n", format_support.NumQualityLevels);

        vkd3d_test_pop_context();
    }

    /* DXGI_FORMAT_UNKNOWN */
    for (i = 1; i < ARRAY_SIZE(sample_counts); ++i)
    {
        vkd3d_test_push_context("samples %#x", sample_counts[i]);

        memset(&format_support, 0, sizeof(format_support));
        format_support.SampleCount = sample_counts[i];
        format_support.NumQualityLevels = 0xdeadbeef;
        hr = ID3D12Device_CheckFeatureSupport(device, D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS,
                &format_support, sizeof(format_support));
        ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
        ok(!format_support.Flags, "Got unexpected flags %#x.\n", format_support.Flags);
        ok(!format_support.NumQualityLevels, "Got unexpected quality levels %u.\n", format_support.NumQualityLevels);

        format_support.Flags = D3D12_MULTISAMPLE_QUALITY_LEVELS_FLAG_TILED_RESOURCE;
        format_support.NumQualityLevels = 0xdeadbeef;
        hr = ID3D12Device_CheckFeatureSupport(device, D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS,
                &format_support, sizeof(format_support));
        ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
        ok(format_support.Flags == D3D12_MULTISAMPLE_QUALITY_LEVELS_FLAG_TILED_RESOURCE,
                "Got unexpected flags %#x.\n", format_support.Flags);
        ok(!format_support.NumQualityLevels, "Got unexpected quality levels %u.\n", format_support.NumQualityLevels);

        vkd3d_test_pop_context();
    }

    /* invalid sample counts */
    for (i = 1; i <= 32; ++i)
    {
        bool valid_sample_count = false;
        for (j = 0; j < ARRAY_SIZE(sample_counts); ++j)
        {
            if (sample_counts[j] == i)
            {
                valid_sample_count = true;
                break;
            }
        }
        if (valid_sample_count)
            continue;

        vkd3d_test_push_context("samples %#x", i);

        memset(&format_support, 0, sizeof(format_support));
        format_support.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
        format_support.SampleCount = i;
        format_support.NumQualityLevels = 0xdeadbeef;
        hr = ID3D12Device_CheckFeatureSupport(device, D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS,
                &format_support, sizeof(format_support));
        ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
        ok(!format_support.Flags, "Got unexpected flags %#x.\n", format_support.Flags);
        ok(!format_support.NumQualityLevels, "Got unexpected quality levels %u.\n", format_support.NumQualityLevels);

        vkd3d_test_pop_context();
    }

    /* DXGI_FORMAT_R8G8B8A8_UNORM */
    memset(&format_support, 0, sizeof(format_support));
    format_support.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    format_support.SampleCount = 4;
    format_support.NumQualityLevels = 0xdeadbeef;
    hr = ID3D12Device_CheckFeatureSupport(device, D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS,
            &format_support, sizeof(format_support));
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    ok(!format_support.Flags, "Got unexpected flags %#x.\n", format_support.Flags);
    ok(format_support.NumQualityLevels >= 1, "Got unexpected quality levels %u.\n", format_support.NumQualityLevels);

    for (i = 0; i < ARRAY_SIZE(depth_stencil_formats); ++i)
    {
        memset(&format_support, 0, sizeof(format_support));
        format_support.Format = depth_stencil_formats[i];
        format_support.SampleCount = 4;
        format_support.NumQualityLevels = 0xdeadbeef;
        hr = ID3D12Device_CheckFeatureSupport(device, D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS,
                &format_support, sizeof(format_support));
        ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    }

    refcount = ID3D12Device_Release(device);
    ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount);
}

static void test_create_command_allocator(void)
{
    ID3D12CommandAllocator *command_allocator;
    ID3D12Device *device, *tmp_device;
    ULONG refcount;
    HRESULT hr;

    if (!(device = create_device()))
    {
        skip("Failed to create device.\n");
        return;
    }

    hr = ID3D12Device_CreateCommandAllocator(device, D3D12_COMMAND_LIST_TYPE_DIRECT,
            &IID_ID3D12CommandAllocator, (void **)&command_allocator);
    ok(SUCCEEDED(hr), "Failed to create command allocator, hr %#x.\n", hr);

    refcount = get_refcount(device);
    ok(refcount == 2, "Got unexpected refcount %u.\n", (unsigned int)refcount);
    hr = ID3D12CommandAllocator_GetDevice(command_allocator, &IID_ID3D12Device, (void **)&tmp_device);
    ok(SUCCEEDED(hr), "Failed to get device, hr %#x.\n", hr);
    refcount = get_refcount(device);
    ok(refcount == 3, "Got unexpected refcount %u.\n", (unsigned int)refcount);
    refcount = ID3D12Device_Release(tmp_device);
    ok(refcount == 2, "Got unexpected refcount %u.\n", (unsigned int)refcount);

    check_interface(command_allocator, &IID_ID3D12Object, true);
    check_interface(command_allocator, &IID_ID3D12DeviceChild, true);
    check_interface(command_allocator, &IID_ID3D12Pageable, true);
    check_interface(command_allocator, &IID_ID3D12CommandAllocator, true);

    refcount = ID3D12CommandAllocator_Release(command_allocator);
    ok(!refcount, "ID3D12CommandAllocator has %u references left.\n", (unsigned int)refcount);

    hr = ID3D12Device_CreateCommandAllocator(device, D3D12_COMMAND_LIST_TYPE_BUNDLE,
            &IID_ID3D12CommandAllocator, (void **)&command_allocator);
    ok(SUCCEEDED(hr), "Failed to create command allocator, hr %#x.\n", hr);
    refcount = ID3D12CommandAllocator_Release(command_allocator);
    ok(!refcount, "ID3D12CommandAllocator has %u references left.\n", (unsigned int)refcount);

    hr = ID3D12Device_CreateCommandAllocator(device, D3D12_COMMAND_LIST_TYPE_COMPUTE,
            &IID_ID3D12CommandAllocator, (void **)&command_allocator);
    ok(SUCCEEDED(hr), "Failed to create command allocator, hr %#x.\n", hr);
    refcount = ID3D12CommandAllocator_Release(command_allocator);
    ok(!refcount, "ID3D12CommandAllocator has %u references left.\n", (unsigned int)refcount);

    hr = ID3D12Device_CreateCommandAllocator(device, D3D12_COMMAND_LIST_TYPE_COPY,
            &IID_ID3D12CommandAllocator, (void **)&command_allocator);
    ok(SUCCEEDED(hr), "Failed to create command allocator, hr %#x.\n", hr);
    refcount = ID3D12CommandAllocator_Release(command_allocator);
    ok(!refcount, "ID3D12CommandAllocator has %u references left.\n", (unsigned int)refcount);

    hr = ID3D12Device_CreateCommandAllocator(device, ~0u,
            &IID_ID3D12CommandAllocator, (void **)&command_allocator);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    refcount = ID3D12Device_Release(device);
    ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount);
}

static void test_create_command_list(void)
{
    ID3D12CommandAllocator *command_allocator;
    ID3D12Device *device, *tmp_device;
    ID3D12CommandList *command_list;
    ULONG refcount;
    HRESULT hr;

    if (!(device = create_device()))
    {
        skip("Failed to create device.\n");
        return;
    }

    hr = ID3D12Device_CreateCommandList(device, 0, D3D12_COMMAND_LIST_TYPE_DIRECT,
            NULL, NULL, &IID_ID3D12CommandList, (void **)&command_list);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    hr = ID3D12Device_CreateCommandAllocator(device, D3D12_COMMAND_LIST_TYPE_DIRECT,
            &IID_ID3D12CommandAllocator, (void **)&command_allocator);
    ok(SUCCEEDED(hr), "Failed to create command allocator, hr %#x.\n", hr);

    refcount = get_refcount(device);
    ok(refcount == 2, "Got unexpected refcount %u.\n", (unsigned int)refcount);

    hr = ID3D12Device_CreateCommandList(device, 0, D3D12_COMMAND_LIST_TYPE_DIRECT,
            command_allocator, NULL, &IID_ID3D12CommandList, (void **)&command_list);
    ok(SUCCEEDED(hr), "Failed to create command list, hr %#x.\n", hr);

    refcount = get_refcount(command_allocator);
    ok(refcount == 1, "Got unexpected refcount %u.\n", (unsigned int)refcount);

    refcount = get_refcount(device);
    ok(refcount == 3, "Got unexpected refcount %u.\n", (unsigned int)refcount);
    hr = ID3D12CommandList_GetDevice(command_list, &IID_ID3D12Device, (void **)&tmp_device);
    ok(SUCCEEDED(hr), "Failed to get device, hr %#x.\n", hr);
    refcount = get_refcount(device);
    ok(refcount == 4, "Got unexpected refcount %u.\n", (unsigned int)refcount);
    refcount = ID3D12Device_Release(tmp_device);
    ok(refcount == 3, "Got unexpected refcount %u.\n", (unsigned int)refcount);

    check_interface(command_list, &IID_ID3D12Object, true);
    check_interface(command_list, &IID_ID3D12DeviceChild, true);
    check_interface(command_list, &IID_ID3D12Pageable, false);
    check_interface(command_list, &IID_ID3D12CommandList, true);
    check_interface(command_list, &IID_ID3D12GraphicsCommandList, true);
    check_interface(command_list, &IID_ID3D12CommandAllocator, false);

    refcount = ID3D12CommandList_Release(command_list);
    ok(!refcount, "ID3D12CommandList has %u references left.\n", (unsigned int)refcount);
    refcount = ID3D12CommandAllocator_Release(command_allocator);
    ok(!refcount, "ID3D12CommandAllocator has %u references left.\n", (unsigned int)refcount);

    hr = ID3D12Device_CreateCommandAllocator(device, D3D12_COMMAND_LIST_TYPE_BUNDLE,
            &IID_ID3D12CommandAllocator, (void **)&command_allocator);
    ok(SUCCEEDED(hr), "Failed to create command allocator, hr %#x.\n", hr);
    hr = ID3D12Device_CreateCommandList(device, 0, D3D12_COMMAND_LIST_TYPE_DIRECT,
            command_allocator, NULL, &IID_ID3D12CommandList, (void **)&command_list);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    hr = ID3D12Device_CreateCommandList(device, 0, D3D12_COMMAND_LIST_TYPE_BUNDLE,
            command_allocator, NULL, &IID_ID3D12CommandList, (void **)&command_list);
    ok(SUCCEEDED(hr), "Failed to create command list, hr %#x.\n", hr);
    check_interface(command_list, &IID_ID3D12GraphicsCommandList, true);
    refcount = ID3D12CommandList_Release(command_list);
    ok(!refcount, "ID3D12CommandList has %u references left.\n", (unsigned int)refcount);
    refcount = ID3D12CommandAllocator_Release(command_allocator);
    ok(!refcount, "ID3D12CommandAllocator has %u references left.\n", (unsigned int)refcount);

    hr = ID3D12Device_CreateCommandAllocator(device, D3D12_COMMAND_LIST_TYPE_COMPUTE,
            &IID_ID3D12CommandAllocator, (void **)&command_allocator);
    ok(SUCCEEDED(hr), "Failed to create command allocator, hr %#x.\n", hr);
    hr = ID3D12Device_CreateCommandList(device, 0, D3D12_COMMAND_LIST_TYPE_BUNDLE,
            command_allocator, NULL, &IID_ID3D12CommandList, (void **)&command_list);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    hr = ID3D12Device_CreateCommandList(device, 0, D3D12_COMMAND_LIST_TYPE_COMPUTE,
            command_allocator, NULL, &IID_ID3D12CommandList, (void **)&command_list);
    ok(SUCCEEDED(hr), "Failed to create command list, hr %#x.\n", hr);
    check_interface(command_list, &IID_ID3D12GraphicsCommandList, true);
    refcount = ID3D12CommandList_Release(command_list);
    ok(!refcount, "ID3D12CommandList has %u references left.\n", (unsigned int)refcount);
    refcount = ID3D12CommandAllocator_Release(command_allocator);
    ok(!refcount, "ID3D12CommandAllocator has %u references left.\n", (unsigned int)refcount);

    hr = ID3D12Device_CreateCommandAllocator(device, D3D12_COMMAND_LIST_TYPE_COPY,
            &IID_ID3D12CommandAllocator, (void **)&command_allocator);
    ok(SUCCEEDED(hr), "Failed to create command allocator, hr %#x.\n", hr);
    hr = ID3D12Device_CreateCommandList(device, 0, D3D12_COMMAND_LIST_TYPE_DIRECT,
            command_allocator, NULL, &IID_ID3D12CommandList, (void **)&command_list);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    hr = ID3D12Device_CreateCommandList(device, 0, D3D12_COMMAND_LIST_TYPE_COMPUTE,
            command_allocator, NULL, &IID_ID3D12CommandList, (void **)&command_list);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    hr = ID3D12Device_CreateCommandList(device, 0, D3D12_COMMAND_LIST_TYPE_COPY,
            command_allocator, NULL, &IID_ID3D12CommandList, (void **)&command_list);
    ok(SUCCEEDED(hr), "Failed to create command list, hr %#x.\n", hr);
    check_interface(command_list, &IID_ID3D12GraphicsCommandList, true);
    refcount = ID3D12CommandList_Release(command_list);
    ok(!refcount, "ID3D12CommandList has %u references left.\n", (unsigned int)refcount);
    refcount = ID3D12CommandAllocator_Release(command_allocator);
    ok(!refcount, "ID3D12CommandAllocator has %u references left.\n", (unsigned int)refcount);

    refcount = ID3D12Device_Release(device);
    ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount);
}

static void test_create_command_queue(void)
{
    ID3D12CommandQueue* direct_queues[32], *compute_queues[32];
    D3D12_COMMAND_QUEUE_DESC desc, result_desc;
    ID3D12Device *device, *tmp_device;
    ID3D12CommandQueue *queue;
    unsigned int i;
    ULONG refcount;
    HRESULT hr;

    if (!(device = create_device()))
    {
        skip("Failed to create device.\n");
        return;
    }

    desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
    desc.Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL;
    desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
    desc.NodeMask = 0;
    hr = ID3D12Device_CreateCommandQueue(device, &desc, &IID_ID3D12CommandQueue, (void **)&queue);
    ok(SUCCEEDED(hr), "Failed to create command queue, hr %#x.\n", hr);

    refcount = get_refcount(device);
    ok(refcount == 2, "Got unexpected refcount %u.\n", (unsigned int)refcount);
    hr = ID3D12CommandQueue_GetDevice(queue, &IID_ID3D12Device, (void **)&tmp_device);
    ok(SUCCEEDED(hr), "Failed to get device, hr %#x.\n", hr);
    refcount = get_refcount(device);
    ok(refcount == 3, "Got unexpected refcount %u.\n", (unsigned int)refcount);
    refcount = ID3D12Device_Release(tmp_device);
    ok(refcount == 2, "Got unexpected refcount %u.\n", (unsigned int)refcount);

    check_interface(queue, &IID_ID3D12Object, true);
    check_interface(queue, &IID_ID3D12DeviceChild, true);
    check_interface(queue, &IID_ID3D12Pageable, true);
    check_interface(queue, &IID_ID3D12CommandQueue, true);

    result_desc = ID3D12CommandQueue_GetDesc(queue);
    ok(result_desc.Type == desc.Type, "Got unexpected type %#x.\n", result_desc.Type);
    ok(result_desc.Priority == desc.Priority, "Got unexpected priority %#x.\n", result_desc.Priority);
    ok(result_desc.Flags == desc.Flags, "Got unexpected flags %#x.\n", result_desc.Flags);
    ok(result_desc.NodeMask == 0x1, "Got unexpected node mask 0x%08x.\n", result_desc.NodeMask);

    refcount = ID3D12CommandQueue_Release(queue);
    ok(!refcount, "ID3D12CommandQueue has %u references left.\n", (unsigned int)refcount);

    desc.Type = D3D12_COMMAND_LIST_TYPE_COMPUTE;
    hr = ID3D12Device_CreateCommandQueue(device, &desc, &IID_ID3D12CommandQueue, (void **)&queue);
    ok(SUCCEEDED(hr), "Failed to create command queue, hr %#x.\n", hr);

    result_desc = ID3D12CommandQueue_GetDesc(queue);
    ok(result_desc.Type == desc.Type, "Got unexpected type %#x.\n", result_desc.Type);
    ok(result_desc.Priority == desc.Priority, "Got unexpected priority %#x.\n", result_desc.Priority);
    ok(result_desc.Flags == desc.Flags, "Got unexpected flags %#x.\n", result_desc.Flags);
    ok(result_desc.NodeMask == 0x1, "Got unexpected node mask 0x%08x.\n", result_desc.NodeMask);

    refcount = ID3D12CommandQueue_Release(queue);
    ok(!refcount, "ID3D12CommandQueue has %u references left.\n", (unsigned int)refcount);

    desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
    for (i = 0; i < ARRAY_SIZE(direct_queues); ++i)
    {
        hr = ID3D12Device_CreateCommandQueue(device, &desc, &IID_ID3D12CommandQueue, (void **)&direct_queues[i]);
        ok(hr == S_OK, "Failed to create direct command queue %u, hr %#x.\n", hr, i);
    }
    desc.Type = D3D12_COMMAND_LIST_TYPE_COMPUTE;
    for (i = 0; i < ARRAY_SIZE(compute_queues); ++i)
    {
        hr = ID3D12Device_CreateCommandQueue(device, &desc, &IID_ID3D12CommandQueue, (void **)&compute_queues[i]);
        ok(hr == S_OK, "Failed to create compute command queue %u, hr %#x.\n", hr, i);
    }

    for (i = 0; i < ARRAY_SIZE(direct_queues); ++i)
        ID3D12CommandQueue_Release(direct_queues[i]);
    for (i = 0; i < ARRAY_SIZE(compute_queues); ++i)
        ID3D12CommandQueue_Release(compute_queues[i]);

    refcount = ID3D12Device_Release(device);
    ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount);
}

static void test_create_command_signature(void)
{
    D3D12_INDIRECT_ARGUMENT_DESC argument_desc[3];
    D3D12_COMMAND_SIGNATURE_DESC signature_desc;
    ID3D12CommandSignature *command_signature;
    ID3D12Device *device;
    unsigned int i;
    ULONG refcount;
    HRESULT hr;

    if (!(device = create_device()))
    {
        skip("Failed to create device.\n");
        return;
    }

    signature_desc.ByteStride = 1024;
    signature_desc.NumArgumentDescs = ARRAY_SIZE(argument_desc);
    signature_desc.pArgumentDescs = argument_desc;
    signature_desc.NodeMask = 0;

    for (i = 0; i < ARRAY_SIZE(argument_desc); ++i)
        argument_desc[i].Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW;
    hr = ID3D12Device_CreateCommandSignature(device, &signature_desc,
            NULL, &IID_ID3D12CommandSignature, (void **)&command_signature);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    for (i = 0; i < ARRAY_SIZE(argument_desc); ++i)
        argument_desc[i].Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW_INDEXED;
    hr = ID3D12Device_CreateCommandSignature(device, &signature_desc,
            NULL, &IID_ID3D12CommandSignature, (void **)&command_signature);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    for (i = 0; i < ARRAY_SIZE(argument_desc); ++i)
        argument_desc[i].Type = D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH;
    hr = ID3D12Device_CreateCommandSignature(device, &signature_desc,
            NULL, &IID_ID3D12CommandSignature, (void **)&command_signature);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    argument_desc[0].Type = D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH;
    argument_desc[1].Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW;
    signature_desc.NumArgumentDescs = 2;
    hr = ID3D12Device_CreateCommandSignature(device, &signature_desc,
            NULL, &IID_ID3D12CommandSignature, (void **)&command_signature);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    refcount = ID3D12Device_Release(device);
    ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount);
}

static void test_create_committed_resource(void)
{
    ID3D12ProtectedResourceSession *protected_session;
    D3D12_GPU_VIRTUAL_ADDRESS gpu_address;
    D3D12_HEAP_PROPERTIES heap_properties;
    D3D12_RESOURCE_DESC1 resource_desc1;
    D3D12_RESOURCE_DESC resource_desc;
    ID3D12Device *device, *tmp_device;
    HRESULT hr, unaligned_expected_hr;
    D3D12_CLEAR_VALUE clear_value;
    D3D12_RESOURCE_STATES state;
    ID3D12Resource2 *resource2;
    ID3D12Resource1 *resource1;
    ID3D12Resource *resource;
    ID3D12Device8 *device8;
    ID3D12Device4 *device4;
    unsigned int i;
    ULONG refcount;

    static const struct
    {
        D3D12_HEAP_TYPE heap_type;
        D3D12_RESOURCE_FLAGS flags;
    }
    invalid_buffer_desc_tests[] =
    {
        /* Render target or unordered access resources are not allowed with UPLOAD or READBACK. */
        {D3D12_HEAP_TYPE_UPLOAD,   D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET},
        {D3D12_HEAP_TYPE_READBACK, D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET},
        {D3D12_HEAP_TYPE_UPLOAD,   D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS},
        {D3D12_HEAP_TYPE_READBACK, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS},
        {D3D12_HEAP_TYPE_DEFAULT,  D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS},
        {D3D12_HEAP_TYPE_UPLOAD,   D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS},
        {D3D12_HEAP_TYPE_READBACK, D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS},
    };

    if (!(device = create_device()))
    {
        skip("Failed to create device.\n");
        return;
    }

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

    resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
    resource_desc.Alignment = 0;
    resource_desc.Width = 32;
    resource_desc.Height = 32;
    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;

    clear_value.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    clear_value.Color[0] = 1.0f;
    clear_value.Color[1] = 0.0f;
    clear_value.Color[2] = 0.0f;
    clear_value.Color[3] = 1.0f;

    hr = ID3D12Device_CreateCommittedResource(device, &heap_properties, D3D12_HEAP_FLAG_NONE,
            &resource_desc, D3D12_RESOURCE_STATE_RENDER_TARGET, &clear_value,
            &IID_ID3D12Resource, (void **)&resource);
    ok(hr == S_OK, "Failed to create committed resource, hr %#x.\n", hr);

    refcount = get_refcount(device);
    ok(refcount == 2, "Got unexpected refcount %u.\n", (unsigned int)refcount);
    hr = ID3D12Resource_GetDevice(resource, &IID_ID3D12Device, (void **)&tmp_device);
    ok(hr == S_OK, "Failed to get device, hr %#x.\n", hr);
    refcount = get_refcount(device);
    ok(refcount == 3, "Got unexpected refcount %u.\n", (unsigned int)refcount);
    refcount = ID3D12Device_Release(tmp_device);
    ok(refcount == 2, "Got unexpected refcount %u.\n", (unsigned int)refcount);

    check_interface(resource, &IID_ID3D12Object, true);
    check_interface(resource, &IID_ID3D12DeviceChild, true);
    check_interface(resource, &IID_ID3D12Pageable, true);
    check_interface(resource, &IID_ID3D12Resource, true);

    gpu_address = ID3D12Resource_GetGPUVirtualAddress(resource);
    ok(!gpu_address, "Got unexpected GPU virtual address %#"PRIx64".\n", gpu_address);

    refcount = ID3D12Resource_Release(resource);
    ok(!refcount, "ID3D12Resource has %u references left.\n", (unsigned int)refcount);

    resource_desc.MipLevels = 0;
    hr = ID3D12Device_CreateCommittedResource(device, &heap_properties, D3D12_HEAP_FLAG_NONE,
            &resource_desc, D3D12_RESOURCE_STATE_RENDER_TARGET, &clear_value,
            &IID_ID3D12Resource, (void **)&resource);
    ok(hr == S_OK, "Failed to create committed resource, hr %#x.\n", hr);
    resource_desc = ID3D12Resource_GetDesc(resource);
    ok(resource_desc.MipLevels == 6, "Got unexpected miplevels %u.\n", resource_desc.MipLevels);
    ID3D12Resource_Release(resource);
    resource_desc.MipLevels = 10;
    hr = ID3D12Device_CreateCommittedResource(device, &heap_properties, D3D12_HEAP_FLAG_NONE,
            &resource_desc, D3D12_RESOURCE_STATE_RENDER_TARGET, &clear_value,
            &IID_ID3D12Resource, (void **)&resource);
    ok(hr == S_OK, "Failed to create committed resource, hr %#x.\n", hr);
    resource_desc = ID3D12Resource_GetDesc(resource);
    ok(resource_desc.MipLevels == 10, "Got unexpected miplevels %u.\n", resource_desc.MipLevels);
    ID3D12Resource_Release(resource);
    resource_desc.MipLevels = 1;

    resource_desc.SampleDesc.Count = 0;
    hr = ID3D12Device_CreateCommittedResource(device, &heap_properties, D3D12_HEAP_FLAG_NONE, &resource_desc,
            D3D12_RESOURCE_STATE_RENDER_TARGET, &clear_value, &IID_ID3D12Resource, (void **)&resource);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    /* The ALLOW_RENDER_TARGET or ALLOW_DEPTH_STENCIL flag is required for multisampled resources. */
    resource_desc.SampleDesc.Count = 4;
    resource_desc.Flags = 0;
    hr = ID3D12Device_CreateCommittedResource(device, &heap_properties, D3D12_HEAP_FLAG_NONE, &resource_desc,
            D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, NULL, &IID_ID3D12Resource, (void **)&resource);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    resource_desc.Format = DXGI_FORMAT_D32_FLOAT;
    resource_desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
    hr = ID3D12Device_CreateCommittedResource(device, &heap_properties, D3D12_HEAP_FLAG_NONE, &resource_desc,
            D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, NULL, &IID_ID3D12Resource, (void **)&resource);
    ok(hr == S_OK, "Failed to create committed resource, hr %#x.\n", hr);
    ID3D12Resource_Release(resource);
    resource_desc.SampleDesc.Count = 1;
    resource_desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;

    resource_desc.Format = DXGI_FORMAT_UNKNOWN;
    hr = ID3D12Device_CreateCommittedResource(device, &heap_properties, D3D12_HEAP_FLAG_NONE, &resource_desc,
            D3D12_RESOURCE_STATE_RENDER_TARGET, &clear_value, &IID_ID3D12Resource, (void **)&resource);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    resource_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;

    hr = ID3D12Device_CreateCommittedResource(device, &heap_properties, D3D12_HEAP_FLAG_NONE,
            &resource_desc, D3D12_RESOURCE_STATE_RENDER_TARGET | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
            &clear_value, &IID_ID3D12Resource, (void **)&resource);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    /* For D3D12_RESOURCE_STATE_RENDER_TARGET the D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET flag is required. */
    resource_desc.Flags = 0;
    hr = ID3D12Device_CreateCommittedResource(device, &heap_properties, D3D12_HEAP_FLAG_NONE,
            &resource_desc, D3D12_RESOURCE_STATE_RENDER_TARGET, NULL,
            &IID_ID3D12Resource, (void **)&resource);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    /* A texture cannot be created on a UPLOAD heap. */
    heap_properties.Type = D3D12_HEAP_TYPE_UPLOAD;
    resource = (void *)0xdeadbeef;
    hr = ID3D12Device_CreateCommittedResource(device, &heap_properties, D3D12_HEAP_FLAG_NONE,
            &resource_desc, D3D12_RESOURCE_STATE_GENERIC_READ, NULL,
            &IID_ID3D12Resource, (void **)&resource);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    ok(!resource, "Got unexpected pointer %p.\n", resource);

    resource = (void *)0xdeadbeef;
    hr = ID3D12Device_CreateCommittedResource(device, &heap_properties, D3D12_HEAP_FLAG_NONE,
            &resource_desc, D3D12_RESOURCE_STATE_GENERIC_READ, NULL,
            &IID_ID3D12Device, (void **)&resource);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    ok(!resource, "Got unexpected pointer %p.\n", resource);

    /* A texture cannot be created on a READBACK heap. */
    heap_properties.Type = D3D12_HEAP_TYPE_READBACK;
    hr = ID3D12Device_CreateCommittedResource(device, &heap_properties, D3D12_HEAP_FLAG_NONE,
            &resource_desc, D3D12_RESOURCE_STATE_COPY_DEST, NULL,
            &IID_ID3D12Resource, (void **)&resource);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    heap_properties.Type = D3D12_HEAP_TYPE_DEFAULT;
    resource_desc.Format = DXGI_FORMAT_BC1_UNORM;
    hr = ID3D12Device_CreateCommittedResource(device, &heap_properties, D3D12_HEAP_FLAG_NONE,
            &resource_desc, D3D12_RESOURCE_STATE_COMMON, NULL,
            &IID_ID3D12Resource, (void **)&resource);
    ok(hr == S_OK, "Failed to create committed resource, hr %#x.\n", hr);
    ID3D12Resource_Release(resource);

    unaligned_expected_hr = are_unaligned_block_textures_supported(device) ? S_OK : E_INVALIDARG;

    resource_desc.Height = 31;
    hr = ID3D12Device_CreateCommittedResource(device, &heap_properties, D3D12_HEAP_FLAG_NONE,
            &resource_desc, D3D12_RESOURCE_STATE_COMMON, NULL,
            &IID_ID3D12Resource, (void **)&resource);
    ok(hr == unaligned_expected_hr, "Got unexpected hr %#x.\n", hr);
    if (SUCCEEDED(hr))
        ID3D12Resource_Release(resource);

    resource_desc.Width = 31;
    resource_desc.Height = 32;
    hr = ID3D12Device_CreateCommittedResource(device, &heap_properties, D3D12_HEAP_FLAG_NONE,
            &resource_desc, D3D12_RESOURCE_STATE_COMMON, NULL,
            &IID_ID3D12Resource, (void **)&resource);
    ok(hr == unaligned_expected_hr, "Got unexpected hr %#x.\n", hr);
    if (SUCCEEDED(hr))
        ID3D12Resource_Release(resource);

    resource_desc.Width = 30;
    resource_desc.Height = 30;
    hr = ID3D12Device_CreateCommittedResource(device, &heap_properties, D3D12_HEAP_FLAG_NONE,
            &resource_desc, D3D12_RESOURCE_STATE_COMMON, NULL,
            &IID_ID3D12Resource, (void **)&resource);
    ok(hr == unaligned_expected_hr, "Got unexpected hr %#x.\n", hr);
    if (SUCCEEDED(hr))
        ID3D12Resource_Release(resource);

    resource_desc.Width = 2;
    resource_desc.Height = 2;
    hr = ID3D12Device_CreateCommittedResource(device, &heap_properties, D3D12_HEAP_FLAG_NONE,
            &resource_desc, D3D12_RESOURCE_STATE_COMMON, NULL,
            &IID_ID3D12Resource, (void **)&resource);
    ok(hr == unaligned_expected_hr, "Got unexpected hr %#x.\n", hr);
    if (SUCCEEDED(hr))
        ID3D12Resource_Release(resource);

    resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE1D;
    resource_desc.Width = 32;
    resource_desc.Height = 1;
    hr = ID3D12Device_CreateCommittedResource(device, &heap_properties, D3D12_HEAP_FLAG_NONE,
            &resource_desc, D3D12_RESOURCE_STATE_COMMON, NULL,
            &IID_ID3D12Resource, (void **)&resource);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    heap_properties.Type = D3D12_HEAP_TYPE_UPLOAD;

    resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
    resource_desc.Alignment = 0;
    resource_desc.Width = 32;
    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(device, &heap_properties, D3D12_HEAP_FLAG_NONE,
            &resource_desc, D3D12_RESOURCE_STATE_GENERIC_READ, NULL,
            &IID_ID3D12Resource, (void **)&resource);
    ok(hr == S_OK, "Failed to create committed resource, hr %#x.\n", hr);

    check_interface(resource, &IID_ID3D12Object, true);
    check_interface(resource, &IID_ID3D12DeviceChild, true);
    check_interface(resource, &IID_ID3D12Pageable, true);
    check_interface(resource, &IID_ID3D12Resource, true);

    gpu_address = ID3D12Resource_GetGPUVirtualAddress(resource);
    ok(gpu_address, "Got unexpected GPU virtual address %#"PRIx64".\n", gpu_address);

    refcount = ID3D12Resource_Release(resource);
    ok(!refcount, "ID3D12Resource has %u references left.\n", (unsigned int)refcount);

    resource_desc.MipLevels = 0;
    hr = ID3D12Device_CreateCommittedResource(device, &heap_properties, D3D12_HEAP_FLAG_NONE,
            &resource_desc, D3D12_RESOURCE_STATE_GENERIC_READ, &clear_value,
            &IID_ID3D12Resource, (void **)&resource);
    ok(hr == E_INVALIDARG, "Failed to create committed resource, hr %#x.\n", hr);
    resource_desc.MipLevels = 1;

    /* The clear value must be NULL for buffers. */
    hr = ID3D12Device_CreateCommittedResource(device, &heap_properties, D3D12_HEAP_FLAG_NONE,
            &resource_desc, D3D12_RESOURCE_STATE_GENERIC_READ, &clear_value,
            &IID_ID3D12Resource, (void **)&resource);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    hr = ID3D12Device_CreateCommittedResource(device, &heap_properties, D3D12_HEAP_FLAG_NONE,
            &resource_desc, D3D12_RESOURCE_STATE_COMMON, NULL,
            &IID_ID3D12Resource, (void **)&resource);
    todo ok(hr == S_OK || broken(hr == E_INVALIDARG), "Got unexpected hr %#x.\n", hr);
    if (SUCCEEDED(hr))
        ID3D12Resource_Release(resource);

    hr = ID3D12Device_CreateCommittedResource(device, &heap_properties, D3D12_HEAP_FLAG_NONE,
            &resource_desc, D3D12_RESOURCE_STATE_COPY_SOURCE, NULL,
            &IID_ID3D12Resource, (void **)&resource);
    todo ok(hr == S_OK || broken(hr == E_INVALIDARG), "Got unexpected hr %#x.\n", hr);
    if (SUCCEEDED(hr))
        ID3D12Resource_Release(resource);

    heap_properties.Type = D3D12_HEAP_TYPE_READBACK;

    hr = ID3D12Device_CreateCommittedResource(device, &heap_properties, D3D12_HEAP_FLAG_NONE,
            &resource_desc, D3D12_RESOURCE_STATE_COPY_DEST, NULL,
            &IID_ID3D12Resource, (void **)&resource);
    ok(hr == S_OK, "Failed to create committed resource, hr %#x.\n", hr);

    gpu_address = ID3D12Resource_GetGPUVirtualAddress(resource);
    ok(gpu_address, "Got unexpected GPU virtual address %#"PRIx64".\n", gpu_address);

    refcount = ID3D12Resource_Release(resource);
    ok(!refcount, "ID3D12Resource has %u references left.\n", (unsigned int)refcount);

    hr = ID3D12Device_CreateCommittedResource(device, &heap_properties, D3D12_HEAP_FLAG_NONE,
            &resource_desc, D3D12_RESOURCE_STATE_COMMON, NULL,
            &IID_ID3D12Resource, (void **)&resource);
    todo ok(hr == S_OK || broken(hr == E_INVALIDARG), "Got unexpected hr %#x.\n", hr);
    if (SUCCEEDED(hr))
        ID3D12Resource_Release(resource);

    hr = ID3D12Device_CreateCommittedResource(device, &heap_properties, D3D12_HEAP_FLAG_NONE,
            &resource_desc, D3D12_RESOURCE_STATE_GENERIC_READ, NULL,
            &IID_ID3D12Resource, (void **)&resource);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    hr = ID3D12Device_CreateCommittedResource(device, &heap_properties, D3D12_HEAP_FLAG_NONE,
            &resource_desc, D3D12_RESOURCE_STATE_COPY_SOURCE, NULL,
            &IID_ID3D12Resource, (void **)&resource);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    for (i = 0; i < ARRAY_SIZE(invalid_buffer_desc_tests); ++i)
    {
        memset(&heap_properties, 0, sizeof(heap_properties));
        heap_properties.Type = invalid_buffer_desc_tests[i].heap_type;

        resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
        resource_desc.Alignment = 0;
        resource_desc.Width = 32;
        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 = invalid_buffer_desc_tests[i].flags;

        if (invalid_buffer_desc_tests[i].heap_type == D3D12_HEAP_TYPE_UPLOAD)
            state = D3D12_RESOURCE_STATE_GENERIC_READ;
        else
            state = D3D12_RESOURCE_STATE_COPY_DEST;

        hr = ID3D12Device_CreateCommittedResource(device, &heap_properties, D3D12_HEAP_FLAG_NONE,
                &resource_desc, state, NULL, &IID_ID3D12Resource, (void **)&resource);
        ok(hr == E_INVALIDARG, "Test %u: Got unexpected hr %#x.\n", i, hr);
    }

    if (SUCCEEDED(ID3D12Device_QueryInterface(device, &IID_ID3D12Device4, (void **)&device4)))
    {
        memset(&heap_properties, 0, sizeof(heap_properties));
        heap_properties.Type = D3D12_HEAP_TYPE_DEFAULT;

        resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
        resource_desc.Alignment = 0;
        resource_desc.Width = 32;
        resource_desc.Height = 32;
        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;

        clear_value.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
        clear_value.Color[0] = 1.0f;
        clear_value.Color[1] = 0.0f;
        clear_value.Color[2] = 0.0f;
        clear_value.Color[3] = 1.0f;

        hr = ID3D12Device4_CreateCommittedResource1(device4, &heap_properties, D3D12_HEAP_FLAG_NONE,
                &resource_desc, D3D12_RESOURCE_STATE_RENDER_TARGET, &clear_value, NULL,
                &IID_ID3D12Resource1, (void **)&resource1);
        ok(hr == S_OK, "Failed to create committed resource, hr %#x.\n", hr);

        check_interface(resource1, &IID_ID3D12Resource1, true);

        hr = ID3D12Resource1_GetProtectedResourceSession(resource1, &IID_ID3D12ProtectedResourceSession,
                (void **)&protected_session);
        ok(hr == DXGI_ERROR_NOT_FOUND, "Got unexpected hr %#x.\n", hr);

        ID3D12Resource1_Release(resource1);
        ID3D12Device4_Release(device4);
    }

    if (SUCCEEDED(ID3D12Device_QueryInterface(device, &IID_ID3D12Device8, (void **)&device8)))
    {
        resource_desc1.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
        resource_desc1.Alignment = 0;
        resource_desc1.Width = 32;
        resource_desc1.Height = 32;
        resource_desc1.DepthOrArraySize = 1;
        resource_desc1.MipLevels = 1;
        resource_desc1.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
        resource_desc1.SampleDesc.Count = 1;
        resource_desc1.SampleDesc.Quality = 0;
        resource_desc1.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
        resource_desc1.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
        memset(&resource_desc1.SamplerFeedbackMipRegion, 0, sizeof(resource_desc1.SamplerFeedbackMipRegion));

        hr = ID3D12Device8_CreateCommittedResource2(device8, &heap_properties, D3D12_HEAP_FLAG_NONE,
                &resource_desc1, D3D12_RESOURCE_STATE_RENDER_TARGET, &clear_value, NULL,
                &IID_ID3D12Resource2, (void **)&resource2);
        ok(hr == S_OK, "Failed to create committed resource, hr %#x.\n", hr);

        check_interface(resource2, &IID_ID3D12Resource2, true);

        ID3D12Resource2_Release(resource2);
        ID3D12Device8_Release(device8);
    }

    refcount = ID3D12Device_Release(device);
    ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount);
}

static void test_create_heap(void)
{
    bool is_pool_L1_supported, gpu_upload_heap_supported;
    D3D12_FEATURE_DATA_ARCHITECTURE architecture;
    D3D12_FEATURE_DATA_D3D12_OPTIONS options;
    D3D12_HEAP_DESC desc, result_desc;
    ID3D12Device *device, *tmp_device;
    HRESULT hr, expected_hr;
    ID3D12Device4 *device4;
    unsigned int i, j;
    ID3D12Heap *heap;
    ULONG refcount;

    static const struct
    {
        uint64_t alignment;
        HRESULT expected_hr;
    }
    tests[] =
    {
        {D3D12_DEFAULT_MSAA_RESOURCE_PLACEMENT_ALIGNMENT,     S_OK},
        {D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT,          S_OK},
        {2 * D3D12_DEFAULT_MSAA_RESOURCE_PLACEMENT_ALIGNMENT, E_INVALIDARG},
        {2 * D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT,      E_INVALIDARG},
        {D3D12_SMALL_RESOURCE_PLACEMENT_ALIGNMENT,            E_INVALIDARG},
    };
    static const struct
    {
        D3D12_HEAP_FLAGS flags;
        const char *name;
    }
    heap_flags[] =
    {
        {D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS, "buffers"},
        {D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES, "textures"},
        {D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES, "rt_ds_textures"},
    };
    static const struct
    {
        D3D12_CPU_PAGE_PROPERTY page_property;
        D3D12_MEMORY_POOL pool_preference;
        HRESULT expected_hr;
    }
    custom_tests[] =
    {
        {D3D12_CPU_PAGE_PROPERTY_UNKNOWN, D3D12_MEMORY_POOL_UNKNOWN, E_INVALIDARG},
        {D3D12_CPU_PAGE_PROPERTY_NOT_AVAILABLE, D3D12_MEMORY_POOL_UNKNOWN, E_INVALIDARG},
        {D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE, D3D12_MEMORY_POOL_UNKNOWN, E_INVALIDARG},
        {D3D12_CPU_PAGE_PROPERTY_WRITE_BACK, D3D12_MEMORY_POOL_UNKNOWN, E_INVALIDARG},
        {D3D12_CPU_PAGE_PROPERTY_UNKNOWN, D3D12_MEMORY_POOL_L0, E_INVALIDARG},
        {D3D12_CPU_PAGE_PROPERTY_NOT_AVAILABLE, D3D12_MEMORY_POOL_L0, S_OK},
        {D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE, D3D12_MEMORY_POOL_L0, S_OK},
        {D3D12_CPU_PAGE_PROPERTY_WRITE_BACK, D3D12_MEMORY_POOL_L0, S_OK},
        {D3D12_CPU_PAGE_PROPERTY_UNKNOWN, D3D12_MEMORY_POOL_L1, E_INVALIDARG},
        {D3D12_CPU_PAGE_PROPERTY_NOT_AVAILABLE, D3D12_MEMORY_POOL_L1, S_OK},
        {D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE, D3D12_MEMORY_POOL_L1, S_OK},
        {D3D12_CPU_PAGE_PROPERTY_WRITE_BACK, D3D12_MEMORY_POOL_L1, E_INVALIDARG},
    };

    if (!(device = create_device()))
    {
        skip("Failed to create device.\n");
        return;
    }

    desc.SizeInBytes = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
    memset(&desc.Properties, 0, sizeof(desc.Properties));
    desc.Properties.Type = D3D12_HEAP_TYPE_DEFAULT;
    desc.Alignment = 0;
    desc.Flags = D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES;
    hr = ID3D12Device_CreateHeap(device, &desc, &IID_ID3D12Heap, (void **)&heap);
    ok(hr == S_OK, "Failed to create heap, hr %#x.\n", hr);

    refcount = get_refcount(device);
    ok(refcount == 2, "Got unexpected refcount %u.\n", (unsigned int)refcount);
    hr = ID3D12Heap_GetDevice(heap, &IID_ID3D12Device, (void **)&tmp_device);
    ok(hr == S_OK, "Failed to get device, hr %#x.\n", hr);
    refcount = get_refcount(device);
    ok(refcount == 3, "Got unexpected refcount %u.\n", (unsigned int)refcount);
    refcount = ID3D12Device_Release(tmp_device);
    ok(refcount == 2, "Got unexpected refcount %u.\n", (unsigned int)refcount);

    check_interface(heap, &IID_ID3D12Object, true);
    check_interface(heap, &IID_ID3D12DeviceChild, true);
    check_interface(heap, &IID_ID3D12Pageable, true);
    check_interface(heap, &IID_ID3D12Heap, true);

    result_desc = ID3D12Heap_GetDesc(heap);
    check_heap_desc(&result_desc, &desc);

    refcount = ID3D12Heap_Release(heap);
    ok(!refcount, "ID3D12Heap has %u references left.\n", (unsigned int)refcount);

    desc.SizeInBytes = 0;
    hr = ID3D12Device_CreateHeap(device, &desc, &IID_ID3D12Heap, (void **)&heap);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    desc.SizeInBytes = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
    desc.Flags = D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES | D3D12_HEAP_FLAG_ALLOW_DISPLAY;
    hr = ID3D12Device_CreateHeap(device, &desc, &IID_ID3D12Heap, (void **)&heap);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    heap = (void *)0xdeadbeef;
    desc.Flags = D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES | D3D12_HEAP_FLAG_ALLOW_DISPLAY;
    hr = ID3D12Device_CreateHeap(device, &desc, &IID_ID3D12Heap, (void **)&heap);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    ok(!heap, "Got unexpected pointer %p.\n", heap);

    for (i = 0; i < ARRAY_SIZE(tests); ++i)
    {
        for (j = 0; j < ARRAY_SIZE(heap_flags); ++j)
        {
            vkd3d_test_push_context("Test %u, %s", i, heap_flags[j].name);

            desc.SizeInBytes = 10 * tests[i].alignment;
            desc.Alignment = tests[i].alignment;
            desc.Flags = heap_flags[j].flags;
            hr = ID3D12Device_CreateHeap(device, &desc, &IID_ID3D12Heap, (void **)&heap);
            ok(hr == tests[i].expected_hr, "Got hr %#x, expected %#x.\n", hr, tests[i].expected_hr);
            if (SUCCEEDED(hr))
            {
                result_desc = ID3D12Heap_GetDesc(heap);
                check_heap_desc(&result_desc, &desc);

                refcount = ID3D12Heap_Release(heap);
                ok(!refcount, "ID3D12Heap has %u references left.\n", (unsigned int)refcount);
            }

            vkd3d_test_pop_context();
        }
    }

    hr = ID3D12Device_CheckFeatureSupport(device, D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(options));
    ok(hr == S_OK, "Failed to check feature support, hr %#x.\n", hr);
    if (options.ResourceHeapTier < D3D12_RESOURCE_HEAP_TIER_2)
    {
        skip("Resource heap tier %u.\n", options.ResourceHeapTier);
        goto done;
    }

    desc.SizeInBytes = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
    desc.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
    desc.Flags = D3D12_HEAP_FLAG_ALLOW_ALL_BUFFERS_AND_TEXTURES;
    hr = ID3D12Device_CreateHeap(device, &desc, &IID_ID3D12Heap, (void **)&heap);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    result_desc = ID3D12Heap_GetDesc(heap);
    check_heap_desc(&result_desc, &desc);
    refcount = ID3D12Heap_Release(heap);
    ok(!refcount, "ID3D12Heap has %u references left.\n", (unsigned int)refcount);

    memset(&architecture, 0, sizeof(architecture));
    hr = ID3D12Device_CheckFeatureSupport(device, D3D12_FEATURE_ARCHITECTURE, &architecture, sizeof(architecture));
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    for (i = D3D12_HEAP_TYPE_DEFAULT; i < D3D12_HEAP_TYPE_CUSTOM; ++i)
    {
        vkd3d_test_push_context("Test %u\n", i);
        desc.Properties = ID3D12Device_GetCustomHeapProperties(device, 1, i);
        ok(desc.Properties.Type == D3D12_HEAP_TYPE_CUSTOM, "Got unexpected heap type %#x.\n", desc.Properties.Type);

        switch (i)
        {
            case D3D12_HEAP_TYPE_DEFAULT:
                ok(desc.Properties.CPUPageProperty == D3D12_CPU_PAGE_PROPERTY_NOT_AVAILABLE,
                        "Got unexpected CPUPageProperty %#x.\n", desc.Properties.CPUPageProperty);
                ok(desc.Properties.MemoryPoolPreference == (architecture.UMA
                        ? D3D12_MEMORY_POOL_L0 : D3D12_MEMORY_POOL_L1),
                        "Got unexpected MemoryPoolPreference %#x.\n", desc.Properties.MemoryPoolPreference);
                break;

            case D3D12_HEAP_TYPE_UPLOAD:
                ok(desc.Properties.CPUPageProperty == (architecture.CacheCoherentUMA
                        ? D3D12_CPU_PAGE_PROPERTY_WRITE_BACK : D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE),
                        "Got unexpected CPUPageProperty %#x.\n", desc.Properties.CPUPageProperty);
                ok(desc.Properties.MemoryPoolPreference == D3D12_MEMORY_POOL_L0,
                        "Got unexpected MemoryPoolPreference %#x.\n", desc.Properties.MemoryPoolPreference);
                break;

            case D3D12_HEAP_TYPE_READBACK:
                ok(desc.Properties.CPUPageProperty == D3D12_CPU_PAGE_PROPERTY_WRITE_BACK,
                        "Got unexpected CPUPageProperty %#x.\n", desc.Properties.CPUPageProperty);
                ok(desc.Properties.MemoryPoolPreference == D3D12_MEMORY_POOL_L0,
                        "Got unexpected MemoryPoolPreference %#x.\n", desc.Properties.MemoryPoolPreference);
                break;

            default:
              ok(0, "Invalid heap type %#x.\n", i);
              continue;
        }

        hr = ID3D12Device_CreateHeap(device, &desc, &IID_ID3D12Heap, (void **)&heap);
        ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
        result_desc = ID3D12Heap_GetDesc(heap);
        check_heap_desc(&result_desc, &desc);
        ID3D12Heap_Release(heap);

        vkd3d_test_pop_context();
    }

    is_pool_L1_supported = is_memory_pool_L1_supported(device);
    gpu_upload_heap_supported = is_gpu_upload_heap_supported(device);
    desc.Properties.Type = D3D12_HEAP_TYPE_CUSTOM;
    desc.Properties.CreationNodeMask = 1;
    desc.Properties.VisibleNodeMask = 1;
    for (i = 0; i < ARRAY_SIZE(custom_tests); ++i)
    {
        vkd3d_test_push_context("Test %u", i);

        desc.Properties.CPUPageProperty = custom_tests[i].page_property;
        desc.Properties.MemoryPoolPreference = custom_tests[i].pool_preference;
        hr = ID3D12Device_CreateHeap(device, &desc, &IID_ID3D12Heap, (void **)&heap);
        expected_hr = custom_tests[i].expected_hr;
        if (custom_tests[i].pool_preference == D3D12_MEMORY_POOL_L1 && !is_pool_L1_supported)
            expected_hr = E_INVALIDARG;
        if (custom_tests[i].pool_preference == D3D12_MEMORY_POOL_L1
                && custom_tests[i].page_property == D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE
                && !gpu_upload_heap_supported)
            expected_hr = E_INVALIDARG;
        ok(hr == expected_hr, "Got hr %#x, expected %#x.\n", hr, expected_hr);
        if (SUCCEEDED(hr))
        {
            result_desc = ID3D12Heap_GetDesc(heap);
            check_heap_desc(&result_desc, &desc);

            refcount = ID3D12Heap_Release(heap);
            ok(!refcount, "ID3D12Heap has %u references left.\n", (unsigned int)refcount);
        }

        vkd3d_test_pop_context();
    }

    if (SUCCEEDED(ID3D12Device_QueryInterface(device, &IID_ID3D12Device4, (void **)&device4)))
    {
        desc.SizeInBytes = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
        memset(&desc.Properties, 0, sizeof(desc.Properties));
        desc.Properties.Type = D3D12_HEAP_TYPE_DEFAULT;
        desc.Alignment = 0;
        desc.Flags = D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES;
        hr = ID3D12Device4_CreateHeap1(device4, &desc, NULL, &IID_ID3D12Heap, (void **)&heap);
        ok(hr == S_OK, "Failed to create heap, hr %#x.\n", hr);

        ID3D12Heap_Release(heap);

        ID3D12Device4_Release(device4);
    }

done:
    refcount = ID3D12Device_Release(device);
    ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount);
}

static void test_create_placed_resource(void)
{
    ID3D12ProtectedResourceSession *protected_session;
    D3D12_GPU_VIRTUAL_ADDRESS gpu_address;
    ID3D12Resource *resource, *resource_2;
    D3D12_RESOURCE_DESC1 resource_desc1;
    D3D12_RESOURCE_DESC resource_desc;
    ID3D12Device *device, *tmp_device;
    D3D12_CLEAR_VALUE clear_value;
    D3D12_RESOURCE_STATES state;
    ID3D12Resource2 *resource2;
    ID3D12Resource1 *resource1;
    D3D12_HEAP_DESC heap_desc;
    ID3D12Device8 *device8;
    ID3D12Heap *heap;
    unsigned int i;
    ULONG refcount;
    HRESULT hr;

    static const struct
    {
        D3D12_HEAP_TYPE heap_type;
        D3D12_RESOURCE_FLAGS flags;
    }
    invalid_buffer_desc_tests[] =
    {
        /* Render target or unordered access resources are not allowed with UPLOAD or READBACK. */
        {D3D12_HEAP_TYPE_UPLOAD,   D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET},
        {D3D12_HEAP_TYPE_READBACK, D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET},
        {D3D12_HEAP_TYPE_UPLOAD,   D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS},
        {D3D12_HEAP_TYPE_READBACK, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS},
    };

    if (!(device = create_device()))
    {
        skip("Failed to create device.\n");
        return;
    }

    heap_desc.SizeInBytes = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT * 2;
    memset(&heap_desc.Properties, 0, sizeof(heap_desc.Properties));
    heap_desc.Properties.Type = D3D12_HEAP_TYPE_DEFAULT;
    heap_desc.Alignment = 0;
    heap_desc.Flags = D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS;
    hr = ID3D12Device_CreateHeap(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_BUFFER;
    resource_desc.Alignment = 0;
    resource_desc.Width = 32;
    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;

    clear_value.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    clear_value.Color[0] = 1.0f;
    clear_value.Color[1] = 0.0f;
    clear_value.Color[2] = 0.0f;
    clear_value.Color[3] = 1.0f;

    refcount = get_refcount(heap);
    ok(refcount == 1, "Got unexpected refcount %u.\n", (unsigned int)refcount);

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

    refcount = get_refcount(heap);
    ok(refcount == 1, "Got unexpected refcount %u.\n", (unsigned int)refcount);

    refcount = get_refcount(device);
    ok(refcount == 3, "Got unexpected refcount %u.\n", (unsigned int)refcount);
    hr = ID3D12Resource_GetDevice(resource, &IID_ID3D12Device, (void **)&tmp_device);
    ok(hr == S_OK, "Failed to get device, hr %#x.\n", hr);
    refcount = get_refcount(device);
    ok(refcount == 4, "Got unexpected refcount %u.\n", (unsigned int)refcount);
    refcount = ID3D12Device_Release(tmp_device);
    ok(refcount == 3, "Got unexpected refcount %u.\n", (unsigned int)refcount);

    check_interface(resource, &IID_ID3D12Object, true);
    check_interface(resource, &IID_ID3D12DeviceChild, true);
    check_interface(resource, &IID_ID3D12Pageable, true);
    check_interface(resource, &IID_ID3D12Resource, true);

    gpu_address = ID3D12Resource_GetGPUVirtualAddress(resource);
    ok(gpu_address, "Got unexpected GPU virtual address %#"PRIx64".\n", gpu_address);

    if (SUCCEEDED(ID3D12Resource_QueryInterface(resource, &IID_ID3D12Resource1, (void **)&resource1)))
    {
        hr = ID3D12Resource1_GetProtectedResourceSession(resource1, &IID_ID3D12ProtectedResourceSession,
                (void **)&protected_session);
        ok(hr == DXGI_ERROR_NOT_FOUND, "Got unexpected hr %#x.\n", hr);
        ID3D12Resource1_Release(resource1);
    }

    refcount = ID3D12Resource_Release(resource);
    ok(!refcount, "ID3D12Resource has %u references left.\n", (unsigned int)refcount);

    if (SUCCEEDED(ID3D12Device_QueryInterface(device, &IID_ID3D12Device8, (void **)&device8)))
    {
        resource_desc1.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
        resource_desc1.Alignment = 0;
        resource_desc1.Width = 32;
        resource_desc1.Height = 1;
        resource_desc1.DepthOrArraySize = 1;
        resource_desc1.MipLevels = 1;
        resource_desc1.Format = DXGI_FORMAT_UNKNOWN;
        resource_desc1.SampleDesc.Count = 1;
        resource_desc1.SampleDesc.Quality = 0;
        resource_desc1.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
        resource_desc1.Flags = 0;
        memset(&resource_desc1.SamplerFeedbackMipRegion, 0, sizeof(resource_desc1.SamplerFeedbackMipRegion));

        hr = ID3D12Device8_CreatePlacedResource1(device8, heap, 0,
                &resource_desc1, D3D12_RESOURCE_STATE_COMMON, NULL, &IID_ID3D12Resource2, (void **)&resource2);
        ok(hr == S_OK, "Failed to create placed resource, hr %#x.\n", hr);

        check_interface(resource2, &IID_ID3D12Resource2, true);

        ID3D12Resource2_Release(resource2);
        ID3D12Device8_Release(device8);
    }

    /* The clear value must be NULL for buffers. */
    hr = ID3D12Device_CreatePlacedResource(device, heap, 0,
            &resource_desc, D3D12_RESOURCE_STATE_COMMON, &clear_value,
            &IID_ID3D12Resource, (void **)&resource);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    /* Buffer + offset too large for the heap. */
    hr = ID3D12Device_CreatePlacedResource(device, heap, heap_desc.SizeInBytes,
            &resource_desc, D3D12_RESOURCE_STATE_COMMON, NULL,
            &IID_ID3D12Resource, (void **)&resource);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    /* Test heap peristence when its resource count is non-zero. */
    hr = ID3D12Device_CreatePlacedResource(device, heap, 0,
            &resource_desc, D3D12_RESOURCE_STATE_COMMON, NULL, &IID_ID3D12Resource, (void **)&resource);
    ok(hr == S_OK, "Failed to create placed resource, hr %#x.\n", hr);

    ID3D12Heap_Release(heap);
    refcount = get_refcount(heap);
    ok(!refcount, "Got unexpected refcount %u.\n", (unsigned int)refcount);

    hr = ID3D12Device_CreatePlacedResource(device, heap, D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT,
            &resource_desc, D3D12_RESOURCE_STATE_COMMON, NULL, &IID_ID3D12Resource, (void **)&resource_2);
    ok(hr == S_OK, "Failed to create placed resource, hr %#x.\n", hr);

    ID3D12Resource_Release(resource);
    ID3D12Resource_Release(resource_2);

    for (i = 0; i < ARRAY_SIZE(invalid_buffer_desc_tests); ++i)
    {
        heap_desc.SizeInBytes = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
        memset(&heap_desc.Properties, 0, sizeof(heap_desc.Properties));
        heap_desc.Properties.Type = invalid_buffer_desc_tests[i].heap_type;
        heap_desc.Alignment = 0;
        heap_desc.Flags = D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS;
        hr = ID3D12Device_CreateHeap(device, &heap_desc, &IID_ID3D12Heap, (void **)&heap);
        ok(hr == S_OK, "Test %u: Failed to create heap, hr %#x.\n", i, hr);

        resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
        resource_desc.Alignment = 0;
        resource_desc.Width = 32;
        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 = invalid_buffer_desc_tests[i].flags;

        if (invalid_buffer_desc_tests[i].heap_type == D3D12_HEAP_TYPE_UPLOAD)
            state = D3D12_RESOURCE_STATE_GENERIC_READ;
        else
            state = D3D12_RESOURCE_STATE_COPY_DEST;

        hr = ID3D12Device_CreatePlacedResource(device, heap, 0,
                &resource_desc, state, &clear_value, &IID_ID3D12Resource, (void **)&resource);
        ok(hr == E_INVALIDARG, "Test %u: Got unexpected hr %#x.\n", i, hr);

        ID3D12Heap_Release(heap);
    }

    refcount = ID3D12Device_Release(device);
    ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount);
}

static void test_create_reserved_resource(void)
{
    D3D12_GPU_VIRTUAL_ADDRESS gpu_address;
    D3D12_HEAP_PROPERTIES heap_properties;
    D3D12_RESOURCE_DESC resource_desc;
    D3D12_CLEAR_VALUE clear_value;
    D3D12_HEAP_FLAGS heap_flags;
    ID3D12Resource *resource;
    bool standard_swizzle;
    ID3D12Device *device;
    ULONG refcount;
    HRESULT hr;
    void *ptr;

    if (!(device = create_device()))
    {
        skip("Failed to create device.\n");
        return;
    }

    if (get_tiled_resources_tier(device) == D3D12_TILED_RESOURCES_TIER_NOT_SUPPORTED)
    {
        skip("Tiled resources are not supported.\n");
        goto done;
    }

    standard_swizzle = is_standard_swizzle_64kb_supported(device);
    trace("Standard swizzle 64KB: %#x.\n", standard_swizzle);

    resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
    resource_desc.Alignment = 0;
    resource_desc.Width = 32;
    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(device,
            &resource_desc, D3D12_RESOURCE_STATE_COMMON, NULL,
            &IID_ID3D12Resource, (void **)&resource);
    ok(hr == S_OK, "Failed to create reserved resource, hr %#x.\n", hr);

    check_interface(resource, &IID_ID3D12Object, true);
    check_interface(resource, &IID_ID3D12DeviceChild, true);
    check_interface(resource, &IID_ID3D12Pageable, true);
    check_interface(resource, &IID_ID3D12Resource, true);

    gpu_address = ID3D12Resource_GetGPUVirtualAddress(resource);
    ok(gpu_address, "Got unexpected GPU virtual address %#"PRIx64".\n", gpu_address);

    heap_flags = 0xdeadbeef;
    hr = ID3D12Resource_GetHeapProperties(resource, &heap_properties, &heap_flags);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    ok(heap_flags == 0xdeadbeef, "Got unexpected heap flags %#x.\n", heap_flags);

    /* Map() is not allowed on reserved resources */
    hr = ID3D12Resource_Map(resource, 0, NULL, &ptr);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    refcount = ID3D12Resource_Release(resource);
    ok(!refcount, "ID3D12Resource has %u references left.\n", (unsigned int)refcount);

    /* The clear value must be NULL for buffers. */
    clear_value.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    clear_value.Color[0] = 1.0f;
    clear_value.Color[1] = 0.0f;
    clear_value.Color[2] = 0.0f;
    clear_value.Color[3] = 1.0f;

    hr = ID3D12Device_CreateReservedResource(device,
            &resource_desc, D3D12_RESOURCE_STATE_COMMON, &clear_value,
            &IID_ID3D12Resource, (void **)&resource);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    /* D3D12_TEXTURE_LAYOUT_ROW_MAJOR must be used for buffers. */
    resource_desc.Layout = D3D12_TEXTURE_LAYOUT_64KB_UNDEFINED_SWIZZLE;
    hr = ID3D12Device_CreateReservedResource(device,
            &resource_desc, D3D12_RESOURCE_STATE_COMMON, NULL,
            &IID_ID3D12Resource, (void **)&resource);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    resource_desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
    hr = ID3D12Device_CreateReservedResource(device,
            &resource_desc, D3D12_RESOURCE_STATE_COMMON, NULL,
            &IID_ID3D12Resource, (void **)&resource);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    /* D3D12_TEXTURE_LAYOUT_64KB_UNDEFINED_SWIZZLE must be used for textures. */
    resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
    resource_desc.Alignment = 0;
    resource_desc.Width = 64;
    resource_desc.Height = 64;
    resource_desc.DepthOrArraySize = 1;
    resource_desc.MipLevels = 4;
    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 = 0;

    hr = ID3D12Device_CreateReservedResource(device,
            &resource_desc, D3D12_RESOURCE_STATE_COMMON, NULL,
            &IID_ID3D12Resource, (void **)&resource);
    ok(hr == S_OK, "Failed to create reserved resource, hr %#x.\n", hr);
    refcount = ID3D12Resource_Release(resource);
    ok(!refcount, "ID3D12Resource has %u references left.\n", (unsigned int)refcount);

    resource_desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
    hr = ID3D12Device_CreateReservedResource(device,
            &resource_desc, D3D12_RESOURCE_STATE_COMMON, NULL,
            &IID_ID3D12Resource, (void **)&resource);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    resource_desc.MipLevels = 1;
    resource_desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
    hr = ID3D12Device_CreateReservedResource(device,
            &resource_desc, D3D12_RESOURCE_STATE_COMMON, NULL,
            &IID_ID3D12Resource, (void **)&resource);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    resource_desc.Layout = D3D12_TEXTURE_LAYOUT_64KB_STANDARD_SWIZZLE;
    hr = ID3D12Device_CreateReservedResource(device,
            &resource_desc, D3D12_RESOURCE_STATE_COMMON, NULL,
            &IID_ID3D12Resource, (void **)&resource);
    ok(hr == (standard_swizzle ? S_OK : E_INVALIDARG) || broken(test_options.use_warp_device),
            "Got unexpected hr %#x.\n", hr);
    if (SUCCEEDED(hr))
        ID3D12Resource_Release(resource);

done:
    refcount = ID3D12Device_Release(device);
    ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount);
}

static void test_create_descriptor_heap(void)
{
    D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle;
    D3D12_DESCRIPTOR_HEAP_DESC heap_desc;
    ID3D12Device *device, *tmp_device;
    ID3D12DescriptorHeap *heap;
    ULONG refcount;
    HRESULT hr;

    if (!(device = create_device()))
    {
        skip("Failed to create device.\n");
        return;
    }

    heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
    heap_desc.NumDescriptors = 16;
    heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
    heap_desc.NodeMask = 0;
    hr = ID3D12Device_CreateDescriptorHeap(device, &heap_desc, &IID_ID3D12DescriptorHeap, (void **)&heap);
    ok(hr == S_OK, "Failed to create descriptor heap, hr %#x.\n", hr);

    refcount = get_refcount(device);
    ok(refcount == 2, "Got unexpected refcount %u.\n", (unsigned int)refcount);
    hr = ID3D12DescriptorHeap_GetDevice(heap, &IID_ID3D12Device, (void **)&tmp_device);
    ok(hr == S_OK, "Failed to get device, hr %#x.\n", hr);
    refcount = get_refcount(device);
    ok(refcount == 3, "Got unexpected refcount %u.\n", (unsigned int)refcount);
    refcount = ID3D12Device_Release(tmp_device);
    ok(refcount == 2, "Got unexpected refcount %u.\n", (unsigned int)refcount);

    check_interface(heap, &IID_ID3D12Object, true);
    check_interface(heap, &IID_ID3D12DeviceChild, true);
    check_interface(heap, &IID_ID3D12Pageable, true);
    check_interface(heap, &IID_ID3D12DescriptorHeap, true);

    refcount = ID3D12DescriptorHeap_Release(heap);
    ok(!refcount, "ID3D12DescriptorHeap has %u references left.\n", (unsigned int)refcount);

    heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
    heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
    hr = ID3D12Device_CreateDescriptorHeap(device, &heap_desc, &IID_ID3D12DescriptorHeap, (void **)&heap);
    ok(hr == S_OK, "Failed to create descriptor heap, hr %#x.\n", hr);
    gpu_handle = ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(heap);
    ok(gpu_handle.ptr, "Got NULL descriptor handle.\n");
    refcount = ID3D12DescriptorHeap_Release(heap);
    ok(!refcount, "ID3D12DescriptorHeap has %u references left.\n", (unsigned int)refcount);

    heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER;
    heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
    hr = ID3D12Device_CreateDescriptorHeap(device, &heap_desc, &IID_ID3D12DescriptorHeap, (void **)&heap);
    ok(hr == S_OK, "Failed to create descriptor heap, hr %#x.\n", hr);
    gpu_handle = ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(heap);
    ok(gpu_handle.ptr, "Got NULL descriptor handle.\n");
    refcount = ID3D12DescriptorHeap_Release(heap);
    ok(!refcount, "ID3D12DescriptorHeap has %u references left.\n", (unsigned int)refcount);

    heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
    heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
    hr = ID3D12Device_CreateDescriptorHeap(device, &heap_desc, &IID_ID3D12DescriptorHeap, (void **)&heap);
    ok(hr == S_OK, "Failed to create descriptor heap, hr %#x.\n", hr);
    refcount = ID3D12DescriptorHeap_Release(heap);
    ok(!refcount, "ID3D12DescriptorHeap has %u references left.\n", (unsigned int)refcount);

    heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
    hr = ID3D12Device_CreateDescriptorHeap(device, &heap_desc, &IID_ID3D12DescriptorHeap, (void **)&heap);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV;
    heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
    hr = ID3D12Device_CreateDescriptorHeap(device, &heap_desc, &IID_ID3D12DescriptorHeap, (void **)&heap);
    ok(hr == S_OK, "Failed to create descriptor heap, hr %#x.\n", hr);
    refcount = ID3D12DescriptorHeap_Release(heap);
    ok(!refcount, "ID3D12DescriptorHeap has %u references left.\n", (unsigned int)refcount);

    heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
    hr = ID3D12Device_CreateDescriptorHeap(device, &heap_desc, &IID_ID3D12DescriptorHeap, (void **)&heap);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    refcount = ID3D12Device_Release(device);
    ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount);
}

static void test_create_sampler(void)
{
    D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle;
    D3D12_DESCRIPTOR_HEAP_DESC heap_desc;
    unsigned int sampler_increment_size;
    D3D12_SAMPLER_DESC sampler_desc;
    ID3D12DescriptorHeap *heap;
    ID3D12Device *device;
    unsigned int i;
    ULONG refcount;
    HRESULT hr;

    if (!(device = create_device()))
    {
        skip("Failed to create device.\n");
        return;
    }

    sampler_increment_size = ID3D12Device_GetDescriptorHandleIncrementSize(device,
            D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
    trace("Sampler descriptor handle increment size: %u.\n", sampler_increment_size);
    ok(sampler_increment_size, "Got unexpected increment size %#x.\n", sampler_increment_size);

    heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER;
    heap_desc.NumDescriptors = 16;
    heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
    heap_desc.NodeMask = 0;
    hr = ID3D12Device_CreateDescriptorHeap(device, &heap_desc, &IID_ID3D12DescriptorHeap, (void **)&heap);
    ok(SUCCEEDED(hr), "Failed to create descriptor heap, hr %#x.\n", hr);

    cpu_handle = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(heap);
    memset(&sampler_desc, 0, sizeof(sampler_desc));
    sampler_desc.Filter = D3D12_FILTER_MIN_MAG_MIP_POINT;
    sampler_desc.AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
    sampler_desc.AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
    sampler_desc.AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
    sampler_desc.MaxLOD = D3D12_FLOAT32_MAX;
    ID3D12Device_CreateSampler(device, &sampler_desc, cpu_handle);

    cpu_handle.ptr += sampler_increment_size;
    sampler_desc.Filter = D3D12_FILTER_MIN_MAG_POINT_MIP_LINEAR;
    for (i = 1; i < heap_desc.NumDescriptors; ++i)
    {
        ID3D12Device_CreateSampler(device, &sampler_desc, cpu_handle);
        cpu_handle.ptr += sampler_increment_size;
    }

    trace("MinMaxFiltering: %#x.\n", is_min_max_filtering_supported(device));
    if (is_min_max_filtering_supported(device))
    {
        cpu_handle = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(heap);
        sampler_desc.Filter = D3D12_FILTER_MINIMUM_MIN_MAG_MIP_POINT;
        ID3D12Device_CreateSampler(device, &sampler_desc, cpu_handle);

        cpu_handle.ptr += sampler_increment_size;
        sampler_desc.Filter = D3D12_FILTER_MAXIMUM_MIN_MAG_MIP_POINT;
        ID3D12Device_CreateSampler(device, &sampler_desc, cpu_handle);
    }

    cpu_handle = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(heap);
    sampler_desc.Filter = D3D12_FILTER_COMPARISON_MIN_MAG_MIP_POINT;
    sampler_desc.ComparisonFunc = D3D12_COMPARISON_FUNC_LESS;
    ID3D12Device_CreateSampler(device, &sampler_desc, cpu_handle);

    refcount = ID3D12DescriptorHeap_Release(heap);
    ok(!refcount, "ID3D12DescriptorHeap has %u references left.\n", (unsigned int)refcount);
    refcount = ID3D12Device_Release(device);
    ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount);
}

static void test_create_unordered_access_view(void)
{
    D3D12_UNORDERED_ACCESS_VIEW_DESC uav_desc;
    D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle;
    ID3D12Resource *buffer, *texture;
    unsigned int descriptor_size;
    ID3D12DescriptorHeap *heap;
    ID3D12Device *device;
    ULONG refcount;

    if (!(device = create_device()))
    {
        skip("Failed to create device.\n");
        return;
    }

    descriptor_size = ID3D12Device_GetDescriptorHandleIncrementSize(device,
            D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
    trace("CBV/SRV/UAV descriptor size: %u.\n", descriptor_size);
    ok(descriptor_size, "Got unexpected descriptor size %#x.\n", descriptor_size);

    heap = create_gpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 16);

    buffer = create_default_buffer(device, 64 * sizeof(float),
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

    cpu_handle = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(heap);
    uav_desc.Format = DXGI_FORMAT_R32_FLOAT;
    uav_desc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
    uav_desc.Buffer.FirstElement = 0;
    uav_desc.Buffer.NumElements = 64;
    uav_desc.Buffer.StructureByteStride = 0;
    uav_desc.Buffer.CounterOffsetInBytes = 0;
    uav_desc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_NONE;
    ID3D12Device_CreateUnorderedAccessView(device, buffer, NULL, &uav_desc, cpu_handle);

    cpu_handle.ptr += descriptor_size;

    /* DXGI_FORMAT_R32_UINT view for DXGI_FORMAT_R8G8B8A8_TYPELESS resources. */
    texture = create_default_texture(device, 8, 8, DXGI_FORMAT_R8G8B8A8_TYPELESS,
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

    uav_desc.Format = DXGI_FORMAT_R32_UINT;
    uav_desc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D;
    uav_desc.Texture2D.MipSlice = 0;
    uav_desc.Texture2D.PlaneSlice = 0;
    ID3D12Device_CreateUnorderedAccessView(device, texture, NULL, &uav_desc, cpu_handle);

    ID3D12Resource_Release(buffer);
    ID3D12Resource_Release(texture);
    refcount = ID3D12DescriptorHeap_Release(heap);
    ok(!refcount, "ID3D12DescriptorHeap has %u references left.\n", (unsigned int)refcount);
    refcount = ID3D12Device_Release(device);
    ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount);
}

static void test_create_root_signature(void)
{
    ID3D12RootSignature *root_signature, *root_signature2;
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    D3D12_STATIC_SAMPLER_DESC static_samplers[1];
    D3D12_DESCRIPTOR_RANGE descriptor_ranges[2];
    D3D12_RESOURCE_BINDING_TIER binding_tier;
    D3D12_ROOT_PARAMETER root_parameters[3];
    ID3D12Device *device, *tmp_device;
    unsigned int size;
    ULONG refcount;
    HRESULT hr;

    static const GUID test_guid
            = {0xfdb37466, 0x428f, 0x4edf, {0xa3, 0x7f, 0x9b, 0x1d, 0xf4, 0x88, 0xc5, 0xfc}};

    if (!(device = create_device()))
    {
        skip("Failed to create device.\n");
        return;
    }

    /* Tier 2 is required for unbounded SRVs and Tier > 2 for unbounded CBVs and UAVs
     * due to the need for partial binding support. It is also required for overlapping
     * ranges of different types. */
    binding_tier = get_resource_binding_tier(device);

    /* empty root signature */
    root_signature_desc.NumParameters = 0;
    root_signature_desc.pParameters = NULL;
    root_signature_desc.NumStaticSamplers = 0;
    root_signature_desc.pStaticSamplers = NULL;
    root_signature_desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_NONE;
    hr = create_root_signature(device, &root_signature_desc, &root_signature);
    ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr);

    /* Creating the same root signature twice returns the same interface pointer.
     *
     * However, the root signature object actually gets destroyed after releasing
     * the last reference. Re-creating the same root descriptor later does not
     * reliably return the same interface pointer, although it might do so if the
     * heap manager reuses the allocation. */
    hr = create_root_signature(device, &root_signature_desc, &root_signature2);
    ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr);
    todo ok(root_signature == root_signature2, "Got different root signature pointers.\n");
    refcount = ID3D12RootSignature_Release(root_signature2);
    todo ok(refcount == 1, "ID3D12RootSignature has %u references left.\n", (unsigned int)refcount);

    hr = 0xdeadbeef;
    hr = ID3D12RootSignature_SetPrivateData(root_signature, &test_guid, sizeof(hr), &hr);
    ok(hr == S_OK, "Failed to set private data, hr %#x.\n", hr);
    hr = ID3D12RootSignature_GetPrivateData(root_signature, &test_guid, &size, NULL);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);

    refcount = ID3D12RootSignature_Release(root_signature);
    ok(!refcount, "ID3D12RootSignature has %u references left.\n", (unsigned int)refcount);

    hr = create_root_signature(device, &root_signature_desc, &root_signature);
    ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr);
    hr = ID3D12RootSignature_GetPrivateData(root_signature, &test_guid, &size, NULL);
    ok(hr == DXGI_ERROR_NOT_FOUND, "Got unexpected hr %#x.\n", hr);
    refcount = ID3D12RootSignature_Release(root_signature);
    ok(!refcount, "ID3D12RootSignature has %u references left.\n", (unsigned int)refcount);

    /* descriptor table */
    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;
    root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameters[0].DescriptorTable.NumDescriptorRanges = 1;
    root_parameters[0].DescriptorTable.pDescriptorRanges = descriptor_ranges;
    root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    root_signature_desc.NumParameters = 1;
    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(device, &root_signature_desc, &root_signature);
    ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr);

    refcount = get_refcount(device);
    ok(refcount == 2, "Got unexpected refcount %u.\n", (unsigned int)refcount);
    hr = ID3D12RootSignature_GetDevice(root_signature, &IID_ID3D12Device, (void **)&tmp_device);
    ok(hr == S_OK, "Failed to get device, hr %#x.\n", hr);
    refcount = get_refcount(device);
    ok(refcount == 3, "Got unexpected refcount %u.\n", (unsigned int)refcount);
    refcount = ID3D12Device_Release(tmp_device);
    ok(refcount == 2, "Got unexpected refcount %u.\n", (unsigned int)refcount);

    check_interface(root_signature, &IID_ID3D12Object, true);
    check_interface(root_signature, &IID_ID3D12DeviceChild, true);
    check_interface(root_signature, &IID_ID3D12Pageable, false);
    check_interface(root_signature, &IID_ID3D12RootSignature, true);

    hr = create_root_signature(device, &root_signature_desc, &root_signature2);
    ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr);
    todo ok(root_signature == root_signature2, "Got different root signature pointers.\n");
    refcount = ID3D12RootSignature_Release(root_signature2);
    todo ok(refcount == 1, "ID3D12RootSignature has %u references left.\n", (unsigned int)refcount);

    refcount = ID3D12RootSignature_Release(root_signature);
    ok(!refcount, "ID3D12RootSignature has %u references left.\n", (unsigned int)refcount);

    /* Overlapping ranges but unique register indices. */
    descriptor_ranges[0].NumDescriptors = 8;
    descriptor_ranges[1].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
    descriptor_ranges[1].NumDescriptors = 2;
    descriptor_ranges[1].BaseShaderRegister = 8;
    descriptor_ranges[1].RegisterSpace = 0;
    descriptor_ranges[1].OffsetInDescriptorsFromTableStart = 7;
    root_parameters[0].DescriptorTable.NumDescriptorRanges = 2;
    hr = create_root_signature(device, &root_signature_desc, &root_signature);
    ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr);
    ID3D12RootSignature_Release(root_signature);

    /* Separate ranges with ambiguous register indices. */
    descriptor_ranges[1].BaseShaderRegister = 7;
    descriptor_ranges[1].OffsetInDescriptorsFromTableStart = 8;
    hr = create_root_signature(device, &root_signature_desc, &root_signature);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    /* Identical ranges and register indices but different type. */
    descriptor_ranges[1] = descriptor_ranges[0];
    descriptor_ranges[1].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_CBV;
    hr = create_root_signature(device, &root_signature_desc, &root_signature);
    ok(hr == S_OK || (binding_tier <= D3D12_RESOURCE_BINDING_TIER_2 && (hr == E_FAIL || hr == E_INVALIDARG)),
            "Got unexpected hr %#x.\n", hr);
    if (SUCCEEDED(hr))
        ID3D12RootSignature_Release(root_signature);

    /* sampler and SRV in the same descriptor table */
    descriptor_ranges[1].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER;
    descriptor_ranges[1].NumDescriptors = 1;
    descriptor_ranges[1].BaseShaderRegister = 2;
    descriptor_ranges[1].RegisterSpace = 0;
    descriptor_ranges[1].OffsetInDescriptorsFromTableStart = 10;
    root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameters[0].DescriptorTable.NumDescriptorRanges = 2;
    root_parameters[0].DescriptorTable.pDescriptorRanges = descriptor_ranges;
    root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    root_signature_desc.NumParameters = 1;
    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(device, &root_signature_desc, &root_signature);
    ok(hr == E_INVALIDARG, "Failed to create root signature, hr %#x.\n", hr);

    /* empty descriptor table */
    descriptor_ranges[0].NumDescriptors = 0;
    root_parameters[0].DescriptorTable.NumDescriptorRanges = 1;
    hr = create_root_signature(device, &root_signature_desc, &root_signature);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    /* descriptor range overflow
     * Windows results vary for overflowing to zero, but anything beyond that is invalid. */
    descriptor_ranges[0].NumDescriptors = 0x1000;
    descriptor_ranges[0].BaseShaderRegister = 0xfffff001;
    root_parameters[0].DescriptorTable.NumDescriptorRanges = 1;
    hr = create_root_signature(device, &root_signature_desc, &root_signature);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    /* root constants */
    root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
    root_parameters[0].Constants.ShaderRegister = 0;
    root_parameters[0].Constants.RegisterSpace = 0;
    root_parameters[0].Constants.Num32BitValues = 4;
    root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    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 = 8;
    root_parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;
    root_signature_desc.NumParameters = 2;
    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(device, &root_signature_desc, &root_signature);
    ok(hr == E_FAIL || hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    if (SUCCEEDED(hr))
        ID3D12RootSignature_Release(root_signature);
    root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
    hr = create_root_signature(device, &root_signature_desc, &root_signature);
    ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr);
    refcount = ID3D12RootSignature_Release(root_signature);
    ok(!refcount, "ID3D12RootSignature has %u references left.\n", (unsigned int)refcount);

    root_parameters[2].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
    root_parameters[2].Constants.ShaderRegister = 1;
    root_parameters[2].Constants.RegisterSpace = 0;
    root_parameters[2].Constants.Num32BitValues = 3;
    root_parameters[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    root_signature_desc.NumParameters = 3;
    hr = create_root_signature(device, &root_signature_desc, &root_signature);
    ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr);
    refcount = ID3D12RootSignature_Release(root_signature);
    ok(!refcount, "ID3D12RootSignature has %u references left.\n", (unsigned int)refcount);

    /* root descriptors */
    root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV;
    root_parameters[0].Descriptor.ShaderRegister = 0;
    root_parameters[0].Descriptor.RegisterSpace = 0;
    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_PIXEL;
    root_signature_desc.NumParameters = 2;
    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(device, &root_signature_desc, &root_signature);
    ok(hr == E_FAIL || hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    if (SUCCEEDED(hr))
        ID3D12RootSignature_Release(root_signature);
    root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_GEOMETRY;
    hr = create_root_signature(device, &root_signature_desc, &root_signature);
    ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr);
    refcount = ID3D12RootSignature_Release(root_signature);
    ok(!refcount, "ID3D12RootSignature has %u references left.\n", (unsigned int)refcount);

    /* Register conflicts. */

    /* Between two ranges in the same root table. */
    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] = descriptor_ranges[0];
    root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameters[0].DescriptorTable.NumDescriptorRanges = 2;
    root_parameters[0].DescriptorTable.pDescriptorRanges = descriptor_ranges;
    root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    root_signature_desc.NumParameters = 1;
    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(device, &root_signature_desc, &root_signature);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    /* Between two different root tables. */
    root_parameters[0].DescriptorTable.NumDescriptorRanges = 1;
    root_parameters[1] = root_parameters[0];
    root_parameters[1].DescriptorTable.pDescriptorRanges = &descriptor_ranges[1];
    root_signature_desc.NumParameters = 2;
    hr = create_root_signature(device, &root_signature_desc, &root_signature);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    if (SUCCEEDED(hr))
        ID3D12RootSignature_Release(root_signature);

    /* Between a root table and a root descriptor. */
    root_parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_SRV;
    root_parameters[1].Descriptor.RegisterSpace = 0;
    root_parameters[1].Descriptor.ShaderRegister = 0;
    root_parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    hr = create_root_signature(device, &root_signature_desc, &root_signature);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    if (SUCCEEDED(hr))
        ID3D12RootSignature_Release(root_signature);

    /* Between a root table and a root constant. */
    descriptor_ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_CBV;
    root_parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
    root_parameters[1].Constants.RegisterSpace = 0;
    root_parameters[1].Constants.ShaderRegister = 0;
    root_parameters[1].Constants.Num32BitValues = 1;
    root_parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    hr = create_root_signature(device, &root_signature_desc, &root_signature);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    if (SUCCEEDED(hr))
        ID3D12RootSignature_Release(root_signature);

    /* Between a root table and a static sampler. */
    descriptor_ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER;
    static_samplers[0].Filter = D3D12_FILTER_MIN_MAG_MIP_POINT;
    static_samplers[0].AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
    static_samplers[0].AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
    static_samplers[0].AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
    static_samplers[0].MipLODBias = 0.0f;
    static_samplers[0].MaxAnisotropy = 1;
    static_samplers[0].ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER;
    static_samplers[0].BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK;
    static_samplers[0].MinLOD = 0.0f;
    static_samplers[0].MaxLOD = 10.0f;
    static_samplers[0].ShaderRegister = 0;
    static_samplers[0].RegisterSpace = 0;
    static_samplers[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    root_signature_desc.NumParameters = 1;
    root_signature_desc.NumStaticSamplers = 1;
    root_signature_desc.pStaticSamplers = static_samplers;
    hr = create_root_signature(device, &root_signature_desc, &root_signature);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    if (SUCCEEDED(hr))
        ID3D12RootSignature_Release(root_signature);

    /* Unbounded descriptor ranges. */

    /* A bounded range overlapping an unbounded one, mapped to a different
     * register space of the same type. */
    descriptor_ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
    descriptor_ranges[0].NumDescriptors = UINT_MAX;
    descriptor_ranges[0].BaseShaderRegister = 0;
    descriptor_ranges[0].RegisterSpace = 0;
    descriptor_ranges[0].OffsetInDescriptorsFromTableStart = 0;
    descriptor_ranges[1].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
    descriptor_ranges[1].NumDescriptors = 1;
    descriptor_ranges[1].BaseShaderRegister = 16;
    descriptor_ranges[1].RegisterSpace = 1;
    descriptor_ranges[1].OffsetInDescriptorsFromTableStart = 16;
    root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameters[0].DescriptorTable.NumDescriptorRanges = 2;
    root_parameters[0].DescriptorTable.pDescriptorRanges = descriptor_ranges;
    root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    root_signature_desc.NumParameters = 1;
    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(device, &root_signature_desc, &root_signature);
    ok(hr == S_OK || (binding_tier == D3D12_RESOURCE_BINDING_TIER_1 && (hr == E_FAIL || hr == E_INVALIDARG)),
            "Got unexpected hr %#x.\n", hr);
    if (SUCCEEDED(hr))
    {
        refcount = ID3D12RootSignature_Release(root_signature);
        ok(!refcount, "Got unexpected refcount %u.\n", (unsigned int)refcount);
    }

    /* A bounded range overlapping an unbounded one, mapped to a different
     * register space of the same type. Using
     * D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND. */
    descriptor_ranges[1].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
    hr = create_root_signature(device, &root_signature_desc, &root_signature);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    /* A bounded range overlapping an unbounded one, mapped to the same
     * register space and type. */
    descriptor_ranges[1].RegisterSpace = 0;
    descriptor_ranges[1].OffsetInDescriptorsFromTableStart = 16;
    hr = create_root_signature(device, &root_signature_desc, &root_signature);
    /* This and similar tests later currently fail with E_FAIL when
     * VK_EXT_descriptor_indexing is not available. This check happens before
     * detecting the overlap that would trigger E_INVALIDARG. We still check
     * that we're seeing a failure. */
    todo_if(binding_tier <= D3D12_RESOURCE_BINDING_TIER_2)
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    ok(FAILED(hr), "Got unexpected hr %#x.\n", hr);

    /* A bounded range overlapping an unbounded one, mapped to the same
     * register space, but a different type. */
    descriptor_ranges[1].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_CBV;
    hr = create_root_signature(device, &root_signature_desc, &root_signature);
    ok(hr == S_OK || (binding_tier <= D3D12_RESOURCE_BINDING_TIER_2 && (hr == E_FAIL || hr == E_INVALIDARG)),
            "Got unexpected hr %#x.\n", hr);
    if (SUCCEEDED(hr))
    {
        refcount = ID3D12RootSignature_Release(root_signature);
        ok(!refcount, "Got unexpected refcount %u.\n", (unsigned int)refcount);
    }

    /* An unbounded range overlapping another unbounded range, mapped to the
     * same register space and type. */
    descriptor_ranges[1].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
    descriptor_ranges[1].NumDescriptors = UINT_MAX;
    hr = create_root_signature(device, &root_signature_desc, &root_signature);
    todo_if(binding_tier <= D3D12_RESOURCE_BINDING_TIER_2)
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    ok(FAILED(hr), "Got unexpected hr %#x.\n", hr);

    /* And unbounded range overlapping a bounded one, mapped to the same
     * register space and type. */
    descriptor_ranges[0].NumDescriptors = 16;
    descriptor_ranges[1].BaseShaderRegister = 0;
    descriptor_ranges[1].OffsetInDescriptorsFromTableStart = 15;
    hr = create_root_signature(device, &root_signature_desc, &root_signature);
    todo_if(binding_tier <= D3D12_RESOURCE_BINDING_TIER_2)
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    ok(FAILED(hr), "Got unexpected hr %#x.\n", hr);

    refcount = ID3D12Device_Release(device);
    ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount);
}

static void test_root_signature_limits(void)
{
    D3D12_DESCRIPTOR_RANGE descriptor_ranges[D3D12_MAX_ROOT_COST + 1];
    D3D12_ROOT_PARAMETER root_parameters[D3D12_MAX_ROOT_COST + 1];
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    ID3D12RootSignature *root_signature;
    ID3D12Device *device;
    ULONG refcount;
    unsigned int i;
    HRESULT hr;

    if (!(device = create_device()))
    {
        skip("Failed to create device.\n");
        return;
    }

    /* A descriptor table costs 1 DWORD. */
    for (i = 0; i < ARRAY_SIZE(root_parameters); ++i)
    {
        descriptor_ranges[i].RangeType = i % 2
                ? D3D12_DESCRIPTOR_RANGE_TYPE_SRV : D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
        descriptor_ranges[i].NumDescriptors = 1;
        descriptor_ranges[i].BaseShaderRegister = i / 2;
        descriptor_ranges[i].RegisterSpace = 0;
        descriptor_ranges[i].OffsetInDescriptorsFromTableStart = 0;
        root_parameters[i].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
        root_parameters[i].DescriptorTable.NumDescriptorRanges = 1;
        root_parameters[i].DescriptorTable.pDescriptorRanges = &descriptor_ranges[i];
        root_parameters[i].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    }

    root_signature_desc.NumParameters = D3D12_MAX_ROOT_COST;
    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(device, &root_signature_desc, &root_signature);
    ok(SUCCEEDED(hr), "Failed to create root signature, hr %#x.\n", hr);
    ID3D12RootSignature_Release(root_signature);

    root_signature_desc.NumParameters = D3D12_MAX_ROOT_COST + 1;
    hr = create_root_signature(device, &root_signature_desc, &root_signature);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    /* Windows results vary for overflowing to zero, but anything beyond that is invalid. */
    root_signature_desc.NumParameters = 1;
    descriptor_ranges[0].NumDescriptors = 0x1001;
    descriptor_ranges[0].OffsetInDescriptorsFromTableStart = 0xfffff000;
    hr = create_root_signature(device, &root_signature_desc, &root_signature);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    refcount = ID3D12Device_Release(device);
    ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount);
}

static void test_create_compute_pipeline_state(void)
{
    D3D12_COMPUTE_PIPELINE_STATE_DESC pipeline_state_desc;
    ID3D12PipelineState *pipeline_state, *pipeline_state2;
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    ID3D12RootSignature *root_signature;
    ID3D12Device *device, *tmp_device;
    ID3D10Blob *bytecode;
    ULONG refcount;
    HRESULT hr;

    static const DWORD cs_with_rs[] =
    {
#if 0
        [RootSignature("")]
        [numthreads(1, 1, 1)]
        void main()
        {
        }
#endif
        0x43425844, 0x215835dd, 0xdcf65f2e, 0x076d1ec0, 0xb1664d2b, 0x00000001, 0x00000098, 0x00000004,
        0x00000030, 0x00000040, 0x00000050, 0x00000078, 0x4e475349, 0x00000008, 0x00000000, 0x00000008,
        0x4e47534f, 0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x00000020, 0x00050050, 0x00000008,
        0x0100086a, 0x0400009b, 0x00000001, 0x00000001, 0x00000001, 0x0100003e, 0x30535452, 0x00000018,
        0x00000001, 0x00000000, 0x00000018, 0x00000000, 0x00000018, 0x00000000,
    };

    static const char shader_code[] =
            "[numthreads(1, 1, 1)]\n"
            "void main() { }\n";

    bytecode = compile_shader(shader_code, sizeof(shader_code) - 1, "cs_4_0");

    if (!(device = create_device()))
    {
        skip("Failed to create device.\n");

        refcount = ID3D10Blob_Release(bytecode);
        ok(!refcount, "ID3D10Blob has %u references left.\n", (unsigned int)refcount);

        return;
    }

    root_signature_desc.NumParameters = 0;
    root_signature_desc.pParameters = NULL;
    root_signature_desc.NumStaticSamplers = 0;
    root_signature_desc.pStaticSamplers = NULL;
    root_signature_desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_NONE;
    hr = create_root_signature(device, &root_signature_desc, &root_signature);
    ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr);

    refcount = get_refcount(device);
    ok(refcount == 2, "Got unexpected refcount %u.\n", (unsigned int)refcount);

    memset(&pipeline_state_desc, 0, sizeof(pipeline_state_desc));
    pipeline_state_desc.pRootSignature = root_signature;
    pipeline_state_desc.CS = shader_bytecode_from_blob(bytecode);
    pipeline_state_desc.NodeMask = 0;
    pipeline_state_desc.Flags = D3D12_PIPELINE_STATE_FLAG_NONE;

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

    hr = ID3D12Device_CreateComputePipelineState(device, &pipeline_state_desc,
            &IID_ID3D12PipelineState, (void **)&pipeline_state2);
    ok(hr == S_OK, "Failed to create compute pipeline, hr %#x.\n", hr);
    ok(pipeline_state != pipeline_state2, "Got the same pipeline state object.\n");
    refcount = ID3D12PipelineState_Release(pipeline_state2);
    ok(!refcount, "ID3D12PipelineState has %u references left.\n", (unsigned int)refcount);

    refcount = get_refcount(root_signature);
    ok(refcount == 1, "Got unexpected refcount %u.\n", (unsigned int)refcount);

    refcount = get_refcount(device);
    ok(refcount == 3, "Got unexpected refcount %u.\n", (unsigned int)refcount);
    hr = ID3D12PipelineState_GetDevice(pipeline_state, &IID_ID3D12Device, (void **)&tmp_device);
    ok(hr == S_OK, "Failed to get device, hr %#x.\n", hr);
    refcount = get_refcount(device);
    ok(refcount == 4, "Got unexpected refcount %u.\n", (unsigned int)refcount);
    refcount = ID3D12Device_Release(tmp_device);
    ok(refcount == 3, "Got unexpected refcount %u.\n", (unsigned int)refcount);

    check_interface(pipeline_state, &IID_ID3D12Object, true);
    check_interface(pipeline_state, &IID_ID3D12DeviceChild, true);
    check_interface(pipeline_state, &IID_ID3D12Pageable, true);
    check_interface(pipeline_state, &IID_ID3D12PipelineState, true);

    refcount = ID3D12PipelineState_Release(pipeline_state);
    ok(!refcount, "ID3D12PipelineState has %u references left.\n", (unsigned int)refcount);
    refcount = ID3D12RootSignature_Release(root_signature);
    ok(!refcount, "ID3D12RootSignature has %u references left.\n", (unsigned int)refcount);

    pipeline_state_desc.pRootSignature = NULL;
    hr = ID3D12Device_CreateComputePipelineState(device, &pipeline_state_desc,
            &IID_ID3D12PipelineState, (void **)&pipeline_state);
    ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr);

    pipeline_state_desc.CS = shader_bytecode(cs_with_rs, sizeof(cs_with_rs));
    hr = ID3D12Device_CreateComputePipelineState(device, &pipeline_state_desc,
            &IID_ID3D12PipelineState, (void **)&pipeline_state);
    ok(hr == S_OK, "Got hr %#x.\n", hr);
    ID3D12PipelineState_Release(pipeline_state);

    refcount = ID3D12Device_Release(device);
    ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount);

    refcount = ID3D10Blob_Release(bytecode);
    ok(!refcount, "ID3D10Blob has %u references left.\n", (unsigned int)refcount);
}

static void test_create_graphics_pipeline_state(void)
{
    ID3D12PipelineState *pipeline_state, *pipeline_state2;
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
    ID3D12RootSignature *root_signature;
    ID3D12Device *device, *tmp_device;
    D3D12_BLEND_DESC *blend;
    ULONG refcount;
    unsigned int i;
    HRESULT hr;

    static const DWORD vs_with_rs_code[] =
    {
#if 0
        [RootSignature("")]
        void main(uint id : SV_VertexID, out float4 position : SV_Position)
        {
            float2 coords = float2((id << 1) & 2, id & 2);
            position = float4(coords * float2(2, -2) + float2(-1, 1), 0, 1);
        }
#endif
        0x43425844, 0xf892ac0c, 0x339eab3f, 0x237bdb02, 0x828dd475, 0x00000001, 0x000001b0, 0x00000004,
        0x00000030, 0x00000064, 0x00000098, 0x00000190, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008,
        0x00000020, 0x00000000, 0x00000006, 0x00000001, 0x00000000, 0x00000101, 0x565f5653, 0x65747265,
        0x00444978, 0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000001,
        0x00000003, 0x00000000, 0x0000000f, 0x505f5653, 0x7469736f, 0x006e6f69, 0x58454853, 0x000000f0,
        0x00010050, 0x0000003c, 0x0100086a, 0x04000060, 0x00101012, 0x00000000, 0x00000006, 0x04000067,
        0x001020f2, 0x00000000, 0x00000001, 0x02000068, 0x00000001, 0x0b00008c, 0x00100012, 0x00000000,
        0x00004001, 0x00000001, 0x00004001, 0x00000001, 0x0010100a, 0x00000000, 0x00004001, 0x00000000,
        0x07000001, 0x00100042, 0x00000000, 0x0010100a, 0x00000000, 0x00004001, 0x00000002, 0x05000056,
        0x00100032, 0x00000000, 0x00100086, 0x00000000, 0x0f000032, 0x00102032, 0x00000000, 0x00100046,
        0x00000000, 0x00004002, 0x40000000, 0xc0000000, 0x00000000, 0x00000000, 0x00004002, 0xbf800000,
        0x3f800000, 0x00000000, 0x00000000, 0x08000036, 0x001020c2, 0x00000000, 0x00004002, 0x00000000,
        0x00000000, 0x00000000, 0x3f800000, 0x0100003e, 0x30535452, 0x00000018, 0x00000002, 0x00000000,
        0x00000018, 0x00000000, 0x00000018, 0x00000000,
    };
    static const DWORD ps_with_rs_code[] =
    {
#if 0
        [RootSignature("")]
        void main(const in float4 position : SV_Position, out float4 target : SV_Target0)
        {
            target = float4(0.0f, 1.0f, 0.0f, 1.0f);
        }
#endif
        0x43425844, 0x7c74e73b, 0x1dc7c715, 0x61248899, 0x672da97a, 0x00000001, 0x000000fc, 0x00000004,
        0x00000030, 0x00000064, 0x00000098, 0x000000dc, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008,
        0x00000020, 0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x505f5653, 0x7469736f,
        0x006e6f69, 0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000,
        0x00000003, 0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x0000003c,
        0x00000050, 0x0000000f, 0x0100086a, 0x03000065, 0x001020f2, 0x00000000, 0x08000036, 0x001020f2,
        0x00000000, 0x00004002, 0x00000000, 0x3f800000, 0x00000000, 0x3f800000, 0x0100003e, 0x30535452,
        0x00000018, 0x00000002, 0x00000000, 0x00000018, 0x00000000, 0x00000018, 0x00000000,
    };


    static const D3D12_SO_DECLARATION_ENTRY so_declaration[] =
    {
        {0, "SV_Position", 0, 0, 4, 0},
    };
    static const unsigned int strides[] = {16};

    if (!(device = create_device()))
    {
        skip("Failed to create device.\n");
        return;
    }

    root_signature_desc.NumParameters = 0;
    root_signature_desc.pParameters = NULL;
    root_signature_desc.NumStaticSamplers = 0;
    root_signature_desc.pStaticSamplers = NULL;
    root_signature_desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_NONE;
    hr = create_root_signature(device, &root_signature_desc, &root_signature);
    ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr);

    refcount = get_refcount(device);
    ok(refcount == 2, "Got unexpected refcount %u.\n", (unsigned int)refcount);

    init_pipeline_state_desc(&pso_desc, root_signature, DXGI_FORMAT_R8G8B8A8_UNORM, NULL, NULL, NULL);
    hr = ID3D12Device_CreateGraphicsPipelineState(device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&pipeline_state);
    ok(hr == S_OK, "Failed to create pipeline, hr %#x.\n", hr);

    hr = ID3D12Device_CreateGraphicsPipelineState(device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&pipeline_state2);
    ok(hr == S_OK, "Failed to create pipeline, hr %#x.\n", hr);
    ok(pipeline_state != pipeline_state2, "Got the same pipeline state object.\n");
    refcount = ID3D12PipelineState_Release(pipeline_state2);
    ok(!refcount, "ID3D12PipelineState has %u references left.\n", (unsigned int)refcount);

    refcount = get_refcount(root_signature);
    ok(refcount == 1, "Got unexpected refcount %u.\n", (unsigned int)refcount);

    refcount = get_refcount(device);
    ok(refcount == 3, "Got unexpected refcount %u.\n", (unsigned int)refcount);
    hr = ID3D12PipelineState_GetDevice(pipeline_state, &IID_ID3D12Device, (void **)&tmp_device);
    ok(hr == S_OK, "Failed to get device, hr %#x.\n", hr);
    refcount = get_refcount(device);
    ok(refcount == 4, "Got unexpected refcount %u.\n", (unsigned int)refcount);
    refcount = ID3D12Device_Release(tmp_device);
    ok(refcount == 3, "Got unexpected refcount %u.\n", (unsigned int)refcount);

    check_interface(pipeline_state, &IID_ID3D12Object, true);
    check_interface(pipeline_state, &IID_ID3D12DeviceChild, true);
    check_interface(pipeline_state, &IID_ID3D12Pageable, true);
    check_interface(pipeline_state, &IID_ID3D12PipelineState, true);

    refcount = ID3D12PipelineState_Release(pipeline_state);
    ok(!refcount, "ID3D12PipelineState has %u references left.\n", (unsigned int)refcount);

    blend = &pso_desc.BlendState;
    blend->IndependentBlendEnable = false;
    blend->RenderTarget[0].BlendEnable = true;
    blend->RenderTarget[0].SrcBlend = D3D12_BLEND_SRC_COLOR;
    blend->RenderTarget[0].DestBlend = D3D12_BLEND_DEST_COLOR;
    blend->RenderTarget[0].BlendOp = D3D12_BLEND_OP_ADD;
    blend->RenderTarget[0].SrcBlendAlpha = D3D12_BLEND_SRC_ALPHA;
    blend->RenderTarget[0].DestBlendAlpha = D3D12_BLEND_DEST_ALPHA;
    blend->RenderTarget[0].BlendOpAlpha = D3D12_BLEND_OP_ADD;
    blend->RenderTarget[0].RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;
    hr = ID3D12Device_CreateGraphicsPipelineState(device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&pipeline_state);
    ok(hr == S_OK, "Failed to create pipeline, hr %#x.\n", hr);
    ID3D12PipelineState_Release(pipeline_state);

    /* Only one of BlendEnable or LogicOpEnable can be set to true. */
    blend->IndependentBlendEnable = false;
    blend->RenderTarget[0].BlendEnable = true;
    blend->RenderTarget[0].LogicOpEnable = true;
    hr = ID3D12Device_CreateGraphicsPipelineState(device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&pipeline_state);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    pso_desc.RTVFormats[0] = DXGI_FORMAT_R32_UINT;
    hr = ID3D12Device_CreateGraphicsPipelineState(device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&pipeline_state);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    blend->IndependentBlendEnable = false;
    blend->RenderTarget[0].BlendEnable = false;
    blend->RenderTarget[0].LogicOpEnable = true;
    hr = ID3D12Device_CreateGraphicsPipelineState(device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&pipeline_state);
    ok(hr == S_OK, "Failed to create pipeline, hr %#x.\n", hr);
    ID3D12PipelineState_Release(pipeline_state);

    /* IndependentBlendEnable must be set to false when logic operations are enabled. */
    blend->IndependentBlendEnable = true;
    blend->RenderTarget[0].LogicOpEnable = true;
    for (i = 1; i < ARRAY_SIZE(blend->RenderTarget); ++i)
        blend->RenderTarget[i] = blend->RenderTarget[0];
    hr = ID3D12Device_CreateGraphicsPipelineState(device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&pipeline_state);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    /* DSVFormat = DXGI_FORMAT_UNKNOWN */
    memset(blend, 0, sizeof(*blend));
    pso_desc.DSVFormat = DXGI_FORMAT_UNKNOWN;
    pso_desc.DepthStencilState.DepthEnable = true;
    pso_desc.DepthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
    pso_desc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_LESS;
    hr = ID3D12Device_CreateGraphicsPipelineState(device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&pipeline_state);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    ID3D12PipelineState_Release(pipeline_state);

    /* Invalid DSVFormat */
    pso_desc.DSVFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
    pso_desc.DepthStencilState.DepthEnable = true;
    hr = ID3D12Device_CreateGraphicsPipelineState(device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&pipeline_state);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    ID3D12PipelineState_Release(pipeline_state);

    /* Inactive render targets formats must be set to DXGI_FORMAT_UNKNOWN. */
    init_pipeline_state_desc(&pso_desc, root_signature, DXGI_FORMAT_R8G8B8A8_UNORM, NULL, NULL, NULL);
    pso_desc.RTVFormats[1] = DXGI_FORMAT_R8G8B8A8_UNORM;
    hr = ID3D12Device_CreateGraphicsPipelineState(device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&pipeline_state);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    /* Stream output without D3D12_ROOT_SIGNATURE_FLAG_ALLOW_STREAM_OUTPUT. */
    init_pipeline_state_desc(&pso_desc, root_signature, DXGI_FORMAT_R8G8B8A8_UNORM, NULL, NULL, NULL);
    pso_desc.StreamOutput.NumEntries = ARRAY_SIZE(so_declaration);
    pso_desc.StreamOutput.pSODeclaration = so_declaration;
    pso_desc.StreamOutput.pBufferStrides = strides;
    pso_desc.StreamOutput.NumStrides = ARRAY_SIZE(strides);
    hr = ID3D12Device_CreateGraphicsPipelineState(device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&pipeline_state);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    /* Root signature embedded in the shaders. */
    init_pipeline_state_desc(&pso_desc, root_signature, DXGI_FORMAT_R8G8B8A8_UNORM, NULL, NULL, NULL);
    pso_desc.pRootSignature = NULL;
    hr = ID3D12Device_CreateGraphicsPipelineState(device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&pipeline_state);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    init_pipeline_state_desc(&pso_desc, root_signature, DXGI_FORMAT_R8G8B8A8_UNORM, NULL, NULL, NULL);
    pso_desc.pRootSignature = NULL;
    pso_desc.VS = shader_bytecode(vs_with_rs_code, sizeof(vs_with_rs_code));
    hr = ID3D12Device_CreateGraphicsPipelineState(device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&pipeline_state);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    if (SUCCEEDED(hr))
        ID3D12PipelineState_Release(pipeline_state);

    init_pipeline_state_desc(&pso_desc, root_signature, DXGI_FORMAT_R8G8B8A8_UNORM, NULL, NULL, NULL);
    pso_desc.pRootSignature = NULL;
    pso_desc.PS = shader_bytecode(ps_with_rs_code, sizeof(ps_with_rs_code));
    hr = ID3D12Device_CreateGraphicsPipelineState(device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&pipeline_state);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    if (SUCCEEDED(hr))
        ID3D12PipelineState_Release(pipeline_state);

    init_pipeline_state_desc(&pso_desc, root_signature, DXGI_FORMAT_R8G8B8A8_UNORM, NULL, NULL, NULL);
    pso_desc.pRootSignature = NULL;
    pso_desc.VS = shader_bytecode(vs_with_rs_code, sizeof(vs_with_rs_code));
    pso_desc.PS = shader_bytecode(ps_with_rs_code, sizeof(ps_with_rs_code));
    hr = ID3D12Device_CreateGraphicsPipelineState(device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&pipeline_state);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    if (SUCCEEDED(hr))
        ID3D12PipelineState_Release(pipeline_state);

    refcount = ID3D12RootSignature_Release(root_signature);
    ok(!refcount, "ID3D12RootSignature has %u references left.\n", (unsigned int)refcount);
    refcount = ID3D12Device_Release(device);
    ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount);
}

static void test_create_pipeline_state(void)
{
    ID3D12PipelineState *pipeline_state, *pipeline_state2;
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    ID3D12RootSignature *root_signature;
    ID3D12Device2 *device2;
    ID3D12Device *device;
    unsigned int i;
    ULONG refcount;
    HRESULT hr;

    static const char cs_code[] =
        "[numthreads(1, 1, 1)]\n"
        "void main() { }\n";

    static const char vs_code[] =
        "float4 main(float4 pos : POS) : SV_POSITION\n"
        "{\n"
        "    return pos;\n"
        "}\n";

    static const char ps_code[] =
        "float4 main() : SV_TARGET\n"
        "{\n"
        "    return float4(1.0f, 1.0f, 1.0f, 1.0f);\n"
        "}\n";

    static const struct d3d12_root_signature_subobject root_signature_subobject =
    {
        D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_ROOT_SIGNATURE,
        NULL, /* fill in dynamically */
    };

    ID3D10Blob *cs = compile_shader(cs_code, sizeof(cs_code) - 1, "cs_4_0");
    ID3D10Blob *ps = compile_shader(ps_code, sizeof(ps_code) - 1, "ps_4_0");
    ID3D10Blob *vs = compile_shader(vs_code, sizeof(vs_code) - 1, "vs_4_0");
    const struct d3d12_shader_bytecode_subobject vs_subobject = { D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_VS,
            shader_bytecode_from_blob(vs) };
    const struct d3d12_shader_bytecode_subobject ps_subobject = { D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_PS,
            shader_bytecode_from_blob(ps) };
    const struct d3d12_shader_bytecode_subobject cs_subobject = { D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_CS,
            shader_bytecode_from_blob(cs) };

    static const D3D12_SO_DECLARATION_ENTRY so_entries[] =
    {
        { 0, "SV_POSITION", 0, 0, 4, 0 },
    };

    static const UINT so_strides[] = { 16u };

    static const struct d3d12_stream_output_subobject stream_output_subobject =
    {
        D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_STREAM_OUTPUT,
        {   so_entries, ARRAY_SIZE(so_entries),
            so_strides, ARRAY_SIZE(so_strides),
            D3D12_SO_NO_RASTERIZED_STREAM },
    };

    static const struct d3d12_blend_subobject blend_subobject =
    {
        D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_BLEND,
        {   FALSE, TRUE,
            {{  FALSE, FALSE,
                D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD,
                D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD,
                D3D12_LOGIC_OP_NOOP, 0xf }},
        }
    };

    static const struct d3d12_sample_mask_subobject sample_mask_subobject =
    {
        D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_SAMPLE_MASK,
        0xffffffffu
    };

    static const struct d3d12_rasterizer_subobject rasterizer_subobject =
    {
        D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RASTERIZER,
        {   D3D12_FILL_MODE_SOLID, D3D12_CULL_MODE_BACK,
            FALSE, 0, 0.0f, 0.0f, TRUE, FALSE, FALSE, 0,
            D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF },
    };

    static const struct d3d12_depth_stencil_subobject depth_stencil_subobject =
    {
        D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL,
        {   TRUE, D3D12_DEPTH_WRITE_MASK_ALL, D3D12_COMPARISON_FUNC_LESS_EQUAL, TRUE, 0xff, 0xff,
            {   D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_INCR, D3D12_COMPARISON_FUNC_EQUAL },
            {   D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_INCR, D3D12_COMPARISON_FUNC_EQUAL } },
    };

    static const D3D12_INPUT_ELEMENT_DESC input_elements[] =
    {
        { "POS", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
    };

    static const struct d3d12_input_layout_subobject input_layout_subobject =
    {
        D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_INPUT_LAYOUT,
        { input_elements, ARRAY_SIZE(input_elements) },
    };

    static const struct d3d12_ib_strip_cut_value_subobject ib_strip_cut_value_subobject =
    {
        D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_IB_STRIP_CUT_VALUE,
        D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_0xFFFFFFFF,
    };

    static const struct d3d12_primitive_topology_subobject primitive_topology_subobject =
    {
        D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_PRIMITIVE_TOPOLOGY,
        D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE,
    };

    static const struct d3d12_render_target_formats_subobject render_target_formats_subobject =
    {
        D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RENDER_TARGET_FORMATS,
        { { DXGI_FORMAT_R8G8B8A8_UNORM }, 1 },
    };

    static const struct d3d12_depth_stencil_format_subobject depth_stencil_format_subobject =
    {
        D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL_FORMAT,
        DXGI_FORMAT_D32_FLOAT_S8X24_UINT,
    };

    static const struct d3d12_sample_desc_subobject sample_desc_subobject =
    {
        D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_SAMPLE_DESC,
        { 1, 0 },
    };

    static const struct d3d12_node_mask_subobject node_mask_subobject =
    {
        D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_NODE_MASK,
        0x0,
    };

    static const struct d3d12_cached_pso_subobject cached_pso_subobject =
    {
        D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_CACHED_PSO,
        { NULL, 0 },
    };

    static const struct d3d12_flags_subobject flags_subobject =
    {
        D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_FLAGS,
        D3D12_PIPELINE_STATE_FLAG_NONE,
    };

    static const struct d3d12_depth_stencil1_subobject depth_stencil1_subobject =
    {
        D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL1,
        {   TRUE, D3D12_DEPTH_WRITE_MASK_ALL, D3D12_COMPARISON_FUNC_LESS_EQUAL, TRUE, 0xff, 0xff,
            {   D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_INCR, D3D12_COMPARISON_FUNC_EQUAL },
            {   D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_INCR, D3D12_COMPARISON_FUNC_EQUAL } },
    };

    static const struct d3d12_view_instancing_subobject view_instancing_subobject =
    {
        D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_VIEW_INSTANCING,
        { 0, NULL, D3D12_VIEW_INSTANCING_FLAG_NONE },
    };

    struct
    {
        struct d3d12_root_signature_subobject root_signature;
        struct d3d12_shader_bytecode_subobject vertex_shader;
        struct d3d12_shader_bytecode_subobject pixel_shader;
        struct d3d12_blend_subobject blend;
        struct d3d12_sample_mask_subobject sample_mask;
        struct d3d12_rasterizer_subobject rasterizer;
        struct d3d12_depth_stencil1_subobject depth_stencil;
        struct d3d12_input_layout_subobject input_layout;
        struct d3d12_ib_strip_cut_value_subobject strip_cut;
        struct d3d12_primitive_topology_subobject primitive_topology;
        struct d3d12_render_target_formats_subobject render_target_formats;
        struct d3d12_depth_stencil_format_subobject depth_stencil_format;
        struct d3d12_sample_desc_subobject sample_desc;
        struct d3d12_node_mask_subobject node_mask;
        struct d3d12_cached_pso_subobject cached_pso;
        struct d3d12_flags_subobject flags;
        struct d3d12_view_instancing_subobject view_instancing;
    }
    pipeline_desc_1 =
    {
        root_signature_subobject,
        vs_subobject,
        ps_subobject,
        blend_subobject,
        sample_mask_subobject,
        rasterizer_subobject,
        depth_stencil1_subobject,
        input_layout_subobject,
        ib_strip_cut_value_subobject,
        primitive_topology_subobject,
        render_target_formats_subobject,
        depth_stencil_format_subobject,
        sample_desc_subobject,
        node_mask_subobject,
        cached_pso_subobject,
        flags_subobject,
        view_instancing_subobject,
    };

    struct
    {
        struct d3d12_root_signature_subobject root_signature;
        struct d3d12_shader_bytecode_subobject compute_shader;
    }
    pipeline_desc_2 =
    {
        root_signature_subobject, cs_subobject,
    };

    struct
    {
        struct d3d12_root_signature_subobject root_signature;
        struct d3d12_shader_bytecode_subobject vertex_shader;
        struct d3d12_stream_output_subobject stream_output;
        struct d3d12_input_layout_subobject input_layout;
    }
    pipeline_desc_3 =
    {
        root_signature_subobject, vs_subobject, stream_output_subobject,
        input_layout_subobject,
    };

    struct
    {
        struct d3d12_root_signature_subobject root_signature;
    }
    pipeline_desc_4 =
    {
        root_signature_subobject,
    };

    struct
    {
        struct d3d12_root_signature_subobject root_signature;
        struct d3d12_shader_bytecode_subobject cs;
        struct d3d12_shader_bytecode_subobject vs;
    }
    pipeline_desc_5 =
    {
        root_signature_subobject, cs_subobject, vs_subobject,
    };

    struct
    {
        struct d3d12_root_signature_subobject root_signature;
        struct d3d12_shader_bytecode_subobject cs;
        struct d3d12_shader_bytecode_subobject ps;
        struct d3d12_rasterizer_subobject rasterizer;
    }
    pipeline_desc_6 =
    {
        root_signature_subobject, cs_subobject, ps_subobject,
        rasterizer_subobject,
    };

    struct
    {
        struct d3d12_root_signature_subobject root_signature;
        struct d3d12_depth_stencil_subobject depth_stencil;
        struct d3d12_depth_stencil_format_subobject depth_stencil_format;
        struct d3d12_input_layout_subobject input_layout;
        struct d3d12_shader_bytecode_subobject vertex_shader;
    }
    pipeline_desc_7 =
    {
        root_signature_subobject, depth_stencil_subobject, depth_stencil_format_subobject,
        input_layout_subobject, vs_subobject,
    };

    struct
    {
        struct d3d12_root_signature_subobject root_signature;
        struct d3d12_shader_bytecode_subobject cs;
        struct d3d12_shader_bytecode_subobject cs2;
    }
    pipeline_desc_8 =
    {
        root_signature_subobject, cs_subobject, cs_subobject,
    };

    struct
    {
        struct d3d12_root_signature_subobject root_signature;
        struct d3d12_shader_bytecode_subobject vs;
        D3D12_PIPELINE_STATE_SUBOBJECT_TYPE extra_type;
    }
    pipeline_desc_9 =
    {
        root_signature_subobject, vs_subobject,
        D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL
    };

    struct
    {
        D3D12_PIPELINE_STATE_STREAM_DESC stream_desc;
        HRESULT expected_result;
        bool is_mvk_bug;
    }
    tests[] =
    {
        { { sizeof(pipeline_desc_1), &pipeline_desc_1 }, S_OK },
        { { sizeof(pipeline_desc_2), &pipeline_desc_2 }, S_OK },
        { { sizeof(pipeline_desc_3), &pipeline_desc_3 }, S_OK, true },
        { { sizeof(pipeline_desc_4), &pipeline_desc_4 }, E_INVALIDARG },
        { { sizeof(pipeline_desc_5), &pipeline_desc_5 }, E_INVALIDARG },
        { { sizeof(pipeline_desc_6), &pipeline_desc_6 }, S_OK },
        { { sizeof(pipeline_desc_7), &pipeline_desc_7 }, S_OK },
        { { sizeof(pipeline_desc_8), &pipeline_desc_8 }, E_INVALIDARG },
        { { sizeof(pipeline_desc_9), &pipeline_desc_9 }, E_INVALIDARG },
    };

    if (!(device = create_device()))
    {
        skip("Failed to create device.\n");
        goto cleanup;
    }

    if (ID3D12Device_QueryInterface(device, &IID_ID3D12Device2, (void **)&device2))
    {
        skip("ID3D12Device2 not supported.\n");
        ID3D12Device_Release(device);
        goto cleanup;
    }

    root_signature_desc.NumParameters = 0;
    root_signature_desc.pParameters = NULL;
    root_signature_desc.NumStaticSamplers = 0;
    root_signature_desc.pStaticSamplers = NULL;
    root_signature_desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_STREAM_OUTPUT |
            D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
    hr = create_root_signature(device, &root_signature_desc, &root_signature);
    ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr);

    for (i = 0; i < ARRAY_SIZE(tests); ++i)
    {
        struct d3d12_root_signature_subobject *rs_subobject;
        vkd3d_test_push_context("Test %u", i);

        /* Assign root signature. To keep things simple, assume that the root
         * signature is always the first element in each pipeline stream */
        rs_subobject = tests[i].stream_desc.pPipelineStateSubobjectStream;

        if (rs_subobject && rs_subobject->type == D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_ROOT_SIGNATURE)
            rs_subobject->root_signature = root_signature;

        hr = ID3D12Device2_CreatePipelineState(device2, &tests[i].stream_desc, &IID_ID3D12PipelineState, (void **)&pipeline_state);
        bug_if(tests[i].is_mvk_bug && is_mvk_device(device))
        ok(hr == tests[i].expected_result, "Got unexpected return value %#x.\n", hr);

        if (hr == S_OK)
        {
            hr = ID3D12Device2_CreatePipelineState(device2, &tests[i].stream_desc, &IID_ID3D12PipelineState,
                    (void **)&pipeline_state2);
            ok(hr == S_OK, "Got unexpected return value %#x.\n", hr);
            ok(pipeline_state != pipeline_state2, "Got the same pipeline state object.\n");
            refcount = ID3D12PipelineState_Release(pipeline_state2);
            ok(!refcount, "ID3D12PipelineState has %u references left.\n", (unsigned int)refcount);

            refcount = ID3D12PipelineState_Release(pipeline_state);
            ok(!refcount, "ID3D12PipelineState has %u references left.\n", (unsigned int)refcount);
        }

        vkd3d_test_pop_context();
    }

    refcount = ID3D12RootSignature_Release(root_signature);
    ok(!refcount, "ID3D12RootSignature has %u references left.\n", (unsigned int)refcount);
    refcount = ID3D12Device2_Release(device2);
    ok(refcount == 1, "ID3D12Device2 has %u references left.\n", (unsigned int)refcount);
    refcount = ID3D12Device_Release(device);
    ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount);
cleanup:
    ID3D10Blob_Release(vs);
    ID3D10Blob_Release(ps);
    ID3D10Blob_Release(cs);
}

static void test_create_fence(void)
{
    ID3D12Device *device, *tmp_device;
    ID3D12Fence *fence;
    ULONG refcount;
    uint64_t value;
    HRESULT hr;

    if (!(device = create_device()))
    {
        skip("Failed to create device.\n");
        return;
    }

    hr = ID3D12Device_CreateFence(device, 0, D3D12_FENCE_FLAG_NONE,
            &IID_ID3D12Fence, (void **)&fence);
    ok(SUCCEEDED(hr), "Failed to create fence, hr %#x.\n", hr);

    refcount = get_refcount(device);
    ok(refcount == 2, "Got unexpected refcount %u.\n", (unsigned int)refcount);
    hr = ID3D12Fence_GetDevice(fence, &IID_ID3D12Device, (void **)&tmp_device);
    ok(SUCCEEDED(hr), "Failed to get device, hr %#x.\n", hr);
    refcount = get_refcount(device);
    ok(refcount == 3, "Got unexpected refcount %u.\n", (unsigned int)refcount);
    refcount = ID3D12Device_Release(tmp_device);
    ok(refcount == 2, "Got unexpected refcount %u.\n", (unsigned int)refcount);

    check_interface(fence, &IID_ID3D12Object, true);
    check_interface(fence, &IID_ID3D12DeviceChild, true);
    check_interface(fence, &IID_ID3D12Pageable, true);
    check_interface(fence, &IID_ID3D12Fence, true);

    value = ID3D12Fence_GetCompletedValue(fence);
    ok(value == 0, "Got unexpected value %"PRIu64".\n", value);

    refcount = ID3D12Fence_Release(fence);
    ok(!refcount, "ID3D12Fence has %u references left.\n", (unsigned int)refcount);

    hr = ID3D12Device_CreateFence(device, 99, D3D12_FENCE_FLAG_NONE,
            &IID_ID3D12Fence, (void **)&fence);
    ok(SUCCEEDED(hr), "Failed to create fence, hr %#x.\n", hr);
    value = ID3D12Fence_GetCompletedValue(fence);
    ok(value == 99, "Got unexpected value %"PRIu64".\n", value);
    refcount = ID3D12Fence_Release(fence);
    ok(!refcount, "ID3D12Fence has %u references left.\n", (unsigned int)refcount);

    refcount = ID3D12Device_Release(device);
    ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount);
}

static void test_object_interface(void)
{
    D3D12_DESCRIPTOR_HEAP_DESC descriptor_heap_desc;
    D3D12_QUERY_HEAP_DESC query_heap_desc;
    ID3D12RootSignature *root_signature;
    ULONG refcount, expected_refcount;
    ID3D12CommandAllocator *allocator;
    D3D12_HEAP_DESC heap_desc;
    IUnknown *test_object;
    ID3D12Device *device;
    ID3D12Object *object;
    IUnknown *unknown;
    unsigned int size;
    unsigned int i;
    IUnknown *ptr;
    HRESULT hr;

    static const GUID test_guid
            = {0xfdb37466, 0x428f, 0x4edf, {0xa3, 0x7f, 0x9b, 0x1d, 0xf4, 0x88, 0xc5, 0xfc}};
    static const GUID test_guid2
            = {0x2e5afac2, 0x87b5, 0x4c10, {0x9b, 0x4b, 0x89, 0xd7, 0xd1, 0x12, 0xe7, 0x2b}};
    static const DWORD data[] = {1, 2, 3, 4};
    static const WCHAR deadbeefW[] = {'d', 'e', 'a', 'd', 'b', 'e', 'e', 'f', 0};
    static const WCHAR emptyW[] = {0};
    static const GUID *tests[] =
    {
        &IID_ID3D12CommandAllocator,
        &IID_ID3D12CommandList,
        &IID_ID3D12CommandQueue,
        &IID_ID3D12CommandSignature,
        &IID_ID3D12DescriptorHeap,
        &IID_ID3D12Device,
        &IID_ID3D12Fence,
        &IID_ID3D12Heap,
        &IID_ID3D12PipelineState,
        &IID_ID3D12QueryHeap,
        &IID_ID3D12Resource,
        &IID_ID3D12RootSignature,
    };

    if (!(device = create_device()))
    {
        skip("Failed to create device.\n");
        return;
    }

    for (i = 0; i < ARRAY_SIZE(tests); ++i)
    {
        if (IsEqualGUID(tests[i], &IID_ID3D12CommandAllocator))
        {
            vkd3d_test_push_context("command allocator");
            hr = ID3D12Device_CreateCommandAllocator(device, D3D12_COMMAND_LIST_TYPE_DIRECT,
                    &IID_IUnknown, (void **)&unknown);
            ok(hr == S_OK, "Failed to create command allocator, hr %#x.\n", hr);
        }
        else if (IsEqualGUID(tests[i], &IID_ID3D12CommandList))
        {
            vkd3d_test_push_context("command list");
            hr = ID3D12Device_CreateCommandAllocator(device, D3D12_COMMAND_LIST_TYPE_DIRECT,
                    &IID_ID3D12CommandAllocator, (void **)&allocator);
            ok(hr == S_OK, "Failed to create command allocator, hr %#x.\n", hr);
            hr = ID3D12Device_CreateCommandList(device, 0, D3D12_COMMAND_LIST_TYPE_DIRECT,
                    allocator, NULL, &IID_IUnknown, (void **)&unknown);
            ok(hr == S_OK, "Failed to create command list, hr %#x.\n", hr);
            ID3D12CommandAllocator_Release(allocator);
        }
        else if (IsEqualGUID(tests[i], &IID_ID3D12CommandQueue))
        {
            vkd3d_test_push_context("command queue");
            unknown = (IUnknown *)create_command_queue(device,
                    D3D12_COMMAND_LIST_TYPE_DIRECT, D3D12_COMMAND_QUEUE_PRIORITY_NORMAL);
        }
        else if (IsEqualGUID(tests[i], &IID_ID3D12CommandSignature))
        {
            vkd3d_test_push_context("command signature");
            unknown = (IUnknown *)create_command_signature(device, D3D12_INDIRECT_ARGUMENT_TYPE_DRAW);
        }
        else if (IsEqualGUID(tests[i], &IID_ID3D12DescriptorHeap))
        {
            vkd3d_test_push_context("descriptor heap");
            descriptor_heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
            descriptor_heap_desc.NumDescriptors = 16;
            descriptor_heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
            descriptor_heap_desc.NodeMask = 0;
            hr = ID3D12Device_CreateDescriptorHeap(device, &descriptor_heap_desc,
                    &IID_ID3D12DescriptorHeap, (void **)&unknown);
            ok(hr == S_OK, "Failed to create descriptor heap, hr %#x.\n", hr);
        }
        else if (IsEqualGUID(tests[i], &IID_ID3D12Device))
        {
            vkd3d_test_push_context("device");
            unknown = (IUnknown *)create_device();
        }
        else if (IsEqualGUID(tests[i], &IID_ID3D12Fence))
        {
            vkd3d_test_push_context("fence");
            hr = ID3D12Device_CreateFence(device, 0, D3D12_FENCE_FLAG_NONE,
                    &IID_IUnknown, (void **)&unknown);
            ok(hr == S_OK, "Failed to create fence, hr %#x.\n", hr);
        }
        else if (IsEqualGUID(tests[i], &IID_ID3D12Heap))
        {
            vkd3d_test_push_context("heap");
            heap_desc.SizeInBytes = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
            memset(&heap_desc.Properties, 0, sizeof(heap_desc.Properties));
            heap_desc.Properties.Type = D3D12_HEAP_TYPE_DEFAULT;
            heap_desc.Alignment = 0;
            heap_desc.Flags = D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES;
            hr = ID3D12Device_CreateHeap(device, &heap_desc, &IID_ID3D12Heap, (void **)&unknown);
            ok(hr == S_OK, "Failed to create heap, hr %#x.\n", hr);
        }
        else if (IsEqualGUID(tests[i], &IID_ID3D12PipelineState))
        {
            vkd3d_test_push_context("pipeline state");
            root_signature = create_empty_root_signature(device, 0);
            unknown = (IUnknown *)create_pipeline_state(device,
                    root_signature, DXGI_FORMAT_R8G8B8A8_UNORM, NULL, NULL, NULL);
            ID3D12RootSignature_Release(root_signature);
        }
        else if (IsEqualGUID(tests[i], &IID_ID3D12QueryHeap))
        {
            vkd3d_test_push_context("query heap");
            query_heap_desc.Type = D3D12_QUERY_HEAP_TYPE_OCCLUSION;
            query_heap_desc.Count = 8;
            query_heap_desc.NodeMask = 0;
            hr = ID3D12Device_CreateQueryHeap(device, &query_heap_desc,
                    &IID_ID3D12QueryHeap, (void **)&unknown);
            ok(hr == S_OK, "Failed to create query heap, hr %#x.\n", hr);
        }
        else if (IsEqualGUID(tests[i], &IID_ID3D12Resource))
        {
            vkd3d_test_push_context("resource");
            unknown = (IUnknown *)create_readback_buffer(device, 512);
        }
        else if (IsEqualGUID(tests[i], &IID_ID3D12RootSignature))
        {
            vkd3d_test_push_context("root signature");
            unknown = (IUnknown *)create_empty_root_signature(device, 0);
        }
        else
        {
            unknown = NULL;
        }

        ok(unknown, "Unhandled object type %u.\n", i);
        object = NULL;
        hr = IUnknown_QueryInterface(unknown, &IID_ID3D12Object, (void **)&object);
        ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
        IUnknown_Release(unknown);

        hr = ID3D12Object_SetPrivateData(object, &test_guid, 0, NULL);
        ok(hr == S_FALSE, "Got unexpected hr %#x.\n", hr);
        hr = ID3D12Object_SetPrivateDataInterface(object, &test_guid, NULL);
        ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
        hr = ID3D12Object_SetPrivateData(object, &test_guid, ~0u, NULL);
        ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
        hr = ID3D12Object_SetPrivateData(object, &test_guid, ~0u, NULL);
        ok(hr == S_FALSE, "Got unexpected hr %#x.\n", hr);

        hr = ID3D12Object_SetPrivateDataInterface(object, &test_guid, NULL);
        ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
        size = sizeof(ptr) * 2;
        ptr = (IUnknown *)0xdeadbeef;
        hr = ID3D12Object_GetPrivateData(object, &test_guid, &size, &ptr);
        ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
        ok(!ptr, "Got unexpected pointer %p.\n", ptr);
        ok(size == sizeof(IUnknown *), "Got unexpected size %u.\n", size);

        hr = ID3D12Device_CreateFence(device, 0, D3D12_FENCE_FLAG_NONE,
                &IID_ID3D12Fence, (void **)&test_object);
        ok(hr == S_OK, "Failed to create fence, hr %#x.\n", hr);

        refcount = get_refcount(test_object);
        hr = ID3D12Object_SetPrivateDataInterface(object, &test_guid, (IUnknown *)test_object);
        ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
        expected_refcount = refcount + 1;
        refcount = get_refcount(test_object);
        ok(refcount == expected_refcount, "Got unexpected refcount %u, expected %u.\n",
                (unsigned int)refcount, (unsigned int)expected_refcount);
        hr = ID3D12Object_SetPrivateDataInterface(object, &test_guid, (IUnknown *)test_object);
        ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
        refcount = get_refcount(test_object);
        ok(refcount == expected_refcount, "Got unexpected refcount %u, expected %u.\n",
                (unsigned int)refcount, (unsigned int)expected_refcount);

        hr = ID3D12Object_SetPrivateDataInterface(object, &test_guid, NULL);
        ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
        --expected_refcount;
        refcount = get_refcount(test_object);
        ok(refcount == expected_refcount, "Got unexpected refcount %u, expected %u.\n",
                (unsigned int)refcount, (unsigned int)expected_refcount);

        hr = ID3D12Object_SetPrivateDataInterface(object, &test_guid, (IUnknown *)test_object);
        ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
        size = sizeof(data);
        hr = ID3D12Object_SetPrivateData(object, &test_guid, size, data);
        ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
        refcount = get_refcount(test_object);
        ok(refcount == expected_refcount, "Got unexpected refcount %u, expected %u.\n",
                (unsigned int)refcount, (unsigned int)expected_refcount);
        hr = ID3D12Object_SetPrivateData(object, &test_guid, 42, NULL);
        ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
        hr = ID3D12Object_SetPrivateData(object, &test_guid, 42, NULL);
        ok(hr == S_FALSE, "Got unexpected hr %#x.\n", hr);

        hr = ID3D12Object_SetPrivateDataInterface(object, &test_guid, (IUnknown *)test_object);
        ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
        ++expected_refcount;
        size = 2 * sizeof(ptr);
        ptr = NULL;
        hr = ID3D12Object_GetPrivateData(object, &test_guid, &size, &ptr);
        ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
        ok(size == sizeof(test_object), "Got unexpected size %u.\n", size);
        ++expected_refcount;
        refcount = get_refcount(test_object);
        ok(refcount == expected_refcount, "Got unexpected refcount %u, expected %u.\n",
                (unsigned int)refcount, (unsigned int)expected_refcount);
        IUnknown_Release(ptr);
        --expected_refcount;

        ptr = (IUnknown *)0xdeadbeef;
        size = 1;
        hr = ID3D12Object_GetPrivateData(object, &test_guid, &size, NULL);
        ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
        ok(size == sizeof(ptr), "Got unexpected size %u.\n", size);
        size = 2 * sizeof(ptr);
        hr = ID3D12Object_GetPrivateData(object, &test_guid, &size, NULL);
        ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
        ok(size == sizeof(ptr), "Got unexpected size %u.\n", size);
        refcount = get_refcount(test_object);
        ok(refcount == expected_refcount, "Got unexpected refcount %u, expected %u.\n",
                (unsigned int)refcount, (unsigned int)expected_refcount);

        size = 1;
        hr = ID3D12Object_GetPrivateData(object, &test_guid, &size, &ptr);
        ok(hr == DXGI_ERROR_MORE_DATA, "Got unexpected hr %#x.\n", hr);
        ok(size == sizeof(object), "Got unexpected size %u.\n", size);
        ok(ptr == (IUnknown *)0xdeadbeef, "Got unexpected pointer %p.\n", ptr);
        size = 1;
        hr = ID3D12Object_GetPrivateData(object, &test_guid2, &size, &ptr);
        ok(hr == DXGI_ERROR_NOT_FOUND, "Got unexpected hr %#x.\n", hr);
        ok(!size, "Got unexpected size %u.\n", size);
        ok(ptr == (IUnknown *)0xdeadbeef, "Got unexpected pointer %p.\n", ptr);

        if (IsEqualGUID(tests[i], &IID_ID3D12Device))
        {
            hr = ID3D12Object_SetPrivateDataInterface(object, &test_guid, NULL);
            ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
        }

        hr = ID3D12Object_SetName(object, emptyW);
        ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);

        hr = ID3D12Object_SetName(object, deadbeefW);
        ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);

        ID3D12Object_Release(object);

        refcount = IUnknown_Release(test_object);
        ok(!refcount, "Test object has %u references left.\n", (unsigned int)refcount);

        vkd3d_test_pop_context();
    }

    refcount = ID3D12Device_Release(device);
    ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount);
}

struct private_data
{
    ID3D12Object *object;
    GUID guid;
    unsigned int value;
};

static void private_data_thread_main(void *untyped_data)
{
    struct private_data *data = untyped_data;
    unsigned int i;
    HRESULT hr;

    hr = ID3D12Object_SetPrivateData(data->object, &data->guid, sizeof(data->value), &data->value);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);

    for (i = 0; i < 100000; ++i)
    {
        hr = ID3D12Object_SetPrivateData(data->object, &data->guid, 0, NULL);
        ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
        hr = ID3D12Object_SetPrivateData(data->object, &data->guid, sizeof(data->value), &data->value);
        ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    }
}

struct private_data_interface
{
    ID3D12Object *object;
    GUID guid;
    IUnknown *iface;
};

static void private_data_interface_thread_main(void *untyped_data)
{
    struct private_data_interface *data = untyped_data;
    unsigned int i;
    HRESULT hr;

    for (i = 0; i < 100000; ++i)
    {
        hr = ID3D12Object_SetPrivateDataInterface(data->object, &data->guid, data->iface);
        ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
        hr = ID3D12Object_SetPrivateDataInterface(data->object, &data->guid, NULL);
        ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
        hr = ID3D12Object_SetPrivateDataInterface(data->object, &data->guid, data->iface);
        ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    }
}

static void test_multithread_private_data(void)
{
    static const GUID guid = {0xfdb37466, 0x428f, 0x4edf, {0xa3, 0x7f, 0x9b, 0x1d, 0xf4, 0x88, 0xc5, 0x00}};
    struct private_data_interface private_data_interface[4];
    HANDLE private_data_interface_thread[4];
    struct private_data private_data[4];
    ID3D12RootSignature *root_signature;
    HANDLE private_data_thread[4];
    IUnknown *test_object, *unk;
    ID3D12Device *device;
    ID3D12Object *object;
    unsigned int value;
    unsigned int size;
    unsigned int id;
    unsigned int i;
    ULONG refcount;
    HRESULT hr;

    if (!(device = create_device()))
    {
        skip("Failed to create device.\n");
        return;
    }

    root_signature = create_empty_root_signature(device,
            D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
    hr = ID3D12RootSignature_QueryInterface(root_signature, &IID_ID3D12Object, (void **)&object);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    ID3D12RootSignature_Release(root_signature);

    hr = ID3D12Device_CreateFence(device, 0, D3D12_FENCE_FLAG_NONE,
            &IID_ID3D12Fence, (void **)&test_object);
    ok(hr == S_OK, "Failed to create fence, hr %#x.\n", hr);

    for (i = 0, id = 1; i < ARRAY_SIZE(private_data_interface); ++i, ++id)
    {
        private_data_interface[i].object = object;
        private_data_interface[i].guid = guid;
        private_data_interface[i].guid.Data4[7] = id;

        hr = ID3D12Device_CreateFence(device, 0, D3D12_FENCE_FLAG_NONE,
                &IID_ID3D12Fence, (void **)&private_data_interface[i].iface);
        ok(hr == S_OK, "Failed to create fence %u, hr %#x.\n", i, hr);
    }
    for (i = 0; i < ARRAY_SIZE(private_data); ++i, ++id)
    {
        private_data[i].object = object;
        private_data[i].guid = guid;
        private_data[i].guid.Data4[7] = id;
        private_data[i].value = id;
    }

    for (i = 0; i < 4; ++i)
    {
        private_data_interface_thread[i] = create_thread(private_data_interface_thread_main, &private_data_interface[i]);
        private_data_thread[i] = create_thread(private_data_thread_main, &private_data[i]);
    }

    for (i = 0; i < 100000; ++i)
    {
        hr = ID3D12Object_SetPrivateDataInterface(object, &guid, test_object);
        ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
        hr = ID3D12Object_SetPrivateDataInterface(object, &guid, NULL);
        ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
        hr = ID3D12Object_SetPrivateDataInterface(object, &guid, test_object);
        ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    }

    for (i = 0; i < 4; ++i)
    {
        ok(join_thread(private_data_interface_thread[i]), "Failed to join thread %u.\n", i);
        ok(join_thread(private_data_thread[i]), "Failed to join thread %u.\n", i);
    }

    for (i = 0; i < ARRAY_SIZE(private_data_interface); ++i)
    {
        size = sizeof(unk);
        hr = ID3D12Object_GetPrivateData(object, &private_data_interface[i].guid, &size, &unk);
        ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);

        ok(unk == private_data_interface[i].iface, "Got %p, expected %p.\n", unk, private_data_interface[i].iface);
        IUnknown_Release(unk);
        refcount = IUnknown_Release(private_data_interface[i].iface);
        ok(refcount == 1, "Got unexpected refcount %u.\n", (unsigned int)refcount);
    }
    for (i = 0; i < ARRAY_SIZE(private_data); ++i)
    {
        size = sizeof(value);
        hr = ID3D12Object_GetPrivateData(object, &private_data[i].guid, &size, &value);
        ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);

        ok(value == private_data[i].value, "Got %u, expected %u.\n", value, private_data[i].value);
    }

    hr = ID3D12Object_SetPrivateDataInterface(object, &guid, NULL);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    refcount = IUnknown_Release(test_object);
    ok(!refcount, "Test object has %u references left.\n", (unsigned int)refcount);
    refcount = ID3D12Object_Release(object);
    ok(!refcount, "Object has %u references left.\n", (unsigned int)refcount);
    refcount = ID3D12Device_Release(device);
    ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount);
}

static void test_reset_command_allocator(void)
{
    ID3D12CommandAllocator *command_allocator, *command_allocator2;
    ID3D12GraphicsCommandList *command_list, *command_list2;
    D3D12_COMMAND_QUEUE_DESC command_queue_desc;
    ID3D12CommandQueue *queue;
    ID3D12Device *device;
    ULONG refcount;
    HRESULT hr;

    if (!(device = create_device()))
    {
        skip("Failed to create device.\n");
        return;
    }

    hr = ID3D12Device_CreateCommandAllocator(device, D3D12_COMMAND_LIST_TYPE_DIRECT,
            &IID_ID3D12CommandAllocator, (void **)&command_allocator);
    ok(SUCCEEDED(hr), "Failed to create command allocator, hr %#x.\n", hr);

    hr = ID3D12CommandAllocator_Reset(command_allocator);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    hr = ID3D12CommandAllocator_Reset(command_allocator);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);

    hr = ID3D12Device_CreateCommandList(device, 0, D3D12_COMMAND_LIST_TYPE_DIRECT,
            command_allocator, NULL, &IID_ID3D12GraphicsCommandList, (void **)&command_list);
    ok(SUCCEEDED(hr), "Failed to create command list, hr %#x.\n", hr);

    hr = ID3D12CommandAllocator_Reset(command_allocator);
    ok(hr == E_FAIL, "Got unexpected hr %#x.\n", hr);
    hr = ID3D12CommandAllocator_Reset(command_allocator);
    ok(hr == E_FAIL, "Got unexpected hr %#x.\n", hr);

    hr = ID3D12GraphicsCommandList_Close(command_list);
    ok(SUCCEEDED(hr), "Failed to close command list, hr %#x.\n", hr);

    hr = ID3D12CommandAllocator_Reset(command_allocator);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    hr = ID3D12CommandAllocator_Reset(command_allocator);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);

    hr = ID3D12GraphicsCommandList_Reset(command_list, command_allocator, NULL);
    ok(SUCCEEDED(hr), "Failed to reset command list, hr %#x.\n", hr);

    hr = ID3D12CommandAllocator_Reset(command_allocator);
    ok(hr == E_FAIL, "Got unexpected hr %#x.\n", hr);

    hr = ID3D12GraphicsCommandList_Close(command_list);
    ok(SUCCEEDED(hr), "Failed to close command list, hr %#x.\n", hr);
    hr = ID3D12GraphicsCommandList_Reset(command_list, command_allocator, NULL);
    ok(SUCCEEDED(hr), "Failed to reset command list, hr %#x.\n", hr);

    command_queue_desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
    command_queue_desc.Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL;
    command_queue_desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
    command_queue_desc.NodeMask = 0;
    hr = ID3D12Device_CreateCommandQueue(device, &command_queue_desc,
            &IID_ID3D12CommandQueue, (void **)&queue);
    ok(SUCCEEDED(hr), "Failed to create command queue, hr %#x.\n", hr);
    hr = ID3D12Device_CreateCommandAllocator(device, D3D12_COMMAND_LIST_TYPE_DIRECT,
            &IID_ID3D12CommandAllocator, (void **)&command_allocator2);
    ok(SUCCEEDED(hr), "Failed to create command allocator, hr %#x.\n", hr);

    uav_barrier(command_list, NULL);
    hr = ID3D12GraphicsCommandList_Close(command_list);
    ok(SUCCEEDED(hr), "Failed to close command list, hr %#x.\n", hr);
    exec_command_list(queue, command_list);

    /* A command list can be reset when it is in use. */
    hr = ID3D12GraphicsCommandList_Reset(command_list, command_allocator2, NULL);
    ok(SUCCEEDED(hr), "Failed to reset command list, hr %#x.\n", hr);
    hr = ID3D12GraphicsCommandList_Close(command_list);
    ok(SUCCEEDED(hr), "Failed to close command list, hr %#x.\n", hr);

    wait_queue_idle(device, queue);
    hr = ID3D12CommandAllocator_Reset(command_allocator);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    hr = ID3D12GraphicsCommandList_Reset(command_list, command_allocator, NULL);
    ok(SUCCEEDED(hr), "Failed to reset command list, hr %#x.\n", hr);

    uav_barrier(command_list, NULL);
    hr = ID3D12GraphicsCommandList_Close(command_list);
    ok(SUCCEEDED(hr), "Failed to close command list, hr %#x.\n", hr);
    exec_command_list(queue, command_list);

    hr = ID3D12GraphicsCommandList_Reset(command_list, command_allocator, NULL);
    ok(SUCCEEDED(hr), "Failed to reset command list, hr %#x.\n", hr);
    hr = ID3D12GraphicsCommandList_Close(command_list);
    ok(SUCCEEDED(hr), "Failed to close command list, hr %#x.\n", hr);

    wait_queue_idle(device, queue);
    hr = ID3D12CommandAllocator_Reset(command_allocator);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    hr = ID3D12GraphicsCommandList_Reset(command_list, command_allocator, NULL);
    ok(SUCCEEDED(hr), "Failed to reset command list, hr %#x.\n", hr);

    /* A command allocator can be used with one command list at a time. */
    hr = ID3D12Device_CreateCommandList(device, 0, D3D12_COMMAND_LIST_TYPE_DIRECT,
            command_allocator, NULL, &IID_ID3D12GraphicsCommandList, (void **)&command_list2);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    hr = ID3D12Device_CreateCommandList(device, 0, D3D12_COMMAND_LIST_TYPE_DIRECT,
            command_allocator2, NULL, &IID_ID3D12GraphicsCommandList, (void **)&command_list2);
    ok(hr == S_OK, "Failed to create command list, hr %#x.\n", hr);

    hr = ID3D12GraphicsCommandList_Close(command_list2);
    ok(SUCCEEDED(hr), "Failed to close command list, hr %#x.\n", hr);
    hr = ID3D12GraphicsCommandList_Reset(command_list2, command_allocator, NULL);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    ID3D12GraphicsCommandList_Release(command_list2);

    /* A command allocator can be re-used after closing the command list. */
    hr = ID3D12Device_CreateCommandList(device, 0, D3D12_COMMAND_LIST_TYPE_DIRECT,
            command_allocator, NULL, &IID_ID3D12GraphicsCommandList, (void **)&command_list2);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    hr = ID3D12GraphicsCommandList_Close(command_list);
    ok(SUCCEEDED(hr), "Failed to close command list, hr %#x.\n", hr);
    hr = ID3D12Device_CreateCommandList(device, 0, D3D12_COMMAND_LIST_TYPE_DIRECT,
            command_allocator, NULL, &IID_ID3D12GraphicsCommandList, (void **)&command_list2);
    ok(hr == S_OK, "Failed to create command list, hr %#x.\n", hr);

    ID3D12CommandAllocator_Release(command_allocator);
    ID3D12CommandAllocator_Release(command_allocator2);
    ID3D12CommandQueue_Release(queue);
    ID3D12GraphicsCommandList_Release(command_list);
    ID3D12GraphicsCommandList_Release(command_list2);
    refcount = ID3D12Device_Release(device);
    ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount);
}

static void test_cpu_signal_fence(void)
{
    HANDLE event1, event2;
    ID3D12Device *device;
    unsigned int i, ret;
    ID3D12Fence *fence;
    ULONG refcount;
    uint64_t value;
    HRESULT hr;

    if (!(device = create_device()))
    {
        skip("Failed to create device.\n");
        return;
    }

    hr = ID3D12Device_CreateFence(device, 0, D3D12_FENCE_FLAG_NONE,
            &IID_ID3D12Fence, (void **)&fence);
    ok(SUCCEEDED(hr), "Failed to create fence, hr %#x.\n", hr);

    hr = ID3D12Fence_Signal(fence, 1);
    ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
    value = ID3D12Fence_GetCompletedValue(fence);
    ok(value == 1, "Got unexpected value %"PRIu64".\n", value);

    hr = ID3D12Fence_Signal(fence, 10);
    ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
    value = ID3D12Fence_GetCompletedValue(fence);
    ok(value == 10, "Got unexpected value %"PRIu64".\n", value);

    hr = ID3D12Fence_Signal(fence, 5);
    ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
    value = ID3D12Fence_GetCompletedValue(fence);
    ok(value == 5, "Got unexpected value %"PRIu64".\n", value);

    hr = ID3D12Fence_Signal(fence, 0);
    ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
    value = ID3D12Fence_GetCompletedValue(fence);
    ok(value == 0, "Got unexpected value %"PRIu64".\n", value);

    /* Basic tests with single event. */
    event1 = create_event();
    ok(event1, "Failed to create event.\n");
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);

    hr = ID3D12Fence_SetEventOnCompletion(fence, 5, event1);
    ok(SUCCEEDED(hr), "Failed to set event on completion, hr %#x.\n", hr);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
    hr = ID3D12Fence_Signal(fence, 5);
    ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_OBJECT_0, "Got unexpected return value %#x.\n", ret);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);

    hr = ID3D12Fence_SetEventOnCompletion(fence, 6, event1);
    ok(SUCCEEDED(hr), "Failed to set event on completion, hr %#x.\n", hr);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
    hr = ID3D12Fence_Signal(fence, 7);
    ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_OBJECT_0, "Got unexpected return value %#x.\n", ret);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);

    hr = ID3D12Fence_Signal(fence, 10);
    ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);

    /* Event is signaled immediately when value <= GetCompletedValue(). */
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
    for (i = 0; i <= ID3D12Fence_GetCompletedValue(fence); ++i)
    {
        hr = ID3D12Fence_SetEventOnCompletion(fence, i, event1);
        ok(SUCCEEDED(hr), "Failed to set event on completion, hr %#x.\n", hr);
        ret = wait_event(event1, 0);
        ok(ret == WAIT_OBJECT_0, "Got unexpected return value %#x for %u.\n", ret, i);
        ret = wait_event(event1, 0);
        ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x for %u.\n", ret, i);
    }
    hr = ID3D12Fence_SetEventOnCompletion(fence, i, event1);
    ok(SUCCEEDED(hr), "Failed to set event on completion, hr %#x.\n", hr);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
    hr = ID3D12Fence_Signal(fence, i);
    ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_OBJECT_0, "Got unexpected return value %#x.\n", ret);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);

    /* Attach event to multiple values. */
    hr = ID3D12Fence_Signal(fence, 0);
    ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);

    hr = ID3D12Fence_SetEventOnCompletion(fence, 3, event1);
    ok(SUCCEEDED(hr), "Failed to set event on completion, hr %#x.\n", hr);
    hr = ID3D12Fence_SetEventOnCompletion(fence, 5, event1);
    ok(SUCCEEDED(hr), "Failed to set event on completion, hr %#x.\n", hr);
    hr = ID3D12Fence_SetEventOnCompletion(fence, 9, event1);
    ok(SUCCEEDED(hr), "Failed to set event on completion, hr %#x.\n", hr);
    hr = ID3D12Fence_SetEventOnCompletion(fence, 12, event1);
    ok(SUCCEEDED(hr), "Failed to set event on completion, hr %#x.\n", hr);
    hr = ID3D12Fence_SetEventOnCompletion(fence, 12, event1);
    ok(SUCCEEDED(hr), "Failed to set event on completion, hr %#x.\n", hr);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
    for (i = 1; i < 13; ++i)
    {
        hr = ID3D12Fence_Signal(fence, i);
        ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
        if (i == 3 || i == 5 || i == 9 || i == 12)
        {
            ret = wait_event(event1, 0);
            ok(ret == WAIT_OBJECT_0, "Got unexpected return value %#x for %u.\n", ret, i);
        }
        ret = wait_event(event1, 0);
        ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x for %u.\n", ret, i);
    }

    /* Tests with 2 events. */
    hr = ID3D12Fence_Signal(fence, 0);
    ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
    value = ID3D12Fence_GetCompletedValue(fence);
    ok(value == 0, "Got unexpected value %"PRIu64".\n", value);

    event2 = create_event();
    ok(event2, "Failed to create event.\n");

    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
    ret = wait_event(event2, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
    hr = ID3D12Fence_SetEventOnCompletion(fence, 100, event1);
    ok(SUCCEEDED(hr), "Failed to set event on completion, hr %#x.\n", hr);
    hr = ID3D12Fence_SetEventOnCompletion(fence, ~(uint64_t)0, event2);
    ok(SUCCEEDED(hr), "Failed to set event on completion, hr %#x.\n", hr);

    hr = ID3D12Fence_Signal(fence, 50);
    ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
    ret = wait_event(event2, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);

    hr = ID3D12Fence_Signal(fence, 99);
    ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
    ret = wait_event(event2, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);

    hr = ID3D12Fence_Signal(fence, 100);
    ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_OBJECT_0, "Got unexpected return value %#x.\n", ret);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
    ret = wait_event(event2, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);

    hr = ID3D12Fence_Signal(fence, 101);
    ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
    ret = wait_event(event2, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);

    hr = ID3D12Fence_Signal(fence, 0);
    ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
    ret = wait_event(event2, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);

    hr = ID3D12Fence_Signal(fence, 100);
    ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
    ret = wait_event(event2, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);

    hr = ID3D12Fence_Signal(fence, ~(uint64_t)0);
    ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
    ret = wait_event(event2, 0);
    ok(ret == WAIT_OBJECT_0, "Got unexpected return value %#x.\n", ret);
    ret = wait_event(event2, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);

    hr = ID3D12Fence_Signal(fence, ~(uint64_t)0);
    ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
    ret = wait_event(event2, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
    hr = ID3D12Fence_Signal(fence, 0);
    ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
    ret = wait_event(event2, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);

    /* Attach two events to the same value. */
    hr = ID3D12Fence_Signal(fence, 0);
    ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
    ret = wait_event(event2, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);

    hr = ID3D12Fence_SetEventOnCompletion(fence, 1, event1);
    ok(SUCCEEDED(hr), "Failed to set event on completion, hr %#x.\n", hr);
    hr = ID3D12Fence_SetEventOnCompletion(fence, 1, event2);
    ok(SUCCEEDED(hr), "Failed to set event on completion, hr %#x.\n", hr);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
    ret = wait_event(event2, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
    hr = ID3D12Fence_Signal(fence, 3);
    ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_OBJECT_0, "Got unexpected return value %#x.\n", ret);
    ret = wait_event(event2, 0);
    ok(ret == WAIT_OBJECT_0, "Got unexpected return value %#x.\n", ret);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
    ret = wait_event(event2, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);

    /* Test passing signaled event. */
    hr = ID3D12Fence_Signal(fence, 20);
    ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
    value = ID3D12Fence_GetCompletedValue(fence);
    ok(value == 20, "Got unexpected value %"PRIu64".\n", value);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);

    signal_event(event1);
    hr = ID3D12Fence_SetEventOnCompletion(fence, 30, event1);
    ok(SUCCEEDED(hr), "Failed to set event on completion, hr %#x.\n", hr);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_OBJECT_0, "Got unexpected return value %#x.\n", ret);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);

    hr = ID3D12Fence_Signal(fence, 30);
    ok(SUCCEEDED(hr), "Failed to signal fence, hr %#x.\n", hr);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_OBJECT_0, "Got unexpected return value %#x.\n", ret);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);

    destroy_event(event1);
    destroy_event(event2);

    ID3D12Fence_Release(fence);
    refcount = ID3D12Device_Release(device);
    ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount);
}

static void test_gpu_signal_fence(void)
{
    ID3D12CommandQueue *queue;
    HANDLE event1, event2;
    ID3D12Device *device;
    unsigned int i, ret;
    ID3D12Fence *fence;
    ULONG refcount;
    uint64_t value;
    HRESULT hr;

    if (!(device = create_device()))
    {
        skip("Failed to create device.\n");
        return;
    }

    queue = create_command_queue(device, D3D12_COMMAND_LIST_TYPE_DIRECT, D3D12_COMMAND_QUEUE_PRIORITY_NORMAL);

    hr = ID3D12Device_CreateFence(device, 0, D3D12_FENCE_FLAG_NONE,
            &IID_ID3D12Fence, (void **)&fence);
    ok(hr == S_OK, "Failed to create fence, hr %#x.\n", hr);

    /* XXX: It seems that when a queue is idle a fence is signalled immediately
     * in D3D12. Vulkan implementations don't signal a fence immediately so
     * libvkd3d doesn't as well. In order to make this test reliable
     * wait_queue_idle() is inserted after every ID3D12CommandQueue_Signal(). */
    queue_signal(queue, fence, 10);
    wait_queue_idle(device, queue);
    value = ID3D12Fence_GetCompletedValue(fence);
    ok(value == 10, "Got unexpected value %"PRIu64".\n", value);

    queue_signal(queue, fence, 0);
    wait_queue_idle(device, queue);
    value = ID3D12Fence_GetCompletedValue(fence);
    ok(value == 0, "Got unexpected value %"PRIu64".\n", value);

    /* Basic tests with single event. */
    event1 = create_event();
    ok(event1, "Failed to create event.\n");
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);

    hr = ID3D12Fence_SetEventOnCompletion(fence, 5, event1);
    ok(hr == S_OK, "Failed to set event on completion, hr %#x.\n", hr);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
    queue_signal(queue, fence, 5);
    wait_queue_idle(device, queue);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_OBJECT_0, "Got unexpected return value %#x.\n", ret);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);

    hr = ID3D12Fence_SetEventOnCompletion(fence, 6, event1);
    ok(hr == S_OK, "Failed to set event on completion, hr %#x.\n", hr);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
    queue_signal(queue, fence, 7);
    wait_queue_idle(device, queue);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_OBJECT_0, "Got unexpected return value %#x.\n", ret);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);

    queue_signal(queue, fence, 10);
    wait_queue_idle(device, queue);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);

    /* Attach one event to multiple values. */
    queue_signal(queue, fence, 0);
    wait_queue_idle(device, queue);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);

    hr = ID3D12Fence_SetEventOnCompletion(fence, 3, event1);
    ok(hr == S_OK, "Failed to set event on completion, hr %#x.\n", hr);
    hr = ID3D12Fence_SetEventOnCompletion(fence, 5, event1);
    ok(hr == S_OK, "Failed to set event on completion, hr %#x.\n", hr);
    hr = ID3D12Fence_SetEventOnCompletion(fence, 9, event1);
    ok(hr == S_OK, "Failed to set event on completion, hr %#x.\n", hr);
    hr = ID3D12Fence_SetEventOnCompletion(fence, 12, event1);
    ok(hr == S_OK, "Failed to set event on completion, hr %#x.\n", hr);
    hr = ID3D12Fence_SetEventOnCompletion(fence, 12, event1);
    ok(hr == S_OK, "Failed to set event on completion, hr %#x.\n", hr);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
    for (i = 1; i < 13; ++i)
    {
        queue_signal(queue, fence, i);
        wait_queue_idle(device, queue);
        if (i == 3 || i == 5 || i == 9 || i == 12)
        {
            ret = wait_event(event1, 0);
            ok(ret == WAIT_OBJECT_0, "Got unexpected return value %#x for %u.\n", ret, i);
        }
        ret = wait_event(event1, 0);
        ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x for %u.\n", ret, i);
    }

    /* Tests with 2 events. */
    queue_signal(queue, fence, 0);
    wait_queue_idle(device, queue);
    value = ID3D12Fence_GetCompletedValue(fence);
    ok(value == 0, "Got unexpected value %"PRIu64".\n", value);

    event2 = create_event();
    ok(event2, "Failed to create event.\n");

    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
    ret = wait_event(event2, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
    hr = ID3D12Fence_SetEventOnCompletion(fence, 100, event1);
    ok(hr == S_OK, "Failed to set event on completion, hr %#x.\n", hr);
    hr = ID3D12Fence_SetEventOnCompletion(fence, ~(uint64_t)0, event2);
    ok(hr == S_OK, "Failed to set event on completion, hr %#x.\n", hr);

    queue_signal(queue, fence, 50);
    wait_queue_idle(device, queue);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
    ret = wait_event(event2, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);

    queue_signal(queue, fence, 99);
    wait_queue_idle(device, queue);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
    ret = wait_event(event2, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);

    queue_signal(queue, fence, 100);
    wait_queue_idle(device, queue);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_OBJECT_0, "Got unexpected return value %#x.\n", ret);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
    ret = wait_event(event2, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);

    queue_signal(queue, fence, 101);
    wait_queue_idle(device, queue);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
    ret = wait_event(event2, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);

    queue_signal(queue, fence, 0);
    wait_queue_idle(device, queue);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
    ret = wait_event(event2, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);

    queue_signal(queue, fence, 100);
    wait_queue_idle(device, queue);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
    ret = wait_event(event2, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);

    queue_signal(queue, fence, ~(uint64_t)0);
    wait_queue_idle(device, queue);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
    ret = wait_event(event2, 0);
    ok(ret == WAIT_OBJECT_0, "Got unexpected return value %#x.\n", ret);
    ret = wait_event(event2, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);

    queue_signal(queue, fence, ~(uint64_t)0);
    wait_queue_idle(device, queue);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
    ret = wait_event(event2, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
    queue_signal(queue, fence, 0);
    wait_queue_idle(device, queue);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
    ret = wait_event(event2, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);

    /* Attach two events to the same value. */
    queue_signal(queue, fence, 0);
    wait_queue_idle(device, queue);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
    ret = wait_event(event2, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);

    hr = ID3D12Fence_SetEventOnCompletion(fence, 1, event1);
    ok(hr == S_OK, "Failed to set event on completion, hr %#x.\n", hr);
    hr = ID3D12Fence_SetEventOnCompletion(fence, 1, event2);
    ok(hr == S_OK, "Failed to set event on completion, hr %#x.\n", hr);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
    ret = wait_event(event2, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
    queue_signal(queue, fence, 3);
    wait_queue_idle(device, queue);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_OBJECT_0, "Got unexpected return value %#x.\n", ret);
    ret = wait_event(event2, 0);
    ok(ret == WAIT_OBJECT_0, "Got unexpected return value %#x.\n", ret);
    ret = wait_event(event1, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
    ret = wait_event(event2, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);

    wait_queue_idle(device, queue);

    destroy_event(event1);
    destroy_event(event2);

    ID3D12Fence_Release(fence);
    ID3D12CommandQueue_Release(queue);
    refcount = ID3D12Device_Release(device);
    ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount);
}

struct multithread_fence_wait_data
{
    HANDLE event;
    ID3D12Fence *fence;
    uint64_t value;
};

static void fence_event_wait_main(void *untyped_data)
{
    struct multithread_fence_wait_data *data = untyped_data;
    unsigned int ret;
    HANDLE event;
    HRESULT hr;

    event = create_event();
    ok(event, "Failed to create event.\n");

    hr = ID3D12Fence_SetEventOnCompletion(data->fence, data->value, event);
    ok(SUCCEEDED(hr), "Failed to set event on completion, hr %#x.\n", hr);

    signal_event(data->event);

    ret = wait_event(event, INFINITE);
    ok(ret == WAIT_OBJECT_0, "Got unexpected return value %#x.\n", ret);

    destroy_event(event);
}

static void fence_busy_wait_main(void *untyped_data)
{
    struct multithread_fence_wait_data *data = untyped_data;

    signal_event(data->event);

    while (ID3D12Fence_GetCompletedValue(data->fence) < data->value)
        ;
}

static void test_multithread_fence_wait(void)
{
    struct multithread_fence_wait_data thread_data;
    ID3D12CommandQueue *queue;
    ID3D12Device *device;
    unsigned int ret;
    ULONG refcount;
    HANDLE thread;
    HRESULT hr;

    if (!(device = create_device()))
    {
        skip("Failed to create device.\n");
        return;
    }

    queue = create_command_queue(device, D3D12_COMMAND_LIST_TYPE_DIRECT, D3D12_COMMAND_QUEUE_PRIORITY_NORMAL);

    thread_data.event = create_event();
    thread_data.value = 0;
    ok(thread_data.event, "Failed to create event.\n");
    hr = ID3D12Device_CreateFence(device, thread_data.value, D3D12_FENCE_FLAG_NONE,
            &IID_ID3D12Fence, (void **)&thread_data.fence);
    ok(hr == S_OK, "Failed to create fence, hr %#x.\n", hr);

    /* Signal fence on host. */
    ++thread_data.value;
    thread = create_thread(fence_event_wait_main, &thread_data);
    ok(thread, "Failed to create thread.\n");
    ret = wait_event(thread_data.event, INFINITE);
    ok(ret == WAIT_OBJECT_0, "Failed to wait for thread start, return value %#x.\n", ret);

    hr = ID3D12Fence_Signal(thread_data.fence, thread_data.value);
    ok(hr == S_OK, "Failed to signal fence, hr %#x.\n", hr);

    ok(join_thread(thread), "Failed to join thread.\n");

    ++thread_data.value;
    thread = create_thread(fence_busy_wait_main, &thread_data);
    ok(thread, "Failed to create thread.\n");
    ret = wait_event(thread_data.event, INFINITE);
    ok(ret == WAIT_OBJECT_0, "Failed to wait for thread start, return value %#x.\n", ret);

    hr = ID3D12Fence_Signal(thread_data.fence, thread_data.value);
    ok(hr == S_OK, "Failed to signal fence, hr %#x.\n", hr);

    ok(join_thread(thread), "Failed to join thread.\n");

    /* Signal fence on device. */
    ++thread_data.value;
    thread = create_thread(fence_event_wait_main, &thread_data);
    ok(thread, "Failed to create thread.\n");
    ret = wait_event(thread_data.event, INFINITE);
    ok(ret == WAIT_OBJECT_0, "Failed to wait for thread start, return value %#x.\n", ret);

    queue_signal(queue, thread_data.fence, thread_data.value);

    ok(join_thread(thread), "Failed to join thread.\n");

    ++thread_data.value;
    thread = create_thread(fence_busy_wait_main, &thread_data);
    ok(thread, "Failed to create thread.\n");
    ret = wait_event(thread_data.event, INFINITE);
    ok(ret == WAIT_OBJECT_0, "Failed to wait for thread start, return value %#x.\n", ret);

    queue_signal(queue, thread_data.fence, thread_data.value);

    ok(join_thread(thread), "Failed to join thread.\n");

    destroy_event(thread_data.event);
    ID3D12Fence_Release(thread_data.fence);
    ID3D12CommandQueue_Release(queue);
    refcount = ID3D12Device_Release(device);
    ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount);
}

static void test_fence_values(void)
{
    uint64_t value, next_value;
    ID3D12CommandQueue *queue;
    ID3D12Device *device;
    ID3D12Fence *fence;
    ULONG refcount;
    unsigned int i;
    HRESULT hr;

    if (!(device = create_device()))
    {
        skip("Failed to create device.\n");
        return;
    }

    queue = create_command_queue(device, D3D12_COMMAND_LIST_TYPE_DIRECT, D3D12_COMMAND_QUEUE_PRIORITY_NORMAL);

    next_value = (uint64_t)1 << 60;
    hr = ID3D12Device_CreateFence(device, next_value, D3D12_FENCE_FLAG_NONE, &IID_ID3D12Fence, (void **)&fence);
    ok(hr == S_OK, "Failed to create fence, hr %#x.\n", hr);

    value = ID3D12Fence_GetCompletedValue(fence);
    ok(value == next_value, "Got value %#"PRIx64", expected %#"PRIx64".\n", value, next_value);

    for (i = 0; i < 200; ++i)
    {
        ++next_value;
        queue_signal(queue, fence, next_value);
        if ((i * 11) & 8)
            wait_queue_idle_no_event(device, queue);
        else
            wait_queue_idle(device, queue);
        value = ID3D12Fence_GetCompletedValue(fence);
        ok(value == next_value, "Got value %#"PRIx64", expected %#"PRIx64".\n", value, next_value);
    }

    for (i = 0; i < 100; ++i)
    {
        next_value += 10000;
        hr = ID3D12Fence_Signal(fence, next_value);
        ok(hr == S_OK, "Failed to signal fence, hr %#x.\n", hr);
        value = ID3D12Fence_GetCompletedValue(fence);
        ok(value == next_value, "Got value %#"PRIx64", expected %#"PRIx64".\n", value, next_value);
    }

    ID3D12Fence_Release(fence);

    hr = ID3D12Device_CreateFence(device, 0, D3D12_FENCE_FLAG_NONE, &IID_ID3D12Fence, (void **)&fence);
    ok(hr == S_OK, "Failed to create fence, hr %#x.\n", hr);
    next_value = (uint64_t)1 << 60;
    hr = ID3D12Fence_Signal(fence, next_value);
    ok(hr == S_OK, "Failed to signal fence, hr %#x.\n", hr);
    value = ID3D12Fence_GetCompletedValue(fence);
    ok(value == next_value, "Got value %#"PRIx64", expected %#"PRIx64".\n", value, next_value);
    next_value = 0;
    hr = ID3D12Fence_Signal(fence, next_value);
    ok(hr == S_OK, "Failed to signal fence, hr %#x.\n", hr);
    value = ID3D12Fence_GetCompletedValue(fence);
    ok(value == next_value, "Got value %#"PRIx64", expected %#"PRIx64".\n", value, next_value);
    ID3D12Fence_Release(fence);

    hr = ID3D12Device_CreateFence(device, 0, D3D12_FENCE_FLAG_NONE, &IID_ID3D12Fence, (void **)&fence);
    ok(hr == S_OK, "Failed to create fence, hr %#x.\n", hr);
    next_value = (uint64_t)1 << 60;
    queue_signal(queue, fence, next_value);
    wait_queue_idle(device, queue);
    value = ID3D12Fence_GetCompletedValue(fence);
    ok(value == next_value, "Got value %#"PRIx64", expected %#"PRIx64".\n", value, next_value);
    next_value <<= 1;
    queue_signal(queue, fence, next_value);
    wait_queue_idle_no_event(device, queue);
    value = ID3D12Fence_GetCompletedValue(fence);
    ok(value == next_value, "Got value %#"PRIx64", expected %#"PRIx64".\n", value, next_value);
    next_value = 0;
    queue_signal(queue, fence, next_value);
    wait_queue_idle(device, queue);
    value = ID3D12Fence_GetCompletedValue(fence);
    ok(value == next_value, "Got value %#"PRIx64", expected %#"PRIx64".\n", value, next_value);
    ID3D12Fence_Release(fence);

    ID3D12CommandQueue_Release(queue);
    refcount = ID3D12Device_Release(device);
    ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount);
}

static void test_clear_depth_stencil_view(void)
{
    static const float expected_values[] = {0.5f, 0.1f, 0.1f, 0.6, 1.0f, 0.5f};
    ID3D12GraphicsCommandList *command_list;
    D3D12_DEPTH_STENCIL_VIEW_DESC dsv_desc;
    struct depth_stencil_resource ds;
    unsigned int dsv_increment_size;
    D3D12_CLEAR_VALUE clear_value;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Device *device;
    unsigned int i;

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

    dsv_increment_size = ID3D12Device_GetDescriptorHandleIncrementSize(device,
            D3D12_DESCRIPTOR_HEAP_TYPE_DSV);
    trace("DSV descriptor handle increment size: %u.\n", dsv_increment_size);
    ok(dsv_increment_size, "Got unexpected increment size %#x.\n", dsv_increment_size);

    clear_value.Format = DXGI_FORMAT_D32_FLOAT;
    clear_value.DepthStencil.Depth = 0.5f;
    clear_value.DepthStencil.Stencil = 0x3;
    init_depth_stencil(&ds, device, 32, 32, 1, 1, DXGI_FORMAT_D32_FLOAT, 0, &clear_value);

    ID3D12GraphicsCommandList_ClearDepthStencilView(command_list, ds.dsv_handle,
            D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, 0.75f, 0x7, 0, NULL);
    transition_resource_state(command_list, ds.texture,
            D3D12_RESOURCE_STATE_DEPTH_WRITE, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_float(ds.texture, 0, queue, command_list, 0.75f, 1);

    destroy_depth_stencil(&ds);
    reset_command_list(command_list, context.allocator);
    init_depth_stencil(&ds, device, 32, 32, 6, 1, DXGI_FORMAT_D32_FLOAT, 0, &clear_value);

    ID3D12GraphicsCommandList_ClearDepthStencilView(command_list, ds.dsv_handle,
            D3D12_CLEAR_FLAG_DEPTH, expected_values[0], 0, 0, NULL);
    memset(&dsv_desc, 0, sizeof(dsv_desc));
    dsv_desc.Format = DXGI_FORMAT_D32_FLOAT;
    dsv_desc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DARRAY;
    dsv_desc.Texture2DArray.FirstArraySlice = 1;
    dsv_desc.Texture2DArray.ArraySize = 2;
    ID3D12Device_CreateDepthStencilView(device, ds.texture, &dsv_desc, ds.dsv_handle);
    ID3D12GraphicsCommandList_ClearDepthStencilView(command_list, ds.dsv_handle,
            D3D12_CLEAR_FLAG_DEPTH, expected_values[1], 0, 0, NULL);
    dsv_desc.Texture2DArray.FirstArraySlice = 3;
    dsv_desc.Texture2DArray.ArraySize = 1;
    ID3D12Device_CreateDepthStencilView(device, ds.texture, &dsv_desc, ds.dsv_handle);
    ID3D12GraphicsCommandList_ClearDepthStencilView(command_list, ds.dsv_handle,
            D3D12_CLEAR_FLAG_DEPTH, expected_values[3], 0, 0, NULL);
    dsv_desc.Texture2DArray.FirstArraySlice = 4;
    ID3D12Device_CreateDepthStencilView(device, ds.texture, &dsv_desc, ds.dsv_handle);
    ID3D12GraphicsCommandList_ClearDepthStencilView(command_list, ds.dsv_handle,
            D3D12_CLEAR_FLAG_DEPTH, expected_values[4], 0, 0, NULL);

    transition_resource_state(command_list, ds.texture,
            D3D12_RESOURCE_STATE_DEPTH_WRITE, D3D12_RESOURCE_STATE_COPY_SOURCE);
    for (i = 0; i < ARRAY_SIZE(expected_values); ++i)
    {
        check_sub_resource_float(ds.texture, i, queue, command_list, expected_values[i], 1);
        reset_command_list(command_list, context.allocator);
    }

    transition_resource_state(command_list, ds.texture,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_DEPTH_WRITE);
    dsv_desc.Texture2DArray.ArraySize = UINT_MAX;
    ID3D12Device_CreateDepthStencilView(device, ds.texture, &dsv_desc, ds.dsv_handle);
    ID3D12GraphicsCommandList_ClearDepthStencilView(command_list, ds.dsv_handle,
            D3D12_CLEAR_FLAG_DEPTH, expected_values[4], 0, 0, NULL);

    transition_resource_state(command_list, ds.texture,
            D3D12_RESOURCE_STATE_DEPTH_WRITE, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_float(ds.texture, 5, queue, command_list, expected_values[4], 1);

    destroy_depth_stencil(&ds);
    destroy_test_context(&context);
}

static void test_clear_render_target_view(void)
{
    static const unsigned int array_expected_colors[] = {0xff00ff00, 0xff0000ff, 0xffff0000};
    static const struct vec4 array_colors[] =
    {
        {0.0f, 1.0f, 0.0f, 1.0f},
        {1.0f, 0.0f, 0.0f, 1.0f},
        {0.0f, 0.0f, 1.0f, 1.0f},
    };
    static const float negative_value[] = {1.0f, -1.0f, -0.5f, -2.0f};
    static const float color[] = {0.1f, 0.5f, 0.3f, 0.75f};
    static const float green[] = {0.0f, 1.0f, 0.0f, 1.0f};
    ID3D12GraphicsCommandList *command_list;
    D3D12_CPU_DESCRIPTOR_HANDLE rtv_handle;
    D3D12_RENDER_TARGET_VIEW_DESC rtv_desc;
    D3D12_HEAP_PROPERTIES heap_properties;
    D3D12_RESOURCE_DESC resource_desc;
    struct d3d12_resource_readback rb;
    unsigned int rtv_increment_size;
    ID3D12DescriptorHeap *rtv_heap;
    D3D12_CLEAR_VALUE clear_value;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Resource *resource;
    ID3D12Device *device;
    unsigned int i;
    D3D12_BOX box;
    HRESULT hr;

    static const struct
    {
        const float *color;
        DXGI_FORMAT format;
        uint32_t result;
    }
    r8g8b8a8[] =
    {
        {color,          DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, 0xbf95bc59},

        {green,          DXGI_FORMAT_R8G8B8A8_UNORM,      0xff00ff00},
        {color,          DXGI_FORMAT_R8G8B8A8_UNORM,      0xbf4c7f19},

        {green,          DXGI_FORMAT_R8G8B8A8_UINT,       0x01000100},
        {color,          DXGI_FORMAT_R8G8B8A8_UINT,       0x00000000},
        {negative_value, DXGI_FORMAT_R8G8B8A8_UINT,       0x00000001},

        {green,          DXGI_FORMAT_R8G8B8A8_SINT,       0x01000100},
        {color,          DXGI_FORMAT_R8G8B8A8_SINT,       0x00000000},
        {negative_value, DXGI_FORMAT_R8G8B8A8_SINT,       0xfe00ff01},
    };
    static const struct
    {
        const float *color;
        DXGI_FORMAT format;
        uint64_t result;
    }
    r16g16b16a16[] =
    {
        {green,          DXGI_FORMAT_R16G16B16A16_UNORM, 0xffff0000ffff0000},

        {green,          DXGI_FORMAT_R16G16B16A16_UINT,  0x0001000000010000},
        {color,          DXGI_FORMAT_R16G16B16A16_UINT,  0x0000000000000000},
        {negative_value, DXGI_FORMAT_R16G16B16A16_UINT,  0x0000000000000001},

        {green,          DXGI_FORMAT_R16G16B16A16_SINT,  0x0001000000010000},
        {color,          DXGI_FORMAT_R16G16B16A16_SINT,  0x0000000000000000},
        {negative_value, DXGI_FORMAT_R16G16B16A16_SINT,  0xfffe0000ffff0001},
    };

    STATIC_ASSERT(ARRAY_SIZE(array_colors) == ARRAY_SIZE(array_expected_colors));

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

    rtv_heap = create_cpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_RTV, 1);

    rtv_increment_size = ID3D12Device_GetDescriptorHandleIncrementSize(device,
            D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
    trace("RTV descriptor handle increment size: %u.\n", rtv_increment_size);

    rtv_handle = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(rtv_heap);

    memset(&heap_properties, 0, sizeof(heap_properties));
    heap_properties.Type = D3D12_HEAP_TYPE_DEFAULT;
    resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
    resource_desc.Alignment = 0;
    resource_desc.Width = 32;
    resource_desc.Height = 32;
    resource_desc.DepthOrArraySize = 1;
    resource_desc.MipLevels = 1;
    resource_desc.Format = DXGI_FORMAT_R8G8B8A8_TYPELESS;
    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;
    clear_value.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    clear_value.Color[0] = 1.0f;
    clear_value.Color[1] = 0.0f;
    clear_value.Color[2] = 0.0f;
    clear_value.Color[3] = 1.0f;
    hr = ID3D12Device_CreateCommittedResource(device,
            &heap_properties, D3D12_HEAP_FLAG_NONE, &resource_desc,
            D3D12_RESOURCE_STATE_RENDER_TARGET, &clear_value,
            &IID_ID3D12Resource, (void **)&resource);
    ok(hr == S_OK, "Failed to create texture, hr %#x.\n", hr);

    memset(&rtv_desc, 0, sizeof(rtv_desc));
    rtv_desc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D;

    /* R8G8B8A8 */
    for (i = 0; i < ARRAY_SIZE(r8g8b8a8); ++i)
    {
        vkd3d_test_push_context("Test %u", i);

        rtv_desc.Format = r8g8b8a8[i].format;
        ID3D12Device_CreateRenderTargetView(device, resource, &rtv_desc, rtv_handle);

        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, rtv_handle, r8g8b8a8[i].color, 0, NULL);
        transition_resource_state(command_list, resource,
                D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
        check_sub_resource_uint(resource, 0, queue, command_list, r8g8b8a8[i].result, 2);

        reset_command_list(command_list, context.allocator);
        transition_resource_state(command_list, resource,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

        vkd3d_test_pop_context();
    }

    /* R16G16B16A16 */
    hr = ID3D12GraphicsCommandList_Close(command_list);
    ok(hr == S_OK, "Failed to close command list, hr %#x.\n", hr);
    reset_command_list(command_list, context.allocator);
    ID3D12Resource_Release(resource);
    resource_desc.Format = DXGI_FORMAT_R16G16B16A16_TYPELESS;
    hr = ID3D12Device_CreateCommittedResource(device,
            &heap_properties, D3D12_HEAP_FLAG_NONE, &resource_desc,
            D3D12_RESOURCE_STATE_RENDER_TARGET, NULL,
            &IID_ID3D12Resource, (void **)&resource);
    ok(hr == S_OK, "Failed to create texture, hr %#x.\n", hr);

    for (i = 0; i < ARRAY_SIZE(r16g16b16a16); ++i)
    {
        vkd3d_test_push_context("Test %u", i);

        rtv_desc.Format = r16g16b16a16[i].format;
        ID3D12Device_CreateRenderTargetView(device, resource, &rtv_desc, rtv_handle);

        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, rtv_handle, r16g16b16a16[i].color, 0, NULL);
        transition_resource_state(command_list, resource,
                D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
        check_sub_resource_uint64(resource, 0, queue, command_list, r16g16b16a16[i].result, 0);

        reset_command_list(command_list, context.allocator);
        transition_resource_state(command_list, resource,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

        vkd3d_test_pop_context();
    }

    /* 2D array texture */
    hr = ID3D12GraphicsCommandList_Close(command_list);
    ok(hr == S_OK, "Failed to close command list, hr %#x.\n", hr);
    reset_command_list(command_list, context.allocator);
    ID3D12Resource_Release(resource);
    resource_desc.Format = DXGI_FORMAT_R8G8B8A8_TYPELESS;
    resource_desc.DepthOrArraySize = ARRAY_SIZE(array_colors);
    hr = ID3D12Device_CreateCommittedResource(device,
            &heap_properties, D3D12_HEAP_FLAG_NONE, &resource_desc,
            D3D12_RESOURCE_STATE_RENDER_TARGET, &clear_value,
            &IID_ID3D12Resource, (void **)&resource);
    ok(hr == S_OK, "Failed to create texture, hr %#x.\n", hr);

    for (i = 0; i < ARRAY_SIZE(array_colors); ++i)
    {
        memset(&rtv_desc, 0, sizeof(rtv_desc));
        rtv_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
        rtv_desc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY;
        rtv_desc.Texture2DArray.FirstArraySlice = i;
        rtv_desc.Texture2DArray.ArraySize = (i == ARRAY_SIZE(array_colors) - 1) ? UINT_MAX : 1;

        ID3D12Device_CreateRenderTargetView(device, resource, &rtv_desc, rtv_handle);

        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, rtv_handle, (const float *)&array_colors[i], 0, NULL);
    }

    transition_resource_state(command_list, resource,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    for (i = 0; i < ARRAY_SIZE(array_expected_colors); ++i)
    {
        check_sub_resource_uint(resource, i, queue, command_list, array_expected_colors[i], 2);
        reset_command_list(command_list, context.allocator);
    }

    /* 2D multisample array texture */
    ID3D12Resource_Release(resource);
    resource_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    resource_desc.SampleDesc.Count = 4;
    hr = ID3D12Device_CreateCommittedResource(device,
            &heap_properties, D3D12_HEAP_FLAG_NONE, &resource_desc,
            D3D12_RESOURCE_STATE_RENDER_TARGET, &clear_value,
            &IID_ID3D12Resource, (void **)&resource);
    ok(hr == S_OK, "Failed to create texture, hr %#x.\n", hr);

    for (i = 0; i < ARRAY_SIZE(array_colors); ++i)
    {
        memset(&rtv_desc, 0, sizeof(rtv_desc));
        rtv_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
        rtv_desc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DMSARRAY;
        rtv_desc.Texture2DMSArray.FirstArraySlice = i;
        rtv_desc.Texture2DMSArray.ArraySize = (i == ARRAY_SIZE(array_colors) - 1) ? UINT_MAX : 1;

        ID3D12Device_CreateRenderTargetView(device, resource, &rtv_desc, rtv_handle);

        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, rtv_handle, (const float *)&array_colors[i], 0, NULL);
    }

    transition_resource_state(command_list, resource,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    for (i = 0; i < ARRAY_SIZE(array_expected_colors); ++i)
    {
        check_sub_resource_uint(resource, i, queue, command_list, array_expected_colors[i], 2);
        reset_command_list(command_list, context.allocator);
    }

    /* 3D texture */
    ID3D12Resource_Release(resource);
    resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE3D;
    resource_desc.DepthOrArraySize = 32;
    resource_desc.MipLevels = 1;
    resource_desc.SampleDesc.Count = 1;
    hr = ID3D12Device_CreateCommittedResource(device,
            &heap_properties, D3D12_HEAP_FLAG_NONE, &resource_desc,
            D3D12_RESOURCE_STATE_RENDER_TARGET, &clear_value,
            &IID_ID3D12Resource, (void **)&resource);
    ok(hr == S_OK, "Failed to create texture, hr %#x.\n", hr);

    ID3D12Device_CreateRenderTargetView(device, resource, NULL, rtv_handle);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, rtv_handle, color, 0, NULL);
    transition_resource_state(command_list, resource,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_uint(resource, 0, queue, command_list, 0xbf4c7f19, 2);

    memset(&rtv_desc, 0, sizeof(rtv_desc));
    rtv_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    rtv_desc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE3D;
    rtv_desc.Texture3D.FirstWSlice = 2;
    rtv_desc.Texture3D.WSize = 2;
    ID3D12Device_CreateRenderTargetView(device, resource, &rtv_desc, rtv_handle);

    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, resource,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, rtv_handle, green, 0, NULL);
    transition_resource_state(command_list, resource,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_resource_readback_with_command_list(resource, 0, &rb, queue, command_list);
    set_box(&box, 0, 0, 0, 32, 32, 2);
    check_readback_data_uint(&rb.rb, &box, 0xbf4c7f19, 1);
    set_box(&box, 0, 0, 2, 32, 32, 4);
    check_readback_data_uint(&rb.rb, &box, 0xff00ff00, 1);
    set_box(&box, 0, 0, 4, 32, 32, 32);
    check_readback_data_uint(&rb.rb, &box, 0xbf4c7f19, 1);
    release_resource_readback(&rb);

    rtv_desc.Texture3D.FirstWSlice = 30;
    rtv_desc.Texture3D.WSize = UINT_MAX;
    ID3D12Device_CreateRenderTargetView(device, resource, &rtv_desc, rtv_handle);

    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, resource,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, rtv_handle, green, 0, NULL);
    transition_resource_state(command_list, resource,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_resource_readback_with_command_list(resource, 0, &rb, queue, command_list);
    set_box(&box, 0, 0, 4, 32, 32, 30);
    check_readback_data_uint(&rb.rb, &box, 0xbf4c7f19, 1);
    set_box(&box, 0, 0, 30, 32, 32, 32);
    check_readback_data_uint(&rb.rb, &box, 0xff00ff00, 1);
    release_resource_readback(&rb);

    ID3D12Resource_Release(resource);
    ID3D12DescriptorHeap_Release(rtv_heap);
    destroy_test_context(&context);
}

static void test_clear_unordered_access_view_buffer(void)
{
    D3D12_UNORDERED_ACCESS_VIEW_DESC uav_desc;
    ID3D12DescriptorHeap *cpu_heap, *gpu_heap;
    ID3D12GraphicsCommandList *command_list;
    struct d3d12_resource_readback rb;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12CommandQueue *queue;
    D3D12_HEAP_DESC heap_desc;
    ID3D12Resource *buffer;
    ID3D12Device *device;
    UINT clear_value[4];
    unsigned int i, j;
    ID3D12Heap *heap;
    D3D12_BOX box;
    HRESULT hr;

#define BUFFER_SIZE (1024 * 1024)
    static const struct
    {
        DXGI_FORMAT format;
        D3D12_BUFFER_UAV buffer_uav;
        unsigned int values[4];
        unsigned int expected;
        bool is_float;
        bool is_todo;
    }
    tests[] =
    {
        {DXGI_FORMAT_R32_UINT, { 0, BUFFER_SIZE / sizeof(uint32_t),      0, 0, D3D12_BUFFER_UAV_FLAG_NONE},
                {0, 0, 0, 0}, 0},
        {DXGI_FORMAT_R32_UINT, {64, BUFFER_SIZE / sizeof(uint32_t) - 64, 0, 0, D3D12_BUFFER_UAV_FLAG_NONE},
                {0, 0, 0, 0}, 0},
        {DXGI_FORMAT_R32_UINT, { 0, BUFFER_SIZE / sizeof(uint32_t),      0, 0, D3D12_BUFFER_UAV_FLAG_NONE},
                {1, 0, 0, 0}, 1},
        {DXGI_FORMAT_R32_UINT, {64, BUFFER_SIZE / sizeof(uint32_t) - 64, 0, 0, D3D12_BUFFER_UAV_FLAG_NONE},
                {2, 0, 0, 0}, 2},
        {DXGI_FORMAT_R32_UINT, {64, BUFFER_SIZE / sizeof(uint32_t) - 64, 0, 0, D3D12_BUFFER_UAV_FLAG_NONE},
                {3, 0, 0, 0}, 3},
        {DXGI_FORMAT_R32_UINT, {64, BUFFER_SIZE / sizeof(uint32_t) - 64, 0, 0, D3D12_BUFFER_UAV_FLAG_NONE},
                {4, 2, 3, 4}, 4},
        {DXGI_FORMAT_R32_UINT, { 0, BUFFER_SIZE / sizeof(uint32_t) - 10, 0, 0, D3D12_BUFFER_UAV_FLAG_NONE},
                {5, 0, 0, 0}, 5},

        {DXGI_FORMAT_R32_TYPELESS, { 0, BUFFER_SIZE / sizeof(uint32_t),      0, 0, D3D12_BUFFER_UAV_FLAG_RAW},
                {0, 0, 0, 0}, 0},
        {DXGI_FORMAT_R32_TYPELESS, {64, BUFFER_SIZE / sizeof(uint32_t) - 64, 0, 0, D3D12_BUFFER_UAV_FLAG_RAW},
                {0, 0, 0, 0}, 0},
        {DXGI_FORMAT_R32_TYPELESS, { 0, BUFFER_SIZE / sizeof(uint32_t),      0, 0, D3D12_BUFFER_UAV_FLAG_RAW},
                {6, 0, 0, 0}, 6},
        {DXGI_FORMAT_R32_TYPELESS, {64, BUFFER_SIZE / sizeof(uint32_t) - 64, 0, 0, D3D12_BUFFER_UAV_FLAG_RAW},
                {7, 0, 0, 0}, 7},
        {DXGI_FORMAT_R32_TYPELESS, {64, BUFFER_SIZE / sizeof(uint32_t) - 64, 0, 0, D3D12_BUFFER_UAV_FLAG_RAW},
                {8, 0, 0, 0}, 8},
        {DXGI_FORMAT_R32_TYPELESS, {64, BUFFER_SIZE / sizeof(uint32_t) - 64, 0, 0, D3D12_BUFFER_UAV_FLAG_RAW},
                {9, 1, 1, 1}, 9},
        {DXGI_FORMAT_R32_TYPELESS, {64, BUFFER_SIZE / sizeof(uint32_t) - 64, 0, 0, D3D12_BUFFER_UAV_FLAG_RAW},
                {~0u, 0, 0, 0}, ~0u},
        {DXGI_FORMAT_R32_TYPELESS, { 0, BUFFER_SIZE / sizeof(uint32_t) - 10, 0, 0, D3D12_BUFFER_UAV_FLAG_RAW},
                {10, 0, 0, 0}, 10},
        {DXGI_FORMAT_R32_TYPELESS, { 0, BUFFER_SIZE / sizeof(uint32_t) - 9,  0, 0, D3D12_BUFFER_UAV_FLAG_RAW},
                {11, 0, 0, 0}, 11},

        {DXGI_FORMAT_R32_FLOAT, { 0, BUFFER_SIZE / sizeof(uint32_t), 0, 0, D3D12_BUFFER_UAV_FLAG_NONE},
                {0, 0, 0, 0}, 0},
        {DXGI_FORMAT_R32_FLOAT, { 0, BUFFER_SIZE / sizeof(uint32_t), 0, 0, D3D12_BUFFER_UAV_FLAG_NONE},
                {1, 0, 0, 0}, 1},
        {DXGI_FORMAT_R32_FLOAT, { 0, BUFFER_SIZE / sizeof(uint32_t), 0, 0, D3D12_BUFFER_UAV_FLAG_NONE},
                {0x3f800000 /* 1.0f */, 0, 0, 0}, 0x3f800000 /* 1.0f */, true},

        {DXGI_FORMAT_R16G16_UINT, { 0, BUFFER_SIZE / sizeof(uint32_t), 0, 0, D3D12_BUFFER_UAV_FLAG_NONE},
                {0x1234, 0xabcd, 0, 0}, 0xabcd1234},

        {DXGI_FORMAT_R16G16_UNORM, { 0, BUFFER_SIZE / sizeof(uint32_t), 0, 0, D3D12_BUFFER_UAV_FLAG_NONE},
                {0x1234, 0xabcd, 0, 0}, 0xabcd1234},
        {DXGI_FORMAT_R16G16_UNORM, { 0, BUFFER_SIZE / sizeof(uint32_t), 0, 0, D3D12_BUFFER_UAV_FLAG_NONE},
                {0x3e800000 /* 0.25f */, 0x3f800000 /* 1.0f */, 0, 0}, 0xffff4000, true},
        {DXGI_FORMAT_R16G16_UNORM, { 0, BUFFER_SIZE / sizeof(uint32_t), 0, 0, D3D12_BUFFER_UAV_FLAG_NONE},
                {0x40000000 /* 2.0f */, 0 /* 0.0f */, 0, 0}, 0x0000ffff, true},
        {DXGI_FORMAT_R16G16_UNORM, { 0, BUFFER_SIZE / sizeof(uint32_t), 0, 0, D3D12_BUFFER_UAV_FLAG_NONE},
                {0xbf800000 /* -1.0f */, 0 /* 0.0f */, 0x3f000000 /* 1.0f */, 0x3f000000 /* 1.0f */}, 0, true},

        {DXGI_FORMAT_R16G16_FLOAT, { 0, BUFFER_SIZE / sizeof(uint32_t), 0, 0, D3D12_BUFFER_UAV_FLAG_NONE},
                {0x1234, 0xabcd, 0, 0}, 0xabcd1234},
        {DXGI_FORMAT_R16G16_FLOAT, { 0, BUFFER_SIZE / sizeof(uint32_t), 0, 0, D3D12_BUFFER_UAV_FLAG_NONE},
                {0x3f000000 /* 0.5f */, 0x3f800000 /* 1.0f */, 0, 0}, 0x3c003800, true},

        {DXGI_FORMAT_R8G8B8A8_UINT, { 0, BUFFER_SIZE / sizeof(uint32_t), 0, 0, D3D12_BUFFER_UAV_FLAG_NONE},
                {0x11, 0x22, 0x33, 0x44}, 0x44332211},

        {DXGI_FORMAT_R11G11B10_FLOAT, { 0, BUFFER_SIZE / sizeof(uint32_t), 0, 0, D3D12_BUFFER_UAV_FLAG_NONE},
                {0, 0, 0, 0}, 0},
        {DXGI_FORMAT_R11G11B10_FLOAT, { 0, BUFFER_SIZE / sizeof(uint32_t), 0, 0, D3D12_BUFFER_UAV_FLAG_NONE},
                {0x3f000000 /* 0.5f */, 0x3f800000 /* 1.0f */, 0x40000000 /* 2.0f */, 0}, 0x801e0380, true},
        {DXGI_FORMAT_R11G11B10_FLOAT, { 0, BUFFER_SIZE / sizeof(uint32_t), 0, 0, D3D12_BUFFER_UAV_FLAG_NONE},
                {0x3f000000 /* 1.0f */, 0 /* 0.0f */, 0xbf800000 /* -1.0f */, 0x3f000000 /* 1.0f */},
                0x00000380, true},
    };

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

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

    heap_desc.SizeInBytes = 2 * BUFFER_SIZE;
    memset(&heap_desc.Properties, 0, sizeof(heap_desc.Properties));
    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(device, &heap_desc, &IID_ID3D12Heap, (void **)&heap);
    ok(hr == S_OK, "Failed to create heap, hr %#x.\n", hr);

    for (i = 0; i < ARRAY_SIZE(tests); ++i)
    {
        vkd3d_test_push_context("Test %u", i);

        buffer = create_placed_buffer(device, heap, BUFFER_SIZE, BUFFER_SIZE,
                D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

        for (j = 0; j < ARRAY_SIZE(clear_value); ++j)
            clear_value[j] = tests[i].expected ? 0 : ~0u;

        memset(&uav_desc, 0, sizeof(uav_desc));
        uav_desc.Format = DXGI_FORMAT_R32_UINT;
        uav_desc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
        uav_desc.Buffer.NumElements = BUFFER_SIZE / sizeof(uint32_t);
        ID3D12Device_CreateUnorderedAccessView(device, buffer, NULL, &uav_desc,
                get_cpu_descriptor_handle(&context, cpu_heap, 1));
        ID3D12Device_CreateUnorderedAccessView(device, buffer, NULL, &uav_desc,
                get_cpu_descriptor_handle(&context, gpu_heap, 1));

        uav_desc.Format = tests[i].format;
        uav_desc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
        uav_desc.Buffer = tests[i].buffer_uav;
        ID3D12Device_CreateUnorderedAccessView(device, buffer, NULL, &uav_desc,
                get_cpu_descriptor_handle(&context, cpu_heap, 0));
        ID3D12Device_CreateUnorderedAccessView(device, buffer, NULL, &uav_desc,
                get_cpu_descriptor_handle(&context, gpu_heap, 0));

        ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &gpu_heap);

        ID3D12GraphicsCommandList_ClearUnorderedAccessViewUint(command_list,
                get_gpu_descriptor_handle(&context, gpu_heap, 1),
                get_cpu_descriptor_handle(&context, cpu_heap, 1),
                buffer, clear_value, 0, NULL);

        uav_barrier(command_list, buffer);

        if (tests[i].is_float)
            ID3D12GraphicsCommandList_ClearUnorderedAccessViewFloat(command_list,
                    get_gpu_descriptor_handle(&context, gpu_heap, 0),
                    get_cpu_descriptor_handle(&context, cpu_heap, 0),
                    buffer, (const float *)tests[i].values, 0, NULL);
        else
            ID3D12GraphicsCommandList_ClearUnorderedAccessViewUint(command_list,
                    get_gpu_descriptor_handle(&context, gpu_heap, 0),
                    get_cpu_descriptor_handle(&context, cpu_heap, 0),
                    buffer, tests[i].values, 0, NULL);

        set_box(&box, 0, 0, 0, 1, 1, 1);
        transition_resource_state(command_list, buffer,
                D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
        get_buffer_readback_with_command_list(buffer, DXGI_FORMAT_R32_TYPELESS, &rb, queue, command_list);
        box.left = 0;
        box.right = uav_desc.Buffer.FirstElement;
        check_readback_data_uint(&rb.rb, &box, clear_value[0], 0);
        box.left = uav_desc.Buffer.FirstElement;
        box.right = uav_desc.Buffer.FirstElement + uav_desc.Buffer.NumElements;
        todo_if(tests[i].is_todo)
        check_readback_data_uint(&rb.rb, &box, tests[i].expected, tests[i].is_float ? 1 : 0);
        box.left = uav_desc.Buffer.FirstElement + uav_desc.Buffer.NumElements;
        box.right = BUFFER_SIZE / format_size(uav_desc.Format);
        check_readback_data_uint(&rb.rb, &box, clear_value[0], 0);
        release_resource_readback(&rb);

        reset_command_list(command_list, context.allocator);
        ID3D12Resource_Release(buffer);

        vkd3d_test_pop_context();
    }

    ID3D12DescriptorHeap_Release(cpu_heap);
    ID3D12DescriptorHeap_Release(gpu_heap);
    ID3D12Heap_Release(heap);
    destroy_test_context(&context);
#undef BUFFER_SIZE
}

static void test_clear_unordered_access_view_large_buffer(void)
{
    D3D12_UNORDERED_ACCESS_VIEW_DESC uav_desc;
    ID3D12DescriptorHeap *cpu_heap, *gpu_heap;
    ID3D12GraphicsCommandList *command_list;
    struct d3d12_resource_readback rb;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Resource *buffer;
    ID3D12Device *device;
    D3D12_BOX box;

    static const UINT clear_value[4] = {0xcafef00d, 0, 0, 0};
    static const size_t buffer_size = 64 * 1024 * 1024;

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

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

    buffer = create_default_buffer(device, buffer_size,
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

    memset(&uav_desc, 0, sizeof(uav_desc));
    uav_desc.Format = DXGI_FORMAT_R32_UINT;
    uav_desc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
    uav_desc.Buffer.NumElements = buffer_size / sizeof(uint32_t);
    ID3D12Device_CreateUnorderedAccessView(device, buffer, NULL, &uav_desc,
            get_cpu_descriptor_handle(&context, cpu_heap, 0));
    ID3D12Device_CreateUnorderedAccessView(device, buffer, NULL, &uav_desc,
            get_cpu_descriptor_handle(&context, gpu_heap, 0));

    ID3D12GraphicsCommandList_ClearUnorderedAccessViewUint(command_list,
            get_gpu_descriptor_handle(&context, gpu_heap, 0),
            get_cpu_descriptor_handle(&context, cpu_heap, 0),
            buffer, clear_value, 0, NULL);

    uav_barrier(command_list, buffer);

    transition_resource_state(command_list, buffer,
            D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_buffer_readback_with_command_list(buffer, DXGI_FORMAT_R32_UINT, &rb, queue, command_list);
    /* Check only the end to save test run time. */
    set_box(&box, uav_desc.Buffer.NumElements - 1024 * 1024, 0, 0, uav_desc.Buffer.NumElements, 1, 1);
    check_readback_data_uint(&rb.rb, &box, 0xcafef00d, 0);
    release_resource_readback(&rb);

    ID3D12Resource_Release(buffer);
    ID3D12DescriptorHeap_Release(cpu_heap);
    ID3D12DescriptorHeap_Release(gpu_heap);
    destroy_test_context(&context);
}

static void test_clear_unordered_access_view_image(void)
{
    unsigned int image_size, image_depth, texel_size;
    unsigned int expected_colour, actual_colour;
    D3D12_UNORDERED_ACCESS_VIEW_DESC uav_desc;
    ID3D12DescriptorHeap *cpu_heap, *gpu_heap;
    ID3D12GraphicsCommandList *command_list;
    unsigned int i, j, d, p, x, y, z, layer;
    D3D12_HEAP_PROPERTIES heap_properties;
    struct d3d12_resource_readback rb;
    D3D12_RESOURCE_DESC resource_desc;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12CommandQueue *queue;
    bool is_inside, success;
    ID3D12Resource *texture;
    ID3D12Device *device;
    UINT clear_value[4];
    HRESULT hr;
    bool is_1d;

#define IMAGE_SIZE 16
    static const struct
    {
        DXGI_FORMAT format;
        unsigned int image_mips;
        unsigned int image_layers;
        unsigned int mip_level;
        unsigned int first_layer;
        unsigned int layer_count;
        unsigned int rect_count;
        RECT clear_rects[2];
        unsigned int values[4];
        unsigned int expected;
        bool is_float;
        bool is_todo;
        bool check_support;
    }
    tests[] =
    {
        /* Test clearing a specific mip level. */
        {DXGI_FORMAT_R32_FLOAT,       2, 1, 0, 0, 1, 0, {}, {1,          0, 0, 0}, 1},
        {DXGI_FORMAT_R32_FLOAT,       2, 1, 1, 0, 1, 0, {}, {1,          0, 0, 0}, 1},
        {DXGI_FORMAT_R32_FLOAT,       2, 1, 0, 0, 1, 0, {}, {0x3f000000, 0, 0, 0}, 0x3f000000, true},
        {DXGI_FORMAT_R32_FLOAT,       2, 1, 1, 0, 1, 0, {}, {0x3f000000, 0, 0, 0}, 0x3f000000, true},
        /* Test clearing specific array layers. */
        {DXGI_FORMAT_R32_FLOAT,       1, IMAGE_SIZE, 0, 0, IMAGE_SIZE, 0, {}, {1, 0, 0, 0}, 1},
        {DXGI_FORMAT_R32_FLOAT,       1, IMAGE_SIZE, 0, 3, 2,          0, {}, {1, 0, 0, 0}, 1},
        {DXGI_FORMAT_R32_FLOAT,       1, IMAGE_SIZE, 0, 0, IMAGE_SIZE, 0, {},
                {0x3f000000, 0, 0, 0}, 0x3f000000, true},
        {DXGI_FORMAT_R32_FLOAT,       1, IMAGE_SIZE, 0, 3, 2,          0, {},
                {0x3f000000, 0, 0, 0}, 0x3f000000, true},
        /* Test clearing a UINT_MAX layer count. */
        {DXGI_FORMAT_R32_FLOAT,       1, IMAGE_SIZE, 0, 0, UINT_MAX, 0, {}, {1, 0, 0, 0}, 1},
        /* Test a single clear rect. */
        {DXGI_FORMAT_R32_FLOAT,       1, 1, 0, 0, 1, 1, {{1, 2, IMAGE_SIZE - 4, IMAGE_SIZE - 2}},
                {1,          0, 0, 0}, 1},
        {DXGI_FORMAT_R32_FLOAT,       1, 1, 0, 0, 1, 1, {{1, 2, IMAGE_SIZE - 4, IMAGE_SIZE - 2}},
                {0x3f000000, 0, 0, 0}, 0x3f000000, true},
        /* Test multiple clear rects. */
        {DXGI_FORMAT_R32_FLOAT,       1, 1, 0, 0, 1, 2, {{1, 2, 3, 4}, {5, 6, 7, 8}},
                {1,          0, 0, 0}, 1},
        {DXGI_FORMAT_R32_FLOAT,       1, 1, 0, 0, 1, 2, {{1, 2, 3, 4}, {5, 6, 7, 8}},
                {0x3f000000, 0, 0, 0}, 0x3f000000, true},
        /* Test uint clears with formats. */
        {DXGI_FORMAT_R16G16_UINT,     1, 1, 0, 0, 1, 0, {}, {1,       2, 3, 4}, 0x00020001},
        {DXGI_FORMAT_R16G16_UNORM,    1, 1, 0, 0, 1, 0, {}, {1,       2, 3, 4}, 0x00020001},
        {DXGI_FORMAT_R16G16_FLOAT,    1, 1, 0, 0, 1, 0, {}, {1,       2, 3, 4}, 0x00020001},
        {DXGI_FORMAT_R8G8B8A8_UINT,   1, 1, 0, 0, 1, 0, {}, {1,       2, 3, 4}, 0x04030201},
        {DXGI_FORMAT_R8G8B8A8_UNORM,  1, 1, 0, 0, 1, 0, {}, {1,       2, 3, 4}, 0x04030201},
        /* Implementations have completely different behaviours here: AMD and
         * WARP just shift the inputs in the bitfields, NVIDIA interprets them
         * as floating-point numbers and converts them to the
         * partial-precision target formats. We retain the test to exercise
         * the code path, but there is no meaningful semantic to check. */
        {DXGI_FORMAT_R11G11B10_FLOAT, 1, 1, 0, 0, 1, 0, {}, {0,       0, 0, 0}, 0x00000000},
        {DXGI_FORMAT_B5G6R5_UNORM,    1, 1, 0, 0, 1, 0, {}, {1,       2, 3, 4}, 0x00000843, false, false, true},
        {DXGI_FORMAT_B5G5R5A1_UNORM,  1, 1, 0, 0, 1, 0, {}, {1,       2, 3, 1}, 0x00008443, false, false, true},
        {DXGI_FORMAT_B4G4R4A4_UNORM,  1, 1, 0, 0, 1, 0, {}, {1,       2, 3, 1}, 0x00001123, false, false, true},
        /* Test float clears with formats. */
        {DXGI_FORMAT_R16G16_UNORM,    1, 1, 0, 0, 1, 0, {},
                {0x3e800000 /* 0.25f */, 0x3f800000 /* 1.0f */, 0, 0}, 0xffff4000, true},
        {DXGI_FORMAT_R16G16_FLOAT,    1, 1, 0, 0, 1, 0, {},
                {0x3f000000 /* 0.5f */, 0x3f800000 /* 1.0f */, 0, 0}, 0x3c003800, true},
        {DXGI_FORMAT_R8G8B8A8_UNORM,  1, 1, 0, 0, 1, 0, {},
                {0x3f000000 /* 0.5f */, 0x3f800000 /* 1.0f */, 0, 0}, 0x0000ff80, true},
        {DXGI_FORMAT_R8G8B8A8_UNORM,  1, 1, 0, 0, 1, 0, {},
                {0, 0, 0x3f000000 /* 0.5f */, 0x3f800000 /* 1.0f */}, 0xff800000, true},
        {DXGI_FORMAT_R11G11B10_FLOAT, 1, 1, 0, 0, 1, 0, {},
                {0x3f000000 /* 1.0f */, 0 /* 0.0f */, 0xbf800000 /* -1.0f */, 0x3f000000 /* 1.0f */},
                0x00000380, true},
        {DXGI_FORMAT_B5G6R5_UNORM,    1, 1, 0, 0, 1, 0, {},
                {0x3f000000 /* 0.5f */, 0x3f800000 /* 1.0f */, 0x40000000 /* -1.0f */, 0},
                0x87ff, true, false, true},
        {DXGI_FORMAT_B5G5R5A1_UNORM,  1, 1, 0, 0, 1, 0, {},
                {0x3f000000 /* 0.5f */, 0x3e800000 /* 0.25f */, 0x3e000000 /* 0.125f */, 0x3f800000 /* 1.0f */},
                0xc104, true, false, true},
        {DXGI_FORMAT_B4G4R4A4_UNORM,  1, 1, 0, 0, 1, 0, {},
                {0x3f000000 /* 0.5f */, 0x3f800000 /* 1.0f */, 0x40000000 /* 2.0f */, 0x40000000 /* -1.0f */},
                0xf8ff, true, false, true},
    };

    static const struct
    {
        D3D12_RESOURCE_DIMENSION resource_dim;
        D3D12_UAV_DIMENSION view_dim;
        bool is_layered;
    }
    uav_dimensions[] =
    {
        {D3D12_RESOURCE_DIMENSION_TEXTURE1D, D3D12_UAV_DIMENSION_TEXTURE1D,      false},
        {D3D12_RESOURCE_DIMENSION_TEXTURE2D, D3D12_UAV_DIMENSION_TEXTURE2D,      false},
        {D3D12_RESOURCE_DIMENSION_TEXTURE2D, D3D12_UAV_DIMENSION_TEXTURE2DARRAY, true },
        /* Expected behaviour with partial layer coverage is unclear. */
        {D3D12_RESOURCE_DIMENSION_TEXTURE3D, D3D12_UAV_DIMENSION_TEXTURE3D,      false},
    };

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

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

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

    for (d = 0; d < ARRAY_SIZE(uav_dimensions); ++d)
    {
        for (i = 0; i < ARRAY_SIZE(tests); ++i)
        {
            if (tests[i].image_layers > 1 && !uav_dimensions[d].is_layered)
                continue;

            if (tests[i].check_support && !is_typed_uav_format_supported(device, tests[i].format))
            {
                skip("Device does not support format %#x; skipping.\n", tests[i].format);
                continue;
            }

            vkd3d_test_push_context("Dim %u, Test %u", d, i);

            is_1d = uav_dimensions[d].resource_dim == D3D12_RESOURCE_DIMENSION_TEXTURE1D;

            resource_desc.Dimension = uav_dimensions[d].resource_dim;
            resource_desc.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
            resource_desc.Width = IMAGE_SIZE;
            resource_desc.Height = is_1d ? 1 : IMAGE_SIZE;
            resource_desc.DepthOrArraySize = tests[i].image_layers;
            resource_desc.MipLevels = tests[i].image_mips;
            resource_desc.Format = tests[i].format;
            resource_desc.SampleDesc.Count = 1;
            resource_desc.SampleDesc.Quality = 0;
            resource_desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
            resource_desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;

            hr = ID3D12Device_CreateCommittedResource(device, &heap_properties,
                    D3D12_HEAP_FLAG_NONE, &resource_desc, D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
                    NULL, &IID_ID3D12Resource, (void **)&texture);
            ok(hr == S_OK, "Got hr %#x.\n", hr);

            uav_desc.Format = tests[i].format;
            uav_desc.ViewDimension = uav_dimensions[d].view_dim;

            texel_size = format_size(uav_desc.Format);

            for (j = 0; j < 2; ++j)
            {
                unsigned int first_layer = j ? 0 : tests[i].first_layer;
                unsigned int layer_count = j ? tests[i].image_layers : tests[i].layer_count;

                switch (uav_desc.ViewDimension)
                {
                    case D3D12_UAV_DIMENSION_TEXTURE1D:
                        uav_desc.Texture1D.MipSlice = tests[i].mip_level;
                        break;

                    case D3D12_UAV_DIMENSION_TEXTURE1DARRAY:
                        uav_desc.Texture1DArray.MipSlice = tests[i].mip_level;
                        uav_desc.Texture1DArray.FirstArraySlice = first_layer;
                        uav_desc.Texture1DArray.ArraySize = layer_count;
                        break;

                    case D3D12_UAV_DIMENSION_TEXTURE2D:
                        uav_desc.Texture2D.MipSlice = tests[i].mip_level;
                        uav_desc.Texture2D.PlaneSlice = 0;
                        break;

                    case D3D12_UAV_DIMENSION_TEXTURE2DARRAY:
                        uav_desc.Texture2DArray.MipSlice = tests[i].mip_level;
                        uav_desc.Texture2DArray.FirstArraySlice = first_layer;
                        uav_desc.Texture2DArray.ArraySize = layer_count;
                        uav_desc.Texture2DArray.PlaneSlice = 0;
                        break;

                    case D3D12_UAV_DIMENSION_TEXTURE3D:
                        uav_desc.Texture3D.MipSlice = tests[i].mip_level;
                        uav_desc.Texture3D.FirstWSlice = first_layer;
                        uav_desc.Texture3D.WSize = layer_count;
                        break;

                    default:
                        continue;
                }

                ID3D12Device_CreateUnorderedAccessView(device, texture, NULL,
                        &uav_desc, get_cpu_descriptor_handle(&context, cpu_heap, j));
                ID3D12Device_CreateUnorderedAccessView(device, texture, NULL,
                        &uav_desc, get_cpu_descriptor_handle(&context, gpu_heap, j));
            }

            ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &gpu_heap);

            for (j = 0; j < 4; ++j)
            {
                clear_value[j] = tests[i].expected ? 0u : ~0u;
            }

            ID3D12GraphicsCommandList_ClearUnorderedAccessViewUint(command_list,
                    get_gpu_descriptor_handle(&context, gpu_heap, 1),
                    get_cpu_descriptor_handle(&context, cpu_heap, 1),
                    texture, clear_value, 0, NULL);

            uav_barrier(command_list, texture);

            if (tests[i].is_float)
                ID3D12GraphicsCommandList_ClearUnorderedAccessViewFloat(command_list,
                        get_gpu_descriptor_handle(&context, gpu_heap, 0),
                        get_cpu_descriptor_handle(&context, cpu_heap, 0),
                        texture, (const float *)tests[i].values, tests[i].rect_count, tests[i].clear_rects);
            else
                ID3D12GraphicsCommandList_ClearUnorderedAccessViewUint(command_list,
                        get_gpu_descriptor_handle(&context, gpu_heap, 0),
                        get_cpu_descriptor_handle(&context, cpu_heap, 0),
                        texture, tests[i].values, tests[i].rect_count, tests[i].clear_rects);

            transition_resource_state(command_list, texture,
                    D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);

            image_depth = uav_dimensions[d].resource_dim == D3D12_RESOURCE_DIMENSION_TEXTURE3D
                    ? max(tests[i].image_layers >> tests[i].mip_level, 1u) : 1;
            image_size = max(IMAGE_SIZE >> tests[i].mip_level, 1u);

            for (layer = 0; layer < tests[i].image_layers / image_depth; ++layer)
            {
                get_resource_readback_with_command_list(texture,
                        tests[i].mip_level + (layer * tests[i].image_mips),
                        &rb, queue, command_list);

                for (p = 0; p < image_depth * (is_1d ? 1 : image_size) * image_size; ++p)
                {
                    x = p % image_size;
                    y = (p / image_size) % image_size;
                    z = p / (image_size * image_size);

                    is_inside = tests[i].rect_count == 0;

                    for (j = 0; j < tests[i].rect_count; ++j)
                    {
                        if (y >= tests[i].clear_rects[j].top && y < tests[i].clear_rects[j].bottom
                                && x >= tests[i].clear_rects[j].left && x < tests[i].clear_rects[j].right)
                        {
                            is_inside = true;
                            break;
                        }
                    }

                    if (uav_dimensions[d].resource_dim == D3D12_RESOURCE_DIMENSION_TEXTURE3D)
                        is_inside = is_inside && z >= tests[i].first_layer
                                && z < tests[i].first_layer + tests[i].layer_count;
                    else
                        is_inside = is_inside && layer >= tests[i].first_layer
                                && layer < tests[i].first_layer + tests[i].layer_count;

                    expected_colour = is_inside ? tests[i].expected : clear_value[0];
                    actual_colour = (texel_size == 2) ? get_readback_uint16(&rb.rb, x, y)
                            : get_readback_uint(&rb.rb, x, y, z);
                    success = compare_color(actual_colour, expected_colour, tests[i].is_float ? 1 : 0);

                    todo_if(tests[i].is_todo && expected_colour)
                    ok(success, "At layer %u, (%u,%u,%u), expected %#x, got %#x.\n",
                            layer, x, y, z, expected_colour, actual_colour);

                    if (!success)
                        break;
                }

                release_resource_readback(&rb);
                reset_command_list(command_list, context.allocator);
            }

            ID3D12Resource_Release(texture);

            vkd3d_test_pop_context();
        }
    }

    ID3D12DescriptorHeap_Release(cpu_heap);
    ID3D12DescriptorHeap_Release(gpu_heap);
    destroy_test_context(&context);
#undef IMAGE_SIZE
}

static void test_set_render_targets(void)
{
    ID3D12DescriptorHeap *dsv_heap, *rtv_heap;
    ID3D12GraphicsCommandList *command_list;
    D3D12_CPU_DESCRIPTOR_HANDLE dsv, rtv;
    struct test_context context;
    ID3D12Device *device;
    HRESULT hr;

    if (!init_test_context(&context, NULL))
        return;
    device = context.device;
    command_list = context.list;

    rtv_heap = create_cpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_RTV, 4);
    dsv_heap = create_cpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_DSV, 4);

    dsv = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(dsv_heap);
    rtv = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(rtv_heap);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &rtv, false, NULL);
    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &rtv, true, NULL);
    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &rtv, true, &dsv);
    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 0, &rtv, true, &dsv);
    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 0, &rtv, false, &dsv);
    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 0, NULL, true, &dsv);
    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 0, NULL, false, &dsv);

    hr = ID3D12GraphicsCommandList_Close(command_list);
    ok(hr == S_OK, "Failed to close command list, hr %#x.\n", hr);

    ID3D12DescriptorHeap_Release(rtv_heap);
    ID3D12DescriptorHeap_Release(dsv_heap);
    destroy_test_context(&context);
}

static void test_draw_instanced(void)
{
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    ID3D12GraphicsCommandList *command_list;
    struct test_context context;
    ID3D12CommandQueue *queue;

    if (!init_test_context(&context, NULL))
        return;
    command_list = context.list;
    queue = context.queue;

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

    if (!test_options.use_warp_device)
    {
        /* This draw call is ignored. */
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);
    }

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);

    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);

    destroy_test_context(&context);
}

static void test_draw_indexed_instanced(void)
{
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    static const uint16_t indices[] = {0, 1, 2};
    ID3D12GraphicsCommandList *command_list;
    struct test_context context;
    D3D12_INDEX_BUFFER_VIEW ibv;
    ID3D12CommandQueue *queue;
    ID3D12Resource *ib;

    if (!init_test_context(&context, NULL))
        return;
    command_list = context.list;
    queue = context.queue;

    ib = create_upload_buffer(context.device, sizeof(indices), indices);

    ibv.SizeInBytes = sizeof(indices);
    ibv.Format = DXGI_FORMAT_R16_UINT;

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

    if (!test_options.use_warp_device)
    {
        /* This draw call is ignored. */
        ID3D12GraphicsCommandList_DrawIndexedInstanced(command_list, 3, 1, 0, 0, 0);
    }

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_IASetIndexBuffer(command_list, NULL);
    ibv.BufferLocation = 0;
    ID3D12GraphicsCommandList_IASetIndexBuffer(command_list, &ibv);
    ibv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(ib);
    ID3D12GraphicsCommandList_IASetIndexBuffer(command_list, &ibv);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_DrawIndexedInstanced(command_list, 3, 1, 0, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);

    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);

    ID3D12Resource_Release(ib);
    destroy_test_context(&context);
}

static void test_draw_no_descriptor_bindings(void)
{
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    D3D12_DESCRIPTOR_RANGE descriptor_range[2];
    ID3D12GraphicsCommandList *command_list;
    D3D12_ROOT_PARAMETER root_parameters[2];
    struct test_context_desc desc;
    struct test_context context;
    ID3D12CommandQueue *queue;
    HRESULT hr;

    memset(&desc, 0, sizeof(desc));
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    command_list = context.list;
    queue = context.queue;

    descriptor_range[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
    descriptor_range[0].NumDescriptors = 2;
    descriptor_range[0].BaseShaderRegister = 0;
    descriptor_range[0].RegisterSpace = 0;
    descriptor_range[0].OffsetInDescriptorsFromTableStart = 1;
    root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameters[0].DescriptorTable.NumDescriptorRanges = 1;
    root_parameters[0].DescriptorTable.pDescriptorRanges = &descriptor_range[0];
    root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;

    descriptor_range[1].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
    descriptor_range[1].NumDescriptors = 1;
    descriptor_range[1].BaseShaderRegister = 0;
    descriptor_range[1].RegisterSpace = 0;
    descriptor_range[1].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
    root_parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameters[1].DescriptorTable.NumDescriptorRanges = 1;
    root_parameters[1].DescriptorTable.pDescriptorRanges = &descriptor_range[1];
    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, &context.root_signature);
    ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr);

    context.pipeline_state = create_pipeline_state(context.device,
            context.root_signature, context.render_target_desc.Format, NULL, NULL, NULL);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);

    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);

    destroy_test_context(&context);
}

static void test_multiple_render_targets(void)
{
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    struct vec4 expected_vec4 = {0.0f, 0.0f, 0.0f, 1.0f};
    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
    ID3D12GraphicsCommandList *command_list;
    D3D12_CPU_DESCRIPTOR_HANDLE rtvs[3];
    ID3D12Resource *render_targets[2];
    struct test_context_desc desc;
    struct test_context context;
    ID3D12CommandQueue *queue;
    D3D12_SHADER_BYTECODE ps;
    ID3D10Blob *bytecode;
    unsigned int i;
    HRESULT hr;

    static const char ps_code[] =
            "void main(out float4 target0 : SV_Target0, out float4 target1 : SV_Target1,\n"
            "        out float4 target2 : SV_Target2)\n"
            "{\n"
            "    target0 = float4(1.0f, 0.0f, 0.0f, 1.0f);\n"
            "    target1 = float4(2.0f, 0.0f, 0.0f, 1.0f);\n"
            "    target2 = float4(3.0f, 0.0f, 0.0f, 1.0f);\n"
            "}\n";

    bytecode = compile_shader(ps_code, sizeof(ps_code) - 1, "ps_4_0");
    ps = shader_bytecode_from_blob(bytecode);

    memset(&desc, 0, sizeof(desc));
    desc.rt_format = DXGI_FORMAT_R32G32B32A32_FLOAT;
    desc.rt_descriptor_count = ARRAY_SIZE(rtvs);
    desc.no_pipeline = true;
    if (!init_test_context(&context, &desc))
    {
        ID3D10Blob_Release(bytecode);
        return;
    }
    command_list = context.list;
    queue = context.queue;

    init_pipeline_state_desc(&pso_desc, context.root_signature, 0, NULL, &ps, NULL);
    pso_desc.NumRenderTargets = ARRAY_SIZE(rtvs);
    for (i = 0; i < ARRAY_SIZE(rtvs); ++i)
        pso_desc.RTVFormats[i] = desc.rt_format;
    hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
    ok(hr == S_OK, "Failed to create state, hr %#x.\n", hr);

    rtvs[0] = get_cpu_rtv_handle(&context, context.rtv_heap, 2);
    rtvs[1] = get_cpu_rtv_handle(&context, context.rtv_heap, 0);
    rtvs[2] = get_cpu_rtv_handle(&context, context.rtv_heap, 1);

    create_render_target(&context, &desc, &render_targets[0], &rtvs[0]);
    create_render_target(&context, &desc, &render_targets[1], &rtvs[2]);

    for (i = 0; i < ARRAY_SIZE(rtvs); ++i)
        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, rtvs[i], white, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, ARRAY_SIZE(rtvs), rtvs, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    transition_resource_state(command_list, render_targets[0],
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    transition_resource_state(command_list, render_targets[1],
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);

    expected_vec4.x = 2.0f;
    check_sub_resource_vec4(context.render_target, 0, queue, command_list, &expected_vec4, 0);
    reset_command_list(command_list, context.allocator);
    expected_vec4.x = 1.0f;
    check_sub_resource_vec4(render_targets[0], 0, queue, command_list, &expected_vec4, 0);
    reset_command_list(command_list, context.allocator);
    expected_vec4.x = 3.0f;
    check_sub_resource_vec4(render_targets[1], 0, queue, command_list, &expected_vec4, 0);
    reset_command_list(command_list, context.allocator);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
    transition_resource_state(command_list, render_targets[0],
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
    transition_resource_state(command_list, render_targets[1],
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, ARRAY_SIZE(rtvs), &context.rtv, true, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    transition_resource_state(command_list, render_targets[0],
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    transition_resource_state(command_list, render_targets[1],
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);

    expected_vec4.x = 1.0f;
    check_sub_resource_vec4(context.render_target, 0, queue, command_list, &expected_vec4, 0);
    reset_command_list(command_list, context.allocator);
    expected_vec4.x = 3.0f;
    check_sub_resource_vec4(render_targets[0], 0, queue, command_list, &expected_vec4, 0);
    reset_command_list(command_list, context.allocator);
    expected_vec4.x = 2.0f;
    check_sub_resource_vec4(render_targets[1], 0, queue, command_list, &expected_vec4, 0);
    reset_command_list(command_list, context.allocator);

    for (i = 0; i < ARRAY_SIZE(render_targets); ++i)
        ID3D12Resource_Release(render_targets[i]);
    destroy_test_context(&context);

    ID3D10Blob_Release(bytecode);
}

static void test_unknown_rtv_format(void)
{
    static const struct vec4 white = {1.0f, 1.0f, 1.0f, 1.0f};
    struct vec4 expected_vec4 = {0.0f, 0.0f, 0.0f, 1.0f};
    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
    ID3D12GraphicsCommandList *command_list;
    D3D12_RENDER_TARGET_VIEW_DESC rtv_desc;
    D3D12_CPU_DESCRIPTOR_HANDLE rtvs[3];
    ID3D12Resource *render_targets[2];
    struct depth_stencil_resource ds;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12CommandQueue *queue;
    D3D12_SHADER_BYTECODE ps;
    ID3D10Blob *bytecode;
    unsigned int i;
    HRESULT hr;

    static const char ps_code[] =
            "void main(out float4 target1 : SV_Target1, out float4 target2 : SV_Target2)\n"
            "{\n"
            "    target1 = float4(2.0f, 0.0f, 0.0f, 1.0f);\n"
            "    target2 = float4(3.0f, 0.0f, 0.0f, 1.0f);\n"
            "}\n";

    bytecode = compile_shader(ps_code, sizeof(ps_code) - 1, "ps_4_0");
    ps = shader_bytecode_from_blob(bytecode);

    memset(&desc, 0, sizeof(desc));
    desc.rt_format = DXGI_FORMAT_R32G32B32A32_FLOAT;
    desc.rt_descriptor_count = 16;
    desc.no_pipeline = true;
    if (!init_test_context(&context, &desc))
    {
        ID3D10Blob_Release(bytecode);
        return;
    }
    command_list = context.list;
    queue = context.queue;

    init_depth_stencil(&ds, context.device, 32, 32, 1, 1, DXGI_FORMAT_D32_FLOAT, 0, NULL);

    init_pipeline_state_desc(&pso_desc, context.root_signature, 0, NULL, &ps, NULL);
    pso_desc.NumRenderTargets = ARRAY_SIZE(rtvs);
    for (i = 0; i < ARRAY_SIZE(rtvs); ++i)
        pso_desc.RTVFormats[i] = desc.rt_format;
    pso_desc.RTVFormats[0] = DXGI_FORMAT_UNKNOWN;
    pso_desc.DSVFormat = DXGI_FORMAT_D32_FLOAT;
    pso_desc.DepthStencilState.DepthEnable = true;
    pso_desc.DepthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
    pso_desc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS;
    hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
    ok(hr == S_OK, "Failed to create state, hr %#x.\n", hr);

    rtvs[0] = get_cpu_rtv_handle(&context, context.rtv_heap, 0);
    rtvs[1] = get_cpu_rtv_handle(&context, context.rtv_heap, 1);
    rtvs[2] = get_cpu_rtv_handle(&context, context.rtv_heap, 2);
    create_render_target(&context, &desc, &render_targets[0], &rtvs[1]);
    create_render_target(&context, &desc, &render_targets[1], &rtvs[2]);

    for (i = 0; i < ARRAY_SIZE(rtvs); ++i)
        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, rtvs[i], (const float *)&white, 0, NULL);

    /* NULL RTV */
    memset(&rtv_desc, 0, sizeof(rtv_desc));
    rtv_desc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D;
    rtv_desc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
    rtv_desc.Texture2D.MipSlice = 0;
    rtv_desc.Texture2D.PlaneSlice = 0;
    ID3D12Device_CreateRenderTargetView(context.device, NULL, &rtv_desc,
            get_cpu_rtv_handle(&context, context.rtv_heap, 0));

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, ARRAY_SIZE(rtvs), rtvs, false, &ds.dsv_handle);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    set_viewport(&context.viewport, 0.0f, 0.0f, 32.0f, 32.0f, 0.5f, 0.5f);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    transition_resource_state(command_list, render_targets[0],
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    transition_resource_state(command_list, render_targets[1],
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);

    check_sub_resource_vec4(context.render_target, 0, queue, command_list, &white, 0);
    reset_command_list(command_list, context.allocator);
    expected_vec4.x = 2.0f;
    check_sub_resource_vec4(render_targets[0], 0, queue, command_list, &expected_vec4, 0);
    reset_command_list(command_list, context.allocator);
    expected_vec4.x = 3.0f;
    check_sub_resource_vec4(render_targets[1], 0, queue, command_list, &expected_vec4, 0);

    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, ds.texture,
            D3D12_RESOURCE_STATE_DEPTH_WRITE, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_float(ds.texture, 0, queue, command_list, 0.5f, 1);

    for (i = 0; i < ARRAY_SIZE(render_targets); ++i)
        ID3D12Resource_Release(render_targets[i]);
    destroy_depth_stencil(&ds);
    destroy_test_context(&context);

    ID3D10Blob_Release(bytecode);
}

static void test_unknown_dsv_format(void)
{
    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
    ID3D12GraphicsCommandList *command_list;
    struct depth_stencil_resource ds;
    D3D12_SHADER_BYTECODE ps_color;
    D3D12_CLEAR_VALUE clear_value;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D10Blob *bytecode;
    HRESULT hr;

    static const char ps_color_code[] =
            "float4 color;\n"
            "\n"
            "float4 main(float4 position : SV_POSITION) : SV_Target\n"
            "{\n"
            "    return color;\n"
            "}\n";

    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    static const struct vec4 green = {0.0f, 1.0f, 0.0f, 1.0f};
    static const struct vec4 red = {1.0f, 0.0f, 0.0f, 1.0f};

    bytecode = compile_shader(ps_color_code, sizeof(ps_color_code) - 1, "ps_4_0");
    ps_color = shader_bytecode_from_blob(bytecode);

    memset(&desc, 0, sizeof(desc));
    desc.rt_format = DXGI_FORMAT_R32G32B32A32_FLOAT;
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
    {
        ID3D10Blob_Release(bytecode);
        return;
    }
    command_list = context.list;
    queue = context.queue;

    clear_value.Format = DXGI_FORMAT_D32_FLOAT;
    clear_value.DepthStencil.Depth = 0.5f;
    clear_value.DepthStencil.Stencil = 0;
    init_depth_stencil(&ds, context.device, 32, 32, 1, 1, DXGI_FORMAT_D32_FLOAT, 0, &clear_value);

    context.root_signature = create_32bit_constants_root_signature(context.device,
            0, 4, D3D12_SHADER_VISIBILITY_PIXEL);

    /* DSVFormat = DXGI_FORMAT_UNKNOWN and D3D12_DEPTH_WRITE_MASK_ZERO */
    init_pipeline_state_desc(&pso_desc, context.root_signature, desc.rt_format, NULL, &ps_color, NULL);
    pso_desc.DSVFormat = DXGI_FORMAT_UNKNOWN;
    pso_desc.DepthStencilState.DepthEnable = true;
    pso_desc.DepthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ZERO;
    pso_desc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_EQUAL;
    hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
    ok(hr == S_OK, "Failed to create graphics pipeline state, hr %#x.\n", hr);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
    ID3D12GraphicsCommandList_ClearDepthStencilView(command_list, ds.dsv_handle,
            D3D12_CLEAR_FLAG_DEPTH, 0.5f, 0, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, &ds.dsv_handle);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);

    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 0, 4, &green.x, 0);
    set_viewport(&context.viewport, 0.0f, 0.0f, 32.0f, 32.0f, 0.5f, 0.5f);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 0, 4, &red.x, 0);
    set_viewport(&context.viewport, 0.0f, 0.0f, 32.0f, 32.0f, 1.0f, 1.0f);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);
    set_viewport(&context.viewport, 0.0f, 0.0f, 32.0f, 32.0f, 0.0f, 0.0f);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);
    set_viewport(&context.viewport, 0.0f, 0.0f, 32.0f, 32.0f, 0.55f, 0.55f);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, ds.texture,
            D3D12_RESOURCE_STATE_DEPTH_WRITE, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_float(ds.texture, 0, queue, command_list, 0.5f, 1);

    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_vec4(context.render_target, 0, queue, command_list, &green, 0);

    /* DSVFormat = DXGI_FORMAT_UNKNOWN and no DSV */
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, ds.texture,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_DEPTH_WRITE);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);

    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 0, 4, &red.x, 0);
    set_viewport(&context.viewport, 0.0f, 0.0f, 32.0f, 32.0f, 0.0f, 0.0f);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 0, 4, &green.x, 0);
    set_viewport(&context.viewport, 0.0f, 0.0f, 32.0f, 32.0f, 0.5f, 0.5f);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_vec4(context.render_target, 0, queue, command_list, &green, 0);

    /* DSVFormat = DXGI_FORMAT_UNKNOWN and D3D12_COMPARISON_FUNC_ALWAYS */
    ID3D12PipelineState_Release(context.pipeline_state);
    pso_desc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS;
    hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
    ok(hr == S_OK, "Failed to create graphics pipeline state, hr %#x.\n", hr);

    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, &ds.dsv_handle);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);

    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 0, 4, &red.x, 0);
    set_viewport(&context.viewport, 0.0f, 0.0f, 32.0f, 32.0f, 0.0f, 0.0f);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 0, 4, &green.x, 0);
    set_viewport(&context.viewport, 0.0f, 0.0f, 32.0f, 32.0f, 0.6f, 0.6f);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_vec4(context.render_target, 0, queue, command_list, &green, 0);

    /* DSVFormat = DXGI_FORMAT_UNKNOWN and depth write */
    ID3D12PipelineState_Release(context.pipeline_state);
    pso_desc.DepthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
    pso_desc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS;
    hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
    ok(hr == S_OK, "Failed to create graphics pipeline state, hr %#x.\n", hr);

    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    ID3D12GraphicsCommandList_ClearDepthStencilView(command_list, ds.dsv_handle,
            D3D12_CLEAR_FLAG_DEPTH, 0.0f, 0, 0, NULL);

    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, &ds.dsv_handle);
    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 0, 4, &red.x, 0);
    set_viewport(&context.viewport, 0.0f, 0.0f, 32.0f, 32.0f, 1.0f, 1.0f);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 0, 4, &green.x, 0);
    set_viewport(&context.viewport, 0.0f, 0.0f, 32.0f, 32.0f, 0.6f, 0.6f);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_vec4(context.render_target, 0, queue, command_list, &green, 0);

    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, ds.texture,
            D3D12_RESOURCE_STATE_DEPTH_WRITE, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_float(ds.texture, 0, queue, command_list, 1.0f, 1);

    destroy_depth_stencil(&ds);
    destroy_test_context(&context);

    ID3D10Blob_Release(bytecode);
}

static void test_append_aligned_element(void)
{
    ID3D12GraphicsCommandList *command_list;
    D3D12_INPUT_LAYOUT_DESC input_layout;
    struct d3d12_resource_readback rb;
    D3D12_VERTEX_BUFFER_VIEW vbv[6];
    struct test_context_desc desc;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Resource *vb[3];
    unsigned int color;

    /* Semantic names are case-insensitive. */
    static const D3D12_INPUT_ELEMENT_DESC layout_desc[] =
    {
        {"CoLoR",    2, DXGI_FORMAT_R32G32_FLOAT,       1, D3D12_APPEND_ALIGNED_ELEMENT,
                D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA, 1},
        {"ColoR",    3, DXGI_FORMAT_R32G32_FLOAT,       5, D3D12_APPEND_ALIGNED_ELEMENT,
                D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA, 1},
        {"POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT,
                D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
        {"ColoR",    0, DXGI_FORMAT_R32G32_FLOAT,       5, D3D12_APPEND_ALIGNED_ELEMENT,
                D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA, 1},
        {"cOLOr",    1, DXGI_FORMAT_R32G32_FLOAT,       1, D3D12_APPEND_ALIGNED_ELEMENT,
                D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA, 1},
    };
    static const DWORD vs_code[] =
    {
#if 0
        struct vs_in
        {
            float4 position : POSITION;
            float2 color_xy : COLOR0;
            float2 color_zw : COLOR1;
            unsigned int instance_id : SV_INSTANCEID;
        };

        struct vs_out
        {
            float4 position : SV_POSITION;
            float2 color_xy : COLOR0;
            float2 color_zw : COLOR1;
        };

        struct vs_out main(struct vs_in i)
        {
            struct vs_out o;

            o.position = i.position;
            o.position.x += i.instance_id * 0.5;
            o.color_xy = i.color_xy;
            o.color_zw = i.color_zw;

            return o;
        }
#endif
        0x43425844, 0x52e3bf46, 0x6300403d, 0x624cffe4, 0xa4fc0013, 0x00000001, 0x00000214, 0x00000003,
        0x0000002c, 0x000000bc, 0x00000128, 0x4e475349, 0x00000088, 0x00000004, 0x00000008, 0x00000068,
        0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000f0f, 0x00000071, 0x00000000, 0x00000000,
        0x00000003, 0x00000001, 0x00000303, 0x00000071, 0x00000001, 0x00000000, 0x00000003, 0x00000002,
        0x00000303, 0x00000077, 0x00000000, 0x00000008, 0x00000001, 0x00000003, 0x00000101, 0x49534f50,
        0x4e4f4954, 0x4c4f4300, 0x5300524f, 0x4e495f56, 0x4e415453, 0x44494543, 0xababab00, 0x4e47534f,
        0x00000064, 0x00000003, 0x00000008, 0x00000050, 0x00000000, 0x00000001, 0x00000003, 0x00000000,
        0x0000000f, 0x0000005c, 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x00000c03, 0x0000005c,
        0x00000001, 0x00000000, 0x00000003, 0x00000001, 0x0000030c, 0x505f5653, 0x5449534f, 0x004e4f49,
        0x4f4c4f43, 0xabab0052, 0x52444853, 0x000000e4, 0x00010040, 0x00000039, 0x0300005f, 0x001010f2,
        0x00000000, 0x0300005f, 0x00101032, 0x00000001, 0x0300005f, 0x00101032, 0x00000002, 0x04000060,
        0x00101012, 0x00000003, 0x00000008, 0x04000067, 0x001020f2, 0x00000000, 0x00000001, 0x03000065,
        0x00102032, 0x00000001, 0x03000065, 0x001020c2, 0x00000001, 0x02000068, 0x00000001, 0x05000056,
        0x00100012, 0x00000000, 0x0010100a, 0x00000003, 0x09000032, 0x00102012, 0x00000000, 0x0010000a,
        0x00000000, 0x00004001, 0x3f000000, 0x0010100a, 0x00000000, 0x05000036, 0x001020e2, 0x00000000,
        0x00101e56, 0x00000000, 0x05000036, 0x00102032, 0x00000001, 0x00101046, 0x00000001, 0x05000036,
        0x001020c2, 0x00000001, 0x00101406, 0x00000002, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE vs = {vs_code, sizeof(vs_code)};
    static const DWORD ps_code[] =
    {
#if 0
        struct vs_out
        {
            float4 position : SV_POSITION;
            float2 color_xy : COLOR0;
            float2 color_zw : COLOR1;
        };

        float4 main(struct vs_out i) : SV_TARGET
        {
            return float4(i.color_xy.xy, i.color_zw.xy);
        }
#endif
        0x43425844, 0x64e48a09, 0xaa484d46, 0xe40a6e78, 0x9885edf3, 0x00000001, 0x00000118, 0x00000003,
        0x0000002c, 0x00000098, 0x000000cc, 0x4e475349, 0x00000064, 0x00000003, 0x00000008, 0x00000050,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x0000005c, 0x00000000, 0x00000000,
        0x00000003, 0x00000001, 0x00000303, 0x0000005c, 0x00000001, 0x00000000, 0x00000003, 0x00000001,
        0x00000c0c, 0x505f5653, 0x5449534f, 0x004e4f49, 0x4f4c4f43, 0xabab0052, 0x4e47534f, 0x0000002c,
        0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x0000000f,
        0x545f5653, 0x45475241, 0xabab0054, 0x52444853, 0x00000044, 0x00000040, 0x00000011, 0x03001062,
        0x00101032, 0x00000001, 0x03001062, 0x001010c2, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x05000036, 0x001020f2, 0x00000000, 0x00101e46, 0x00000001, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};
    static const struct
    {
        struct vec4 position;
    }
    stream0[] =
    {
        {{-1.0f, -1.0f, 0.0f, 1.0f}},
        {{-1.0f,  1.0f, 0.0f, 1.0f}},
        {{-0.5f, -1.0f, 0.0f, 1.0f}},
        {{-0.5f,  1.0f, 0.0f, 1.0f}},
    };
    static const struct
    {
        struct vec2 color2;
        struct vec2 color1;
    }
    stream1[] =
    {
        {{0.5f, 0.5f}, {0.0f, 1.0f}},
        {{0.5f, 0.5f}, {0.0f, 1.0f}},
        {{0.5f, 0.5f}, {1.0f, 1.0f}},
        {{0.5f, 0.5f}, {1.0f, 1.0f}},
    };
    static const struct
    {
        struct vec2 color3;
        struct vec2 color0;
    }
    stream2[] =
    {
        {{0.5f, 0.5f}, {1.0f, 0.0f}},
        {{0.5f, 0.5f}, {0.0f, 1.0f}},
        {{0.5f, 0.5f}, {0.0f, 0.0f}},
        {{0.5f, 0.5f}, {1.0f, 0.0f}},
    };
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};

    memset(&desc, 0, sizeof(desc));
    desc.rt_width = 640;
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    command_list = context.list;
    queue = context.queue;

    context.root_signature = create_empty_root_signature(context.device,
            D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
    input_layout.pInputElementDescs = layout_desc;
    input_layout.NumElements = ARRAY_SIZE(layout_desc);
    context.pipeline_state = create_pipeline_state(context.device,
            context.root_signature, context.render_target_desc.Format, &vs, &ps, &input_layout);

    memset(vbv, 0, sizeof(vbv));
    vb[0] = create_upload_buffer(context.device, sizeof(stream0), stream0);
    vbv[0].BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vb[0]);
    vbv[0].StrideInBytes = sizeof(*stream0);
    vbv[0].SizeInBytes = sizeof(stream0);

    vb[1] = create_upload_buffer(context.device, sizeof(stream1), stream1);
    vbv[1].BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vb[1]);
    vbv[1].StrideInBytes = sizeof(*stream1);
    vbv[1].SizeInBytes = sizeof(stream1);

    vb[2] = create_upload_buffer(context.device, sizeof(stream2), stream2);
    vbv[5].BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vb[2]);
    vbv[5].StrideInBytes = sizeof(*stream2);
    vbv[5].SizeInBytes = sizeof(stream2);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
    ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, ARRAY_SIZE(vbv), vbv);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 4, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_resource_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
    color = get_readback_uint(&rb.rb, 80, 16, 0);
    ok(compare_color(color, 0xff0000ff, 1), "Got unexpected color 0x%08x.\n", color);
    color = get_readback_uint(&rb.rb, 240, 16, 0);
    ok(compare_color(color, 0xff00ff00, 1), "Got unexpected color 0x%08x.\n", color);
    color = get_readback_uint(&rb.rb, 400, 16, 0);
    ok(compare_color(color, 0xffff0000, 1), "Got unexpected color 0x%08x.\n", color);
    color = get_readback_uint(&rb.rb, 560, 16, 0);
    ok(compare_color(color, 0xffff00ff, 1), "Got unexpected color 0x%08x.\n", color);
    release_resource_readback(&rb);

    ID3D12Resource_Release(vb[2]);
    ID3D12Resource_Release(vb[1]);
    ID3D12Resource_Release(vb[0]);
    destroy_test_context(&context);
}

static void test_gpu_virtual_address(void)
{
    D3D12_GPU_VIRTUAL_ADDRESS vb_offset, ib_offset;
    ID3D12GraphicsCommandList *command_list;
    D3D12_INPUT_LAYOUT_DESC input_layout;
    struct test_context_desc desc;
    D3D12_VERTEX_BUFFER_VIEW vbv;
    struct test_context context;
    D3D12_INDEX_BUFFER_VIEW ibv;
    ID3D12CommandQueue *queue;
    ID3D12Resource *buffer;
    HRESULT hr;
    BYTE *ptr;

    static const DWORD vs_code[] =
    {
#if 0
        void main(float4 in_position : POSITION, float4 in_color : COLOR,
                out float4 out_position : SV_POSITION, out float4 out_color : COLOR)
        {
            out_position = in_position;
            out_color = in_color;
        }
#endif
        0x43425844, 0xa58fc911, 0x280038e9, 0x14cfff54, 0xe43fc328, 0x00000001, 0x00000144, 0x00000003,
        0x0000002c, 0x0000007c, 0x000000d0, 0x4e475349, 0x00000048, 0x00000002, 0x00000008, 0x00000038,
        0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000f0f, 0x00000041, 0x00000000, 0x00000000,
        0x00000003, 0x00000001, 0x00000f0f, 0x49534f50, 0x4e4f4954, 0x4c4f4300, 0xab00524f, 0x4e47534f,
        0x0000004c, 0x00000002, 0x00000008, 0x00000038, 0x00000000, 0x00000001, 0x00000003, 0x00000000,
        0x0000000f, 0x00000044, 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x0000000f, 0x505f5653,
        0x5449534f, 0x004e4f49, 0x4f4c4f43, 0xabab0052, 0x58454853, 0x0000006c, 0x00010050, 0x0000001b,
        0x0100086a, 0x0300005f, 0x001010f2, 0x00000000, 0x0300005f, 0x001010f2, 0x00000001, 0x04000067,
        0x001020f2, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000001, 0x05000036, 0x001020f2,
        0x00000000, 0x00101e46, 0x00000000, 0x05000036, 0x001020f2, 0x00000001, 0x00101e46, 0x00000001,
        0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE vs = {vs_code, sizeof(vs_code)};
    static const DWORD ps_code[] =
    {
#if 0
        void main(float4 in_position : SV_POSITION, float4 in_color : COLOR,
                out float4 out_color : SV_TARGET)
        {
            out_color = in_color;
        }
#endif
        0x43425844, 0x1a6def50, 0x9c069300, 0x7cce68f0, 0x621239b9, 0x00000001, 0x000000f8, 0x00000003,
        0x0000002c, 0x00000080, 0x000000b4, 0x4e475349, 0x0000004c, 0x00000002, 0x00000008, 0x00000038,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x00000044, 0x00000000, 0x00000000,
        0x00000003, 0x00000001, 0x00000f0f, 0x505f5653, 0x5449534f, 0x004e4f49, 0x4f4c4f43, 0xabab0052,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x45475241, 0xabab0054, 0x58454853, 0x0000003c, 0x00000050,
        0x0000000f, 0x0100086a, 0x03001062, 0x001010f2, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x05000036, 0x001020f2, 0x00000000, 0x00101e46, 0x00000001, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    static const uint32_t indices[] = {0, 1, 2, 3};
    static const D3D12_INPUT_ELEMENT_DESC layout_desc[] =
    {
        {"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT,       0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
        {"COLOR",    0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 8, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
    };
    static const struct
    {
        struct vec2 position;
        struct vec4 color;
    }
    quad[] =
    {
        {{-1.0f, -1.0f}, {0.0f, 1.0f, 0.0f, 1.0f}},
        {{-1.0f,  1.0f}, {0.0f, 1.0f, 0.0f, 1.0f}},
        {{ 1.0f, -1.0f}, {0.0f, 1.0f, 0.0f, 1.0f}},
        {{ 1.0f,  1.0f}, {0.0f, 1.0f, 0.0f, 1.0f}},
    };

    memset(&desc, 0, sizeof(desc));
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    command_list = context.list;
    queue = context.queue;

    context.root_signature = create_empty_root_signature(context.device,
            D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
    input_layout.pInputElementDescs = layout_desc;
    input_layout.NumElements = ARRAY_SIZE(layout_desc);
    context.pipeline_state = create_pipeline_state(context.device,
            context.root_signature, context.render_target_desc.Format, &vs, &ps, &input_layout);

    vb_offset = 0x200;
    ib_offset = 0x500;
    buffer = create_upload_buffer(context.device, ib_offset + sizeof(indices), NULL);

    hr = ID3D12Resource_Map(buffer, 0, NULL, (void **)&ptr);
    ok(SUCCEEDED(hr), "Failed to map upload buffer, hr %#x.\n", hr);
    memcpy(ptr + vb_offset, quad, sizeof(quad));
    memcpy(ptr + ib_offset, indices, sizeof(indices));
    ID3D12Resource_Unmap(buffer, 0, NULL);

    vbv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(buffer) + vb_offset;
    vbv.StrideInBytes = sizeof(*quad);
    vbv.SizeInBytes = sizeof(quad);
    ibv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(buffer) + ib_offset;
    ibv.SizeInBytes = sizeof(indices);
    ibv.Format = DXGI_FORMAT_R32_UINT;

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
    ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, 1, &vbv);
    ID3D12GraphicsCommandList_IASetIndexBuffer(command_list, &ibv);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_DrawIndexedInstanced(command_list, 4, 1, 0, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);

    ID3D12Resource_Release(buffer);
    destroy_test_context(&context);
}

static void test_fragment_coords(void)
{
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    ID3D12GraphicsCommandList *command_list;
    D3D12_INPUT_LAYOUT_DESC input_layout;
    struct d3d12_resource_readback rb;
    struct test_context_desc desc;
    D3D12_VERTEX_BUFFER_VIEW vbv;
    struct test_context context;
    const struct vec4 *v = NULL;
    struct vec4 expected = {0};
    ID3D12CommandQueue *queue;
    unsigned int i, x = 0, y;
    ID3D12Resource *vb;
    bool all_match;

    static const DWORD vs_code[] =
    {
#if 0
        void main(float4 in_position : POSITION, out float4 out_position : SV_POSITION)
        {
            out_position = in_position;
        }
#endif
        0x43425844, 0xa7a2f22d, 0x83ff2560, 0xe61638bd, 0x87e3ce90, 0x00000001, 0x000000d8, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000f0f, 0x49534f50, 0x4e4f4954, 0xababab00,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000001, 0x00000003,
        0x00000000, 0x0000000f, 0x505f5653, 0x5449534f, 0x004e4f49, 0x52444853, 0x0000003c, 0x00010040,
        0x0000000f, 0x0300005f, 0x001010f2, 0x00000000, 0x04000067, 0x001020f2, 0x00000000, 0x00000001,
        0x05000036, 0x001020f2, 0x00000000, 0x00101e46, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE vs = {vs_code, sizeof(vs_code)};
    static const DWORD ps_code[] =
    {
#if 0
        float4 main(float4 position: sv_position) : sv_target
        {
            return position;
        }
#endif
        0x43425844, 0xac408178, 0x2ca4213f, 0x4f2551e1, 0x1626b422, 0x00000001, 0x000000d8, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x00000f0f, 0x705f7673, 0x7469736f, 0x006e6f69,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x745f7673, 0x65677261, 0xabab0074, 0x52444853, 0x0000003c, 0x00000040,
        0x0000000f, 0x04002064, 0x001010f2, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x05000036, 0x001020f2, 0x00000000, 0x00101e46, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};
    static const D3D12_INPUT_ELEMENT_DESC layout_desc[] =
    {
        {"POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
    };
    static const struct vec4 vertices[] =
    {
        {-1.0f, -1.0f, 0.00f, 1.00f},
        {-1.0f,  1.0f, 0.00f, 1.00f},
        { 1.0f, -1.0f, 0.00f, 1.00f},
        { 1.0f,  1.0f, 0.00f, 1.00f},

        {-1.0f, -1.0f, 0.25f, 1.00f},
        {-1.0f,  1.0f, 0.25f, 1.00f},
        { 1.0f, -1.0f, 0.25f, 1.00f},
        { 1.0f,  1.0f, 0.25f, 1.00f},

        {-1.0f, -1.0f, 0.50f, 1.00f},
        {-1.0f,  1.0f, 0.50f, 1.00f},
        { 1.0f, -1.0f, 0.50f, 1.00f},
        { 1.0f,  1.0f, 0.50f, 1.00f},

        {-1.0f, -1.0f, 0.75f, 1.00f},
        {-1.0f,  1.0f, 0.75f, 1.00f},
        { 1.0f, -1.0f, 0.75f, 1.00f},
        { 1.0f,  1.0f, 0.75f, 1.00f},

        {-1.0f, -1.0f, 1.00f, 1.00f},
        {-1.0f,  1.0f, 1.00f, 1.00f},
        { 1.0f, -1.0f, 1.00f, 1.00f},
        { 1.0f,  1.0f, 1.00f, 1.00f},

        {-1.0f, -1.0f, 1.00f, 0.50f},
        {-1.0f,  1.0f, 1.00f, 0.50f},
        { 1.0f, -1.0f, 1.00f, 0.50f},
        { 1.0f,  1.0f, 1.00f, 0.50f},

        {-1.0f, -1.0f, 1.00f, 0.25f},
        {-1.0f,  1.0f, 1.00f, 0.25f},
        { 1.0f, -1.0f, 1.00f, 0.25f},
        { 1.0f,  1.0f, 1.00f, 0.25f},
    };

    memset(&desc, 0, sizeof(desc));
    desc.rt_format = DXGI_FORMAT_R32G32B32A32_FLOAT;
    desc.root_signature_flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
    desc.no_pipeline = true;
    if (!init_test_context(&context, &desc))
        return;
    command_list = context.list;
    queue = context.queue;

    input_layout.pInputElementDescs = layout_desc;
    input_layout.NumElements = ARRAY_SIZE(layout_desc);
    context.pipeline_state = create_pipeline_state(context.device,
            context.root_signature, context.render_target_desc.Format, &vs, &ps, &input_layout);

    vb = create_upload_buffer(context.device, sizeof(vertices), vertices);
    vbv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vb);
    vbv.StrideInBytes = sizeof(*vertices);
    vbv.SizeInBytes = sizeof(vertices);

    for (i = 0; i < ARRAY_SIZE(vertices) / 4; ++i)
    {
        vkd3d_test_push_context("Test %u", i);

        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

        ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
        ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
        ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
        set_viewport(&context.viewport, 0.0f, 0.0f, 32.0f, 32.0f, 0.0f, 1.0f);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
        ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
        ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, 1, &vbv);
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 1, 4 * i, 0);

        set_viewport(&context.viewport, 10.0f, 10.0f, 20.0f, 30.0f, 0.0f, 1.0f);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 1, 4 * i, 0);

        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);

        get_resource_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
        all_match = true;
        for (y = 0; y < rb.rb.height; ++y)
        {
            for (x = 0; x < rb.rb.width; ++x)
            {
                v = get_readback_vec4(&rb.rb, x, y);
                expected.x = x + 0.5f;
                expected.y = y + 0.5f;
                expected.z = vertices[4 * i].z / vertices[4 * i].w;
                expected.w = vertices[4 * i].w;
                if (!compare_vec4(v, &expected, 2))
                {
                    all_match = false;
                    break;
                }
            }
            if (!all_match)
                break;
        }
        ok(all_match, "Got {%.8e, %.8e, %.8e, %.8e} expected {%.8e, %.8e, %.8e, %.8e} at (%u, %u).\n",
                v->x, v->y, v->z, v->w, expected.x, expected.y, expected.z, expected.w, x, y);
        release_resource_readback(&rb);

        reset_command_list(command_list, context.allocator);
        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

        vkd3d_test_pop_context();
    }

    ID3D12Resource_Release(vb);
    destroy_test_context(&context);
}

static void test_fractional_viewports(void)
{
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    ID3D12GraphicsCommandList *command_list;
    ID3D10Blob *vs_bytecode, *ps_bytecode;
    D3D12_INPUT_LAYOUT_DESC input_layout;
    struct d3d12_resource_readback rb;
    struct test_context_desc desc;
    D3D12_SHADER_BYTECODE vs, ps;
    D3D12_VERTEX_BUFFER_VIEW vbv;
    struct test_context context;
    ID3D12CommandQueue *queue;
    D3D12_VIEWPORT viewport;
    unsigned int i, x, y;
    ID3D12Resource *vb;

    static const char vs_code[] =
            "void main(in float4 in_position : POSITION,\n"
            "        in float2 in_texcoord : TEXCOORD,\n"
            "        out float4 position : SV_Position,\n"
            "        out float2 texcoord : TEXCOORD)\n"
            "{\n"
            "    position = in_position;\n"
            "    texcoord = in_texcoord;\n"
            "}\n";
    static const char ps_code[] =
            "float4 main(float4 position : SV_Position,\n"
            "        float2 texcoord : TEXCOORD) : SV_Target\n"
            "{\n"
            "    return float4(position.xy, texcoord);\n"
            "}\n";
    static const D3D12_INPUT_ELEMENT_DESC layout_desc[] =
    {
        {"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
        {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 8, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
    };
    static const struct
    {
        struct vec2 position;
        struct vec2 texcoord;
    }
    quad[] =
    {
        {{-1.0f, -1.0f}, {0.0f, 0.0f}},
        {{-1.0f,  1.0f}, {0.0f, 1.0f}},
        {{ 1.0f, -1.0f}, {1.0f, 0.0f}},
        {{ 1.0f,  1.0f}, {1.0f, 1.0f}},
    };
    static const float viewport_offsets[] =
    {
        0.0f, 1.0f / 2.0f, 1.0f / 4.0f, 1.0f / 8.0f, 1.0f / 16.0f, 1.0f / 32.0f,
        1.0f / 64.0f, 1.0f / 128.0f, 1.0f / 256.0f, 63.0f / 128.0f,
    };

    vs_bytecode = compile_shader(vs_code, sizeof(vs_code) - 1, "vs_4_0");
    vs = shader_bytecode_from_blob(vs_bytecode);

    ps_bytecode = compile_shader(ps_code, sizeof(ps_code) - 1, "ps_4_0");
    ps = shader_bytecode_from_blob(ps_bytecode);

    memset(&desc, 0, sizeof(desc));
    desc.rt_format = DXGI_FORMAT_R32G32B32A32_FLOAT;
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
    {
        ID3D10Blob_Release(vs_bytecode);
        ID3D10Blob_Release(ps_bytecode);
        return;
    }
    command_list = context.list;
    queue = context.queue;

    context.root_signature = create_empty_root_signature(context.device,
            D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);

    input_layout.pInputElementDescs = layout_desc;
    input_layout.NumElements = ARRAY_SIZE(layout_desc);
    context.pipeline_state = create_pipeline_state(context.device,
            context.root_signature, desc.rt_format, &vs, &ps, &input_layout);

    vb = create_upload_buffer(context.device, sizeof(quad), quad);

    vbv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vb);
    vbv.StrideInBytes = sizeof(*quad);
    vbv.SizeInBytes = sizeof(quad);

    for (i = 0; i < ARRAY_SIZE(viewport_offsets); ++i)
    {
        set_viewport(&viewport, viewport_offsets[i], viewport_offsets[i],
                context.render_target_desc.Width, context.render_target_desc.Height, 0.0f, 1.0f);

        if (i)
            transition_resource_state(command_list, context.render_target,
                    D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

        ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
        ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
        ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
        ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, 1, &vbv);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &viewport);
        ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 1, 0, 0);

        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);

        get_resource_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
        for (y = 0; y < rb.rb.height; ++y)
        {
            for (x = 0; x < rb.rb.width; ++x)
            {
                const struct vec4 *v = get_readback_vec4(&rb.rb, x, y);
                struct vec4 expected = {x + 0.5f, y + 0.5f,
                        (x + 0.5f - viewport_offsets[i]) / context.render_target_desc.Width,
                        1.0f - (y + 0.5f - viewport_offsets[i]) / context.render_target_desc.Height};
                ok(compare_float(v->x, expected.x, 0) && compare_float(v->y, expected.y, 0),
                        "Got fragcoord {%.8e, %.8e}, expected {%.8e, %.8e} at (%u, %u), offset %.8e.\n",
                        v->x, v->y, expected.x, expected.y, x, y, viewport_offsets[i]);
                ok(compare_float(v->z, expected.z, 2) && compare_float(v->w, expected.w, 2),
                        "Got texcoord {%.8e, %.8e}, expected {%.8e, %.8e} at (%u, %u), offset %.8e.\n",
                        v->z, v->w, expected.z, expected.w, x, y, viewport_offsets[i]);
            }
        }
        release_resource_readback(&rb);

        reset_command_list(command_list, context.allocator);
    }

    ID3D12Resource_Release(vb);
    destroy_test_context(&context);

    ID3D10Blob_Release(vs_bytecode);
    ID3D10Blob_Release(ps_bytecode);
}

static void test_scissor(void)
{
    ID3D12GraphicsCommandList *command_list;
    struct d3d12_resource_readback rb;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12CommandQueue *queue;
    D3D12_SHADER_BYTECODE ps;
    ID3D10Blob *ps_bytecode;
    unsigned int color;
    RECT scissor_rect;

    static const char ps_code[] =
            "float4 main(float4 position : SV_POSITION) : SV_Target\n"
            "{\n"
            "    return float4(0.0, 1.0, 0.0, 1.0);\n"
            "}\n";
    static const float red[] = {1.0f, 0.0f, 0.0f, 1.0f};

    ps_bytecode = compile_shader(ps_code, sizeof(ps_code) - 1, "ps_4_0");
    ps = shader_bytecode_from_blob(ps_bytecode);

    memset(&desc, 0, sizeof(desc));
    desc.rt_width = 640;
    desc.rt_height = 480;
    desc.ps = &ps;
    if (!init_test_context(&context, &desc))
    {
        ID3D10Blob_Release(ps_bytecode);
        return;
    }
    command_list = context.list;
    queue = context.queue;

    set_rect(&scissor_rect, 160, 120, 480, 360);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, red, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &scissor_rect);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);

    get_resource_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
    color = get_readback_uint(&rb.rb, 320, 60, 0);
    ok(compare_color(color, 0xff0000ff, 1), "Got unexpected color 0x%08x.\n", color);
    color = get_readback_uint(&rb.rb, 80, 240, 0);
    ok(compare_color(color, 0xff0000ff, 1), "Got unexpected color 0x%08x.\n", color);
    color = get_readback_uint(&rb.rb, 320, 240, 0);
    ok(compare_color(color, 0xff00ff00, 1), "Got unexpected color 0x%08x.\n", color);
    color = get_readback_uint(&rb.rb, 560, 240, 0);
    ok(compare_color(color, 0xff0000ff, 1), "Got unexpected color 0x%08x.\n", color);
    color = get_readback_uint(&rb.rb, 320, 420, 0);
    ok(compare_color(color, 0xff0000ff, 1), "Got unexpected color 0x%08x.\n", color);
    release_resource_readback(&rb);

    destroy_test_context(&context);

    ID3D10Blob_Release(ps_bytecode);
}

static void test_draw_depth_no_ps(void)
{
    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
    ID3D12GraphicsCommandList *command_list;
    D3D12_INPUT_LAYOUT_DESC input_layout;
    struct depth_stencil_resource ds;
    struct test_context_desc desc;
    D3D12_VERTEX_BUFFER_VIEW vbv;
    struct test_context context;
    ID3D12CommandQueue *queue;
    D3D12_SHADER_BYTECODE vs;
    ID3D10Blob *vs_bytecode;
    ID3D12Resource *vb;
    HRESULT hr;

    static const struct vec4 clear_colour = {0.25f, 0.5f, 0.75f, 1.0f};
    static const D3D12_INPUT_ELEMENT_DESC layout_desc[] =
    {
        {"POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
    };
    static const struct
    {
        struct vec4 position;
    }
    quad[] =
    {
        {{-1.0f, -1.0f, 0.5f, 1.0f}},
        {{-1.0f,  1.0f, 0.5f, 1.0f}},
        {{ 1.0f, -1.0f, 0.5f, 1.0f}},
        {{ 1.0f,  1.0f, 0.5f, 1.0f}},
    };
    static const char vs_code[] =
            "void main(float4 in_position : POSITION, out float4 out_position : SV_POSITION)\n"
            "{\n"
            "    out_position = in_position;\n"
            "}\n";

    vs_bytecode = compile_shader(vs_code, sizeof(vs_code) - 1, "vs_4_0");
    vs = shader_bytecode_from_blob(vs_bytecode);

    memset(&desc, 0, sizeof(desc));
    desc.rt_width = 640;
    desc.rt_height = 480;
    desc.rt_format = DXGI_FORMAT_R32G32B32A32_FLOAT;
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
    {
        ID3D10Blob_Release(vs_bytecode);
        return;
    }
    command_list = context.list;
    queue = context.queue;

    vb = create_upload_buffer(context.device, sizeof(quad), quad);

    vbv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vb);
    vbv.StrideInBytes = sizeof(*quad);
    vbv.SizeInBytes = sizeof(quad);

    init_depth_stencil(&ds, context.device, 640, 480, 1, 1, DXGI_FORMAT_D32_FLOAT, 0, NULL);

    context.root_signature = create_empty_root_signature(context.device,
            D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
    input_layout.pInputElementDescs = layout_desc;
    input_layout.NumElements = ARRAY_SIZE(layout_desc);
    init_pipeline_state_desc(&pso_desc, context.root_signature, 0,  &vs, NULL, &input_layout);
    memset(&pso_desc.PS, 0, sizeof(pso_desc.PS));
    pso_desc.NumRenderTargets = 1;
    pso_desc.RTVFormats[0] = context.render_target_desc.Format;
    pso_desc.DSVFormat = DXGI_FORMAT_D32_FLOAT;
    pso_desc.DepthStencilState.DepthEnable = true;
    pso_desc.DepthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
    pso_desc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS;
    hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
    ok(hr == S_OK, "Failed to create graphics pipeline state, hr %#x.\n", hr);

    ID3D12GraphicsCommandList_ClearDepthStencilView(command_list, ds.dsv_handle,
            D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, NULL);
    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, (const float *)&clear_colour, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, &ds.dsv_handle);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, 1, &vbv);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 1, 0, 0);

    transition_resource_state(command_list, ds.texture,
            D3D12_RESOURCE_STATE_DEPTH_WRITE, D3D12_RESOURCE_STATE_COPY_SOURCE);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_float(ds.texture, 0, queue, command_list, 0.5f, 1);
    reset_command_list(command_list, context.allocator);
    check_sub_resource_vec4(context.render_target, 0, queue, command_list, &clear_colour, 0);

    destroy_depth_stencil(&ds);
    ID3D12Resource_Release(vb);
    destroy_test_context(&context);

    ID3D10Blob_Release(vs_bytecode);
}

static void test_draw_depth_only(void)
{
    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
    ID3D12GraphicsCommandList *command_list;
    struct d3d12_resource_readback rb;
    struct depth_stencil_resource ds;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12CommandQueue *queue;
    D3D12_SHADER_BYTECODE ps;
    ID3D10Blob *ps_bytecode;
    unsigned int i, j;
    HRESULT hr;

    static const char ps_code[] =
            "float depth;\n"
            "\n"
            "float main() : SV_Depth\n"
            "{\n"
            "    return depth;\n"
            "}\n";
    static const struct
    {
        float clear_depth;
        float depth;
        float expected_depth;
    }
    tests[] =
    {
        {0.0f, 0.0f, 0.0f},
        {0.0f, 0.7f, 0.0f},
        {0.0f, 0.8f, 0.0f},
        {0.0f, 0.5f, 0.0f},

        {1.0f, 0.0f, 0.0f},
        {1.0f, 0.7f, 0.7f},
        {1.0f, 0.8f, 0.8f},
        {1.0f, 0.5f, 0.5f},
    };

    ps_bytecode = compile_shader(ps_code, sizeof(ps_code) - 1, "ps_4_0");
    ps = shader_bytecode_from_blob(ps_bytecode);

    memset(&desc, 0, sizeof(desc));
    desc.no_render_target = true;
    if (!init_test_context(&context, &desc))
    {
        ID3D10Blob_Release(ps_bytecode);
        return;
    }
    command_list = context.list;
    queue = context.queue;

    init_depth_stencil(&ds, context.device, 640, 480, 1, 1, DXGI_FORMAT_D32_FLOAT, 0, NULL);
    set_viewport(&context.viewport, 0.0f, 0.0f, 640.0f, 480.0f, 0.0f, 1.0f);
    set_rect(&context.scissor_rect, 0, 0, 640, 480);

    context.root_signature = create_32bit_constants_root_signature(context.device,
            0, 1, D3D12_SHADER_VISIBILITY_PIXEL);
    init_pipeline_state_desc(&pso_desc, context.root_signature, 0, NULL, &ps, NULL);
    pso_desc.NumRenderTargets = 0;
    pso_desc.DSVFormat = DXGI_FORMAT_D32_FLOAT;
    pso_desc.DepthStencilState.DepthEnable = true;
    pso_desc.DepthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
    pso_desc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_LESS;
    hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
    ok(SUCCEEDED(hr), "Failed to create graphics pipeline state, hr %#x.\n", hr);

    for (i = 0; i < ARRAY_SIZE(tests); ++i)
    {
        vkd3d_test_push_context("Test %u", i);

        ID3D12GraphicsCommandList_ClearDepthStencilView(command_list, ds.dsv_handle,
                D3D12_CLEAR_FLAG_DEPTH, tests[i].clear_depth, 0, 0, NULL);

        ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 0, NULL, false, &ds.dsv_handle);
        ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
        ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
        ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);

        ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 0, 1, &tests[i].depth, 0);
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

        transition_resource_state(command_list, ds.texture,
                D3D12_RESOURCE_STATE_DEPTH_WRITE, D3D12_RESOURCE_STATE_COPY_SOURCE);
        check_sub_resource_float(ds.texture, 0, queue, command_list, tests[i].expected_depth, 1);

        reset_command_list(command_list, context.allocator);
        transition_resource_state(command_list, ds.texture,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_DEPTH_WRITE);

        vkd3d_test_pop_context();
    }

    ID3D12GraphicsCommandList_ClearDepthStencilView(command_list, ds.dsv_handle,
            D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, NULL);
    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 0, NULL, false, &ds.dsv_handle);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    for (i = 0; i < 4; ++i)
    {
        for (j = 0; j < 4; ++j)
        {
            float depth = 1.0f / 16.0f * (j + 4 * i);
            ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 0, 1, &depth, 0);

            set_viewport(&context.viewport, 160.0f * j, 120.0f * i, 160.0f, 120.0f, 0.0f, 1.0f);
            ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);

            ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);
        }
    }
    transition_resource_state(command_list, ds.texture,
            D3D12_RESOURCE_STATE_DEPTH_WRITE, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_resource_readback_with_command_list(ds.texture, 0, &rb, queue, command_list);
    for (i = 0; i < 4; ++i)
    {
        for (j = 0; j < 4; ++j)
        {
            float obtained_depth, expected_depth;

            obtained_depth = get_readback_float(&rb.rb, 80 + j * 160, 60 + i * 120);
            expected_depth = 1.0f / 16.0f * (j + 4 * i);
            ok(compare_float(obtained_depth, expected_depth, 1),
                    "Got unexpected depth %.8e at (%u, %u), expected %.8e.\n",
                    obtained_depth, j, i, expected_depth);
        }
    }
    release_resource_readback(&rb);

    destroy_depth_stencil(&ds);
    destroy_test_context(&context);

    ID3D10Blob_Release(ps_bytecode);
}

static void test_draw_uav_only(void)
{
    ID3D12DescriptorHeap *cpu_descriptor_heap, *descriptor_heap;
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    D3D12_DESCRIPTOR_RANGE descriptor_range;
    ID3D12GraphicsCommandList *command_list;
    D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle;
    D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle;
    D3D12_ROOT_PARAMETER root_parameter;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Resource *resource;
    unsigned int i;
    HRESULT hr;

    static const DWORD ps_code[] =
    {
#if 0
        RWTexture2D<int> u;

        void main()
        {
            InterlockedAdd(u[uint2(0, 0)], 1);
        }
#endif
        0x43425844, 0x237a8398, 0xe7b34c17, 0xa28c91a4, 0xb3614d73, 0x00000001, 0x0000009c, 0x00000003,
        0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x00000048, 0x00000050, 0x00000012, 0x0100086a,
        0x0400189c, 0x0011e000, 0x00000000, 0x00003333, 0x0a0000ad, 0x0011e000, 0x00000000, 0x00004002,
        0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00004001, 0x00000001, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};
    static const float zero[4] = {0};

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

    descriptor_range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
    descriptor_range.NumDescriptors = 1;
    descriptor_range.BaseShaderRegister = 0;
    descriptor_range.RegisterSpace = 0;
    descriptor_range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
    root_parameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameter.DescriptorTable.NumDescriptorRanges = 1;
    root_parameter.DescriptorTable.pDescriptorRanges = &descriptor_range;
    root_parameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    root_signature_desc.NumParameters = 1;
    root_signature_desc.pParameters = &root_parameter;
    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, &context.root_signature);
    ok(SUCCEEDED(hr), "Failed to create root signature, hr %#x.\n", hr);

    context.pipeline_state = create_pipeline_state(context.device, context.root_signature, 0, NULL, &ps, NULL);

    resource = create_default_texture(context.device, 1, 1, DXGI_FORMAT_R32_SINT,
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

    descriptor_heap = create_gpu_descriptor_heap(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1);
    cpu_descriptor_heap = create_cpu_descriptor_heap(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1);
    cpu_handle = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(descriptor_heap);
    gpu_handle = ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(descriptor_heap);
    ID3D12Device_CreateUnorderedAccessView(context.device, resource, NULL, NULL, cpu_handle);
    cpu_handle = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(cpu_descriptor_heap);
    ID3D12Device_CreateUnorderedAccessView(context.device, resource, NULL, NULL, cpu_handle);

    ID3D12GraphicsCommandList_ClearUnorderedAccessViewFloat(command_list,
            gpu_handle, cpu_handle, resource, zero, 0, NULL);

    set_rect(&context.scissor_rect, 0, 0, 1000, 1000);
    set_viewport(&context.viewport, 0.0f, 0.0f, 1.0f, 100.0f, 0.0f, 0.0f);

    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &descriptor_heap);
    ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0, gpu_handle);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);

    for (i = 0; i < 5; ++i)
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, resource,
            D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
    bug_if(is_radv_device(context.device) || is_macos_lt(15, 0, 0))
    check_sub_resource_uint(resource, 0, queue, command_list, 500, 0);

    ID3D12DescriptorHeap_Release(cpu_descriptor_heap);
    ID3D12DescriptorHeap_Release(descriptor_heap);
    ID3D12Resource_Release(resource);
    destroy_test_context(&context);
}

static void test_texture_resource_barriers(void)
{
    ID3D12CommandAllocator *command_allocator;
    ID3D12GraphicsCommandList *command_list;
    D3D12_RESOURCE_BARRIER barriers[8];
    ID3D12CommandQueue *queue;
    ID3D12Resource *resource;
    ID3D12Device *device;
    ULONG refcount;
    HRESULT hr;

    if (!(device = create_device()))
    {
        skip("Failed to create device.\n");
        return;
    }

    queue = create_command_queue(device, D3D12_COMMAND_LIST_TYPE_DIRECT, D3D12_COMMAND_QUEUE_PRIORITY_NORMAL);

    hr = ID3D12Device_CreateCommandAllocator(device, D3D12_COMMAND_LIST_TYPE_DIRECT,
            &IID_ID3D12CommandAllocator, (void **)&command_allocator);
    ok(SUCCEEDED(hr), "Failed to create command allocator, hr %#x.\n", hr);

    hr = ID3D12Device_CreateCommandList(device, 0, D3D12_COMMAND_LIST_TYPE_DIRECT,
            command_allocator, NULL, &IID_ID3D12GraphicsCommandList, (void **)&command_list);
    ok(SUCCEEDED(hr), "Failed to create command list, hr %#x.\n", hr);

    resource = create_default_texture(device, 32, 32, DXGI_FORMAT_R8G8B8A8_UNORM,
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COMMON);

    barriers[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
    barriers[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
    barriers[0].Transition.pResource = resource;
    barriers[0].Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
    barriers[0].Transition.StateBefore = D3D12_RESOURCE_STATE_COMMON;
    barriers[0].Transition.StateAfter = D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
    ID3D12GraphicsCommandList_ResourceBarrier(command_list, 1, &barriers[0]);

    barriers[1].Type = D3D12_RESOURCE_BARRIER_TYPE_UAV;
    barriers[1].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
    barriers[1].UAV.pResource = resource;
    ID3D12GraphicsCommandList_ResourceBarrier(command_list, 1, &barriers[1]);

    barriers[2].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
    barriers[2].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
    barriers[2].Transition.pResource = resource;
    barriers[2].Transition.Subresource = 0;
    barriers[2].Transition.StateBefore = D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
    barriers[2].Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE;
    ID3D12GraphicsCommandList_ResourceBarrier(command_list, 1, &barriers[2]);

    barriers[3].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
    barriers[3].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
    barriers[3].Transition.pResource = resource;
    barriers[3].Transition.Subresource = 0;
    barriers[3].Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE;
    barriers[3].Transition.StateAfter = D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE
            | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
    ID3D12GraphicsCommandList_ResourceBarrier(command_list, 1, &barriers[3]);

    barriers[4].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
    barriers[4].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
    barriers[4].Transition.pResource = resource;
    barriers[4].Transition.Subresource = 0;
    barriers[4].Transition.StateBefore = D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE
            | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
    barriers[4].Transition.StateAfter  = D3D12_RESOURCE_STATE_COPY_SOURCE;
    ID3D12GraphicsCommandList_ResourceBarrier(command_list, 1, &barriers[4]);

    barriers[5].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
    barriers[5].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
    barriers[5].Transition.pResource = resource;
    barriers[5].Transition.Subresource = 0;
    barriers[5].Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE;
    barriers[5].Transition.StateAfter = D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
    ID3D12GraphicsCommandList_ResourceBarrier(command_list, 1, &barriers[5]);

    barriers[6].Type = D3D12_RESOURCE_BARRIER_TYPE_UAV;
    barriers[6].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
    barriers[6].UAV.pResource = resource;
    ID3D12GraphicsCommandList_ResourceBarrier(command_list, 1, &barriers[6]);
    ID3D12GraphicsCommandList_ResourceBarrier(command_list, 1, &barriers[6]);

    barriers[7].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
    barriers[7].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
    barriers[7].Transition.pResource = resource;
    barriers[7].Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
    barriers[7].Transition.StateBefore = D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
    barriers[7].Transition.StateAfter = D3D12_RESOURCE_STATE_COMMON;
    ID3D12GraphicsCommandList_ResourceBarrier(command_list, 1, &barriers[7]);

    ID3D12GraphicsCommandList_ResourceBarrier(command_list, 8, barriers);

    hr = ID3D12GraphicsCommandList_Close(command_list);
    ok(SUCCEEDED(hr), "Failed to close command list, hr %#x.\n", hr);
    exec_command_list(queue, command_list);
    wait_queue_idle(device, queue);

    ID3D12GraphicsCommandList_Release(command_list);
    ID3D12CommandAllocator_Release(command_allocator);
    ID3D12Resource_Release(resource);
    ID3D12CommandQueue_Release(queue);
    refcount = ID3D12Device_Release(device);
    ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount);
}

static void test_device_removed_reason(void)
{
    D3D12_COMMAND_QUEUE_DESC command_queue_desc;
    ID3D12CommandAllocator *command_allocator;
    ID3D12GraphicsCommandList *command_list;
    ID3D12CommandQueue *queue, *tmp_queue;
    ID3D12Device *device;
    ULONG refcount;
    HRESULT hr;

    if (!(device = create_device()))
    {
        skip("Failed to create device.\n");
        return;
    }

    hr = ID3D12Device_GetDeviceRemovedReason(device);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);

    command_queue_desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
    command_queue_desc.Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL;
    command_queue_desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
    command_queue_desc.NodeMask = 0;
    hr = ID3D12Device_CreateCommandQueue(device, &command_queue_desc,
            &IID_ID3D12CommandQueue, (void **)&queue);
    ok(SUCCEEDED(hr), "Failed to create command queue, hr %#x.\n", hr);

    hr = ID3D12Device_CreateCommandAllocator(device, D3D12_COMMAND_LIST_TYPE_DIRECT,
            &IID_ID3D12CommandAllocator, (void **)&command_allocator);
    ok(SUCCEEDED(hr), "Failed to create command allocator, hr %#x.\n", hr);

    hr = ID3D12Device_CreateCommandList(device, 0, D3D12_COMMAND_LIST_TYPE_DIRECT,
            command_allocator, NULL, &IID_ID3D12GraphicsCommandList, (void **)&command_list);
    ok(SUCCEEDED(hr), "Failed to create command list, hr %#x.\n", hr);

    /* Execute a command list in the recording state. */
    exec_command_list(queue, command_list);

    hr = ID3D12Device_GetDeviceRemovedReason(device);
    ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#x.\n", hr);

    hr = ID3D12Device_CreateCommandQueue(device, &command_queue_desc,
            &IID_ID3D12CommandQueue, (void **)&tmp_queue);
    todo ok(hr == DXGI_ERROR_DEVICE_REMOVED, "Got unexpected hr %#x.\n", hr);
    if (SUCCEEDED(hr))
        ID3D12CommandQueue_Release(tmp_queue);

    hr = ID3D12Device_GetDeviceRemovedReason(device);
    ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#x.\n", hr);

    ID3D12GraphicsCommandList_Release(command_list);
    ID3D12CommandAllocator_Release(command_allocator);
    ID3D12CommandQueue_Release(queue);
    refcount = ID3D12Device_Release(device);
    ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount);
}

static void test_map_resource(void)
{
    D3D12_HEAP_PROPERTIES heap_properties;
    D3D12_RESOURCE_DESC resource_desc;
    ID3D12Resource *resource;
    ID3D12Device *device;
    ULONG refcount;
    void *data;
    HRESULT hr;

    if (!(device = create_device()))
    {
        skip("Failed to create device.\n");
        return;
    }

    resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
    resource_desc.Alignment = 0;
    resource_desc.Width = 32;
    resource_desc.Height = 32;
    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 = 0;

    memset(&heap_properties, 0, sizeof(heap_properties));
    heap_properties.Type = D3D12_HEAP_TYPE_DEFAULT;
    hr = ID3D12Device_CreateCommittedResource(device, &heap_properties, D3D12_HEAP_FLAG_NONE,
            &resource_desc, D3D12_RESOURCE_STATE_COMMON, NULL,
            &IID_ID3D12Resource, (void **)&resource);
    ok(hr == S_OK, "Failed to create texture, hr %#x.\n", hr);

    /* Resources on a DEFAULT heap cannot be mapped. */
    data = (void *)0xdeadbeef;
    hr = ID3D12Resource_Map(resource, 0, NULL, &data);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    ok(data == (void *)0xdeadbeef, "Pointer was modified %p.\n", data);

    ID3D12Resource_Release(resource);

    heap_properties.Type = D3D12_HEAP_TYPE_CUSTOM;
    heap_properties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE;
    heap_properties.MemoryPoolPreference = D3D12_MEMORY_POOL_L0;
    hr = ID3D12Device_CreateCommittedResource(device, &heap_properties, D3D12_HEAP_FLAG_NONE,
            &resource_desc, D3D12_RESOURCE_STATE_COMMON, NULL,
            &IID_ID3D12Resource, (void **)&resource);
    if (FAILED(hr))
    {
        skip("Failed to create texture on custom heap.\n");
    }
    else
    {
        /* The data pointer must be NULL for the UNKNOWN layout. */
        data = (void *)0xdeadbeef;
        hr = ID3D12Resource_Map(resource, 0, NULL, &data);
        ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
        ok(data == (void *)0xdeadbeef, "Pointer was modified %p.\n", data);

        /* Texture on custom heaps can be mapped, but the address doesn't get disclosed to applications */
        hr = ID3D12Resource_Map(resource, 0, NULL, NULL);
        todo ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
        ID3D12Resource_Unmap(resource, 0, NULL);

        ID3D12Resource_Release(resource);
    }

    resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
    resource_desc.Height = 1;
    resource_desc.Format = DXGI_FORMAT_UNKNOWN;
    resource_desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;

    memset(&heap_properties, 0, sizeof(heap_properties));
    heap_properties.Type = D3D12_HEAP_TYPE_DEFAULT;
    hr = ID3D12Device_CreateCommittedResource(device, &heap_properties, D3D12_HEAP_FLAG_NONE,
            &resource_desc, D3D12_RESOURCE_STATE_COMMON, NULL,
            &IID_ID3D12Resource, (void **)&resource);
    ok(hr == S_OK, "Failed to create committed resource, hr %#x.\n", hr);

    /* Resources on a DEFAULT heap cannot be mapped. */
    data = (void *)0xdeadbeef;
    hr = ID3D12Resource_Map(resource, 0, NULL, &data);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    ok(data == (void *)0xdeadbeef, "Pointer was modified %p.\n", data);

    ID3D12Resource_Release(resource);

    heap_properties.Type = D3D12_HEAP_TYPE_UPLOAD;
    hr = ID3D12Device_CreateCommittedResource(device, &heap_properties, D3D12_HEAP_FLAG_NONE,
            &resource_desc, D3D12_RESOURCE_STATE_GENERIC_READ, NULL,
            &IID_ID3D12Resource, (void **)&resource);
    ok(hr == S_OK, "Failed to create committed resource, hr %#x.\n", hr);

    data = NULL;
    hr = ID3D12Resource_Map(resource, 0, NULL, &data);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    ok(data, "Got NULL pointer.\n");
    ID3D12Resource_Unmap(resource, 0, NULL);

    data = (void *)0xdeadbeef;
    hr = ID3D12Resource_Map(resource, 1, NULL, &data);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    ok(data == (void *)0xdeadbeef, "Pointer was modified %p.\n", data);

    data = NULL;
    hr = ID3D12Resource_Map(resource, 0, NULL, &data);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    ok(data, "Got NULL pointer.\n");
    ID3D12Resource_Unmap(resource, 1, NULL);
    ID3D12Resource_Unmap(resource, 0, NULL);

    /* Passing NULL to Map should map, but not disclose the CPU VA to caller. */
    hr = ID3D12Resource_Map(resource, 0, NULL, NULL);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    ID3D12Resource_Unmap(resource, 0, NULL);

    ID3D12Resource_Release(resource);

    refcount = ID3D12Device_Release(device);
    ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount);
}

static void test_map_placed_resources(void)
{
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    ID3D12GraphicsCommandList *command_list;
    ID3D12Heap *upload_heap, *readback_heap;
    D3D12_ROOT_PARAMETER root_parameters[2];
    struct d3d12_resource_readback rb;
    D3D12_RESOURCE_DESC resource_desc;
    ID3D12Resource *readback_buffer;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12Resource *uav_buffer;
    D3D12_HEAP_DESC heap_desc;
    ID3D12CommandQueue *queue;
    ID3D12Resource *cb[4];
    uint32_t *cb_data[4];
    ID3D12Device *device;
    D3D12_RANGE range;
    unsigned int i;
    uint32_t *ptr;
    HRESULT hr;

    STATIC_ASSERT(ARRAY_SIZE(cb) == ARRAY_SIZE(cb_data));

    static const DWORD ps_code[] =
    {
#if 0
        uint offset;
        uint value;

        RWByteAddressBuffer u;

        void main()
        {
            u.Store(offset, value);
        }
#endif
        0x43425844, 0x0dcbdd90, 0x7dad2857, 0x4ee149ee, 0x72a13d21, 0x00000001, 0x000000a4, 0x00000003,
        0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x00000050, 0x00000050, 0x00000014, 0x0100086a,
        0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x0300009d, 0x0011e000, 0x00000000, 0x090000a6,
        0x0011e012, 0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x0020801a, 0x00000000, 0x00000000,
        0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};
    static const uint32_t expected_values[] = {0xdead, 0xbeef, 0xfeed, 0xc0de};

    memset(&desc, 0, sizeof(desc));
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV;
    root_parameters[0].Descriptor.ShaderRegister = 0;
    root_parameters[0].Descriptor.RegisterSpace = 0;
    root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    root_parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
    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(device, &root_signature_desc, &context.root_signature);
    ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr);

    context.pipeline_state = create_pipeline_state(device, context.root_signature, 0, NULL, &ps, NULL);

    heap_desc.SizeInBytes = ARRAY_SIZE(cb) * D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
    memset(&heap_desc.Properties, 0, sizeof(heap_desc.Properties));
    heap_desc.Properties.Type = D3D12_HEAP_TYPE_UPLOAD;
    heap_desc.Alignment = 0;
    heap_desc.Flags = D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS;
    hr = ID3D12Device_CreateHeap(device, &heap_desc, &IID_ID3D12Heap, (void **)&upload_heap);
    ok(hr == S_OK, "Failed to create heap, hr %#x.\n", hr);

    heap_desc.SizeInBytes = 1024;
    heap_desc.Properties.Type = D3D12_HEAP_TYPE_READBACK;
    hr = ID3D12Device_CreateHeap(device, &heap_desc, &IID_ID3D12Heap, (void **)&readback_heap);
    ok(hr == S_OK, "Failed to create heap, hr %#x.\n", hr);

    resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
    resource_desc.Alignment = 0;
    resource_desc.Width = D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT;
    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;

    for (i = 0; i < ARRAY_SIZE(cb); ++i)
    {
        hr = ID3D12Device_CreatePlacedResource(device, upload_heap,
                i * D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT,
                &resource_desc, D3D12_RESOURCE_STATE_GENERIC_READ, NULL,
                &IID_ID3D12Resource, (void **)&cb[i]);
        ok(hr == S_OK, "Failed to create placed resource %u, hr %#x.\n", i, hr);
    }

    resource_desc.Width = 1024;
    hr = ID3D12Device_CreatePlacedResource(device, readback_heap, 0,
            &resource_desc, D3D12_RESOURCE_STATE_COPY_DEST, NULL,
            &IID_ID3D12Resource, (void **)&readback_buffer);
    ok(hr == S_OK, "Failed to create placed resource, hr %#x.\n", hr);

    uav_buffer = create_default_buffer(device, resource_desc.Width,
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

    for (i = 0; i < ARRAY_SIZE(cb); ++i)
    {
        hr = ID3D12Resource_Map(cb[i], 0, NULL, (void **)&cb_data[i]);
        ok(hr == S_OK, "Failed to map buffer %u, hr %#x.\n", i, hr);
    }

    hr = ID3D12Resource_Map(cb[0], 0, NULL, (void **)&ptr);
    ok(hr == S_OK, "Failed to map buffer, hr %#x.\n", hr);
    ok(ptr == cb_data[0], "Got map ptr %p, expected %p.\n", ptr, cb_data[0]);
    cb_data[0][0] = 0;
    cb_data[0][1] = expected_values[0];
    ID3D12Resource_Unmap(cb[0], 0, NULL);
    ID3D12Resource_Unmap(cb[0], 0, NULL);
    cb_data[0] = NULL;

    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_SetGraphicsRootUnorderedAccessView(command_list, 0,
            ID3D12Resource_GetGPUVirtualAddress(uav_buffer));

    ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 1,
            ID3D12Resource_GetGPUVirtualAddress(cb[0]));
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 1,
            ID3D12Resource_GetGPUVirtualAddress(cb[2]));
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);
    cb_data[2][0] = 4;
    cb_data[2][1] = expected_values[1];

    ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 1,
            ID3D12Resource_GetGPUVirtualAddress(cb[1]));
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);
    cb_data[1][0] = 8;
    cb_data[1][1] = expected_values[2];

    ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 1,
            ID3D12Resource_GetGPUVirtualAddress(cb[3]));
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);
    cb_data[3][0] = 12;
    cb_data[3][1] = expected_values[3];
    range.Begin = 0;
    range.End = 2 * sizeof(uint32_t);
    ID3D12Resource_Unmap(cb[3], 0, &range);
    cb_data[3] = NULL;

    transition_resource_state(command_list, uav_buffer,
            D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
    ID3D12GraphicsCommandList_CopyResource(command_list, readback_buffer, uav_buffer);

    get_buffer_readback_with_command_list(readback_buffer, DXGI_FORMAT_R32_UINT, &rb, queue, command_list);
    for (i = 0; i < ARRAY_SIZE(expected_values); ++i)
    {
        unsigned int value = get_readback_uint(&rb.rb, i, 0, 0);
        ok(value == expected_values[i], "Got %#x, expected %#x at %u.\n", value, expected_values[i], i);
    }
    release_resource_readback(&rb);

    ID3D12Resource_Release(uav_buffer);
    ID3D12Resource_Release(readback_buffer);
    ID3D12Heap_Release(upload_heap);
    ID3D12Heap_Release(readback_heap);
    for (i = 0; i < ARRAY_SIZE(cb); ++i)
        ID3D12Resource_Release(cb[i]);
    destroy_test_context(&context);
}

static void test_bundle_state_inheritance(void)
{
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    ID3D12GraphicsCommandList *command_list, *bundle;
    ID3D12CommandAllocator *bundle_allocator;
    struct d3d12_resource_readback rb;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Device *device;
    unsigned int x, y;
    HRESULT hr;

    if (!vkd3d_test_platform_is_windows())
    {
        /* FIXME: Avoid 2048 test todos. */
        skip("Bundles are not implemented yet.\n");
        return;
    }

    if (test_options.use_warp_device)
    {
        skip("Bundle state inheritance test crashes on WARP.\n");
        return;
    }

    if (!init_test_context(&context, NULL))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    hr = ID3D12Device_CreateCommandAllocator(device, D3D12_COMMAND_LIST_TYPE_BUNDLE,
            &IID_ID3D12CommandAllocator, (void **)&bundle_allocator);
    ok(SUCCEEDED(hr), "Failed to create command allocator, hr %#x.\n", hr);
    hr = ID3D12Device_CreateCommandList(device, 0, D3D12_COMMAND_LIST_TYPE_BUNDLE,
            bundle_allocator, NULL, &IID_ID3D12GraphicsCommandList, (void **)&bundle);
    ok(SUCCEEDED(hr), "Failed to create command list, hr %#x.\n", hr);

    /* A bundle does not inherit the current pipeline state. */
    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);

    ID3D12GraphicsCommandList_DrawInstanced(bundle, 3, 1, 0, 0);
    hr = ID3D12GraphicsCommandList_Close(bundle);
    ok(SUCCEEDED(hr), "Failed to close bundle, hr %#x.\n", hr);

    ID3D12GraphicsCommandList_ExecuteBundle(command_list, bundle);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);

    get_resource_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
    for (y = 0; y < rb.rb.height; ++y)
    {
        for (x = 0; x < rb.rb.width; ++x)
        {
           unsigned int v = get_readback_uint(&rb.rb, x, y, 0);
           /* This works on AMD. */
           ok(v == 0xffffffff || v == 0xff00ff00, "Got unexpected value 0x%08x at (%u, %u).\n", v, x, y);
        }
    }
    release_resource_readback(&rb);

    reset_command_list(command_list, context.allocator);
    reset_command_list(bundle, bundle_allocator);

    /* A bundle does not inherit the current primitive topology. */
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);

    ID3D12GraphicsCommandList_SetPipelineState(bundle, context.pipeline_state);
    ID3D12GraphicsCommandList_DrawInstanced(bundle, 3, 1, 0, 0);
    hr = ID3D12GraphicsCommandList_Close(bundle);
    ok(SUCCEEDED(hr), "Failed to close bundle, hr %#x.\n", hr);

    ID3D12GraphicsCommandList_ExecuteBundle(command_list, bundle);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);

    get_resource_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
    for (y = 0; y < rb.rb.height; ++y)
    {
        for (x = 0; x < rb.rb.width; ++x)
        {
           unsigned int v = get_readback_uint(&rb.rb, x, y, 0);
           /* This works on AMD, even though the debug layer says that the primitive topology is undefined. */
           ok(v == 0xffffffff || v == 0xff00ff00, "Got unexpected value 0x%08x at (%u, %u).\n", v, x, y);
        }
    }
    release_resource_readback(&rb);

    reset_command_list(command_list, context.allocator);
    reset_command_list(bundle, bundle_allocator);

    /* A bundle inherit all other states. */
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);

    ID3D12GraphicsCommandList_SetPipelineState(bundle, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(bundle, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_DrawInstanced(bundle, 3, 1, 0, 0);
    hr = ID3D12GraphicsCommandList_Close(bundle);
    ok(SUCCEEDED(hr), "Failed to close bundle, hr %#x.\n", hr);

    ID3D12GraphicsCommandList_ExecuteBundle(command_list, bundle);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);

    reset_command_list(command_list, context.allocator);
    reset_command_list(bundle, bundle_allocator);

    /* All state that is set in a bundle affects a command list. */
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);

    ID3D12GraphicsCommandList_SetGraphicsRootSignature(bundle, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(bundle, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(bundle, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    hr = ID3D12GraphicsCommandList_Close(bundle);
    ok(SUCCEEDED(hr), "Failed to close bundle, hr %#x.\n", hr);

    ID3D12GraphicsCommandList_ExecuteBundle(command_list, bundle);

    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);

    ID3D12CommandAllocator_Release(bundle_allocator);
    ID3D12GraphicsCommandList_Release(bundle);
    destroy_test_context(&context);
}

static void test_shader_instructions(void)
{
    struct named_shader
    {
        const char *name;
        const void *code;
        size_t size;
    };

    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    ID3D12GraphicsCommandList *command_list;
    const struct named_shader *current_ps;
    struct test_context_desc desc;
    D3D12_SHADER_BYTECODE shader;
    struct test_context context;
    ID3D12CommandQueue *queue;
    bool test_shader_float64;
    ID3D12Resource *cb;
    unsigned int i;
    HRESULT hr;

    static const DWORD ps_div_code[] =
    {
#if 0
        float4 src0;
        float4 src1;

        void main(out float4 dst : SV_Target)
        {
            dst.x = src0.x / src1.x;
            dst.yzw = (float3)0;
        }
#endif
        0x43425844, 0x19578813, 0xb1e4de1e, 0x3adee1dc, 0x478cd5d3, 0x00000001, 0x000000e8, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000070, 0x00000050, 0x0000001c,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000002, 0x03000065, 0x001020f2, 0x00000000,
        0x0900000e, 0x00102012, 0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x0020800a, 0x00000000,
        0x00000001, 0x08000036, 0x001020e2, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000,
        0x00000000, 0x0100003e,
    };
    static const struct named_shader ps_div = {"div", ps_div_code, sizeof(ps_div_code)};
    static const DWORD ps_dot2_code[] =
    {
#if 0
        float4 src0;
        float4 src1;

        void main(out float4 dst : SV_Target)
        {
            dst.x = dot(src0.xy, src1.xy);
            dst.yzw = (float3)0;
        }
#endif
        0x43425844, 0x3621a1c7, 0x79d3be21, 0x9f14138c, 0x9f5506f2, 0x00000001, 0x000000e8, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000070, 0x00000050, 0x0000001c,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000002, 0x03000065, 0x001020f2, 0x00000000,
        0x0900000f, 0x00102012, 0x00000000, 0x00208046, 0x00000000, 0x00000000, 0x00208046, 0x00000000,
        0x00000001, 0x08000036, 0x001020e2, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000,
        0x00000000, 0x0100003e,
    };
    static struct named_shader ps_dot2 = {"dot2", ps_dot2_code, sizeof(ps_dot2_code)};
    static const DWORD ps_dot3_code[] =
    {
#if 0
        float4 src0;
        float3 src1;

        float4 main() : SV_Target
        {
            return dot(src0, src1);
        }
#endif
        0x43425844, 0xa75a4a95, 0x5d09936e, 0xdc5c694f, 0x68b6b04f, 0x00000001, 0x000000c8, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000050, 0x00000050, 0x00000014,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000002, 0x03000065, 0x001020f2, 0x00000000,
        0x09000010, 0x001020f2, 0x00000000, 0x00208246, 0x00000000, 0x00000000, 0x00208246, 0x00000000,
        0x00000001, 0x0100003e,
    };
    static struct named_shader ps_dot3 = {"dot3", ps_dot3_code, sizeof(ps_dot3_code)};
    static const DWORD ps_eq_code[] =
    {
#if 0
        float4 src0;
        float4 src1;

        void main(out float4 dst : SV_Target)
        {
            dst = (uint4)0;
            if (src0.x == src1.x)
                dst.x = asfloat(0xffffffff);
        }
#endif
        0x43425844, 0x7bce1728, 0xa7d5d0f0, 0xaef5bc00, 0x7bb6b161, 0x00000001, 0x000000e8, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000070, 0x00000050, 0x0000001c,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000002, 0x03000065, 0x001020f2, 0x00000000,
        0x09000018, 0x00102012, 0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x0020800a, 0x00000000,
        0x00000001, 0x08000036, 0x001020e2, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000,
        0x00000000, 0x0100003e,
    };
    static struct named_shader ps_eq = {"eq", ps_eq_code, sizeof(ps_eq_code)};
    static const DWORD ps_ne_code[] =
    {
#if 0
        float4 src0;
        float4 src1;

        void main(out float4 dst : SV_Target)
        {
            dst = (uint4)0;
            if (src0.x != src1.x)
                dst.x = asfloat(0xffffffff);
        }
#endif
        0x43425844, 0x5bbb7f90, 0x1a44971c, 0x4ee3d92e, 0x149ceecf, 0x00000001, 0x000000e8, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000070, 0x00000050, 0x0000001c,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000002, 0x03000065, 0x001020f2, 0x00000000,
        0x09000039, 0x00102012, 0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x0020800a, 0x00000000,
        0x00000001, 0x08000036, 0x001020e2, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000,
        0x00000000, 0x0100003e,
    };
    static struct named_shader ps_ne = {"ne", ps_ne_code, sizeof(ps_ne_code)};
    static const DWORD ps_if_code[] =
    {
        /* compiled with /Gfp option */
#if 0
        float4 src0;

        void main(out float4 dst : SV_Target)
        {
            if (src0.x)
                dst = float4(0, 1, 0, 1);
            else
                dst = float4(1, 0, 0, 1);
        }
#endif
        0x43425844, 0xfe5b6a47, 0x123f8934, 0xfa5910fe, 0x497aad93, 0x00000001, 0x0000012c, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x000000b4, 0x00000050, 0x0000002d,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x02000068, 0x00000001, 0x0b000039, 0x00100012, 0x00000000, 0x00004002, 0x00000000, 0x00000000,
        0x00000000, 0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x0304001f, 0x0010000a, 0x00000000,
        0x08000036, 0x001020f2, 0x00000000, 0x00004002, 0x00000000, 0x3f800000, 0x00000000, 0x3f800000,
        0x01000012, 0x08000036, 0x001020f2, 0x00000000, 0x00004002, 0x3f800000, 0x00000000, 0x00000000,
        0x3f800000, 0x01000015, 0x0100003e
    };
    static struct named_shader ps_if = {"if", ps_if_code, sizeof(ps_if_code)};
    static const DWORD ps_if_return_code[] =
    {
#if 0
        float4 src0;

        void main(out float4 dst : SV_Target)
        {
            dst = (float4)0;
            if (src0.x < 4)
                return;
            dst.x = 1;
            if (src0.y < 4)
                return;
            dst.y = 1;
            if (src0.z >= 4)
                return;
            dst.z = 1;
            if (src0.w <= src0.x)
                return;
            dst.w = 1;
        }
#endif
        0x43425844, 0xa2797349, 0xd0a60aee, 0x7ae89f23, 0xf9681bfe, 0x00000001, 0x00000220, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x000001a8, 0x00000050, 0x0000006a,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x02000068, 0x00000001, 0x08000031, 0x00100012, 0x00000000, 0x0020800a, 0x00000000, 0x00000000,
        0x00004001, 0x40800000, 0x0304001f, 0x0010000a, 0x00000000, 0x08000036, 0x001020f2, 0x00000000,
        0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0100003e, 0x01000015, 0x08000031,
        0x00100012, 0x00000000, 0x0020801a, 0x00000000, 0x00000000, 0x00004001, 0x40800000, 0x0304001f,
        0x0010000a, 0x00000000, 0x08000036, 0x001020f2, 0x00000000, 0x00004002, 0x3f800000, 0x00000000,
        0x00000000, 0x00000000, 0x0100003e, 0x01000015, 0x0800001d, 0x00100012, 0x00000000, 0x0020802a,
        0x00000000, 0x00000000, 0x00004001, 0x40800000, 0x0304001f, 0x0010000a, 0x00000000, 0x08000036,
        0x001020f2, 0x00000000, 0x00004002, 0x3f800000, 0x3f800000, 0x00000000, 0x00000000, 0x0100003e,
        0x01000015, 0x0900001d, 0x00100012, 0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x0020803a,
        0x00000000, 0x00000000, 0x0304001f, 0x0010000a, 0x00000000, 0x08000036, 0x001020f2, 0x00000000,
        0x00004002, 0x3f800000, 0x3f800000, 0x3f800000, 0x00000000, 0x0100003e, 0x01000015, 0x08000036,
        0x001020f2, 0x00000000, 0x00004002, 0x3f800000, 0x3f800000, 0x3f800000, 0x3f800000, 0x0100003e,
    };
    static struct named_shader ps_if_return = {"if_return", ps_if_return_code, sizeof(ps_if_return_code)};
    static const DWORD ps_nested_if_code[] =
    {
        /* compiled with /Gfp option */
#if 0
        float4 src0;

        void main(out float4 dst : SV_Target)
        {
            if (src0.x)
            {
                if (src0.y)
                    dst = float4(0, 1, 0, 1);
                else
                    dst = float4(0, 0, 1, 1);
            }
            else
            {
                if (src0.z)
                    dst = float4(1, 0, 0, 1);
                else
                    dst = float4(0, 0, 0, 1);
            }
        }
#endif
        0x43425844, 0x35e50e88, 0x68c45bdd, 0x0dc60de1, 0x4bc29735, 0x00000001, 0x000001ec, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000174, 0x00000050, 0x0000005d,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x02000068, 0x00000001, 0x0b000039, 0x00100012, 0x00000000, 0x00004002, 0x00000000, 0x00000000,
        0x00000000, 0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x0304001f, 0x0010000a, 0x00000000,
        0x0b000039, 0x00100012, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
        0x0020801a, 0x00000000, 0x00000000, 0x0304001f, 0x0010000a, 0x00000000, 0x08000036, 0x001020f2,
        0x00000000, 0x00004002, 0x00000000, 0x3f800000, 0x00000000, 0x3f800000, 0x01000012, 0x08000036,
        0x001020f2, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x3f800000, 0x3f800000, 0x01000015,
        0x01000012, 0x0b000039, 0x00100012, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000,
        0x00000000, 0x0020802a, 0x00000000, 0x00000000, 0x0304001f, 0x0010000a, 0x00000000, 0x08000036,
        0x001020f2, 0x00000000, 0x00004002, 0x3f800000, 0x00000000, 0x00000000, 0x3f800000, 0x01000012,
        0x08000036, 0x001020f2, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x3f800000,
        0x01000015, 0x01000015, 0x0100003e,
    };
    static struct named_shader ps_nested_if = {"nested_if", ps_nested_if_code, sizeof(ps_nested_if_code)};
    static const DWORD ps_loop_break_code[] =
    {
#if 0
        float4 src0;

        void main(out float4 dst : SV_Target)
        {
            float tmp = 0.0f;
            for (int i = 0; i < src0.x; ++i)
            {
                if (i == src0.y)
                {
                    tmp = 1.0f;
                    break;
                }
                tmp += 1.0f;
            }

            dst = float4(tmp, 0, 0, 0);
        }
#endif
        0x43425844, 0xbd9dabbd, 0xe56cab2a, 0xfd37cd76, 0x5dd181c4, 0x00000001, 0x000001c8, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000150, 0x00000050, 0x00000054,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x02000068, 0x00000001, 0x08000036, 0x00100032, 0x00000000, 0x00004002, 0x00000000, 0x00000000,
        0x00000000, 0x00000000, 0x01000030, 0x0500002b, 0x00100042, 0x00000000, 0x0010001a, 0x00000000,
        0x0800001d, 0x00100082, 0x00000000, 0x0010002a, 0x00000000, 0x0020800a, 0x00000000, 0x00000000,
        0x03040003, 0x0010003a, 0x00000000, 0x08000018, 0x00100042, 0x00000000, 0x0010002a, 0x00000000,
        0x0020801a, 0x00000000, 0x00000000, 0x0304001f, 0x0010002a, 0x00000000, 0x05000036, 0x00100012,
        0x00000000, 0x00004001, 0x3f800000, 0x01000002, 0x01000015, 0x07000000, 0x00100012, 0x00000000,
        0x0010000a, 0x00000000, 0x00004001, 0x3f800000, 0x0700001e, 0x00100022, 0x00000000, 0x0010001a,
        0x00000000, 0x00004001, 0x00000001, 0x01000016, 0x05000036, 0x00102012, 0x00000000, 0x0010000a,
        0x00000000, 0x08000036, 0x001020e2, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000,
        0x00000000, 0x0100003e,
    };
    static struct named_shader ps_loop_break = {"loop_break", ps_loop_break_code, sizeof(ps_loop_break_code)};
    static const DWORD ps_loop_end_break_code[] =
    {
#if 0
        float4 src0;

        void main(out float4 dst : SV_Target)
        {
            float tmp = 0.0f;
            for (int i = 0; i < src0.x; ++i)
            {
                if (i != src0.y)
                {
                    tmp += 1.0f;
                    continue;
                }
                tmp = 1.0f;
                break;
            }

            dst = float4(tmp, 0, 0, 0);
        }
#endif
        0x43425844, 0x0239f900, 0x5b94f73f, 0x68b75aa3, 0x27d353d2, 0x00000001, 0x000001e0, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000168, 0x00000050, 0x0000005a,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x02000068, 0x00000001, 0x08000036, 0x00100032, 0x00000000, 0x00004002, 0x00000000, 0x00000000,
        0x00000000, 0x00000000, 0x01000030, 0x0500002b, 0x00100042, 0x00000000, 0x0010001a, 0x00000000,
        0x0800001d, 0x00100082, 0x00000000, 0x0010002a, 0x00000000, 0x0020800a, 0x00000000, 0x00000000,
        0x03040003, 0x0010003a, 0x00000000, 0x08000039, 0x00100042, 0x00000000, 0x0010002a, 0x00000000,
        0x0020801a, 0x00000000, 0x00000000, 0x0304001f, 0x0010002a, 0x00000000, 0x07000000, 0x00100012,
        0x00000000, 0x0010000a, 0x00000000, 0x00004001, 0x3f800000, 0x0700001e, 0x00100042, 0x00000000,
        0x0010001a, 0x00000000, 0x00004001, 0x00000001, 0x05000036, 0x00100032, 0x00000000, 0x00100086,
        0x00000000, 0x01000007, 0x01000015, 0x05000036, 0x00100012, 0x00000000, 0x00004001, 0x3f800000,
        0x01000002, 0x01000016, 0x05000036, 0x00102012, 0x00000000, 0x0010000a, 0x00000000, 0x08000036,
        0x001020e2, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0100003e,
    };
    static struct named_shader ps_loop_end_break = {"loop_end_break", ps_loop_end_break_code, sizeof(ps_loop_end_break_code)};
    static const DWORD ps_loop_ret_code[] =
    {
#if 0
        float4 src0;

        void main(out float4 dst : SV_Target)
        {
            float tmp = 0.0f;
            for (int i = 0; i < src0.x; ++i)
            {
                if (i == src0.y)
                {
                    dst = 1;
                    return;
                }
                tmp += 1.0f;
            }

            dst = float4(tmp, 0, 0, 0);
        }
#endif
        0x43425844, 0xb327003a, 0x5812a5f6, 0xb5a78d54, 0xa72a8db8, 0x00000001, 0x000001d4, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x0000015c, 0x00000050, 0x00000057,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x02000068, 0x00000001, 0x08000036, 0x00100032, 0x00000000, 0x00004002, 0x00000000, 0x00000000,
        0x00000000, 0x00000000, 0x01000030, 0x0500002b, 0x00100042, 0x00000000, 0x0010001a, 0x00000000,
        0x0800001d, 0x00100082, 0x00000000, 0x0010002a, 0x00000000, 0x0020800a, 0x00000000, 0x00000000,
        0x03040003, 0x0010003a, 0x00000000, 0x08000018, 0x00100042, 0x00000000, 0x0010002a, 0x00000000,
        0x0020801a, 0x00000000, 0x00000000, 0x0304001f, 0x0010002a, 0x00000000, 0x08000036, 0x001020f2,
        0x00000000, 0x00004002, 0x3f800000, 0x3f800000, 0x3f800000, 0x3f800000, 0x0100003e, 0x01000015,
        0x07000000, 0x00100012, 0x00000000, 0x0010000a, 0x00000000, 0x00004001, 0x3f800000, 0x0700001e,
        0x00100022, 0x00000000, 0x0010001a, 0x00000000, 0x00004001, 0x00000001, 0x01000016, 0x05000036,
        0x00102012, 0x00000000, 0x0010000a, 0x00000000, 0x08000036, 0x001020e2, 0x00000000, 0x00004002,
        0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0100003e,
    };
    static struct named_shader ps_loop_ret = {"loop_ret", ps_loop_ret_code, sizeof(ps_loop_ret_code)};
    static const DWORD ps_breakc_nz_code[] =
    {
#if 0
        float4 main() : SV_TARGET
        {
            uint counter = 0;

            for (uint i = 0; i < 255; ++i)
                ++counter;

            if (counter == 255)
                return float4(0.0f, 1.0f, 0.0f, 1.0f);
            else
                return float4(1.0f, 0.0f, 0.0f, 1.0f);
        }
#endif
        0x43425844, 0x065ac80a, 0x24369e7e, 0x218d5dc1, 0x3532868c, 0x00000001, 0x00000188, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x45475241, 0xabab0054, 0x52444853, 0x00000110, 0x00000040, 0x00000044,
        0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000001, 0x08000036, 0x00100032, 0x00000000,
        0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x01000030, 0x07000050, 0x00100042,
        0x00000000, 0x0010001a, 0x00000000, 0x00004001, 0x000000ff, 0x03040003, 0x0010002a, 0x00000000,
        0x0a00001e, 0x00100032, 0x00000000, 0x00100046, 0x00000000, 0x00004002, 0x00000001, 0x00000001,
        0x00000000, 0x00000000, 0x01000016, 0x07000020, 0x00100012, 0x00000000, 0x0010000a, 0x00000000,
        0x00004001, 0x000000ff, 0x0304001f, 0x0010000a, 0x00000000, 0x08000036, 0x001020f2, 0x00000000,
        0x00004002, 0x00000000, 0x3f800000, 0x00000000, 0x3f800000, 0x0100003e, 0x01000012, 0x08000036,
        0x001020f2, 0x00000000, 0x00004002, 0x3f800000, 0x00000000, 0x00000000, 0x3f800000, 0x0100003e,
        0x01000015, 0x0100003e,
    };
    static struct named_shader ps_breakc_nz = {"breakc_nz", ps_breakc_nz_code, sizeof(ps_breakc_nz_code)};
    static const DWORD ps_breakc_z_code[] =
    {
#if 0
        float4 main() : SV_TARGET
        {
            uint counter = 0;

            for (int i = 0, j = 254; i < 255 && j >= 0; ++i, --j)
                ++counter;

            if (counter == 255)
                return float4(0.0f, 1.0f, 0.0f, 1.0f);
            else
                return float4(1.0f, 0.0f, 0.0f, 1.0f);
        }
#endif
        0x43425844, 0x687406ef, 0x7bdeb7d1, 0xb3282292, 0x934a9101, 0x00000001, 0x000001c0, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x45475241, 0xabab0054, 0x52444853, 0x00000148, 0x00000040, 0x00000052,
        0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000002, 0x08000036, 0x00100072, 0x00000000,
        0x00004002, 0x00000000, 0x00000000, 0x000000fe, 0x00000000, 0x01000030, 0x07000022, 0x00100082,
        0x00000000, 0x0010001a, 0x00000000, 0x00004001, 0x000000ff, 0x07000021, 0x00100012, 0x00000001,
        0x0010002a, 0x00000000, 0x00004001, 0x00000000, 0x07000001, 0x00100082, 0x00000000, 0x0010003a,
        0x00000000, 0x0010000a, 0x00000001, 0x03000003, 0x0010003a, 0x00000000, 0x0a00001e, 0x00100072,
        0x00000000, 0x00100246, 0x00000000, 0x00004002, 0x00000001, 0x00000001, 0xffffffff, 0x00000000,
        0x01000016, 0x07000020, 0x00100012, 0x00000000, 0x0010000a, 0x00000000, 0x00004001, 0x000000ff,
        0x0304001f, 0x0010000a, 0x00000000, 0x08000036, 0x001020f2, 0x00000000, 0x00004002, 0x00000000,
        0x3f800000, 0x00000000, 0x3f800000, 0x0100003e, 0x01000012, 0x08000036, 0x001020f2, 0x00000000,
        0x00004002, 0x3f800000, 0x00000000, 0x00000000, 0x3f800000, 0x0100003e, 0x01000015, 0x0100003e,
    };
    static struct named_shader ps_breakc_z = {"breakc_z", ps_breakc_z_code, sizeof(ps_breakc_z_code)};
    static const DWORD ps_continue_code[] =
    {
#if 0
        float4 main() : SV_TARGET
        {
            uint counter = 0;

            for (uint i = 0; i < 255; ++i)
            {
                if (i == 10)
                    continue;
                ++counter;
            }

            return float4(counter, 0.0f, 0.0f, 0.0f);
        }
#endif
        0x43425844, 0x8cab8e1f, 0x527560f9, 0x04eb888b, 0x20d89b05, 0x00000001, 0x000001c4, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x45475241, 0xabab0054, 0x58454853, 0x0000014c, 0x00000050, 0x00000053,
        0x0100086a, 0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000002, 0x05000036, 0x00100022,
        0x00000000, 0x00004001, 0x0000000b, 0x08000036, 0x001000c2, 0x00000000, 0x00004002, 0x00000000,
        0x00000000, 0x00000000, 0x00000000, 0x01000030, 0x07000050, 0x00100012, 0x00000001, 0x0010002a,
        0x00000000, 0x00004001, 0x000000ff, 0x03040003, 0x0010000a, 0x00000001, 0x07000020, 0x00100012,
        0x00000001, 0x0010002a, 0x00000000, 0x00004001, 0x0000000a, 0x0304001f, 0x0010000a, 0x00000001,
        0x05000036, 0x00100012, 0x00000000, 0x0010003a, 0x00000000, 0x05000036, 0x001000c2, 0x00000000,
        0x00100156, 0x00000000, 0x01000007, 0x01000015, 0x0700001e, 0x00100082, 0x00000000, 0x0010003a,
        0x00000000, 0x00004001, 0x00000001, 0x0700001e, 0x00100042, 0x00000000, 0x0010002a, 0x00000000,
        0x00004001, 0x00000001, 0x01000016, 0x05000056, 0x00102012, 0x00000000, 0x0010003a, 0x00000000,
        0x08000036, 0x001020e2, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
        0x0100003e,
    };
    static struct named_shader ps_continue = {"continue", ps_continue_code, sizeof(ps_continue_code)};
    static const DWORD ps_continuec_nz_code[] =
    {
#if 0
        float4 main() : SV_TARGET
        {
            uint counter = 0;

            for (uint i = 0; i < 255; ++i)
            {
                ++counter;
                if (i % 2 == 0)
                    continue;
                ++counter;
                if (i != 0)
                    continue;
                ++counter;
            }

            return float4(counter, 0.0f, 0.0f, 0.0f);
        }
#endif
        /* compiled with /Gfa */
        0x43425844, 0xf35d8ce6, 0x54988f56, 0x5848863e, 0xa1618498, 0x00000001, 0x00000278, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x45475241, 0xabab0054, 0x58454853, 0x00000200, 0x00000050, 0x00000080,
        0x0100086a, 0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000002, 0x08000036, 0x00100032,
        0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x01000030, 0x07000050,
        0x00100042, 0x00000000, 0x0010001a, 0x00000000, 0x00004001, 0x000000ff, 0x03040003, 0x0010002a,
        0x00000000, 0x0700001e, 0x00100042, 0x00000000, 0x0010000a, 0x00000000, 0x00004001, 0x00000001,
        0x07000001, 0x00100082, 0x00000000, 0x0010001a, 0x00000000, 0x00004001, 0x00000001, 0x0700001e,
        0x00100012, 0x00000001, 0x0010001a, 0x00000000, 0x00004001, 0x00000001, 0x09000037, 0x00100022,
        0x00000001, 0x0010003a, 0x00000000, 0x0010001a, 0x00000000, 0x0010000a, 0x00000001, 0x05000036,
        0x00100012, 0x00000000, 0x0010002a, 0x00000000, 0x05000036, 0x00100022, 0x00000000, 0x0010001a,
        0x00000001, 0x03000008, 0x0010003a, 0x00000000, 0x0700001e, 0x00100042, 0x00000000, 0x0010000a,
        0x00000000, 0x00004001, 0x00000002, 0x07000027, 0x00100082, 0x00000000, 0x0010001a, 0x00000000,
        0x00004001, 0x00000000, 0x09000037, 0x00100022, 0x00000000, 0x0010001a, 0x00000000, 0x0010000a,
        0x00000001, 0x00004001, 0x00000000, 0x05000036, 0x00100032, 0x00000000, 0x00100a66, 0x00000000,
        0x03040008, 0x0010003a, 0x00000000, 0x0700001e, 0x00100012, 0x00000000, 0x0010000a, 0x00000000,
        0x00004001, 0x00000003, 0x05000036, 0x00100022, 0x00000000, 0x0010000a, 0x00000001, 0x01000016,
        0x05000056, 0x00102012, 0x00000000, 0x0010000a, 0x00000000, 0x08000036, 0x001020e2, 0x00000000,
        0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0100003e,
    };
    static struct named_shader ps_continuec_nz = {"continuec_nz", ps_continuec_nz_code, sizeof(ps_continuec_nz_code)};
    static const DWORD ps_retc_nz_code[] =
    {
#if 0
        float src;

        float4 main() : SV_TARGET
        {
            for (int i = 0; i < 255; ++i)
            {
                if (i == src)
                    return float4(1, 0, 0, 0);
            }

            return 0;
        }
#endif
        /* compiled with /Gfa */
        0x43425844, 0xf829c302, 0xf21361cb, 0x963b87e9, 0x92f9470e, 0x00000001, 0x00000188, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x45475241, 0xabab0054, 0x52444853, 0x00000110, 0x00000040, 0x00000044,
        0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000, 0x02000068,
        0x00000001, 0x05000036, 0x00100012, 0x00000000, 0x00004001, 0x00000000, 0x01000030, 0x07000021,
        0x00100022, 0x00000000, 0x0010000a, 0x00000000, 0x00004001, 0x000000ff, 0x03040003, 0x0010001a,
        0x00000000, 0x0500002b, 0x00100022, 0x00000000, 0x0010000a, 0x00000000, 0x08000018, 0x00100022,
        0x00000000, 0x0010001a, 0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x08000036, 0x001020f2,
        0x00000000, 0x00004002, 0x3f800000, 0x00000000, 0x00000000, 0x00000000, 0x0304003f, 0x0010001a,
        0x00000000, 0x0700001e, 0x00100012, 0x00000000, 0x0010000a, 0x00000000, 0x00004001, 0x00000001,
        0x01000016, 0x08000036, 0x001020f2, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000,
        0x00000000, 0x0100003e,
    };
    static struct named_shader ps_retc_nz = {"retc_nz", ps_retc_nz_code, sizeof(ps_retc_nz_code)};
    static const DWORD ps_src_modifiers_code[] =
    {
#if 0
        float4 src0;

        void main(out float4 dst : SV_Target)
        {
            dst.x = -src0.x;
            dst.y = abs(src0.y);
            dst.zw = -abs(src0.zw);
        }
#endif
        0x43425844, 0xa5f66fa8, 0xd430e547, 0x1cd28240, 0xaf5bc0f4, 0x00000001, 0x000000f8, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000080, 0x00000050, 0x00000020,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x07000036, 0x00102012, 0x00000000, 0x8020800a, 0x00000041, 0x00000000, 0x00000000, 0x07000036,
        0x00102022, 0x00000000, 0x8020801a, 0x00000081, 0x00000000, 0x00000000, 0x07000036, 0x001020c2,
        0x00000000, 0x80208ea6, 0x000000c1, 0x00000000, 0x00000000, 0x0100003e,
    };
    static struct named_shader ps_src_modifiers = {"src_modifiers", ps_src_modifiers_code, sizeof(ps_src_modifiers_code)};
    static const DWORD ps_sat_code[] =
    {
#if 0
        float4 src;

        void main(out float4 dst : SV_Target)
        {
            dst = clamp(src, 0, 1);
        }
#endif
        0x43425844, 0x50af2f8b, 0xaadad7cd, 0x77815f01, 0x612ec066, 0x00000001, 0x000000bc, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000044, 0x00000050, 0x00000011,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x06002036, 0x001020f2, 0x00000000, 0x00208e46, 0x00000000, 0x00000000, 0x0100003e,
    };
    static struct named_shader ps_sat = {"sat", ps_sat_code, sizeof(ps_sat_code)};
    static const DWORD ps_min_max_code[] =
    {
#if 0
        float4 src0;
        float4 src1;

        void main(out float4 dst : SV_Target)
        {
            dst = (float4)0;
            dst.x = min(src0.x, src1.x);
            dst.y = max(src0.x, src1.x);
        }
#endif
        0x43425844, 0xb570ee39, 0xcf84fe48, 0x7fa59ede, 0x6151def2, 0x00000001, 0x0000010c, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000094, 0x00000050, 0x00000025,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000002, 0x03000065, 0x001020f2, 0x00000000,
        0x09000033, 0x00102012, 0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x0020800a, 0x00000000,
        0x00000001, 0x09000034, 0x00102022, 0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x0020800a,
        0x00000000, 0x00000001, 0x08000036, 0x001020c2, 0x00000000, 0x00004002, 0x00000000, 0x00000000,
        0x00000000, 0x00000000, 0x0100003e,
    };
    static struct named_shader ps_min_max = {"min_max", ps_min_max_code, sizeof(ps_min_max_code)};
    static const DWORD ps_ftou_code[] =
    {
#if 0
        float src;

        void main(out float4 dst : SV_Target)
        {
            dst = asfloat(uint4(src, -src, 0, 0));
        }
#endif
        0x43425844, 0x7a61c2fa, 0x4f20de14, 0x3492a5ae, 0x0a1fdc98, 0x00000001, 0x000000f8, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000080, 0x00000050, 0x00000020,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x0600001c, 0x00102012, 0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x0700001c, 0x00102022,
        0x00000000, 0x8020800a, 0x00000041, 0x00000000, 0x00000000, 0x08000036, 0x001020c2, 0x00000000,
        0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0100003e,
    };
    static struct named_shader ps_ftou = {"ftou", ps_ftou_code, sizeof(ps_ftou_code)};
    static const DWORD ps_ftoi_code[] =
    {
#if 0
        float src;

        void main(out float4 dst : SV_Target)
        {
            dst = asfloat(int4(src, -src, 0, 0));
        }
#endif
        0x43425844, 0x2737f059, 0x5a2faecc, 0x7eab1956, 0xf96357b5, 0x00000001, 0x000000f8, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000080, 0x00000050, 0x00000020,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x0600001b, 0x00102012, 0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x0700001b, 0x00102022,
        0x00000000, 0x8020800a, 0x00000041, 0x00000000, 0x00000000, 0x08000036, 0x001020c2, 0x00000000,
        0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0100003e,
    };
    static struct named_shader ps_ftoi = {"ftoi", ps_ftoi_code, sizeof(ps_ftoi_code)};
    static const DWORD ps_round_code[] =
    {
#if 0
        float src0;

        void main(out float4 dst : SV_Target)
        {
            dst.x = floor(src0);
            dst.y = ceil(src0);
            dst.z = trunc(src0);
            dst.w = 0;
        }
#endif
        0x43425844, 0x44e2c554, 0x216a8c83, 0x87e90dd8, 0x3fde3e57, 0x00000001, 0x00000100, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000088, 0x00000050, 0x00000022,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x06000041, 0x00102012, 0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x06000042, 0x00102022,
        0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x06000043, 0x00102042, 0x00000000, 0x0020800a,
        0x00000000, 0x00000000, 0x05000036, 0x00102082, 0x00000000, 0x00004001, 0x00000000, 0x0100003e,
    };
    static struct named_shader ps_round = {"round", ps_round_code, sizeof(ps_round_code)};
    static const DWORD ps_round_ne_code[] =
    {
#if 0
        float4 src0;

        void main(out float4 dst : SV_Target)
        {
            dst = round(src0);
        }
#endif
        0x43425844, 0xa2be1ad3, 0xf1389007, 0xc8221829, 0xcbef8ed0, 0x00000001, 0x000000bc, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000044, 0x00000050, 0x00000011,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x06000040, 0x001020f2, 0x00000000, 0x00208e46, 0x00000000, 0x00000000, 0x0100003e,
    };
    static struct named_shader ps_round_ne = {"round_ne", ps_round_ne_code, sizeof(ps_round_ne_code)};
    static const DWORD ps_frc_code[] =
    {
#if 0
        float src;

        void main(out float4 dst : SV_Target)
        {
            dst = 0;
            dst.x = frac(src);
            dst.y = frac(-src);
        }
#endif
        0x43425844, 0xd52bc741, 0xda411d9a, 0x199054a2, 0x7461462d, 0x00000001, 0x000000f8, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000080, 0x00000050, 0x00000020,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x0600001a, 0x00102012, 0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x0700001a, 0x00102022,
        0x00000000, 0x8020800a, 0x00000041, 0x00000000, 0x00000000, 0x08000036, 0x001020c2, 0x00000000,
        0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0100003e,
    };
    static struct named_shader ps_frc = {"frc", ps_frc_code, sizeof(ps_frc_code)};
    static const DWORD ps_exp_code[] =
    {
#if 0
        float src;

        void main(out float4 dst : SV_Target)
        {
            dst = 0;
            dst.x = exp2(src);
        }
#endif
        0x43425844, 0xa742b300, 0x10b64393, 0x7827fc4a, 0x328b8db5, 0x00000001, 0x000000dc, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000064, 0x00000050, 0x00000019,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x06000019, 0x00102012, 0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x08000036, 0x001020e2,
        0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0100003e,
    };
    static struct named_shader ps_exp = {"exp", ps_exp_code, sizeof(ps_exp_code)};
    static const DWORD ps_log_code[] =
    {
#if 0
        float src;

        void main(out float4 dst : SV_Target)
        {
            dst = 0;
            dst.x = log2(src);
        }
#endif
        0x43425844, 0x2f1cc195, 0x6cc7d061, 0xe63df3b1, 0x9c68b968, 0x00000001, 0x000000dc, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000064, 0x00000050, 0x00000019,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x0600002f, 0x00102012, 0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x08000036, 0x001020e2,
        0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0100003e,
    };
    static struct named_shader ps_log = {"log", ps_log_code, sizeof(ps_log_code)};
    static const DWORD ps_rcp_code[] =
    {
#if 0
        float4 src;

        void main(out float4 dst : SV_Target)
        {
            dst = 0;
            dst.x = rcp(src.x);
        }
#endif
        0x43425844, 0x3b0ab43e, 0xff4dcb50, 0x22531bf6, 0xe44bbc8c, 0x00000001, 0x000000dc, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000064, 0x00000050, 0x00000019,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x06000081, 0x00102012, 0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x08000036, 0x001020e2,
        0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0100003e,
    };
    static struct named_shader ps_rcp = {"rcp", ps_rcp_code, sizeof(ps_rcp_code)};
    static const DWORD ps_rcp_vector_code[] =
    {
#if 0
        float4 src;

        void main(out float4 dst : SV_Target)
        {
            dst.xyzw = rcp(src.xyzw);
        }
#endif
        0x43425844, 0x4952e20e, 0x859b9f18, 0x7a31907a, 0x3f1cc4af, 0x00000001, 0x000000bc, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000044, 0x00000050, 0x00000011,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x06000081, 0x001020f2, 0x00000000, 0x00208e46, 0x00000000, 0x00000000, 0x0100003e,
    };
    static struct named_shader ps_rcp_vector = {"rcp_vector", ps_rcp_vector_code, sizeof(ps_rcp_vector_code)};
    static const DWORD ps_sincos_code[] =
    {
#if 0
        float2 src0;

        void main(out float4 dst : SV_Target)
        {
            sincos(src0, dst.xy, dst.zw);
        }
#endif
        0x43425844, 0xb47a22ec, 0xdb165106, 0xeee971d7, 0x8836fcc0, 0x00000001, 0x000000dc, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000064, 0x00000050, 0x00000019,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x0700004d, 0x00102032, 0x00000000, 0x0000d000, 0x00208046, 0x00000000, 0x00000000, 0x0700004d,
        0x0000d000, 0x001020c2, 0x00000000, 0x00208406, 0x00000000, 0x00000000, 0x0100003e,
    };
    static struct named_shader ps_sincos = {"sincos", ps_sincos_code, sizeof(ps_sincos_code)};
    static const DWORD ps_indexable_temp_code[] =
    {
#if 0
        float index;

        float4 main() : SV_Target
        {
            float4 colors[] =
            {
                float4(1.0f, 0.0f, 0.0f, 1.0f),
                float4(0.0f, 1.0f, 0.0f, 1.0f),
                float4(0.0f, 0.0f, 1.0f, 1.0f),
            };
            return colors[index];
        }
#endif
        0x43425844, 0x82c65bbb, 0x5b713473, 0xa16ebe60, 0xdcc329be, 0x00000001, 0x00000170, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x000000f8, 0x00000050, 0x0000003e,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x02000068, 0x00000001, 0x04000069, 0x00000000, 0x00000003, 0x00000004, 0x09000036, 0x00203072,
        0x00000000, 0x00000000, 0x00004002, 0x3f800000, 0x00000000, 0x00000000, 0x00000000, 0x09000036,
        0x00203072, 0x00000000, 0x00000001, 0x00004002, 0x00000000, 0x3f800000, 0x00000000, 0x00000000,
        0x09000036, 0x00203072, 0x00000000, 0x00000002, 0x00004002, 0x00000000, 0x00000000, 0x3f800000,
        0x00000000, 0x0600001c, 0x00100012, 0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x07000036,
        0x00102072, 0x00000000, 0x04203246, 0x00000000, 0x0010000a, 0x00000000, 0x05000036, 0x00102082,
        0x00000000, 0x00004001, 0x3f800000, 0x0100003e,
    };
    static struct named_shader ps_indexable_temp = {"indexable_temp", ps_indexable_temp_code, sizeof(ps_indexable_temp_code)};
    static const DWORD ps_indexable_temp2_code[] =
    {
#if 0
        float index;

        float4 main() : SV_Target
        {
            uint remap[] = {0, 1, 2, 2, 1, 0, 1, 1, 2, 2};
            float4 colors[] =
            {
                float4(1.0f, 0.0f, 0.0f, 1.0f),
                float4(0.0f, 1.0f, 0.0f, 1.0f),
                float4(0.0f, 0.0f, 1.0f, 1.0f),
            };
            return colors[remap[index]];
        }
#endif
        0x43425844, 0xcacc5b8f, 0x19bb905e, 0x6af8eae1, 0x80654684, 0x00000001, 0x0000028c, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000214, 0x00000050, 0x00000085,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x02000068, 0x00000001, 0x04000069, 0x00000000, 0x0000000a, 0x00000004, 0x04000069, 0x00000001,
        0x00000003, 0x00000004, 0x06000036, 0x00203012, 0x00000000, 0x00000000, 0x00004001, 0x00000000,
        0x06000036, 0x00203012, 0x00000000, 0x00000001, 0x00004001, 0x00000001, 0x06000036, 0x00203012,
        0x00000000, 0x00000002, 0x00004001, 0x00000002, 0x06000036, 0x00203012, 0x00000000, 0x00000003,
        0x00004001, 0x00000002, 0x06000036, 0x00203012, 0x00000000, 0x00000004, 0x00004001, 0x00000001,
        0x06000036, 0x00203012, 0x00000000, 0x00000005, 0x00004001, 0x00000000, 0x06000036, 0x00203012,
        0x00000000, 0x00000006, 0x00004001, 0x00000001, 0x06000036, 0x00203012, 0x00000000, 0x00000007,
        0x00004001, 0x00000001, 0x06000036, 0x00203012, 0x00000000, 0x00000008, 0x00004001, 0x00000002,
        0x06000036, 0x00203012, 0x00000000, 0x00000009, 0x00004001, 0x00000002, 0x09000036, 0x00203072,
        0x00000001, 0x00000000, 0x00004002, 0x3f800000, 0x00000000, 0x00000000, 0x00000000, 0x09000036,
        0x00203072, 0x00000001, 0x00000001, 0x00004002, 0x00000000, 0x3f800000, 0x00000000, 0x00000000,
        0x09000036, 0x00203072, 0x00000001, 0x00000002, 0x00004002, 0x00000000, 0x00000000, 0x3f800000,
        0x00000000, 0x0600001c, 0x00100012, 0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x07000036,
        0x00100012, 0x00000000, 0x0420300a, 0x00000000, 0x0010000a, 0x00000000, 0x07000036, 0x00102072,
        0x00000000, 0x04203246, 0x00000001, 0x0010000a, 0x00000000, 0x05000036, 0x00102082, 0x00000000,
        0x00004001, 0x3f800000, 0x0100003e,
    };
    static struct named_shader ps_indexable_temp2 = {"indexable_temp2", ps_indexable_temp2_code, sizeof(ps_indexable_temp2_code)};
    static const DWORD ps_bfi_code[] =
    {
#if 0
        uint bits, offset, insert, base;

        uint4 main() : SV_Target
        {
            uint mask = ((1 << bits) - 1) << offset;
            return ((insert << offset) & mask) | (base & ~mask);
        }
#endif
        0x43425844, 0xbe9af688, 0xf5caec6f, 0x63ed2522, 0x5f91f209, 0x00000001, 0x000000e0, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000068, 0x00000050, 0x0000001a,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x0f00008c, 0x001020f2, 0x00000000, 0x00208006, 0x00000000, 0x00000000, 0x00208556, 0x00000000,
        0x00000000, 0x00208aa6, 0x00000000, 0x00000000, 0x00208ff6, 0x00000000, 0x00000000, 0x0100003e,
    };
    static struct named_shader ps_bfi = {"bfi", ps_bfi_code, sizeof(ps_bfi_code)};
    static const DWORD ps_ibfe_code[] =
    {
#if 0
        ps_5_0
        dcl_globalFlags refactoringAllowed
        dcl_constantbuffer cb0[1], immediateIndexed
        dcl_output o0.xyzw
        ibfe o0.xyzw, cb0[0].xxxx, cb0[0].yyyy, cb0[0].zzzz
        ret
#endif
        0x43425844, 0x4b2225f7, 0xd0860f66, 0xe38775bb, 0x6d23d1d2, 0x00000001, 0x000000d4, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x0000005c, 0x00000050, 0x00000017,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x0c00008b, 0x001020f2, 0x00000000, 0x00208006, 0x00000000, 0x00000000, 0x00208556, 0x00000000,
        0x00000000, 0x00208aa6, 0x00000000, 0x00000000, 0x0100003e,
    };
    static struct named_shader ps_ibfe = {"ibfe", ps_ibfe_code, sizeof(ps_ibfe_code)};
    static const DWORD ps_ibfe2_code[] =
    {
#if 0
        ps_5_0
        dcl_globalFlags refactoringAllowed
        dcl_constantbuffer cb0[1], immediateIndexed
        dcl_output o0.xyzw
        dcl_temps 1
        mov r0.xyzw, cb0[0].xyzw
        ibfe r0.xyzw, r0.xxxx, r0.yyyy, r0.zzzz
        mov o0.xyzw, r0.xyzw
        ret
#endif
        0x43425844, 0x347a9c0e, 0x3eff39a4, 0x3dd41cc5, 0xff87ec8d, 0x00000001, 0x000000fc, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000084, 0x00000050, 0x00000021,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x02000068, 0x00000001, 0x06000036, 0x001000f2, 0x00000000, 0x00208e46, 0x00000000, 0x00000000,
        0x0900008b, 0x001000f2, 0x00000000, 0x00100006, 0x00000000, 0x00100556, 0x00000000, 0x00100aa6,
        0x00000000, 0x05000036, 0x001020f2, 0x00000000, 0x00100e46, 0x00000000, 0x0100003e,
    };
    static struct named_shader ps_ibfe2 = {"ibfe2", ps_ibfe2_code, sizeof(ps_ibfe2_code)};
    static const DWORD ps_ubfe_code[] =
    {
#if 0
        uint u;

        uint4 main() : SV_Target
        {
            return uint4((u & 0xf0) >> 4, (u & 0x7fffff00) >> 8, (u & 0xfe) >> 1, (u & 0x7fffffff) >> 1);
        }
#endif
        0x43425844, 0xc4ac0509, 0xaea83154, 0xf1fb3b80, 0x4c22e3cc, 0x00000001, 0x000000e4, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x0000006c, 0x00000050, 0x0000001b,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x1000008a, 0x001020f2, 0x00000000, 0x00004002, 0x00000004, 0x00000017, 0x00000007, 0x0000001e,
        0x00004002, 0x00000004, 0x00000008, 0x00000001, 0x00000001, 0x00208006, 0x00000000, 0x00000000,
        0x0100003e,
    };
    static struct named_shader ps_ubfe = {"ubfe", ps_ubfe_code, sizeof(ps_ubfe_code)};
    static const DWORD ps_bfrev_code[] =
    {
#if 0
        uint bits;

        uint4 main() : SV_Target
        {
            return uint4(reversebits(bits), reversebits(reversebits(bits)),
                    reversebits(bits & 0xFFFF), reversebits(bits >> 16));
        }
#endif
        0x43425844, 0x73daef82, 0xe52befa3, 0x8504d5f0, 0xebdb321d, 0x00000001, 0x00000154, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x000000dc, 0x00000050, 0x00000037,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x02000068, 0x00000001, 0x08000001, 0x00100012, 0x00000000, 0x0020800a, 0x00000000, 0x00000000,
        0x00004001, 0x0000ffff, 0x0500008d, 0x00102042, 0x00000000, 0x0010000a, 0x00000000, 0x08000055,
        0x00100012, 0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x00004001, 0x00000010, 0x0500008d,
        0x00102082, 0x00000000, 0x0010000a, 0x00000000, 0x0600008d, 0x00100012, 0x00000000, 0x0020800a,
        0x00000000, 0x00000000, 0x0500008d, 0x00102022, 0x00000000, 0x0010000a, 0x00000000, 0x05000036,
        0x00102012, 0x00000000, 0x0010000a, 0x00000000, 0x0100003e,
    };
    static struct named_shader ps_bfrev = {"bfrev", ps_bfrev_code, sizeof(ps_bfrev_code)};
    static const DWORD ps_bits_code[] =
    {
#if 0
        uint u;
        int i;

        uint4 main() : SV_Target
        {
            return uint4(countbits(u), firstbitlow(u), firstbithigh(u), firstbithigh(i));
        }
#endif
        0x43425844, 0x23fee911, 0x145287d1, 0xea904419, 0x8aa59a6a, 0x00000001, 0x000001b4, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x0000013c, 0x00000050, 0x0000004f,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x02000068, 0x00000001, 0x06000089, 0x00100012, 0x00000000, 0x0020801a, 0x00000000, 0x00000000,
        0x07000020, 0x00100022, 0x00000000, 0x0010000a, 0x00000000, 0x00004001, 0xffffffff, 0x0800001e,
        0x00100012, 0x00000000, 0x00004001, 0x0000001f, 0x8010000a, 0x00000041, 0x00000000, 0x09000037,
        0x00102082, 0x00000000, 0x0010001a, 0x00000000, 0x00004001, 0xffffffff, 0x0010000a, 0x00000000,
        0x06000087, 0x00100012, 0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x0800001e, 0x00100012,
        0x00000000, 0x00004001, 0x0000001f, 0x8010000a, 0x00000041, 0x00000000, 0x0a000037, 0x00102042,
        0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x0010000a, 0x00000000, 0x00004001, 0xffffffff,
        0x06000086, 0x00102012, 0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x06000088, 0x00102022,
        0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x0100003e,
    };
    static struct named_shader ps_bits = {"bits", ps_bits_code, sizeof(ps_bits_code)};
    static const DWORD ps_bits_vector_code[] =
    {
#if 0
        uint2 u;

        uint4 main() : SV_Target
        {
            return uint4(countbits(u), firstbithigh(u));
        }
#endif
        0x43425844, 0x799764f6, 0x7e5d98cf, 0x4dd694ef, 0xa30ee79a, 0x00000001, 0x0000013c, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x000000c4, 0x00000050, 0x00000031,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x02000068, 0x00000001, 0x06000087, 0x00100032, 0x00000000, 0x00208046, 0x00000000, 0x00000000,
        0x0b00001e, 0x00100032, 0x00000000, 0x80100046, 0x00000041, 0x00000000, 0x00004002, 0x0000001f,
        0x0000001f, 0x00000000, 0x00000000, 0x0d000037, 0x001020c2, 0x00000000, 0x00208406, 0x00000000,
        0x00000000, 0x00100406, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff,
        0x06000086, 0x00102032, 0x00000000, 0x00208046, 0x00000000, 0x00000000, 0x0100003e,
    };
    static struct named_shader ps_bits_vector = {"bits_vector", ps_bits_vector_code, sizeof(ps_bits_vector_code)};
    static const DWORD ps_firstbit_raw_code[] =
    {
        /* Without the fixup instructions emitted by fxc. */
#if 0
        ps_5_0
        dcl_globalFlags refactoringAllowed
        dcl_constantBuffer cb0[1], immediateIndexed
        dcl_output o0.xyzw <v4:float>
        firstbit_lo o0.x <v4:uint>, cb0[0].x <v4:uint>
        firstbit_hi o0.y <v4:uint>, cb0[0].x <v4:uint>
        firstbit_shi o0.z <v4:uint>, cb0[0].y <v4:int>
        mov o0.w <v4:float>, l(0) <s:float>
        ret
#endif
        0x43425844, 0xea469de0, 0x78a93361, 0xed27f28b, 0x8c34dd51, 0x00000001, 0x00000100, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000088, 0x00000050, 0x00000022,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x06000088, 0x00102012, 0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x06000087, 0x00102022,
        0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x06000089, 0x00102042, 0x00000000, 0x0020801a,
        0x00000000, 0x00000000, 0x05000036, 0x00102082, 0x00000000, 0x00004001, 0x00000000, 0x0100003e,
    };
    static struct named_shader ps_firstbit_raw = {"firstbit_raw", ps_firstbit_raw_code, sizeof(ps_firstbit_raw_code)};
    static const DWORD ps_ishr_code[] =
    {
#if 0
        int4 src0;
        int4 src1;

        void main(out uint4 dst : SV_Target)
        {
            dst = src0 >> src1;
        }
#endif
        0x43425844, 0x4551d737, 0xd3dcf723, 0xdf387a99, 0xb6d6b00b, 0x00000001, 0x000000c8, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000050, 0x00000050, 0x00000014,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000002, 0x03000065, 0x001020f2, 0x00000000,
        0x0900002a, 0x001020f2, 0x00000000, 0x00208e46, 0x00000000, 0x00000000, 0x00208e46, 0x00000000,
        0x00000001, 0x0100003e,
    };
    static struct named_shader ps_ishr = {"ishr", ps_ishr_code, sizeof(ps_ishr_code)};
    static const DWORD ps_ushr_code[] =
    {
#if 0
        uint4 src0;
        uint4 src1;

        void main(out uint4 dst : SV_Target)
        {
            dst = src0 >> src1;
        }
#endif
        0x43425844, 0x00f49f17, 0xe7933d92, 0xf527d4e6, 0x1fe1c216, 0x00000001, 0x000000c8, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000050, 0x00000050, 0x00000014,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000002, 0x03000065, 0x001020f2, 0x00000000,
        0x09000055, 0x001020f2, 0x00000000, 0x00208e46, 0x00000000, 0x00000000, 0x00208e46, 0x00000000,
        0x00000001, 0x0100003e,
    };
    static struct named_shader ps_ushr = {"ushr", ps_ushr_code, sizeof(ps_ushr_code)};
    static const DWORD ps_ishl_code[] =
    {
#if 0
        uint4 src0;
        uint4 src1;

        void main(out uint4 dst : SV_Target)
        {
            dst = src0 << src1;
        }
#endif
        0x43425844, 0xc88f5e4d, 0x64e1e5c6, 0x70e7173e, 0x960d6691, 0x00000001, 0x000000c8, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000050, 0x00000050, 0x00000014,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000002, 0x03000065, 0x001020f2, 0x00000000,
        0x09000029, 0x001020f2, 0x00000000, 0x00208e46, 0x00000000, 0x00000000, 0x00208e46, 0x00000000,
        0x00000001, 0x0100003e,
    };
    static struct named_shader ps_ishl = {"ishl", ps_ishl_code, sizeof(ps_ishl_code)};
    static const DWORD ps_ishl_const_code[] =
    {
#if 0
        uint4 src;

        void main(out uint4 dst : SV_Target)
        {
            dst = src << uint4(2, 2, 0x22, 0x22);
        }
#endif
        0x43425844, 0x999070b8, 0x623414f1, 0xb1ce04f4, 0x58554022, 0x00000001, 0x000000d0, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000058, 0x00000050, 0x00000016,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x0b000029, 0x001020f2, 0x00000000, 0x00208e46, 0x00000000, 0x00000000, 0x00004002, 0x00000002,
        0x00000002, 0x00000022, 0x00000022, 0x0100003e,
    };
    static struct named_shader ps_ishl_const = {"ishl_const", ps_ishl_const_code, sizeof(ps_ishl_const_code)};
    static const DWORD ps_not_code[] =
    {
#if 0
        uint2 bits;

        uint4 main() : SV_Target
        {
            return uint4(~bits.x, ~(bits.x ^ ~0u), ~bits.y, ~(bits.y ^ ~0u));
        }
#endif
        0x43425844, 0xaed0fd26, 0xf719a878, 0xc832efd6, 0xba03c264, 0x00000001, 0x00000100, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x52444853, 0x00000088, 0x00000040, 0x00000022,
        0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000, 0x02000068,
        0x00000001, 0x0b000057, 0x00100032, 0x00000000, 0x00208046, 0x00000000, 0x00000000, 0x00004002,
        0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x0500003b, 0x001020a2, 0x00000000, 0x00100406,
        0x00000000, 0x0600003b, 0x00102052, 0x00000000, 0x00208106, 0x00000000, 0x00000000, 0x0100003e,
    };
    static struct named_shader ps_not = {"not", ps_not_code, sizeof(ps_not_code)};
    static const DWORD ps_icmp_code[] =
    {
#if 0
        int2 src;

        void main(out uint4 dst : SV_Target)
        {
            dst.x = src.x == src.y ? ~0u : 0;
            dst.y = src.x >= src.y ? ~0u : 0;
            dst.z = src.x < src.y  ? ~0u : 0;
            dst.w = src.x != src.y ? ~0u : 0;
        }
#endif
        0x43425844, 0xa39748f0, 0x39d5c9e4, 0xdf073d48, 0x7946c5c4, 0x00000001, 0x00000134, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x000000bc, 0x00000050, 0x0000002f,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x09000020, 0x00102012, 0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x0020801a, 0x00000000,
        0x00000000, 0x09000021, 0x00102022, 0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x0020801a,
        0x00000000, 0x00000000, 0x09000022, 0x00102042, 0x00000000, 0x0020800a, 0x00000000, 0x00000000,
        0x0020801a, 0x00000000, 0x00000000, 0x09000027, 0x00102082, 0x00000000, 0x0020800a, 0x00000000,
        0x00000000, 0x0020801a, 0x00000000, 0x00000000, 0x0100003e,
    };
    static struct named_shader ps_icmp = {"icmp", ps_icmp_code, sizeof(ps_icmp_code)};
    static const DWORD ps_ucmp_code[] =
    {
#if 0
        uint2 src;

        void main(out uint4 dst : SV_Target)
        {
            dst = 0;
            dst.x = src.x >= src.y ? ~0u : 0;
            dst.y = src.x < src.y  ? ~0u : 0;
        }
#endif
        0x43425844, 0xe083954f, 0xb55bf642, 0xeb2fa36c, 0x60ee1782, 0x00000001, 0x0000010c, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000094, 0x00000050, 0x00000025,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x09000050, 0x00102012, 0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x0020801a, 0x00000000,
        0x00000000, 0x0900004f, 0x00102022, 0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x0020801a,
        0x00000000, 0x00000000, 0x08000036, 0x001020c2, 0x00000000, 0x00004002, 0x00000000, 0x00000000,
        0x00000000, 0x00000000, 0x0100003e,
    };
    static struct named_shader ps_ucmp = {"ucmp", ps_ucmp_code, sizeof(ps_ucmp_code)};
    static const DWORD ps_umin_umax_code[] =
    {
#if 0
        uint2 src;

        void main(out uint4 dst : SV_Target)
        {
            dst.x = min(src.x, src.y);
            dst.y = max(src.x, src.y);
            dst.zw = 0;
        }
#endif
        0x43425844, 0xe705f812, 0xa515c8df, 0xb82066d9, 0xb05c8ad3, 0x00000001, 0x0000010c, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000094, 0x00000050, 0x00000025,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x09000054, 0x00102012, 0x00000000, 0x0020801a, 0x00000000, 0x00000000, 0x0020800a, 0x00000000,
        0x00000000, 0x09000053, 0x00102022, 0x00000000, 0x0020801a, 0x00000000, 0x00000000, 0x0020800a,
        0x00000000, 0x00000000, 0x08000036, 0x001020c2, 0x00000000, 0x00004002, 0x00000000, 0x00000000,
        0x00000000, 0x00000000, 0x0100003e,
    };
    static struct named_shader ps_umin_umax = {"umin_umax", ps_umin_umax_code, sizeof(ps_umin_umax_code)};
    static const DWORD ps_f16tof32_code[] =
    {
#if 0
        uint4 hf;

        uint4 main() : SV_Target
        {
            return f16tof32(hf);
        }
#endif
        0x43425844, 0xc1816e6e, 0x27562d96, 0x56980fa2, 0x421e6640, 0x00000001, 0x000000d8, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000060, 0x00000050, 0x00000018,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x02000068, 0x00000001, 0x06000083, 0x001000f2, 0x00000000, 0x00208e46, 0x00000000, 0x00000000,
        0x0500001c, 0x001020f2, 0x00000000, 0x00100e46, 0x00000000, 0x0100003e,
    };
    static struct named_shader ps_f16tof32 = {"f16tof32", ps_f16tof32_code, sizeof(ps_f16tof32_code)};
    static const DWORD ps_f16tof32_2_code[] =
    {
#if 0
        ps_5_0
        dcl_globalFlags refactoringAllowed
        dcl_constantbuffer cb0[1], immediateIndexed
        dcl_output o0.xyzw
        dcl_temps 1
        mov r0.xyzw, cb0[0].xyzw
        f16tof32 r0.xyzw, r0.wzyx
        ftou o0.xyzw, r0.xyzw
        ret
#endif
        0x43425844, 0x38472f03, 0x2c49b7dd, 0xc2d76bbf, 0xfc093e1d, 0x00000001, 0x000000ec, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000074, 0x00000050, 0x0000001d,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x02000068, 0x00000001, 0x06000036, 0x001000f2, 0x00000000, 0x00208e46, 0x00000000, 0x00000000,
        0x05000083, 0x001000f2, 0x00000000, 0x001001b6, 0x00000000, 0x0500001c, 0x001020f2, 0x00000000,
        0x00100e46, 0x00000000, 0x0100003e,
    };
    static struct named_shader ps_f16tof32_2 = {"f16tof32_2", ps_f16tof32_2_code, sizeof(ps_f16tof32_2_code)};
    static const DWORD ps_f32tof16_code[] =
    {
#if 0
        float4 f;

        uint4 main() : SV_Target
        {
            return f32tof16(f);
        }
#endif
        0x43425844, 0x523a765c, 0x1a5be3a9, 0xaed69c80, 0xd26fe296, 0x00000001, 0x000000bc, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000044, 0x00000050, 0x00000011,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x06000082, 0x001020f2, 0x00000000, 0x00208e46, 0x00000000, 0x00000000, 0x0100003e,
    };
    static struct named_shader ps_f32tof16 = {"f32tof16", ps_f32tof16_code, sizeof(ps_f32tof16_code)};
    static const DWORD ps_f32tof16_2_code[] =
    {
#if 0
        ps_5_0
        dcl_globalFlags refactoringAllowed
        dcl_constantbuffer cb0[1], immediateIndexed
        dcl_output o0.xyzw
        dcl_temps 1
        mov r0.xyzw, cb0[0].xyzw
        f32tof16 r0.xyzw, r0.wzyx
        mov o0.xyzw, r0.xyzw
        ret
#endif
        0x43425844, 0x607c82d2, 0x940cc7c2, 0xe9de23c6, 0x696beb90, 0x00000001, 0x000000ec, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000074, 0x00000050, 0x0000001d,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x02000068, 0x00000001, 0x06000036, 0x001000f2, 0x00000000, 0x00208e46, 0x00000000, 0x00000000,
        0x05000082, 0x001000f2, 0x00000000, 0x001001b6, 0x00000000, 0x05000036, 0x001020f2, 0x00000000,
        0x00100e46, 0x00000000, 0x0100003e,
    };
    static struct named_shader ps_f32tof16_2 = {"f32tof16_2", ps_f32tof16_2_code, sizeof(ps_f32tof16_2_code)};
    static const DWORD ps_imad_code[] =
    {
#if 0
        int4 src0;
        int4 src1;
        int4 src2;

        void main(out uint4 dst : SV_Target)
        {
            dst.xy = src0.xy * src1.xy + src2.xy;
            dst.zw = src0.zw * src1.zw - src2.zw;
        }
#endif
        0x43425844, 0xb6a7735a, 0xc891e560, 0x6df8f267, 0x2753395c, 0x00000001, 0x00000108, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000090, 0x00000050, 0x00000024,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000003, 0x03000065, 0x001020f2, 0x00000000,
        0x0c000023, 0x00102032, 0x00000000, 0x00208046, 0x00000000, 0x00000000, 0x00208046, 0x00000000,
        0x00000001, 0x00208046, 0x00000000, 0x00000002, 0x0d000023, 0x001020c2, 0x00000000, 0x00208ea6,
        0x00000000, 0x00000000, 0x00208ea6, 0x00000000, 0x00000001, 0x80208ea6, 0x00000041, 0x00000000,
        0x00000002, 0x0100003e,
    };
    static struct named_shader ps_imad = {"imad", ps_imad_code, sizeof(ps_imad_code)};
    static const DWORD ps_imul_code[] =
    {
#if 0
        uint4 src0;
        uint4 src1;

        void main(out uint4 dst : SV_Target)
        {
            dst = 0;
            dst.x = src0.x * src1.x;
        }
#endif
        0x43425844, 0x55ebfe14, 0xc9834c14, 0x5f89388a, 0x523be7e0, 0x00000001, 0x000000ec, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000074, 0x00000050, 0x0000001d,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000002, 0x03000065, 0x001020f2, 0x00000000,
        0x0a000026, 0x0000d000, 0x00102012, 0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x0020800a,
        0x00000000, 0x00000001, 0x08000036, 0x001020e2, 0x00000000, 0x00004002, 0x00000000, 0x00000000,
        0x00000000, 0x00000000, 0x0100003e,
    };
    static struct named_shader ps_imul = {"imul", ps_imul_code, sizeof(ps_imul_code)};
    static const DWORD ps_udiv_code[] =
    {
#if 0
        uint4 src0;
        uint4 src1;

        void main(out uint4 dst : SV_Target)
        {
            dst = 0;
            dst.x = src0.x / src1.x;
            dst.y = src0.x % src1.x;
        }
#endif
        0x43425844, 0x007d5f29, 0x042f2e56, 0x212eddf2, 0xc98cca76, 0x00000001, 0x00000120, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x000000a8, 0x00000050, 0x0000002a,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000002, 0x03000065, 0x001020f2, 0x00000000,
        0x02000068, 0x00000002, 0x08000036, 0x001020c2, 0x00000000, 0x00004002, 0x00000000, 0x00000000,
        0x00000000, 0x00000000, 0x0b00004e, 0x00100012, 0x00000000, 0x00100012, 0x00000001, 0x0020800a,
        0x00000000, 0x00000000, 0x0020800a, 0x00000000, 0x00000001, 0x05000036, 0x00102012, 0x00000000,
        0x0010000a, 0x00000000, 0x05000036, 0x00102022, 0x00000000, 0x0010000a, 0x00000001, 0x0100003e,
    };
    static struct named_shader ps_udiv = {"udiv", ps_udiv_code, sizeof(ps_udiv_code)};
    static const DWORD ps_nested_switch_code[] =
    {
#if 0
        uint4 src0;
        uint4 src1;

        uint4 main() : SV_Target
        {
            uint4 dst = 0;

            switch (src0.x)
            {
                case ~0u:
                    dst.x = 1;
                    break;
                case 0:
                case 1:
                case 2:
                    if (src1.x)
                        break;
                    dst.x = 2;
                    break;
                case 3:
                    break;
                case 4:
                    if (src1.x)
                    {
                        switch (src0.y)
                        {
                            case 0:
                            case 1:
                            case 2:
                            case 3:
                                if (src0.z)
                                    dst += src0.z * (uint4)2;
                                else if (src0.w)
                                    return (uint4)255;
                                else
                                    dst.zw = 1;
                                break;
                            default:
                                dst = 1;
                                break;
                        }
                        break;
                    }
                    else
                    {
                        dst = 128;
                        break;
                    }
            }

            return dst;
        }
#endif
        0x43425844, 0x46d465cb, 0x5d7ed52f, 0x3573b153, 0x1691c479, 0x00000001, 0x00000334, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x000002bc, 0x00000050, 0x000000af,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000002, 0x03000065, 0x001020f2, 0x00000000,
        0x02000068, 0x00000001, 0x0400004c, 0x0020800a, 0x00000000, 0x00000000, 0x03000006, 0x00004001,
        0xffffffff, 0x08000036, 0x001000f2, 0x00000000, 0x00004002, 0x00000001, 0x00000000, 0x00000000,
        0x00000000, 0x01000002, 0x03000006, 0x00004001, 0x00000000, 0x03000006, 0x00004001, 0x00000001,
        0x03000006, 0x00004001, 0x00000002, 0x0404001f, 0x0020800a, 0x00000000, 0x00000001, 0x08000036,
        0x001000f2, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x01000002,
        0x01000015, 0x08000036, 0x001000f2, 0x00000000, 0x00004002, 0x00000002, 0x00000000, 0x00000000,
        0x00000000, 0x01000002, 0x03000006, 0x00004001, 0x00000003, 0x08000036, 0x001000f2, 0x00000000,
        0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x01000002, 0x03000006, 0x00004001,
        0x00000004, 0x0404001f, 0x0020800a, 0x00000000, 0x00000001, 0x0400004c, 0x0020801a, 0x00000000,
        0x00000000, 0x03000006, 0x00004001, 0x00000000, 0x03000006, 0x00004001, 0x00000001, 0x03000006,
        0x00004001, 0x00000002, 0x03000006, 0x00004001, 0x00000003, 0x0404001f, 0x0020802a, 0x00000000,
        0x00000000, 0x0b000029, 0x001000f2, 0x00000000, 0x00208aa6, 0x00000000, 0x00000000, 0x00004002,
        0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x01000012, 0x0404001f, 0x0020803a, 0x00000000,
        0x00000000, 0x08000036, 0x001020f2, 0x00000000, 0x00004002, 0x000000ff, 0x000000ff, 0x000000ff,
        0x000000ff, 0x0100003e, 0x01000015, 0x08000036, 0x001000f2, 0x00000000, 0x00004002, 0x00000000,
        0x00000000, 0x00000001, 0x00000001, 0x01000015, 0x01000002, 0x0100000a, 0x08000036, 0x001000f2,
        0x00000000, 0x00004002, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x01000002, 0x01000017,
        0x01000002, 0x01000012, 0x08000036, 0x001000f2, 0x00000000, 0x00004002, 0x00000080, 0x00000080,
        0x00000080, 0x00000080, 0x01000002, 0x01000015, 0x0100000a, 0x08000036, 0x001000f2, 0x00000000,
        0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x01000002, 0x01000017, 0x05000036,
        0x001020f2, 0x00000000, 0x00100e46, 0x00000000, 0x0100003e,
    };
    static struct named_shader ps_nested_switch = {"nested_switch", ps_nested_switch_code, sizeof(ps_nested_switch_code)};
    static const DWORD ps_switch_no_default_code[] =
    {
#if 0
        ps_5_0
        dcl_globalFlags refactoringAllowed
        dcl_constantbuffer CB0[1], immediateIndexed
        dcl_output o0.xyzw
        switch cb0[0].x
            case l(0)
            mov o0.xyzw, l(1,1,1,1)
            ret
            case l(3)
            mov o0.xyzw, l(2,2,2,2)
            ret
        endswitch
        nop
        nop
        mov o0.xyzw, l(3,3,3,3)
        ret
#endif
        0x43425844, 0x97459226, 0x57ed7614, 0xcda58342, 0xbdf6a9dd, 0x00000001, 0x00000140, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x000000c8, 0x00000050, 0x00000032,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x0400004c, 0x0020800a, 0x00000000, 0x00000000, 0x03000006, 0x00004001, 0x00000000, 0x08000036,
        0x001020f2, 0x00000000, 0x00004002, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x0100003e,
        0x03000006, 0x00004001, 0x00000003, 0x08000036, 0x001020f2, 0x00000000, 0x00004002, 0x00000002,
        0x00000002, 0x00000002, 0x00000002, 0x0100003e, 0x01000017, 0x0100003a, 0x0100003a, 0x08000036,
        0x001020f2, 0x00000000, 0x00004002, 0x00000003, 0x00000003, 0x00000003, 0x00000003, 0x0100003e,
    };
    static const struct named_shader ps_switch_no_default
            = {"switch_no_default", ps_switch_no_default_code, sizeof(ps_switch_no_default_code)};
    static const DWORD ps_movc_code[] =
    {
#if 0
        ps_5_0
        dcl_globalFlags refactoringAllowed
        dcl_constantbuffer cb0[3], immediateIndexed
        dcl_output o0.xyzw
        dcl_temps 1
        mov r0.xyzw, cb0[0].xyzw
        movc r0.xyzw, r0.xyzw, cb0[1].xyzw, cb0[2].xyzw
        mov o0.xyzw, r0.xyzw
        ret
#endif
        0x43425844, 0x59a5be58, 0x260c36c0, 0x7eadcff2, 0x947f4e9d, 0x00000001, 0x00000104, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x0000008c, 0x00000050, 0x00000023,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000003, 0x03000065, 0x001020f2, 0x00000000,
        0x02000068, 0x00000001, 0x06000036, 0x001000f2, 0x00000000, 0x00208e46, 0x00000000, 0x00000000,
        0x0b000037, 0x001000f2, 0x00000000, 0x00100e46, 0x00000000, 0x00208e46, 0x00000000, 0x00000001,
        0x00208e46, 0x00000000, 0x00000002, 0x05000036, 0x001020f2, 0x00000000, 0x00100e46, 0x00000000,
        0x0100003e,
    };
    static struct named_shader ps_movc = {"movc", ps_movc_code, sizeof(ps_movc_code)};
    static const DWORD ps_swapc0_code[] =
    {
#if 0
        ps_5_0
        dcl_globalFlags refactoringAllowed
        dcl_constantbuffer cb0[3], immediateIndexed
        dcl_output o0.xyzw
        dcl_temps 2
        swapc r0.xyzw, r1.xyzw, cb0[0].xyzw, cb0[1].xyzw, cb0[2].xyzw
        mov o0.xyzw, r0.xyzw
        ret
#endif
        0x43425844, 0x9e089246, 0x9f8b5cbe, 0xbac66faf, 0xaef23488, 0x00000001, 0x000000f8, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000080, 0x00000050, 0x00000020,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000003, 0x03000065, 0x001020f2, 0x00000000,
        0x02000068, 0x00000002, 0x0e00008e, 0x001000f2, 0x00000000, 0x001000f2, 0x00000001, 0x00208e46,
        0x00000000, 0x00000000, 0x00208e46, 0x00000000, 0x00000001, 0x00208e46, 0x00000000, 0x00000002,
        0x05000036, 0x001020f2, 0x00000000, 0x00100e46, 0x00000000, 0x0100003e,
    };
    static struct named_shader ps_swapc0 = {"swapc0", ps_swapc0_code, sizeof(ps_swapc0_code)};
    static const DWORD ps_swapc1_code[] =
    {
#if 0
        ps_5_0
        dcl_globalFlags refactoringAllowed
        dcl_constantbuffer cb0[3], immediateIndexed
        dcl_output o0.xyzw
        dcl_temps 2
        swapc r0.xyzw, r1.xyzw, cb0[0].xyzw, cb0[1].xyzw, cb0[2].xyzw
        mov o0.xyzw, r1.xyzw
        ret
#endif
        0x43425844, 0xf2daed61, 0xede211f7, 0x7300dbea, 0x573ed49f, 0x00000001, 0x000000f8, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000080, 0x00000050, 0x00000020,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000003, 0x03000065, 0x001020f2, 0x00000000,
        0x02000068, 0x00000002, 0x0e00008e, 0x001000f2, 0x00000000, 0x001000f2, 0x00000001, 0x00208e46,
        0x00000000, 0x00000000, 0x00208e46, 0x00000000, 0x00000001, 0x00208e46, 0x00000000, 0x00000002,
        0x05000036, 0x001020f2, 0x00000000, 0x00100e46, 0x00000001, 0x0100003e,
    };
    static struct named_shader ps_swapc1 = {"swapc1", ps_swapc1_code, sizeof(ps_swapc1_code)};
    static const DWORD ps_swapc2_code[] =
    {
#if 0
        ps_5_0
        dcl_globalFlags refactoringAllowed
        dcl_constantbuffer cb0[3], immediateIndexed
        dcl_output o0.xyzw
        dcl_temps 2
        mov r0.xyzw, cb0[1].xyzw
        mov r1.xyzw, cb0[2].xyzw
        swapc r0.xyzw, r1.xyzw, cb0[0].xyzw, r0.xyzw, r1.xyzw
        mov o0.xyzw, r0.xyzw
        ret
#endif
        0x43425844, 0x230fcb22, 0x70d99148, 0x65814d89, 0x97473498, 0x00000001, 0x00000120, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x000000a8, 0x00000050, 0x0000002a,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000003, 0x03000065, 0x001020f2, 0x00000000,
        0x02000068, 0x00000002, 0x06000036, 0x001000f2, 0x00000000, 0x00208e46, 0x00000000, 0x00000001,
        0x06000036, 0x001000f2, 0x00000001, 0x00208e46, 0x00000000, 0x00000002, 0x0c00008e, 0x001000f2,
        0x00000000, 0x001000f2, 0x00000001, 0x00208e46, 0x00000000, 0x00000000, 0x00100e46, 0x00000000,
        0x00100e46, 0x00000001, 0x05000036, 0x001020f2, 0x00000000, 0x00100e46, 0x00000000, 0x0100003e,
    };
    static struct named_shader ps_swapc2 = {"swapc2", ps_swapc2_code, sizeof(ps_swapc2_code)};
    static const DWORD ps_swapc3_code[] =
    {
#if 0
        ps_5_0
        dcl_globalFlags refactoringAllowed
        dcl_constantbuffer cb0[3], immediateIndexed
        dcl_output o0.xyzw
        dcl_temps 2
        mov r0.xyzw, cb0[1].xyzw
        mov r1.xyzw, cb0[2].xyzw
        swapc r0.xyzw, r1.xyzw, cb0[0].xyzw, r0.xyzw, r1.xyzw
        mov o0.xyzw, r1.xyzw
        ret
#endif
        0x43425844, 0xce595d62, 0x98305541, 0xb04e74c8, 0xfc010f3a, 0x00000001, 0x00000120, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x000000a8, 0x00000050, 0x0000002a,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000003, 0x03000065, 0x001020f2, 0x00000000,
        0x02000068, 0x00000002, 0x06000036, 0x001000f2, 0x00000000, 0x00208e46, 0x00000000, 0x00000001,
        0x06000036, 0x001000f2, 0x00000001, 0x00208e46, 0x00000000, 0x00000002, 0x0c00008e, 0x001000f2,
        0x00000000, 0x001000f2, 0x00000001, 0x00208e46, 0x00000000, 0x00000000, 0x00100e46, 0x00000000,
        0x00100e46, 0x00000001, 0x05000036, 0x001020f2, 0x00000000, 0x00100e46, 0x00000001, 0x0100003e,
    };
    static struct named_shader ps_swapc3 = {"swapc3", ps_swapc3_code, sizeof(ps_swapc3_code)};
    static const DWORD ps_swapc4_code[] =
    {
#if 0
        ps_5_0
        dcl_globalFlags refactoringAllowed
        dcl_constantbuffer cb0[3], immediateIndexed
        dcl_output o0.xyzw
        dcl_temps 2
        mov r0.xyzw, cb0[0].xyzw
        swapc r0.xyzw, r1.xyzw, r0.xyzw, cb0[1].xyzw, cb0[2].xyzw
        mov o0.xyzw, r0.xyzw
        ret
#endif
        0x43425844, 0x72067c48, 0xb53572a0, 0x9dd9e0fd, 0x903e37e3, 0x00000001, 0x0000010c, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000094, 0x00000050, 0x00000025,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000003, 0x03000065, 0x001020f2, 0x00000000,
        0x02000068, 0x00000002, 0x06000036, 0x001000f2, 0x00000000, 0x00208e46, 0x00000000, 0x00000000,
        0x0d00008e, 0x001000f2, 0x00000000, 0x001000f2, 0x00000001, 0x00100e46, 0x00000000, 0x00208e46,
        0x00000000, 0x00000001, 0x00208e46, 0x00000000, 0x00000002, 0x05000036, 0x001020f2, 0x00000000,
        0x00100e46, 0x00000000, 0x0100003e,
    };
    static struct named_shader ps_swapc4 = {"swapc4", ps_swapc4_code, sizeof(ps_swapc4_code)};
    static const DWORD ps_swapc5_code[] =
    {
#if 0
        ps_5_0
        dcl_globalFlags refactoringAllowed
        dcl_constantbuffer cb0[3], immediateIndexed
        dcl_output o0.xyzw
        dcl_temps 2
        mov r1.xyzw, cb0[0].xyzw
        swapc r0.xyzw, r1.xyzw, r1.xyzw, cb0[1].xyzw, cb0[2].xyzw
        mov o0.xyzw, r1.xyzw
        ret
#endif
        0x43425844, 0x7078fb08, 0xdd24cd44, 0x469d3258, 0x9e33a0bc, 0x00000001, 0x0000010c, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000094, 0x00000050, 0x00000025,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000003, 0x03000065, 0x001020f2, 0x00000000,
        0x02000068, 0x00000002, 0x06000036, 0x001000f2, 0x00000001, 0x00208e46, 0x00000000, 0x00000000,
        0x0d00008e, 0x001000f2, 0x00000000, 0x001000f2, 0x00000001, 0x00100e46, 0x00000001, 0x00208e46,
        0x00000000, 0x00000001, 0x00208e46, 0x00000000, 0x00000002, 0x05000036, 0x001020f2, 0x00000000,
        0x00100e46, 0x00000001, 0x0100003e,
    };
    static struct named_shader ps_swapc5 = {"swapc5", ps_swapc5_code, sizeof(ps_swapc5_code)};
    static const DWORD ps_dmov_code[] =
    {
#if 0
        double2 src0;

        void main(out uint4 dst : SV_Target)
        {
            asuint(-src0.y, dst.x, dst.y);
            asuint(-src0.x, dst.z, dst.w);
        }
#endif
        0x43425844, 0x16bd7e63, 0x6b535ab7, 0xb7f182c2, 0x6f3819a8, 0x00000001, 0x000000f0, 0x00000004,
        0x00000030, 0x00000040, 0x00000074, 0x000000e0, 0x4e475349, 0x00000008, 0x00000000, 0x00000008,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000064, 0x00000050,
        0x00000019, 0x0100186a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2,
        0x00000000, 0x02000068, 0x00000001, 0x070000c7, 0x001000f2, 0x00000000, 0x802084e6, 0x00000041,
        0x00000000, 0x00000000, 0x05000036, 0x001020f2, 0x00000000, 0x00100e46, 0x00000000, 0x0100003e,
        0x30494653, 0x00000008, 0x00000001, 0x00000000,
    };
    static struct named_shader ps_dmov = {"dmov", ps_dmov_code, sizeof(ps_dmov_code)};
    static const DWORD ps_dadd_code[] =
    {
        /* Also test constant double2 vector. */
#if 0
        double src0;

        void main(out uint4 dst : SV_Target)
        {
            double2 a = double2(src0 + 1.0000002433080226l, src0 + 2.000000481493771l);
            asuint(a.x, dst.x, dst.y);
            asuint(a.y, dst.z, dst.w);
        }
#endif
        0x43425844, 0x23e20252, 0xf4d2708e, 0x87956944, 0xc61e7052, 0x00000001, 0x00000100, 0x00000004,
        0x00000030, 0x00000040, 0x00000074, 0x000000f0, 0x4e475349, 0x00000008, 0x00000000, 0x00000008,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000074, 0x00000050,
        0x0000001d, 0x0100186a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2,
        0x00000000, 0x02000068, 0x00000001, 0x0b0000bf, 0x001000f2, 0x00000000, 0x00208446, 0x00000000,
        0x00000000, 0x00005002, 0x41500000, 0x3ff00000, 0x40a00000, 0x40000000, 0x05000036, 0x001020f2,
        0x00000000, 0x00100e46, 0x00000000, 0x0100003e, 0x30494653, 0x00000008, 0x00000001, 0x00000000,
    };
    static struct named_shader ps_dadd = {"dadd", ps_dadd_code, sizeof(ps_dadd_code)};
    static const DWORD ps_dmin_dmax_code[] =
    {
#if 0
        double2 src0;

        void main(out uint4 dst : SV_Target)
        {
            asuint(min(src0.x, src0.y), dst.x, dst.y);
            asuint(max(src0.x, src0.y), dst.z, dst.w);
        }
#endif
        0x43425844, 0x6f8b547e, 0x3552757c, 0x92a81fa1, 0x00990b21, 0x00000001, 0x00000130, 0x00000004,
        0x00000030, 0x00000040, 0x00000074, 0x00000120, 0x4e475349, 0x00000008, 0x00000000, 0x00000008,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x000000a4, 0x00000050,
        0x00000029, 0x0100186a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2,
        0x00000000, 0x02000068, 0x00000001, 0x090000c1, 0x00100032, 0x00000000, 0x00208ee6, 0x00000000,
        0x00000000, 0x00208446, 0x00000000, 0x00000000, 0x05000036, 0x00102032, 0x00000000, 0x00100046,
        0x00000000, 0x090000c0, 0x00100032, 0x00000000, 0x00208ee6, 0x00000000, 0x00000000, 0x00208446,
        0x00000000, 0x00000000, 0x05000036, 0x001020c2, 0x00000000, 0x00100406, 0x00000000, 0x0100003e,
        0x30494653, 0x00000008, 0x00000001, 0x00000000,
    };
    static struct named_shader ps_dmin_dmax = {"dmin_dmax", ps_dmin_dmax_code, sizeof(ps_dmin_dmax_code)};
    static const DWORD ps_dfma_code[] =
    {
#if 0
        double2 src0;

        void main(out uint4 dst : SV_Target)
        {
            asuint(fma(src0.x, src0.y, 1.0000002433080226l), dst.x, dst.y);
            dst.zw = 0;
        }
#endif
        0x43425844, 0xde5b55c7, 0x2660b48d, 0x52b1761e, 0x50319c4d, 0x00000001, 0x0000012c, 0x00000004,
        0x00000030, 0x00000040, 0x00000074, 0x0000011c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x000000a0, 0x00000050,
        0x00000028, 0x0102186a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2,
        0x00000000, 0x02000068, 0x00000001, 0x0e0000d3, 0x00100032, 0x00000000, 0x00208446, 0x00000000,
        0x00000000, 0x00208ee6, 0x00000000, 0x00000000, 0x00005002, 0x41500000, 0x3ff00000, 0x00000000,
        0x00000000, 0x05000036, 0x00102032, 0x00000000, 0x00100046, 0x00000000, 0x08000036, 0x001020c2,
        0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0100003e, 0x30494653,
        0x00000008, 0x00000021, 0x00000000,
    };
    static struct named_shader ps_dfma = {"dfma", ps_dfma_code, sizeof(ps_dfma_code)};
    static const DWORD ps_dmovc_code[] =
    {
#if 0
        double2 src0;

        void main(out uint4 dst : SV_Target)
        {
            double2 a = src0.x > 1.0 ? src0 : double2(4.5, 4.5);
            asuint(a.x, dst.x, dst.y);
            asuint(a.y, dst.z, dst.w);
        }
#endif
        0x43425844, 0xe3bae03a, 0x16178729, 0xc59e7f4a, 0x218fecac, 0x00000001, 0x00000134, 0x00000004,
        0x00000030, 0x00000040, 0x00000074, 0x00000124, 0x4e475349, 0x00000008, 0x00000000, 0x00000008,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x000000a8, 0x00000050,
        0x0000002a, 0x0100186a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2,
        0x00000000, 0x02000068, 0x00000001, 0x0b0000c5, 0x00100012, 0x00000000, 0x00005002, 0x00000000,
        0x3ff00000, 0x00000000, 0x00000000, 0x00208446, 0x00000000, 0x00000000, 0x0d0000c8, 0x001000f2,
        0x00000000, 0x00100006, 0x00000000, 0x00208e46, 0x00000000, 0x00000000, 0x00005002, 0x00000000,
        0x40120000, 0x00000000, 0x40120000, 0x05000036, 0x001020f2, 0x00000000, 0x00100e46, 0x00000000,
        0x0100003e, 0x30494653, 0x00000008, 0x00000001, 0x00000000,
    };
    static struct named_shader ps_dmovc = {"dmovc", ps_dmovc_code, sizeof(ps_dmovc_code)};
    static const DWORD ps_dmodifier_code[] =
    {
        /* Already tested negation in the dmov test. */
#if 0
        double src0;

        void main(out uint4 dst : SV_Target)
        {
            asuint(abs(src0), dst.x, dst.y);
            asuint(saturate(src0) + 1.5, dst.z, dst.w);
        }
#endif
        0x43425844, 0x15bb537e, 0x47ef3ae3, 0xba88acf7, 0x0b3624e0, 0x00000001, 0x00000144, 0x00000004,
        0x00000030, 0x00000040, 0x00000074, 0x00000134, 0x4e475349, 0x00000008, 0x00000000, 0x00000008,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x000000b8, 0x00000050,
        0x0000002e, 0x0100186a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2,
        0x00000000, 0x02000068, 0x00000001, 0x060020c7, 0x00100032, 0x00000000, 0x00208446, 0x00000000,
        0x00000000, 0x0a0000bf, 0x00100032, 0x00000000, 0x00100446, 0x00000000, 0x00005002, 0x00000000,
        0x3ff80000, 0x00000000, 0x00000000, 0x05000036, 0x001020c2, 0x00000000, 0x00100406, 0x00000000,
        0x070000c7, 0x00100032, 0x00000000, 0x80208446, 0x00000081, 0x00000000, 0x00000000, 0x05000036,
        0x00102032, 0x00000000, 0x00100046, 0x00000000, 0x0100003e, 0x30494653, 0x00000008, 0x00000001,
        0x00000000,
    };
    static struct named_shader ps_dmodifier = {"dmodifier", ps_dmodifier_code, sizeof(ps_dmodifier_code)};
    static const DWORD ps_deq_code[] =
    {
#if 0
        double2 src0;

        void main(out uint4 dst : SV_Target)
        {
            dst = (uint4)0;
            if (src0.x == src0.y)
                dst.x = 0xffffffff;
        }
#endif
        0x43425844, 0x24c9cee6, 0xd38c521f, 0xcde7c8b6, 0xc173a8e3, 0x00000001, 0x00000118, 0x00000004,
        0x00000030, 0x00000040, 0x00000074, 0x00000108, 0x4e475349, 0x00000008, 0x00000000, 0x00000008,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x0000008c, 0x00000050,
        0x00000023, 0x0100186a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2,
        0x00000000, 0x02000068, 0x00000001, 0x090000c3, 0x00100012, 0x00000000, 0x00208ee6, 0x00000000,
        0x00000000, 0x00208446, 0x00000000, 0x00000000, 0x05000036, 0x00102012, 0x00000000, 0x0010000a,
        0x00000000, 0x08000036, 0x001020e2, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000,
        0x00000000, 0x0100003e, 0x30494653, 0x00000008, 0x00000001, 0x00000000,
    };
    static struct named_shader ps_deq = {"deq", ps_deq_code, sizeof(ps_deq_code)};
    static const DWORD ps_dne_code[] =
    {
#if 0
        double2 src0;

        void main(out uint4 dst : SV_Target)
        {
            dst = (uint4)0;
            if (src0.x != src0.y)
                dst.x = 0xffffffff;
        }
#endif
        0x43425844, 0x99700929, 0x3b743000, 0xdfc89958, 0xfc2b89ab, 0x00000001, 0x00000118, 0x00000004,
        0x00000030, 0x00000040, 0x00000074, 0x00000108, 0x4e475349, 0x00000008, 0x00000000, 0x00000008,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x0000008c, 0x00000050,
        0x00000023, 0x0100186a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2,
        0x00000000, 0x02000068, 0x00000001, 0x090000c6, 0x00100012, 0x00000000, 0x00208ee6, 0x00000000,
        0x00000000, 0x00208446, 0x00000000, 0x00000000, 0x05000036, 0x00102012, 0x00000000, 0x0010000a,
        0x00000000, 0x08000036, 0x001020e2, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000,
        0x00000000, 0x0100003e, 0x30494653, 0x00000008, 0x00000001, 0x00000000,
    };
    static struct named_shader ps_dne = {"dne", ps_dne_code, sizeof(ps_dne_code)};
    static const DWORD ps_dge_code[] =
    {
#if 0
        double2 src0;

        void main(out uint4 dst : SV_Target)
        {
            dst = (uint4)0;
            if (src0.x >= src0.y)
                dst.x = 0xffffffff;
        }
#endif
        0x43425844, 0xad9e15a9, 0x80ed9af5, 0x039e7dbd, 0xac8b1634, 0x00000001, 0x00000118, 0x00000004,
        0x00000030, 0x00000040, 0x00000074, 0x00000108, 0x4e475349, 0x00000008, 0x00000000, 0x00000008,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x0000008c, 0x00000050,
        0x00000023, 0x0100186a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2,
        0x00000000, 0x02000068, 0x00000001, 0x090000c4, 0x00100012, 0x00000000, 0x00208446, 0x00000000,
        0x00000000, 0x00208ee6, 0x00000000, 0x00000000, 0x05000036, 0x00102012, 0x00000000, 0x0010000a,
        0x00000000, 0x08000036, 0x001020e2, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000,
        0x00000000, 0x0100003e, 0x30494653, 0x00000008, 0x00000001, 0x00000000,
    };
    static struct named_shader ps_dge = {"dge", ps_dge_code, sizeof(ps_dge_code)};
    static const DWORD ps_dlt_code[] =
    {
#if 0
        double2 src0;

        void main(out uint4 dst : SV_Target)
        {
            dst = (uint4)0;
            if (src0.x < src0.y)
                dst.x = 0xffffffff;
        }
#endif
        0x43425844, 0xc42b9ef5, 0xd24453bd, 0xc6bc7933, 0x1ba2cbcd, 0x00000001, 0x00000118, 0x00000004,
        0x00000030, 0x00000040, 0x00000074, 0x00000108, 0x4e475349, 0x00000008, 0x00000000, 0x00000008,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x0000008c, 0x00000050,
        0x00000023, 0x0100186a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2,
        0x00000000, 0x02000068, 0x00000001, 0x090000c5, 0x00100012, 0x00000000, 0x00208446, 0x00000000,
        0x00000000, 0x00208ee6, 0x00000000, 0x00000000, 0x05000036, 0x00102012, 0x00000000, 0x0010000a,
        0x00000000, 0x08000036, 0x001020e2, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000,
        0x00000000, 0x0100003e, 0x30494653, 0x00000008, 0x00000001, 0x00000000,
    };
    static struct named_shader ps_dlt = {"dlt", ps_dlt_code, sizeof(ps_dlt_code)};
    static const DWORD ps_dtou_code[] =
    {
#if 0
        double2 src0, src1;

        void main(out uint4 dst : SV_Target)
        {
            dst.xz = src0.yx;
            dst.yw = -src1;
        }
#endif
        0x43425844, 0xfaba33b8, 0x1e15cf47, 0xcd89f03c, 0xdf2aef62, 0x00000001, 0x000000ec, 0x00000004,
        0x00000030, 0x00000040, 0x00000074, 0x000000dc, 0x4e475349, 0x00000008, 0x00000000, 0x00000008,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000060, 0x00000050,
        0x00000018, 0x0102186a, 0x04000059, 0x00208e46, 0x00000000, 0x00000002, 0x03000065, 0x001020f2,
        0x00000000, 0x060000d7, 0x00102052, 0x00000000, 0x002084e6, 0x00000000, 0x00000000, 0x070000d7,
        0x001020a2, 0x00000000, 0x80208e46, 0x00000041, 0x00000000, 0x00000001, 0x0100003e, 0x30494653,
        0x00000008, 0x00000021, 0x00000000,
    };
    static struct named_shader ps_dtou = {"dtou", ps_dtou_code, sizeof(ps_dtou_code)};
    static const DWORD ps_dtoi_code[] =
    {
#if 0
        double2 src0, src1;

        void main(out uint4 dst : SV_Target)
        {
            dst.xz = int2(src0.yx);
            dst.yw = int2(-src1);
        }
#endif
        0x43425844, 0xbc0fca8f, 0x063a975f, 0x4699eeed, 0xa39c6d2c, 0x00000001, 0x000000ec, 0x00000004,
        0x00000030, 0x00000040, 0x00000074, 0x000000dc, 0x4e475349, 0x00000008, 0x00000000, 0x00000008,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000060, 0x00000050,
        0x00000018, 0x0102186a, 0x04000059, 0x00208e46, 0x00000000, 0x00000002, 0x03000065, 0x001020f2,
        0x00000000, 0x060000d6, 0x00102052, 0x00000000, 0x002084e6, 0x00000000, 0x00000000, 0x070000d6,
        0x001020a2, 0x00000000, 0x80208e46, 0x00000041, 0x00000000, 0x00000001, 0x0100003e, 0x30494653,
        0x00000008, 0x00000021, 0x00000000,
    };
    static const struct named_shader ps_dtoi = {"dtoi", ps_dtoi_code, sizeof(ps_dtoi_code)};
    static const DWORD ps_dtof_code[] =
    {
#if 0
        double2 src0, src1;

        void main(out uint4 dst : SV_Target)
        {
            float4 f;
            f.xz = src0.yx;
            f.yw = -src1;
            dst = asuint(f);
        }
#endif
        0x43425844, 0xc72c8b4d, 0x455c6951, 0xd193c640, 0x4dce07a4, 0x00000001, 0x000000ec, 0x00000004,
        0x00000030, 0x00000040, 0x00000074, 0x000000dc, 0x4e475349, 0x00000008, 0x00000000, 0x00000008,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000060, 0x00000050,
        0x00000018, 0x0100186a, 0x04000059, 0x00208e46, 0x00000000, 0x00000002, 0x03000065, 0x001020f2,
        0x00000000, 0x060000c9, 0x00102052, 0x00000000, 0x002084e6, 0x00000000, 0x00000000, 0x070000c9,
        0x001020a2, 0x00000000, 0x80208e46, 0x00000041, 0x00000000, 0x00000001, 0x0100003e, 0x30494653,
        0x00000008, 0x00000001, 0x00000000,
    };
    static const struct named_shader ps_dtof = {"dtof", ps_dtof_code, sizeof(ps_dtof_code)};
    static const DWORD ps_utod_code[] =
    {
#if 0
        uint2 src0;

        void main(out uint4 dst : SV_Target)
        {
            double2 a = src0;
            asuint(a.x, dst.x, dst.y);
            asuint(a.y, dst.z, dst.w);
        }
#endif
        0x43425844, 0x05733379, 0x8683d4b9, 0x718f0111, 0x28153519, 0x00000001, 0x000000ec, 0x00000004,
        0x00000030, 0x00000040, 0x00000074, 0x000000dc, 0x4e475349, 0x00000008, 0x00000000, 0x00000008,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000060, 0x00000050,
        0x00000018, 0x0102186a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2,
        0x00000000, 0x02000068, 0x00000001, 0x060000d9, 0x001000f2, 0x00000000, 0x00208046, 0x00000000,
        0x00000000, 0x05000036, 0x001020f2, 0x00000000, 0x00100e46, 0x00000000, 0x0100003e, 0x30494653,
        0x00000008, 0x00000021, 0x00000000,
    };
    static struct named_shader ps_utod = {"utod", ps_utod_code, sizeof(ps_utod_code)};
    static const DWORD ps_itod_code[] =
    {
#if 0
        int2 src0;

        void main(out uint4 dst : SV_Target)
        {
            double2 a = src0;
            asuint(a.x, dst.x, dst.y);
            asuint(a.y, dst.z, dst.w);
        }
#endif
        0x43425844, 0xbf96ef6f, 0x3ca9fd26, 0xd9b5cfc6, 0x4ef98f41, 0x00000001, 0x000000ec, 0x00000004,
        0x00000030, 0x00000040, 0x00000074, 0x000000dc, 0x4e475349, 0x00000008, 0x00000000, 0x00000008,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000060, 0x00000050,
        0x00000018, 0x0102186a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2,
        0x00000000, 0x02000068, 0x00000001, 0x060000d8, 0x001000f2, 0x00000000, 0x00208046, 0x00000000,
        0x00000000, 0x05000036, 0x001020f2, 0x00000000, 0x00100e46, 0x00000000, 0x0100003e, 0x30494653,
        0x00000008, 0x00000021, 0x00000000,
    };
    static struct named_shader ps_itod = {"itod", ps_itod_code, sizeof(ps_itod_code)};
    static const DWORD ps_ftod_code[] =
    {
#if 0
        float2 src0;

        void main(out uint4 dst : SV_Target)
        {
            double2 a = double2(src0.x, -src0.y);
            asuint(a.x, dst.x, dst.y);
            asuint(a.y, dst.z, dst.w);
        }
#endif
        0x43425844, 0xef37cacc, 0x9ff35467, 0x0c9af3ed, 0x01e295eb, 0x00000001, 0x00000108, 0x00000004,
        0x00000030, 0x00000040, 0x00000074, 0x000000f8, 0x4e475349, 0x00000008, 0x00000000, 0x00000008,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x0000007c, 0x00000050,
        0x0000001f, 0x0100186a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2,
        0x00000000, 0x02000068, 0x00000001, 0x060000ca, 0x00100032, 0x00000000, 0x0020800a, 0x00000000,
        0x00000000, 0x070000ca, 0x001000c2, 0x00000000, 0x8020801a, 0x00000041, 0x00000000, 0x00000000,
        0x05000036, 0x001020f2, 0x00000000, 0x00100e46, 0x00000000, 0x0100003e, 0x30494653, 0x00000008,
        0x00000001, 0x00000000,
    };
    static struct named_shader ps_ftod = {"ftod", ps_ftod_code, sizeof(ps_ftod_code)};
    static const DWORD ps_dmul_code[] =
    {
#if 0
        double2 src0;

        void main(out uint4 dst : SV_Target)
        {
            asuint(src0.x * src0.y, dst.x, dst.y);
            asuint(src0.x * -src0.y, dst.z, dst.w);
        }
#endif
        0x43425844, 0xe8fbac4e, 0x46b3cd8c, 0x6a38f67a, 0xfe02dbd5, 0x00000001, 0x00000134, 0x00000004,
        0x00000030, 0x00000040, 0x00000074, 0x00000124, 0x4e475349, 0x00000008, 0x00000000, 0x00000008,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x000000a8, 0x00000050,
        0x0000002a, 0x0100186a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2,
        0x00000000, 0x02000068, 0x00000001, 0x090000c2, 0x00100032, 0x00000000, 0x00208ee6, 0x00000000,
        0x00000000, 0x00208446, 0x00000000, 0x00000000, 0x05000036, 0x00102032, 0x00000000, 0x00100046,
        0x00000000, 0x0a0000c2, 0x00100032, 0x00000000, 0x80208ee6, 0x00000041, 0x00000000, 0x00000000,
        0x00208446, 0x00000000, 0x00000000, 0x05000036, 0x001020c2, 0x00000000, 0x00100406, 0x00000000,
        0x0100003e, 0x30494653, 0x00000008, 0x00000001, 0x00000000,
    };
    static const struct named_shader ps_dmul = {"dmul", ps_dmul_code, sizeof(ps_dmul_code)};
    static const DWORD ps_ddiv_code[] =
    {
#if 0
        double2 src0;

        void main(out uint4 dst : SV_Target)
        {
            asuint(src0.x / src0.y, dst.x, dst.y);
            asuint(src0.y / src0.x, dst.z, dst.w);
        }
#endif
        0x43425844, 0x6570ff79, 0xe1fd64a5, 0xb44313bb, 0xb5fdfa06, 0x00000001, 0x000000f8, 0x00000004,
        0x00000030, 0x00000040, 0x00000074, 0x000000e8, 0x4e475349, 0x00000008, 0x00000000, 0x00000008,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x0000006c, 0x00000050,
        0x0000001b, 0x0102186a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2,
        0x00000000, 0x02000068, 0x00000001, 0x090000d2, 0x001000f2, 0x00000000, 0x00208e46, 0x00000000,
        0x00000000, 0x002084e6, 0x00000000, 0x00000000, 0x05000036, 0x001020f2, 0x00000000, 0x00100e46,
        0x00000000, 0x0100003e, 0x30494653, 0x00000008, 0x00000021, 0x00000000,
    };
    static const struct named_shader ps_ddiv = {"ddiv", ps_ddiv_code, sizeof(ps_ddiv_code)};
    static const DWORD ps_drcp_code[] =
    {
#if 0
        double2 src0;

        void main(out uint4 dst : SV_Target)
        {
            double2 a = rcp(src0);
            asuint(a.x, dst.x, dst.y);
            asuint(a.y, dst.z, dst.w);
        }
#endif
        0x43425844, 0xdcbd25ba, 0x2d5cccb9, 0x84ed8028, 0x3d62632a, 0x00000001, 0x000000ec, 0x00000004,
        0x00000030, 0x00000040, 0x00000074, 0x000000dc, 0x4e475349, 0x00000008, 0x00000000, 0x00000008,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000060, 0x00000050,
        0x00000018, 0x0102186a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2,
        0x00000000, 0x02000068, 0x00000001, 0x060000d4, 0x001000f2, 0x00000000, 0x00208e46, 0x00000000,
        0x00000000, 0x05000036, 0x001020f2, 0x00000000, 0x00100e46, 0x00000000, 0x0100003e, 0x30494653,
        0x00000008, 0x00000021, 0x00000000,
    };
    static struct named_shader ps_drcp = {"drcp", ps_drcp_code, sizeof(ps_drcp_code)};
    static const struct
    {
        const struct named_shader *ps;
        struct
        {
            struct vec4 src0;
            struct vec4 src1;
            struct vec4 src2;
        } input;
        union
        {
            struct vec4 f;
            struct uvec4 u;
            struct ivec4 i;
        } output;
        bool skip_on_warp;
        bool is_mesa_bug;
    }
    tests[] =
    {
        {&ps_div, {{ 2.0f}, { 4.0f}}, {{     0.5f}}},
        {&ps_div, {{ 2.0f}, {-4.0f}}, {{    -0.5f}}},
        {&ps_div, {{-2.0f}, { 4.0f}}, {{    -0.5f}}},
        {&ps_div, {{-2.0f}, {-4.0f}}, {{     0.5f}}},
        {&ps_div, {{ 0.0f}, { 1.0f}}, {{     0.0f}}},
        {&ps_div, {{ 0.0f}, {-1.0f}}, {{    -0.0f}}},
        {&ps_div, {{ 1.0f}, { 0.0f}}, {{ INFINITY}}},
        {&ps_div, {{ 1.0f}, {-0.0f}}, {{-INFINITY}}},
        {&ps_div, {{-1.0f}, { 0.0f}}, {{-INFINITY}}},
        {&ps_div, {{-1.0f}, {-0.0f}}, {{ INFINITY}}},

        {&ps_dot2, {{1.0f, 1.0f}, {1.0f, 1.0f}}, {{2.0f}}},
        {&ps_dot2, {{1.0f, 1.0f}, {2.0f, 3.0f}}, {{5.0f}}},

        {&ps_dot3, {{1.0f, 2.0f, 3.0f, 4.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}, {{6.0f, 6.0f, 6.0f, 6.0f}}},
        {&ps_dot3, {{1.0f, 2.0f, 3.0f}, {3.0f, 1.0f, 2.0f}}, {{11.0f, 11.0f, 11.0f, 11.0f}}},

        {&ps_eq, {{0.0f}, {0.0f}}, {.u = {0xffffffff}}},
        {&ps_eq, {{1.0f}, {0.0f}}, {.u = {0x00000000}}},
        {&ps_eq, {{0.0f}, {1.0f}}, {.u = {0x00000000}}},
        {&ps_eq, {{1.0f}, {1.0f}}, {.u = {0xffffffff}}},
        {&ps_eq, {{0.0f},  {NAN}}, {.u = {0x00000000}}},
        {&ps_eq, {{1.0f},  {NAN}}, {.u = {0x00000000}}},
        {&ps_eq, { {NAN},  {NAN}}, {.u = {0x00000000}}},

        {&ps_ne, {{0.0f}, {0.0f}}, {.u = {0x00000000}}},
        {&ps_ne, {{1.0f}, {0.0f}}, {.u = {0xffffffff}}},
        {&ps_ne, {{0.0f}, {1.0f}}, {.u = {0xffffffff}}},
        {&ps_ne, {{1.0f}, {1.0f}}, {.u = {0x00000000}}},
        {&ps_ne, {{0.0f},  {NAN}}, {.u = {0xffffffff}}},
        {&ps_ne, {{1.0f},  {NAN}}, {.u = {0xffffffff}}},
        {&ps_ne, { {NAN},  {NAN}}, {.u = {0xffffffff}}},

        {&ps_if, {{0.0f}}, {{1.0f, 0.0f, 0.0f, 1.0f}}},
        {&ps_if, {{1.0f}}, {{0.0f, 1.0f, 0.0f, 1.0f}}},

        /* Ordered/unordered comparisons were broken on Mesa. They are fixed at least from 23.1.7. */
        {&ps_if_return, {{0.0f, 0.0f, 0.0f, 0.0f}}, {{0.0f, 0.0f, 0.0f, 0.0f}}},
        {&ps_if_return, {{ NAN, 0.0f, 0.0f, 0.0f}}, {{1.0f, 0.0f, 0.0f, 0.0f}}, false, true},
        {&ps_if_return, {{3.0f, 0.0f, 0.0f, 0.0f}}, {{0.0f, 0.0f, 0.0f, 0.0f}}},
        {&ps_if_return, {{4.0f, 0.0f, 0.0f, 0.0f}}, {{1.0f, 0.0f, 0.0f, 0.0f}}},
        {&ps_if_return, {{4.0f,  NAN, 0.0f, 0.0f}}, {{1.0f, 1.0f, 1.0f, 0.0f}}, false, true},
        {&ps_if_return, {{4.0f, 3.0f, 0.0f, 0.0f}}, {{1.0f, 0.0f, 0.0f, 0.0f}}},
        {&ps_if_return, {{4.0f, 4.0f, 0.0f, 0.0f}}, {{1.0f, 1.0f, 1.0f, 0.0f}}},
        {&ps_if_return, {{4.0f, 4.0f,  NAN, 0.0f}}, {{1.0f, 1.0f, 1.0f, 0.0f}}, false, true},
        {&ps_if_return, {{4.0f, 4.0f, 3.0f, 0.0f}}, {{1.0f, 1.0f, 1.0f, 0.0f}}},
        {&ps_if_return, {{4.0f, 4.0f, 4.0f, 0.0f}}, {{1.0f, 1.0f, 0.0f, 0.0f}}},
        {&ps_if_return, {{4.0f, 4.0f, 5.0f, 0.0f}}, {{1.0f, 1.0f, 0.0f, 0.0f}}},
        {&ps_if_return, {{4.0f, 4.0f, 0.0f,  NAN}}, {{1.0f, 1.0f, 1.0f, 1.0f}}, false, true},
        {&ps_if_return, {{4.0f, 4.0f, 0.0f, 1.0f}}, {{1.0f, 1.0f, 1.0f, 0.0f}}},
        {&ps_if_return, {{4.0f, 4.0f, 0.0f, 2.0f}}, {{1.0f, 1.0f, 1.0f, 0.0f}}},
        {&ps_if_return, {{4.0f, 4.0f, 0.0f, 3.0f}}, {{1.0f, 1.0f, 1.0f, 0.0f}}},
        {&ps_if_return, {{4.0f, 4.0f, 0.0f, 4.0f}}, {{1.0f, 1.0f, 1.0f, 0.0f}}},
        {&ps_if_return, {{4.0f, 4.0f, 0.0f, 5.0f}}, {{1.0f, 1.0f, 1.0f, 1.0f}}},
        {&ps_if_return, {{5.0f, 4.0f, 0.0f, 5.0f}}, {{1.0f, 1.0f, 1.0f, 0.0f}}},
        {&ps_if_return, {{ NAN,  NAN,  NAN,  NAN}}, {{1.0f, 1.0f, 1.0f, 1.0f}}, false, true},

        {&ps_nested_if, {{0.0f, 0.0f, 0.0f}}, {{0.0f, 0.0f, 0.0f, 1.0f}}},
        {&ps_nested_if, {{0.0f, 0.0f, 1.0f}}, {{1.0f, 0.0f, 0.0f, 1.0f}}},
        {&ps_nested_if, {{1.0f, 0.0f, 1.0f}}, {{0.0f, 0.0f, 1.0f, 1.0f}}},
        {&ps_nested_if, {{1.0f, 1.0f, 1.0f}}, {{0.0f, 1.0f, 0.0f, 1.0f}}},

        {&ps_loop_break, {{0.0f, 0.0f}}, {{0.0f}}},
        {&ps_loop_break, {{1.0f, 0.0f}}, {{1.0f}}},
        {&ps_loop_break, {{1.0f, 1.0f}}, {{1.0f}}},
        {&ps_loop_break, {{1.0f, 2.0f}}, {{1.0f}}},
        {&ps_loop_break, {{1.0f, 3.0f}}, {{1.0f}}},
        {&ps_loop_break, {{7.0f, 0.0f}}, {{1.0f}}},
        {&ps_loop_break, {{7.0f, 2.0f}}, {{1.0f}}},
        {&ps_loop_break, {{7.0f, 6.0f}}, {{1.0f}}},
        {&ps_loop_break, {{7.0f, 7.0f}}, {{7.0f}}},
        {&ps_loop_break, {{7.0f, 8.0f}}, {{7.0f}}},
        {&ps_loop_break, {{7.0f, 9.0f}}, {{7.0f}}},

        {&ps_loop_end_break, {{7.0f, 2.0f}}, {{1.0f}}},

        {&ps_loop_ret, {{0.0f, 0.0f}}, {{0.0f}}},
        {&ps_loop_ret, {{1.0f, 9.0f}}, {{1.0f}}},
        {&ps_loop_ret, {{2.0f, 2.0f}}, {{2.0f}}},
        {&ps_loop_ret, {{5.0f, 9.0f}}, {{5.0f}}},
        {&ps_loop_ret, {{1.0f, 0.0f}}, {{1.0f, 1.0f, 1.0f, 1.0f}}},
        {&ps_loop_ret, {{2.0f, 1.0f}}, {{1.0f, 1.0f, 1.0f, 1.0f}}},
        {&ps_loop_ret, {{8.0f, 7.0f}}, {{1.0f, 1.0f, 1.0f, 1.0f}}},

        {&ps_breakc_nz, {{0}}, {{0.0f, 1.0f, 0.0f, 1.0f}}},
        {&ps_breakc_z,  {{0}}, {{0.0f, 1.0f, 0.0f, 1.0f}}},

        {&ps_continue,     {{0}}, {{254.0f}}, true},
        {&ps_continuec_nz, {{0}}, {{509.0f}}},

        {&ps_retc_nz, {{  0.0f}}, {{1.0f}}},
        {&ps_retc_nz, {{ 10.0f}}, {{1.0f}}},
        {&ps_retc_nz, {{ 99.0f}}, {{1.0f}}},
        {&ps_retc_nz, {{300.0f}}, {{0.0f}}},

        {&ps_src_modifiers, {{ 1.0f,  1.0f,  1.0f,  2.0f}}, {{-1.0f, 1.0f, -1.0f, -2.0f}}},
        {&ps_src_modifiers, {{-1.0f, -1.0f, -1.0f, -2.0f}}, {{ 1.0f, 1.0f, -1.0f, -2.0f}}},

        {&ps_sat, {{ 0.0f,  1.0f,     2.0f,      3.0f}}, {{0.0f, 1.0f, 1.0f, 1.0f}}},
        {&ps_sat, {{-0.0f, -1.0f,    -2.0f,     -3.0f}}, {{0.0f, 0.0f, 0.0f, 0.0f}}},
        {&ps_sat, {{  NAN,  -NAN, INFINITY, -INFINITY}}, {{0.0f, 0.0f, 1.0f, 0.0f}}},

        {&ps_min_max, {{0.0f}, {     1.0f}}, {{     0.0f,     1.0f}}},
        {&ps_min_max, {{0.0f}, {    -1.0f}}, {{    -1.0f,     0.0f}}},
        {&ps_min_max, {{ NAN}, {     1.0f}}, {{     1.0f,     1.0f}}},
        {&ps_min_max, {{0.0f}, {      NAN}}, {{     0.0f,     0.0f}}},
        {&ps_min_max, {{0.0f}, { INFINITY}}, {{     0.0f, INFINITY}}},
        {&ps_min_max, {{1.0f}, { INFINITY}}, {{     1.0f, INFINITY}}},
        {&ps_min_max, {{0.0f}, {-INFINITY}}, {{-INFINITY,     0.0f}}},
        {&ps_min_max, {{1.0f}, {-INFINITY}}, {{-INFINITY,     1.0f}}},

        {&ps_ftou, {{     -NAN}}, {.u = { 0,  0 }}},
        {&ps_ftou, {{      NAN}}, {.u = { 0,  0 }}},
        {&ps_ftou, {{-INFINITY}}, {.u = { 0, ~0u}}},
        {&ps_ftou, {{ INFINITY}}, {.u = {~0u, 0 }}},
        {&ps_ftou, {{    -1.0f}}, {.u = { 0,  1 }}},
        {&ps_ftou, {{     1.0f}}, {.u = { 1,  0 }}},

        {&ps_ftoi, {{     -NAN}}, {.u = {      0,       0}}},
        {&ps_ftoi, {{      NAN}}, {.u = {      0,       0}}},
        {&ps_ftoi, {{-INFINITY}}, {.u = {INT_MIN, INT_MAX}}},
        {&ps_ftoi, {{ INFINITY}}, {.i = {INT_MAX, INT_MIN}}},
        {&ps_ftoi, {{    -1.0f}}, {.i = {     -1,       1}}},
        {&ps_ftoi, {{     1.0f}}, {.i = {      1,      -1}}},

        {&ps_round, {{    -0.5f}}, {{    -1.0f,      0.0f,     -0.0f}}},
        {&ps_round, {{    -0.0f}}, {{    -0.0f,     -0.0f,     -0.0f}}},
        {&ps_round, {{     0.0f}}, {{     0.0f,      0.0f,      0.0f}}},
        {&ps_round, {{     0.5f}}, {{     0.0f,      1.0f,      0.0f}}},
        {&ps_round, {{     3.0f}}, {{     3.0f,      3.0f,      3.0f}}},
        {&ps_round, {{ INFINITY}}, {{ INFINITY,  INFINITY,  INFINITY}}},
        {&ps_round, {{-INFINITY}}, {{-INFINITY, -INFINITY, -INFINITY}}},

        {&ps_round_ne, {{ 0.0f, -0.0f,  0.5f, -0.5f}}, {{ 0.0f, -0.0f,  0.0f, -0.0f}}},
        {&ps_round_ne, {{ 2.0f,  3.0f,  4.0f,  5.0f}}, {{ 2.0f,  3.0f,  4.0f,  5.0f}}},
        {&ps_round_ne, {{ 2.4f,  3.4f,  4.4f,  5.4f}}, {{ 2.0f,  3.0f,  4.0f,  5.0f}}},
        {&ps_round_ne, {{ 2.5f,  3.5f,  4.5f,  5.5f}}, {{ 2.0f,  4.0f,  4.0f,  6.0f}}},
        {&ps_round_ne, {{ 2.6f,  3.6f,  4.6f,  5.6f}}, {{ 3.0f,  4.0f,  5.0f,  6.0f}}},
        {&ps_round_ne, {{-2.5f, -3.5f, -4.5f, -5.5f}}, {{-2.0f, -4.0f, -4.0f, -6.0f}}},
        {&ps_round_ne, {{-2.4f, -3.4f, -4.4f, -5.4f}}, {{-2.0f, -3.0f, -4.0f, -5.0f}}},
        {&ps_round_ne, {{ INFINITY}}, {{ INFINITY}}},
        {&ps_round_ne, {{-INFINITY}}, {{-INFINITY}}},

        {&ps_frc, {{ 0.0f}}, {{0.0f, 0.0f}}},
        {&ps_frc, {{-0.0f}}, {{0.0f, 0.0f}}},
        {&ps_frc, {{ 1.0f}}, {{0.0f, 0.0f}}},
        {&ps_frc, {{-1.0f}}, {{0.0f, 0.0f}}},
        {&ps_frc, {{ 0.5f}}, {{0.5f, 0.5f}}},
        {&ps_frc, {{-0.5f}}, {{0.5f, 0.5f}}},

        {&ps_exp, {{     0.0f}}, {{   1.00f}}},
        {&ps_exp, {{    -0.0f}}, {{   1.00f}}},
        {&ps_exp, {{     2.0f}}, {{   4.00f}}},
        {&ps_exp, {{    -2.0f}}, {{   0.25f}}},
        {&ps_exp, {{ INFINITY}}, {{INFINITY}}},
        {&ps_exp, {{-INFINITY}}, {{   0.00f}}},

        {&ps_log, {{  -0.00f}}, {{-INFINITY}}},
        {&ps_log, {{   0.00f}}, {{-INFINITY}}},
        {&ps_log, {{INFINITY}}, {{ INFINITY}}},
        {&ps_log, {{   0.25f}}, {{    -2.0f}}},
        {&ps_log, {{   0.50f}}, {{    -1.0f}}},
        {&ps_log, {{   2.00f}}, {{     1.0f}}},
        {&ps_log, {{   8.00f}}, {{     3.0f}}},

        {&ps_rcp, {{-INFINITY}}, {{    -0.0f}}},
        {&ps_rcp, {{ INFINITY}}, {{     0.0f}}},
        {&ps_rcp, {{    -0.0f}}, {{-INFINITY}}},
        {&ps_rcp, {{     0.0f}}, {{ INFINITY}}},
        {&ps_rcp, {{    -1.0f}}, {{    -1.0f}}},
        {&ps_rcp, {{     1.0f}}, {{     1.0f}}},
        {&ps_rcp, {{    -2.0f}}, {{    -0.5f}}},
        {&ps_rcp, {{     2.0f}}, {{     0.5f}}},

        {&ps_rcp_vector, {{-1.0f, 1.0f, 4.0f, -4.0f}}, {{-1.0f, 1.0f, 0.25f, -0.25f}}},

        {&ps_sincos, {{ 0.0f, -0.0f,  0.0f, -0.0f}}, {{ 0.0f, -0.0f,  1.0f,  1.0f}}},
        {&ps_sincos, {{ 0.0f, -0.0f,  M_PI, -M_PI}}, {{ 0.0f, -0.0f,  1.0f,  1.0f}}},

        {&ps_indexable_temp, {{0.0f}}, {{1.0f, 0.0f, 0.0f,  1.0f}}},
        {&ps_indexable_temp, {{1.0f}}, {{0.0f, 1.0f, 0.0f,  1.0f}}},
        {&ps_indexable_temp, {{2.0f}}, {{0.0f, 0.0f, 1.0f,  1.0f}}},

        {&ps_indexable_temp2, {{0.0f}}, {{1.0f, 0.0f, 0.0f,  1.0f}}},
        {&ps_indexable_temp2, {{1.0f}}, {{0.0f, 1.0f, 0.0f,  1.0f}}},
        {&ps_indexable_temp2, {{2.0f}}, {{0.0f, 0.0f, 1.0f,  1.0f}}},
        {&ps_indexable_temp2, {{3.0f}}, {{0.0f, 0.0f, 1.0f,  1.0f}}},
        {&ps_indexable_temp2, {{4.0f}}, {{0.0f, 1.0f, 0.0f,  1.0f}}},
        {&ps_indexable_temp2, {{5.0f}}, {{1.0f, 0.0f, 0.0f,  1.0f}}},
        {&ps_indexable_temp2, {{6.0f}}, {{0.0f, 1.0f, 0.0f,  1.0f}}},
        {&ps_indexable_temp2, {{7.0f}}, {{0.0f, 1.0f, 0.0f,  1.0f}}},
        {&ps_indexable_temp2, {{8.0f}}, {{0.0f, 0.0f, 1.0f,  1.0f}}},
        {&ps_indexable_temp2, {{9.0f}}, {{0.0f, 0.0f, 1.0f,  1.0f}}},
    };

    static const struct
    {
        const struct named_shader *ps;
        union
        {
            struct
            {
                struct uvec4 src0;
                struct uvec4 src1;
                struct uvec4 src2;
            } u;
            struct
            {
                struct ivec4 src0;
                struct ivec4 src1;
                struct ivec4 src2;
            } i;
            struct
            {
                struct vec4 src0;
                struct vec4 src1;
                struct vec4 src2;
            } f;
            struct
            {
                struct dvec2 src0;
                struct dvec2 src1;
                struct dvec2 src2;
            } d;
        } input;
        union
        {
            struct uvec4 u;
            struct ivec4 i;
            struct vec4 f;
            struct dvec2 d;
        } output;
        bool is_float64;
        bool is_todo;
        bool skip_on_warp;
        bool todo_on_nvidia;
    }
    uint_tests[] =
    {
        {&ps_bfi, {{{     0,      0,    0,    0}}}, {{         0,          0,          0,          0}}},
        {&ps_bfi, {{{     0,      0,    0,    1}}}, {{         1,          1,          1,          1}}},
        {&ps_bfi, {{{   ~0u,      0,  ~0u,    0}}}, {{0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff}}},
        {&ps_bfi, {{{   ~0u,    ~0u,  ~0u,    0}}}, {{0x80000000, 0x80000000, 0x80000000, 0x80000000}}},
        {&ps_bfi, {{{   ~0u,  0x1fu,  ~0u,    0}}}, {{0x80000000, 0x80000000, 0x80000000, 0x80000000}}},
        {&ps_bfi, {{{   ~0u, ~0x1fu,  ~0u,    0}}}, {{0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff}}},
        {&ps_bfi, {{{     0,      0, 0xff,    1}}}, {{         1,          1,          1,          1}}},
        {&ps_bfi, {{{     0,      0, 0xff,    2}}}, {{         2,          2,          2,          2}}},
        {&ps_bfi, {{{    16,     16, 0xff, 0xff}}}, {{0x00ff00ff, 0x00ff00ff, 0x00ff00ff, 0x00ff00ff}}},
        {&ps_bfi, {{{     0,      0,  ~0u,  ~0u}}}, {{       ~0u,        ~0u,        ~0u,        ~0u}}},
        {&ps_bfi, {{{~0x1fu,      0,  ~0u,    0}}}, {{         0,          0,          0,          0}}},
        {&ps_bfi, {{{~0x1fu,      0,  ~0u,    1}}}, {{         1,          1,          1,          1}}},
        {&ps_bfi, {{{~0x1fu,      0,  ~0u,    2}}}, {{         2,          2,          2,          2}}},
        {&ps_bfi, {{{     0, ~0x1fu,  ~0u,    0}}}, {{         0,          0,          0,          0}}},
        {&ps_bfi, {{{     0, ~0x1fu,  ~0u,    1}}}, {{         1,          1,          1,          1}}},
        {&ps_bfi, {{{     0, ~0x1fu,  ~0u,    2}}}, {{         2,          2,          2,          2}}},
        {&ps_bfi, {{{~0x1fu, ~0x1fu,  ~0u,    0}}}, {{         0,          0,          0,          0}}},
        {&ps_bfi, {{{~0x1fu, ~0x1fu,  ~0u,    1}}}, {{         1,          1,          1,          1}}},
        {&ps_bfi, {{{~0x1fu, ~0x1fu,  ~0u,    2}}}, {{         2,          2,          2,          2}}},

        {&ps_ibfe, {{{ 0,  4, 0x00000000}}}, {{0x00000000, 0x00000000, 0x00000000, 0x00000000}}},
        {&ps_ibfe, {{{ 0,  4, 0xffffffff}}}, {{0x00000000, 0x00000000, 0x00000000, 0x00000000}}},
        {&ps_ibfe, {{{ 0,  4, 0x7fffffff}}}, {{0x00000000, 0x00000000, 0x00000000, 0x00000000}}},
        {&ps_ibfe, {{{ 4,  0, 0x00000000}}}, {{0x00000000, 0x00000000, 0x00000000, 0x00000000}}},
        {&ps_ibfe, {{{ 4,  0, 0xfffffffa}}}, {{0xfffffffa, 0xfffffffa, 0xfffffffa, 0xfffffffa}}},
        {&ps_ibfe, {{{ 4,  0, 0x7ffffffc}}}, {{0xfffffffc, 0xfffffffc, 0xfffffffc, 0xfffffffc}}},
        {&ps_ibfe, {{{ 4,  4, 0x00000000}}}, {{0x00000000, 0x00000000, 0x00000000, 0x00000000}}},
        {&ps_ibfe, {{{ 4,  4, 0xffffffff}}}, {{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}}},
        {&ps_ibfe, {{{ 4,  4, 0xffffff1f}}}, {{0x00000001, 0x00000001, 0x00000001, 0x00000001}}},
        {&ps_ibfe, {{{ 4,  4, 0x7fffffff}}}, {{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}}},
        {&ps_ibfe, {{{23,  8, 0x00000000}}}, {{0x00000000, 0x00000000, 0x00000000, 0x00000000}}},
        {&ps_ibfe, {{{23,  8, 0xffffffff}}}, {{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}}},
        {&ps_ibfe, {{{23,  8, 0x7fffffff}}}, {{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}}},
        {&ps_ibfe, {{{30,  1, 0x00000000}}}, {{0x00000000, 0x00000000, 0x00000000, 0x00000000}}},
        {&ps_ibfe, {{{30,  1, 0xffffffff}}}, {{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}}},
        {&ps_ibfe, {{{30,  1, 0x7fffffff}}}, {{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}}},
        {&ps_ibfe, {{{15, 15, 0x7fffffff}}}, {{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}}},
        {&ps_ibfe, {{{15, 15, 0x3fffffff}}}, {{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}}},
        {&ps_ibfe, {{{15, 15, 0x1fffffff}}}, {{0x00003fff, 0x00003fff, 0x00003fff, 0x00003fff}}},
        {&ps_ibfe, {{{15, 15, 0xffff00ff}}}, {{0xfffffffe, 0xfffffffe, 0xfffffffe, 0xfffffffe}}},
        {&ps_ibfe, {{{16, 15, 0xffffffff}}}, {{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}}},
        {&ps_ibfe, {{{16, 15, 0x3fffffff}}}, {{0x00007fff, 0x00007fff, 0x00007fff, 0x00007fff}}},
        {&ps_ibfe, {{{20, 15, 0xffffffff}}}, {{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}}},
        {&ps_ibfe, {{{31, 31, 0xffffffff}}}, {{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}}},
        {&ps_ibfe, {{{31, 31, 0x80000000}}}, {{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}}},
        {&ps_ibfe, {{{31, 31, 0x7fffffff}}}, {{0x00000000, 0x00000000, 0x00000000, 0x00000000}}},

        {&ps_ibfe2, {{{16, 15, 0x3fffffff}}}, {{0x00007fff, 0x00007fff, 0x00007fff, 0x00007fff}}},

        {&ps_ubfe, {{{0x00000000}}}, {{0x00000000, 0x00000000, 0x00000000, 0x00000000}}},
        {&ps_ubfe, {{{0xffffffff}}}, {{0x0000000f, 0x007fffff, 0x0000007f, 0x3fffffff}}},
        {&ps_ubfe, {{{0xff000000}}}, {{0x00000000, 0x007f0000, 0x00000000, 0x3f800000}}},
        {&ps_ubfe, {{{0x00ff0000}}}, {{0x00000000, 0x0000ff00, 0x00000000, 0x007f8000}}},
        {&ps_ubfe, {{{0x000000ff}}}, {{0x0000000f, 0x00000000, 0x0000007f, 0x0000007f}}},
        {&ps_ubfe, {{{0x80000001}}}, {{0x00000000, 0x00000000, 0x00000000, 0x00000000}}},
        {&ps_ubfe, {{{0xc0000003}}}, {{0x00000000, 0x00400000, 0x00000001, 0x20000001}}},

        {&ps_bfrev, {{{0x12345678}}}, {{0x1e6a2c48, 0x12345678, 0x1e6a0000, 0x2c480000}}},
        {&ps_bfrev, {{{0xffff0000}}}, {{0x0000ffff, 0xffff0000, 0x00000000, 0xffff0000}}},
        {&ps_bfrev, {{{0xffffffff}}}, {{0xffffffff, 0xffffffff, 0xffff0000, 0xffff0000}}},

        {&ps_bits, {{{         0,          0}}}, {{ 0, ~0u, ~0u, ~0u}}},
        {&ps_bits, {{{       ~0u,        ~0u}}}, {{32,   0,  31, ~0u}}},
        {&ps_bits, {{{0x7fffffff, 0x7fffffff}}}, {{31,   0,  30,  30}}},
        {&ps_bits, {{{0x80000000, 0x80000000}}}, {{ 1,  31,  31,  30}}},
        {&ps_bits, {{{0x00000001, 0x00000001}}}, {{ 1,   0,   0,   0}}},
        {&ps_bits, {{{0x80000001, 0x80000001}}}, {{ 2,   0,  31,  30}}},
        {&ps_bits, {{{0x88888888, 0x88888888}}}, {{ 8,   3,  31,  30}}},
        {&ps_bits, {{{0xcccccccc, 0xcccccccc}}}, {{16,   2,  31,  29}}},
        {&ps_bits, {{{0x11111111, 0x11111c11}}}, {{ 8,   0,  28,  28}}},
        {&ps_bits, {{{0x0000000f, 0x0000000f}}}, {{ 4,   0,   3,   3}}},
        {&ps_bits, {{{0x8000000f, 0x8000000f}}}, {{ 5,   0,  31,  30}}},
        {&ps_bits, {{{0x00080000, 0x00080000}}}, {{ 1,  19,  19,  19}}},

        {&ps_bits_vector, {{{0x11111111, 0x00080000}}}, {{8,   1,  28,  19}}},

        {&ps_firstbit_raw, {{{         0,          0}}}, {{ ~0u, ~0u, ~0u, 0}}},
        {&ps_firstbit_raw, {{{0x80000000, 0x80000000}}}, {{  31,   0,   1, 0}}},
        {&ps_firstbit_raw, {{{0x80000001, 0x80000001}}}, {{   0,   0,   1, 0}}},
        {&ps_firstbit_raw, {{{0x11111111, 0x11111111}}}, {{   0,   3,   3, 0}}},
        {&ps_firstbit_raw, {{{0x00080000, 0xffffffff}}}, {{  19,  12, ~0u, 0}}},

        {&ps_ishr, {{{0x00000000, 0x00000000, 0x00000000, 0x00000000}, {~0x1fu, 0, 32, 64}}},
                   {{0x00000000, 0x00000000, 0x00000000, 0x00000000}}},
        {&ps_ishr, {{{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}, {~0x1fu, 0, 32, 64}}},
                   {{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}}},
        {&ps_ishr, {{{0xfefefefe, 0x0fefefef, 0x0f0f0f0f, 0x12345678}, {~0x1fu, 0, 32, 64}}},
                   {{0xfefefefe, 0x0fefefef, 0x0f0f0f0f, 0x12345678}}},
        {&ps_ishr, {{{0x00000000, 0x00000000, 0x00000000, 0x00000000}, {    31, 7, 15, 11}}},
                   {{0x00000000, 0x00000000, 0x00000000, 0x00000000}}},
        {&ps_ishr, {{{0x80000000, 0x80000000, 0x80000000, 0x80000000}, {    31, 7, 15, 11}}},
                   {{0xffffffff, 0xff000000, 0xffff0000, 0xfff00000}}},

        {&ps_ushr, {{{0x00000000, 0x00000000, 0x00000000, 0x00000000}, {~0x1fu, 0, 32, 64}}},
                   {{0x00000000, 0x00000000, 0x00000000, 0x00000000}}},
        {&ps_ushr, {{{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}, {~0x1fu, 0, 32, 64}}},
                   {{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}}},
        {&ps_ushr, {{{0xfefefefe, 0x0fefefef, 0x0f0f0f0f, 0x12345678}, {~0x1fu, 0, 32, 64}}},
                   {{0xfefefefe, 0x0fefefef, 0x0f0f0f0f, 0x12345678}}},
        {&ps_ushr, {{{0x00000000, 0x00000000, 0x00000000, 0x00000000}, {    31, 7, 15, 11}}},
                   {{0x00000000, 0x00000000, 0x00000000, 0x00000000}}},
        {&ps_ushr, {{{0x80000000, 0x80000000, 0x80000000, 0x80000000}, {    31, 7, 15, 11}}},
                   {{0x00000001, 0x01000000, 0x00010000, 0x00100000}}},

        {&ps_ishl, {{{0x00000000, 0x00000000, 0x00000000, 0x00000000}, {~0x1fu, 0, 32, 64}}},
                   {{0x00000000, 0x00000000, 0x00000000, 0x00000000}}},
        {&ps_ishl, {{{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}, {~0x1fu, 0, 32, 64}}},
                   {{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}}},
        {&ps_ishl, {{{0xfefefefe, 0x0fefefef, 0x0f0f0f0f, 0x12345678}, {~0x1fu, 0, 32, 64}}},
                   {{0xfefefefe, 0x0fefefef, 0x0f0f0f0f, 0x12345678}}},
        {&ps_ishl, {{{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}, {   33, 34, 65, 66}}},
                   {{0xfffffffe, 0xfffffffc, 0xfffffffe, 0xfffffffc}}},
        {&ps_ishl, {{{0x00000000, 0x00000000, 0x00000000, 0x00000000}, {    31, 7, 15, 11}}},
                   {{0x00000000, 0x00000000, 0x00000000, 0x00000000}}},
        {&ps_ishl, {{{0x80000000, 0x80000000, 0x80000000, 0x80000000}, {    31, 7, 15, 11}}},
                   {{0x00000000, 0x00000000, 0x00000000, 0x00000000}}},
        {&ps_ishl, {{{0x00000001, 0x00000001, 0x00000001, 0x800feac1}, {    31, 7, 15, 11}}},
                   {{0x80000000, 0x00000080, 0x00008000, 0x7f560800}}},

        {&ps_ishl_const, {{{0x00000000, 0x00000000, 0x00000000, 0x00000000}}},
                          {{0x00000000, 0x00000000, 0x00000000, 0x00000000}}},
        {&ps_ishl_const, {{{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}}},
                          {{0xfffffffc, 0xfffffffc, 0xfffffffc, 0xfffffffc}}},

        {&ps_not, {{{0x00000000, 0xffffffff}}}, {{0xffffffff, 0x00000000, 0x00000000, 0xffffffff}}},
        {&ps_not, {{{0xf0f0f0f0, 0x0f0f0f0f}}}, {{0x0f0f0f0f, 0xf0f0f0f0, 0xf0f0f0f0, 0x0f0f0f0f}}},

        {&ps_icmp, {.i = {{ 0,  0}}}, {{~0u, ~0u,  0,   0}}},
        {&ps_icmp, {.i = {{ 1,  0}}}, {{ 0,  ~0u,  0,  ~0u}}},
        {&ps_icmp, {.i = {{ 0,  1}}}, {{ 0,   0,  ~0u, ~0u}}},
        {&ps_icmp, {.i = {{ 1,  1}}}, {{~0u, ~0u,  0,   0}}},
        {&ps_icmp, {.i = {{-1, -1}}}, {{~0u, ~0u,  0,   0}}},
        {&ps_icmp, {.i = {{ 0, -1}}}, {{ 0,  ~0u,  0,  ~0u}}},
        {&ps_icmp, {.i = {{-1,  0}}}, {{ 0,   0,  ~0u, ~0u}}},
        {&ps_icmp, {.i = {{ 1, -1}}}, {{ 0,  ~0u,  0,  ~0u}}},
        {&ps_icmp, {.i = {{-1,  1}}}, {{ 0,   0,  ~0u, ~0u}}},
        {&ps_icmp, {.i = {{-2, -1}}}, {{ 0,   0,  ~0u, ~0u}}},

        {&ps_ucmp, {{{0,  0}}}, {{~0u,  0, }}},
        {&ps_ucmp, {{{1,  0}}}, {{~0u,  0, }}},
        {&ps_ucmp, {{{0,  1}}}, {{ 0,  ~0u,}}},
        {&ps_ucmp, {{{1,  1}}}, {{~0u,  0, }}},
        {&ps_ucmp, {{{1,  2}}}, {{ 0,  ~0u,}}},

        {&ps_umin_umax, {{{ 0,   0}}},  {{ 0,   0}}},
        {&ps_umin_umax, {{{ 0,   1}}},  {{ 0,   1}}},
        {&ps_umin_umax, {{{ 1,   0}}},  {{ 0,   1}}},
        {&ps_umin_umax, {{{~0u, ~0u}}}, {{~0u, ~0u}}},
        {&ps_umin_umax, {{{ 0,  ~0u}}}, {{ 0,  ~0u}}},
        {&ps_umin_umax, {{{~0u,  0}}},  {{ 0,  ~0u}}},

        {&ps_f16tof32, {{{0x00000000, 0x00003c00, 0x00005640, 0x00005bd0}}}, {{0, 1, 100, 250}}},
        {&ps_f16tof32, {{{0x00010000, 0x00013c00, 0x00015640, 0x00015bd0}}}, {{0, 1, 100, 250}}},
        {&ps_f16tof32, {{{0x000f0000, 0x000f3c00, 0x000f5640, 0x000f5bd0}}}, {{0, 1, 100, 250}}},
        {&ps_f16tof32, {{{0xffff0000, 0xffff3c00, 0xffff5640, 0xffff5bd0}}}, {{0, 1, 100, 250}}},

        {&ps_f16tof32_2, {{{0x00000000, 0x00003c00, 0x00005640, 0x00005bd0}}}, {{250, 100, 1, 0}}},
        {&ps_f16tof32_2, {{{0x00010000, 0x00013c00, 0x00015640, 0x00015bd0}}}, {{250, 100, 1, 0}}},
        {&ps_f16tof32_2, {{{0x000f0000, 0x000f3c00, 0x000f5640, 0x000f5bd0}}}, {{250, 100, 1, 0}}},
        {&ps_f16tof32_2, {{{0xffff0000, 0xffff3c00, 0xffff5640, 0xffff5bd0}}}, {{250, 100, 1, 0}}},

        {&ps_f32tof16, {.f = {{0.0f, 1.0f, -1.0f, 666.0f}}}, {{0, 0x3c00, 0xbc00, 0x6134}}},

        {&ps_f32tof16_2, {.f = {{0.0f, 1.0f, -1.0f, 666.0f}}}, {{0x6134, 0xbc00, 0x3c00, 0}}},

        {&ps_imad, {{{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}}, {{ 0,  0,  0,  0}}},
        {&ps_imad, {{{0, 0, 0, 0}, {0, 0, 0, 0}, {1, 2, 0, 0}}}, {{ 1,  2,  0,  0}}},
        {&ps_imad, {{{2, 3, 4, 5}, {5, 5, 5, 5}, {0, 0, 0, 0}}}, {{10, 15, 20, 25}}},
        {&ps_imad, {{{2, 3, 4, 5}, {5, 5, 5, 5}, {5, 5, 6, 6}}}, {{15, 20, 14, 19}}},

        {&ps_imul, {{{0}, { 0u}}}, {{ 0u}}},
        {&ps_imul, {{{1}, { 2u}}}, {{ 2u}}},
        {&ps_imul, {{{1}, { 3u}}}, {{ 3u}}},
        {&ps_imul, {{{6}, { 3u}}}, {{18u}}},
        {&ps_imul, {{{1}, {~0u}}}, {{~0u}}},
        {&ps_imul, {{{2}, {~0u}}}, {{~1u}}},
        {&ps_imul, {{{3}, {~0u}}}, {{~2u}}},

        {&ps_udiv, {{{0}, {0}}}, {{~0u, ~0u}}},
        {&ps_udiv, {{{1}, {0}}}, {{~0u, ~0u}}},
        {&ps_udiv, {{{1}, {1}}}, {{ 1u,  0u}}},
        {&ps_udiv, {{{7}, {1}}}, {{ 7u,  0u}}},
        {&ps_udiv, {{{7}, {2}}}, {{ 3u,  1u}}},
        {&ps_udiv, {{{7}, {3}}}, {{ 2u,  1u}}},
        {&ps_udiv, {{{7}, {4}}}, {{ 1u,  3u}}},
        {&ps_udiv, {{{7}, {5}}}, {{ 1u,  2u}}},
        {&ps_udiv, {{{7}, {6}}}, {{ 1u,  1u}}},
        {&ps_udiv, {{{7}, {7}}}, {{ 1u,  0u}}},

        {&ps_nested_switch, {{{~0u, 0, 0, 0}, {0}}}, {{  1,   0,   0,   0}}},
        {&ps_nested_switch, {{{ 0u, 0, 0, 0}, {0}}}, {{  2,   0,   0,   0}}},
        {&ps_nested_switch, {{{ 1u, 0, 0, 0}, {0}}}, {{  2,   0,   0,   0}}},
        {&ps_nested_switch, {{{ 2u, 0, 0, 0}, {0}}}, {{  2,   0,   0,   0}}},
        {&ps_nested_switch, {{{ 0u, 0, 0, 0}, {1}}}, {{  0,   0,   0,   0}}},
        {&ps_nested_switch, {{{ 1u, 0, 0, 0}, {2}}}, {{  0,   0,   0,   0}}},
        {&ps_nested_switch, {{{ 2u, 0, 0, 0}, {3}}}, {{  0,   0,   0,   0}}},
        {&ps_nested_switch, {{{ 3u, 0, 0, 0}, {0}}}, {{  0,   0,   0,   0}}},
        {&ps_nested_switch, {{{ 3u, 0, 0, 0}, {1}}}, {{  0,   0,   0,   0}}},
        {&ps_nested_switch, {{{ 5u, 1, 2, 3}, {0}}}, {{  0,   0,   0,   0}}},
        {&ps_nested_switch, {{{ 6u, 1, 2, 3}, {1}}}, {{  0,   0,   0,   0}}},
        {&ps_nested_switch, {{{ 4u, 0, 0, 0}, {0}}}, {{128, 128, 128, 128}}},
        {&ps_nested_switch, {{{ 4u, 0, 0, 0}, {1}}}, {{  0,   0,   1,   1}}},
        {&ps_nested_switch, {{{ 4u, 1, 0, 0}, {1}}}, {{  0,   0,   1,   1}}},
        {&ps_nested_switch, {{{ 4u, 2, 0, 0}, {1}}}, {{  0,   0,   1,   1}}},
        {&ps_nested_switch, {{{ 4u, 3, 0, 0}, {1}}}, {{  0,   0,   1,   1}}},
        {&ps_nested_switch, {{{ 4u, 0, 0, 1}, {1}}}, {{255, 255, 255, 255}}},
        {&ps_nested_switch, {{{ 4u, 1, 0, 1}, {1}}}, {{255, 255, 255, 255}}},
        {&ps_nested_switch, {{{ 4u, 2, 0, 1}, {1}}}, {{255, 255, 255, 255}}},
        {&ps_nested_switch, {{{ 4u, 3, 0, 1}, {1}}}, {{255, 255, 255, 255}}},
        {&ps_nested_switch, {{{ 4u, 0, 1, 1}, {1}}}, {{  2,   2,   2,   2}}},
        {&ps_nested_switch, {{{ 4u, 1, 1, 1}, {1}}}, {{  2,   2,   2,   2}}},
        {&ps_nested_switch, {{{ 4u, 2, 1, 1}, {1}}}, {{  2,   2,   2,   2}}},
        {&ps_nested_switch, {{{ 4u, 3, 1, 1}, {1}}}, {{  2,   2,   2,   2}}},
        {&ps_nested_switch, {{{ 4u, 0, 3, 1}, {1}}}, {{  6,   6,   6,   6}}},
        {&ps_nested_switch, {{{ 4u, 1, 3, 1}, {1}}}, {{  6,   6,   6,   6}}},
        {&ps_nested_switch, {{{ 4u, 2, 3, 1}, {1}}}, {{  6,   6,   6,   6}}},
        {&ps_nested_switch, {{{ 4u, 3, 3, 1}, {1}}}, {{  6,   6,   6,   6}}},
        {&ps_nested_switch, {{{ 4u, 5, 3, 1}, {1}}}, {{  1,   1,   1,   1}}},
        {&ps_nested_switch, {{{ 4u, 6, 3, 1}, {1}}}, {{  1,   1,   1,   1}}},
        {&ps_nested_switch, {{{ 4u, 7, 3, 1}, {1}}}, {{  1,   1,   1,   1}}},
        {&ps_nested_switch, {{{ 4u, 8, 3, 1}, {1}}}, {{  1,   1,   1,   1}}},

        {&ps_switch_no_default, {{{0}}}, {{1, 1, 1, 1}}},
        {&ps_switch_no_default, {{{1}}}, {{3, 3, 3, 3}}},
        {&ps_switch_no_default, {{{2}}}, {{3, 3, 3, 3}}},
        {&ps_switch_no_default, {{{3}}}, {{2, 2, 2, 2}}},
        {&ps_switch_no_default, {{{4}}}, {{3, 3, 3, 3}}},

        {&ps_movc, {{{0, 0, 0, 0}, {1, 2, 3, 4}, {5, 6, 7, 8}}}, {{5, 6, 7, 8}}},
        {&ps_movc, {{{0, 0, 0, 1}, {1, 2, 3, 4}, {5, 6, 7, 8}}}, {{5, 6, 7, 4}}},
        {&ps_movc, {{{1, 0, 0, 0}, {1, 2, 3, 4}, {5, 6, 7, 8}}}, {{1, 6, 7, 8}}},
        {&ps_movc, {{{1, 0, 0, 1}, {1, 2, 3, 4}, {5, 6, 7, 8}}}, {{1, 6, 7, 4}}},
        {&ps_movc, {{{0, 1, 1, 0}, {1, 2, 3, 4}, {5, 6, 7, 8}}}, {{5, 2, 3, 8}}},
        {&ps_movc, {{{1, 1, 1, 1}, {1, 2, 3, 4}, {5, 6, 7, 8}}}, {{1, 2, 3, 4}}},

        {&ps_dmov,      {.d = {{2.5 + 1.0e-9, -3.5 - 1.0e-9}}},  {.d = {3.5 + 1.0e-9, -2.5 - 1.0e-9}}, true},
        {&ps_dadd,      {.d = {{2.5, 0.0}}},  {.d = {2.5 + 1.0000002433080226, 2.5 + 2.000000481493771}}, true, false, true},
        {&ps_dmin_dmax, {.d = {{-1.0, 1.0}}}, {.d = {-1.0, 1.0}}, true},
        {&ps_dfma,      {.d = {{1.0000002433080226, 2.000000481493771}}},
                        {.d = {1.0000002433080226 * 2.000000481493771 + 1.0000002433080226}}, true},
        {&ps_dmovc,     {.d = {{0.5, 0.0}}},  {.d = {4.5, 4.5}}, true, false, true},
        {&ps_dmovc,     {.d = {{1.5, 0.0}}},  {.d = {1.5, 0.0}}, true},
        {&ps_dmodifier, {.d = {{1.5, 0.0}}},  {.d = {1.5f, 2.5f}}, true},
        {&ps_dmodifier, {.d = {{-1.5, 0.0}}}, {.d = {1.5f, 1.5f}}, true},
        {&ps_deq, {.d = {{0.0, 0.0}}}, {{0xffffffff}}, true},
        {&ps_deq, {.d = {{1.0, 0.0}}}, {{0x00000000}}, true},
        {&ps_dne, {.d = {{0.0, 0.0}}}, {{0x00000000}}, true},
        {&ps_dne, {.d = {{1.0, 0.0}}}, {{0xffffffff}}, true},
        {&ps_dge, {.d = {{0.0, 1.0}}}, {{0x00000000}}, true},
        {&ps_dge, {.d = {{1.0, 1.0}}}, {{0xffffffff}}, true},
        {&ps_dge, {.d = {{1.5, 1.0}}}, {{0xffffffff}}, true},
        {&ps_dlt, {.d = {{0.0, 1.0}}}, {{0xffffffff}}, true},
        {&ps_dlt, {.d = {{1.0, 1.0}}}, {{0x00000000}}, true},
        {&ps_dtou, {.d = {{INFINITY, -INFINITY}, {1.0, -1.0}}}, {{0, 0, UINT_MAX, 1}}, true},
        {&ps_dtou, {.d = {{NAN, -NAN}, {1.0, -1.0}}}, {{0, 0, 0, 1}}, true, .todo_on_nvidia = true},
        {&ps_dtoi, {.d = {{INFINITY, -INFINITY}, {1.0, -1.0}}}, {.i = {INT_MIN, -1, INT_MAX, 1}}, true},
        {&ps_dtof, {.d = {{1.5, (1.0/3.0)}, {1e40, -0.0}}}, {.f = {(1.0f/3.0f), -INFINITY, 1.5f, 0.0f}}, true},
        {&ps_utod, {.u = {{3, 0xffffffff}}}, {.d = {3.0,  4294967295.0}}, true},
        {&ps_itod, {.u = {{3,    INT_MIN}}}, {.d = {3.0, -2147483648.0}}, true},
        {&ps_ftod, {.f = {{-2.5f,  -2.5f}}}, {.d = {-2.5,  2.5}}, true},
        {&ps_dmul, {.d = {{ 1.5,    3.0}}},  {.d = { 4.5, -4.5}}, true},
        {&ps_ddiv, {.d = {{ 2.0,    4.0}}},  {.d = { 0.5,  2.0}}, true},
        {&ps_ddiv, {.d = {{ 2.0,   -4.0}}},  {.d = {-0.5, -2.0}}, true},
        {&ps_drcp, {.d = {{ 2.0,   -0.5}}},  {.d = { 0.5, -2.0}}, true},

        {
            &ps_swapc0,
            {{{0, 0, 0, 0}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xdead, 0xc0de, 0xffff, 0xeeee}},
        },
        {
            &ps_swapc0,
            {{{1, 1, 1, 1}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xaaaa, 0xbbbb, 0xcccc, 0xdddd}},
        },
        {
            &ps_swapc0,
            {{{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff},
                    {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xaaaa, 0xbbbb, 0xcccc, 0xdddd}},
        },
        {
            &ps_swapc0,
            {{{1, 0, 1, 0}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xaaaa, 0xc0de, 0xcccc, 0xeeee}},
        },
        {
            &ps_swapc0,
            {{{1, 0, 0, 1}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xaaaa, 0xc0de, 0xffff, 0xdddd}},
        },
        {
            &ps_swapc0,
            {{{1, 0, 0, 0}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xaaaa, 0xc0de, 0xffff, 0xeeee}},
        },
        {
            &ps_swapc0,
            {{{0, 1, 0, 0}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xdead, 0xbbbb, 0xffff, 0xeeee}},
        },
        {
            &ps_swapc0,
            {{{0, 0, 1, 0}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xdead, 0xc0de, 0xcccc, 0xeeee}},
        },
        {
            &ps_swapc0,
            {{{0, 0, 0, 1}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xdead, 0xc0de, 0xffff, 0xdddd}},
        },

        {
            &ps_swapc1,
            {{{0, 0, 0, 0}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}
        },
        {
            &ps_swapc1,
            {{{1, 1, 1, 1}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xdead, 0xc0de, 0xffff, 0xeeee}},
        },
        {
            &ps_swapc1,
            {{{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff},
                    {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xdead, 0xc0de, 0xffff, 0xeeee}},
        },
        {
            &ps_swapc1,
            {{{1, 0, 1, 0}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xdead, 0xbbbb, 0xffff, 0xdddd}},
        },
        {
            &ps_swapc1,
            {{{1, 0, 0, 1}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xdead, 0xbbbb, 0xcccc, 0xeeee}},
        },
        {
            &ps_swapc1,
            {{{1, 0, 0, 0}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xdead, 0xbbbb, 0xcccc, 0xdddd}}
        },
        {
            &ps_swapc1,
            {{{0, 1, 0, 0}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xaaaa, 0xc0de, 0xcccc, 0xdddd}}
        },
        {
            &ps_swapc1,
            {{{0, 0, 1, 0}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xaaaa, 0xbbbb, 0xffff, 0xdddd}}
        },
        {
            &ps_swapc1,
            {{{0, 0, 0, 1}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xaaaa, 0xbbbb, 0xcccc, 0xeeee}},
        },

        {
            &ps_swapc2,
            {{{0, 0, 0, 0}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xdead, 0xc0de, 0xffff, 0xeeee}},
        },
        {
            &ps_swapc2,
            {{{1, 1, 1, 1}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xaaaa, 0xbbbb, 0xcccc, 0xdddd}},
        },
        {
            &ps_swapc2,
            {{{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff},
                    {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xaaaa, 0xbbbb, 0xcccc, 0xdddd}},
        },
        {
            &ps_swapc2,
            {{{1, 0, 1, 0}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xaaaa, 0xc0de, 0xcccc, 0xeeee}},
        },
        {
            &ps_swapc2,
            {{{1, 0, 0, 1}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xaaaa, 0xc0de, 0xffff, 0xdddd}},
        },
        {
            &ps_swapc2,
            {{{1, 0, 0, 0}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xaaaa, 0xc0de, 0xffff, 0xeeee}},
        },
        {
            &ps_swapc2,
            {{{0, 1, 0, 0}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xdead, 0xbbbb, 0xffff, 0xeeee}},
        },
        {
            &ps_swapc2,
            {{{0, 0, 1, 0}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xdead, 0xc0de, 0xcccc, 0xeeee}},
        },
        {
            &ps_swapc2,
            {{{0, 0, 0, 1}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xdead, 0xc0de, 0xffff, 0xdddd}},
        },

        {
            &ps_swapc3,
            {{{0, 0, 0, 0}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}
        },
        {
            &ps_swapc3,
            {{{1, 1, 1, 1}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xdead, 0xc0de, 0xffff, 0xeeee}},
        },
        {
            &ps_swapc3,
            {{{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff},
                    {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xdead, 0xc0de, 0xffff, 0xeeee}},
        },
        {
            &ps_swapc3,
            {{{1, 0, 1, 0}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xdead, 0xbbbb, 0xffff, 0xdddd}},
        },
        {
            &ps_swapc3,
            {{{1, 0, 0, 1}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xdead, 0xbbbb, 0xcccc, 0xeeee}},
        },
        {
            &ps_swapc3,
            {{{1, 0, 0, 0}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xdead, 0xbbbb, 0xcccc, 0xdddd}}
        },
        {
            &ps_swapc3,
            {{{0, 1, 0, 0}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xaaaa, 0xc0de, 0xcccc, 0xdddd}}
        },
        {
            &ps_swapc3,
            {{{0, 0, 1, 0}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xaaaa, 0xbbbb, 0xffff, 0xdddd}}
        },
        {
            &ps_swapc3,
            {{{0, 0, 0, 1}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xaaaa, 0xbbbb, 0xcccc, 0xeeee}},
        },

        {
            &ps_swapc4,
            {{{0, 0, 0, 0}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xdead, 0xc0de, 0xffff, 0xeeee}},
        },
        {
            &ps_swapc4,
            {{{1, 1, 1, 1}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xaaaa, 0xbbbb, 0xcccc, 0xdddd}},
        },
        {
            &ps_swapc4,
            {{{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff},
                    {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xaaaa, 0xbbbb, 0xcccc, 0xdddd}},
        },
        {
            &ps_swapc4,
            {{{1, 0, 1, 0}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xaaaa, 0xc0de, 0xcccc, 0xeeee}},
        },
        {
            &ps_swapc4,
            {{{1, 0, 0, 1}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xaaaa, 0xc0de, 0xffff, 0xdddd}},
        },
        {
            &ps_swapc4,
            {{{1, 0, 0, 0}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xaaaa, 0xc0de, 0xffff, 0xeeee}},
        },
        {
            &ps_swapc4,
            {{{0, 1, 0, 0}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xdead, 0xbbbb, 0xffff, 0xeeee}},
        },
        {
            &ps_swapc4,
            {{{0, 0, 1, 0}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xdead, 0xc0de, 0xcccc, 0xeeee}},
        },
        {
            &ps_swapc4,
            {{{0, 0, 0, 1}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xdead, 0xc0de, 0xffff, 0xdddd}},
        },

        {
            &ps_swapc5,
            {{{0, 0, 0, 0}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}
        },
        {
            &ps_swapc5,
            {{{1, 1, 1, 1}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xdead, 0xc0de, 0xffff, 0xeeee}},
        },
        {
            &ps_swapc5,
            {{{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff},
                    {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xdead, 0xc0de, 0xffff, 0xeeee}},
        },
        {
            &ps_swapc5,
            {{{1, 0, 1, 0}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xdead, 0xbbbb, 0xffff, 0xdddd}},
        },
        {
            &ps_swapc5,
            {{{1, 0, 0, 1}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xdead, 0xbbbb, 0xcccc, 0xeeee}},
        },
        {
            &ps_swapc5,
            {{{1, 0, 0, 0}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xdead, 0xbbbb, 0xcccc, 0xdddd}}
        },
        {
            &ps_swapc5,
            {{{0, 1, 0, 0}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xaaaa, 0xc0de, 0xcccc, 0xdddd}}
        },
        {
            &ps_swapc5,
            {{{0, 0, 1, 0}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xaaaa, 0xbbbb, 0xffff, 0xdddd}}
        },
        {
            &ps_swapc5,
            {{{0, 0, 0, 1}, {0xdead, 0xc0de, 0xffff, 0xeeee}, {0xaaaa, 0xbbbb, 0xcccc, 0xdddd}}},
            {{0xaaaa, 0xbbbb, 0xcccc, 0xeeee}},
        },
    };

    STATIC_ASSERT(sizeof(tests->input) == sizeof(uint_tests->input));

    memset(&desc, 0, sizeof(desc));
    desc.rt_format = DXGI_FORMAT_R32G32B32A32_FLOAT;
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    command_list = context.list;
    queue = context.queue;
    test_shader_float64 = is_shader_float64_supported(context.device);

    context.root_signature = create_cb_root_signature(context.device,
            0, D3D12_SHADER_VISIBILITY_PIXEL, D3D12_ROOT_SIGNATURE_FLAG_NONE);

    cb = create_upload_buffer(context.device, sizeof(tests->input), NULL);

    current_ps = NULL;
    for (i = 0; i < ARRAY_SIZE(tests); ++i)
    {
        if (tests[i].skip_on_warp && test_options.use_warp_device)
        {
            skip("Skipping shader '%s' test on WARP.\n", tests[i].ps->name);
            continue;
        }

        vkd3d_test_push_context("%u:%s", i, tests[i].ps->name);

        if (current_ps != tests[i].ps)
        {
            if (context.pipeline_state)
                ID3D12PipelineState_Release(context.pipeline_state);
            current_ps = tests[i].ps;
            shader.pShaderBytecode = current_ps->code;
            shader.BytecodeLength = current_ps->size;
            context.pipeline_state = create_pipeline_state(context.device,
                    context.root_signature, desc.rt_format, NULL, &shader, NULL);
        }

        update_buffer_data(cb, 0, sizeof(tests[i].input), &tests[i].input);

        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

        ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
        ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 0,
                ID3D12Resource_GetGPUVirtualAddress(cb));
        ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
        ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
        ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
        bug_if(tests[i].is_mesa_bug && is_mesa_device(context.device))
        check_sub_resource_vec4(context.render_target, 0, queue, command_list, &tests[i].output.f, 2);

        reset_command_list(command_list, context.allocator);
        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

        vkd3d_test_pop_context();
    }

    hr = ID3D12GraphicsCommandList_Close(command_list);
    ok(hr == S_OK, "Failed to close command list, hr %#x.\n", hr);
    reset_command_list(command_list, context.allocator);
    ID3D12Resource_Release(context.render_target);
    desc.rt_format = DXGI_FORMAT_R32G32B32A32_UINT;
    create_render_target(&context, &desc, &context.render_target, &context.rtv);

    for (i = 0; i < ARRAY_SIZE(uint_tests); ++i)
    {
        vkd3d_test_push_context("%u:%s", i, uint_tests[i].ps->name);

        if (uint_tests[i].skip_on_warp && test_options.use_warp_device)
        {
            skip("Skipping shader '%s' test on WARP.\n", uint_tests[i].ps->name);
            vkd3d_test_pop_context();
            continue;
        }

        if (uint_tests[i].is_float64 && !test_shader_float64)
        {
            skip("Skipping shader '%s' float64 test.\n", uint_tests[i].ps->name);
            vkd3d_test_pop_context();
            continue;
        }

        if (current_ps != uint_tests[i].ps)
        {
            if (context.pipeline_state)
                ID3D12PipelineState_Release(context.pipeline_state);
            current_ps = uint_tests[i].ps;
            shader.pShaderBytecode = current_ps->code;
            shader.BytecodeLength = current_ps->size;
            context.pipeline_state = create_pipeline_state(context.device,
                    context.root_signature, desc.rt_format, NULL, &shader, NULL);
            if (!context.pipeline_state)
            {
                current_ps = NULL;
                vkd3d_test_pop_context();
                continue;
            }
        }

        update_buffer_data(cb, 0, sizeof(uint_tests[i].input), &uint_tests[i].input);

        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

        ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
        ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 0,
                ID3D12Resource_GetGPUVirtualAddress(cb));
        ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
        ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
        ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
        todo_if(uint_tests[i].is_todo ||
                (uint_tests[i].todo_on_nvidia && is_nvidia_device_lt(context.device, 535, 183, 1)))
        check_sub_resource_uvec4(context.render_target, 0, queue, command_list, &uint_tests[i].output.u);

        reset_command_list(command_list, context.allocator);
        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

        vkd3d_test_pop_context();
    }

    ID3D12Resource_Release(cb);
    destroy_test_context(&context);
}

static void test_compute_shader_instructions(void)
{
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    const D3D12_SHADER_BYTECODE *current_cs;
    ID3D12GraphicsCommandList *command_list;
    D3D12_ROOT_PARAMETER root_parameters[2];
    struct d3d12_resource_readback rb;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Resource *buffer;
    ID3D12Device *device;
    unsigned int i, j;
    uint32_t value;
    HRESULT hr;

    static const DWORD cs_atomic_iadd_tgsm_raw_code[] =
    {
#if 0
        RWByteAddressBuffer buffer;

        groupshared uint m0;
        groupshared uint m1;

        uint4 u;
        int4 s;

        [numthreads(1, 1, 1)]
        void main()
        {
            m0 = buffer.Load(0 * 4);
            m1 = buffer.Load(1 * 4);

            InterlockedAdd(m0, u.x);
            InterlockedAdd(m1, s.x);

            GroupMemoryBarrierWithGroupSync();

            buffer.Store(0 * 4, m0);
            buffer.Store(1 * 4, m1);
        }
#endif
        0x43425844, 0xcd7bfbec, 0x273e77a4, 0x49b75eb9, 0xe7d291f4, 0x00000001, 0x000001d0, 0x00000003,
        0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x0000017c, 0x00050050, 0x0000005f, 0x0100086a,
        0x04000059, 0x00208e46, 0x00000000, 0x00000002, 0x0300009d, 0x0011e000, 0x00000000, 0x02000068,
        0x00000001, 0x0400009f, 0x0011f000, 0x00000000, 0x00000004, 0x0400009f, 0x0011f000, 0x00000001,
        0x00000004, 0x0400009b, 0x00000001, 0x00000001, 0x00000001, 0x890000a5, 0x800002c2, 0x00199983,
        0x00100012, 0x00000000, 0x00004001, 0x00000000, 0x0011e006, 0x00000000, 0x070000a6, 0x0011f012,
        0x00000000, 0x00004001, 0x00000000, 0x0010000a, 0x00000000, 0x080000ad, 0x0011f000, 0x00000000,
        0x00004001, 0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x890000a5, 0x800002c2, 0x00199983,
        0x00100012, 0x00000000, 0x00004001, 0x00000004, 0x0011e006, 0x00000000, 0x070000a6, 0x0011f012,
        0x00000001, 0x00004001, 0x00000000, 0x0010000a, 0x00000000, 0x080000ad, 0x0011f000, 0x00000001,
        0x00004001, 0x00000000, 0x0020800a, 0x00000000, 0x00000001, 0x010018be, 0x070000a5, 0x00100022,
        0x00000000, 0x00004001, 0x00000000, 0x0011f006, 0x00000001, 0x070000a5, 0x00100012, 0x00000000,
        0x00004001, 0x00000000, 0x0011f006, 0x00000000, 0x070000a6, 0x0011e032, 0x00000000, 0x00004001,
        0x00000000, 0x00100046, 0x00000000, 0x0100003e,
    };
    static D3D12_SHADER_BYTECODE cs_atomic_iadd_tgsm_raw
            = {cs_atomic_iadd_tgsm_raw_code, sizeof(cs_atomic_iadd_tgsm_raw_code)};
    static const DWORD cs_atomic_iadd_const_code[] =
    {
#if 0
        RWByteAddressBuffer buffer;

        groupshared uint m;

        [numthreads(1, 1, 1)]
        void main()
        {
            m = buffer.Load(0 * 4);

            InterlockedAdd(m, -1);
            buffer.InterlockedAdd(1 * 4, -1);

            GroupMemoryBarrierWithGroupSync();

            buffer.Store(0 * 4, m);
        }
#endif
        0x43425844, 0x85f9168a, 0x5fe0c4d5, 0x5989b572, 0xecb6ce3c, 0x00000001, 0x0000014c, 0x00000003,
        0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x000000f8, 0x00050050, 0x0000003e, 0x0100086a,
        0x0300009d, 0x0011e000, 0x00000000, 0x02000068, 0x00000001, 0x0400009f, 0x0011f000, 0x00000000,
        0x00000004, 0x0400009b, 0x00000001, 0x00000001, 0x00000001, 0x890000a5, 0x800002c2, 0x00199983,
        0x00100012, 0x00000000, 0x00004001, 0x00000000, 0x0011e006, 0x00000000, 0x070000a6, 0x0011f012,
        0x00000000, 0x00004001, 0x00000000, 0x0010000a, 0x00000000, 0x070000ad, 0x0011f000, 0x00000000,
        0x00004001, 0x00000000, 0x00004001, 0xffffffff, 0x070000ad, 0x0011e000, 0x00000000, 0x00004001,
        0x00000004, 0x00004001, 0xffffffff, 0x010018be, 0x070000a5, 0x00100012, 0x00000000, 0x00004001,
        0x00000000, 0x0011f006, 0x00000000, 0x070000a6, 0x0011e012, 0x00000000, 0x00004001, 0x00000000,
        0x0010000a, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE cs_atomic_iadd_const
            = {cs_atomic_iadd_const_code, sizeof(cs_atomic_iadd_const_code)};
    static const struct
    {
        const D3D12_SHADER_BYTECODE *cs;
        struct uvec4 u;
        struct ivec4 s;
        uint32_t input_data[10];
        uint32_t expected_data[10];
    }
    tests[] =
    {
        {&cs_atomic_iadd_tgsm_raw, {         0}, { 0}, {0, 0}, {0, 0}},
        {&cs_atomic_iadd_tgsm_raw, {         0}, { 0}, {1, 1}, {1, 1}},
        {&cs_atomic_iadd_tgsm_raw, {         1}, { 1}, {0, 0}, {1, 1}},
        {&cs_atomic_iadd_tgsm_raw, {0xffffffff}, {-1}, {1, 1}, {0, 0}},
        {&cs_atomic_iadd_tgsm_raw, {0xffffffff}, {-1}, {4, 4}, {3, 3}},

        {&cs_atomic_iadd_const, {0}, {0}, {0x00000000, 0x00000000}, {0xffffffff, 0xffffffff}},
        {&cs_atomic_iadd_const, {0}, {0}, {0x00000001, 0x00000001}, {0x00000000, 0x00000000}},
        {&cs_atomic_iadd_const, {0}, {0}, {0xffffffff, 0xffffffff}, {0xfffffffe, 0xfffffffe}},
    };

    if (!init_compute_test_context(&context))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV;
    root_parameters[0].Descriptor.ShaderRegister = 0;
    root_parameters[0].Descriptor.RegisterSpace = 0;
    root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    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 = 8;
    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(device, &root_signature_desc, &context.root_signature);
    ok(SUCCEEDED(hr), "Failed to create root signature, hr %#x.\n", hr);

    buffer = create_default_buffer(device, 512,
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_DEST);

    current_cs = NULL;
    for (i = 0; i < ARRAY_SIZE(tests); ++i)
    {
        if (current_cs != tests[i].cs)
        {
            if (context.pipeline_state)
                ID3D12PipelineState_Release(context.pipeline_state);
            current_cs = tests[i].cs;
            context.pipeline_state = create_compute_pipeline_state(device,
                    context.root_signature, *current_cs);
        }

        upload_buffer_data(buffer, 0, sizeof(tests[i].input_data), tests[i].input_data,
                queue, command_list);
        reset_command_list(command_list, context.allocator);
        transition_resource_state(command_list, buffer,
                    D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

        ID3D12GraphicsCommandList_SetComputeRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetComputeRootUnorderedAccessView(command_list,
                0, ID3D12Resource_GetGPUVirtualAddress(buffer));
        ID3D12GraphicsCommandList_SetComputeRoot32BitConstants(command_list, 1, 4, &tests[i].u, 0);
        ID3D12GraphicsCommandList_SetComputeRoot32BitConstants(command_list, 1, 4, &tests[i].s, 4);

        ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
        ID3D12GraphicsCommandList_Dispatch(command_list, 1, 1, 1);

        transition_resource_state(command_list, buffer,
                D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
        get_buffer_readback_with_command_list(buffer, DXGI_FORMAT_R32_UINT, &rb, queue, command_list);
        for (j = 0; j < ARRAY_SIZE(tests[i].expected_data); ++j)
        {
            value = get_readback_uint(&rb.rb, j, 0, 0);
            ok(value == tests[i].expected_data[j], "Test %u: Got 0x%08x, expected 0x%08x at %u.\n",
                    i, value, tests[i].expected_data[j], j);
        }
        release_resource_readback(&rb);
        reset_command_list(command_list, context.allocator);
        transition_resource_state(command_list, buffer,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_COPY_DEST);
    }

    ID3D12Resource_Release(buffer);
    destroy_test_context(&context);
}

static void test_discard_instruction(void)
{
    ID3D12PipelineState *pso_discard_nz, *pso_discard_z;
    ID3D12GraphicsCommandList *command_list;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Device *device;
    ID3D12Resource *cb;
    unsigned int i;

    static const DWORD ps_discard_nz_code[] =
    {
#if 0
        uint data;

        float4 main() : SV_Target
        {
            if (data)
                discard;
            return float4(0.0f, 0.5f, 0.0f, 1.0f);
        }
#endif
        0x43425844, 0xfa7e5758, 0xd8716ffc, 0x5ad6a940, 0x2b99bba2, 0x00000001, 0x000000d0, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x52444853, 0x00000058, 0x00000040, 0x00000016,
        0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000, 0x0404000d,
        0x0020800a, 0x00000000, 0x00000000, 0x08000036, 0x001020f2, 0x00000000, 0x00004002, 0x00000000,
        0x3f000000, 0x00000000, 0x3f800000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_discard_nz = {ps_discard_nz_code, sizeof(ps_discard_nz_code)};
    static const DWORD ps_discard_z_code[] =
    {
#if 0
        uint data;

        float4 main() : SV_Target
        {
            if (!data)
                discard;
            return float4(0.0f, 1.0f, 0.0f, 1.0f);
        }
#endif
        0x43425844, 0x5c4dd108, 0x1eb43558, 0x7c02c98c, 0xd81eb34c, 0x00000001, 0x000000d0, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x52444853, 0x00000058, 0x00000040, 0x00000016,
        0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000, 0x0400000d,
        0x0020800a, 0x00000000, 0x00000000, 0x08000036, 0x001020f2, 0x00000000, 0x00004002, 0x00000000,
        0x3f800000, 0x00000000, 0x3f800000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_discard_z = {ps_discard_z_code, sizeof(ps_discard_z_code)};
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    static const struct uvec4 values[] =
    {
        {0x0000000},
        {0x0000001},
        {0x8000000},
        {0xfffffff},
    };

    memset(&desc, 0, sizeof(desc));
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    context.root_signature = create_cb_root_signature(device,
            0, D3D12_SHADER_VISIBILITY_PIXEL, D3D12_ROOT_SIGNATURE_FLAG_NONE);
    pso_discard_nz = create_pipeline_state(device, context.root_signature,
            context.render_target_desc.Format, NULL, &ps_discard_nz, NULL);
    pso_discard_z = create_pipeline_state(device, context.root_signature,
            context.render_target_desc.Format, NULL, &ps_discard_z, NULL);

    cb = create_upload_buffer(device, sizeof(*values), NULL);

    for (i = 0; i < ARRAY_SIZE(values); ++i)
    {
        update_buffer_data(cb, 0, sizeof(values[i]), &values[i]);

        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
        ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
        ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 0,
                ID3D12Resource_GetGPUVirtualAddress(cb));
        ID3D12GraphicsCommandList_SetPipelineState(command_list, pso_discard_nz);
        ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
        ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);
        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
        check_sub_resource_uint(context.render_target, 0, queue, command_list,
                values[i].x ? 0xffffffff : 0xff007f00, 1);
        reset_command_list(command_list, context.allocator);
        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
        ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
        ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 0,
                ID3D12Resource_GetGPUVirtualAddress(cb));
        ID3D12GraphicsCommandList_SetPipelineState(command_list, pso_discard_z);
        ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
        ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);
        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
        check_sub_resource_uint(context.render_target, 0, queue, command_list,
                values[i].x ? 0xff00ff00 : 0xffffffff, 1);
        reset_command_list(command_list, context.allocator);
        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
    }

    ID3D12Resource_Release(cb);
    ID3D12PipelineState_Release(pso_discard_nz);
    ID3D12PipelineState_Release(pso_discard_z);
    destroy_test_context(&context);
}

static void test_shader_input_output_components(void)
{
    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
    ID3D12GraphicsCommandList *command_list;
    D3D12_INPUT_LAYOUT_DESC input_layout;
    D3D12_CPU_DESCRIPTOR_HANDLE rtvs[2];
    ID3D12Resource *uint_render_target;
    struct test_context_desc desc;
    D3D12_VERTEX_BUFFER_VIEW vbv;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Resource *vb;
    unsigned int i;
    HRESULT hr;

    static const DWORD vs1_code[] =
    {
#if 0
        void main(float4 in_position : POSITION, uint4 in_uint : UINT,
                out float4 out_position : SV_POSITION, out uint out_uint : UINT,
                out float3 out_float : FLOAT)
        {
            out_position = in_position;
            out_uint = in_uint.y;
            out_float = float3(1, 2, 3);
        }
#endif
        0x43425844, 0x0521bc60, 0xd39733a4, 0x1522eea3, 0x0c741ea3, 0x00000001, 0x0000018c, 0x00000003,
        0x0000002c, 0x0000007c, 0x000000ec, 0x4e475349, 0x00000048, 0x00000002, 0x00000008, 0x00000038,
        0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000f0f, 0x00000041, 0x00000000, 0x00000000,
        0x00000001, 0x00000001, 0x0000020f, 0x49534f50, 0x4e4f4954, 0x4e495500, 0xabab0054, 0x4e47534f,
        0x00000068, 0x00000003, 0x00000008, 0x00000050, 0x00000000, 0x00000001, 0x00000003, 0x00000000,
        0x0000000f, 0x0000005c, 0x00000000, 0x00000000, 0x00000001, 0x00000001, 0x00000e01, 0x00000061,
        0x00000000, 0x00000000, 0x00000003, 0x00000002, 0x00000807, 0x505f5653, 0x5449534f, 0x004e4f49,
        0x544e4955, 0x4f4c4600, 0xab005441, 0x58454853, 0x00000098, 0x00010050, 0x00000026, 0x0100086a,
        0x0300005f, 0x001010f2, 0x00000000, 0x0300005f, 0x00101022, 0x00000001, 0x04000067, 0x001020f2,
        0x00000000, 0x00000001, 0x03000065, 0x00102012, 0x00000001, 0x03000065, 0x00102072, 0x00000002,
        0x05000036, 0x001020f2, 0x00000000, 0x00101e46, 0x00000000, 0x05000036, 0x00102012, 0x00000001,
        0x0010101a, 0x00000001, 0x08000036, 0x00102072, 0x00000002, 0x00004002, 0x3f800000, 0x40000000,
        0x40400000, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE vs1 = {vs1_code, sizeof(vs1_code)};
    static const DWORD ps1_code[] =
    {
#if 0
        void main(float4 position : SV_POSITION, uint in_uint : UINT,
                float3 in_float : FLOAT, out float4 out_float : SV_TARGET0,
                out uint4 out_uint : SV_TARGET1)
        {
            out_float.x = position.w;
            out_float.y = in_uint;
            out_float.z = in_float.z;
            out_float.w = 0;
            out_uint.x = 0xdeadbeef;
            out_uint.y = 0;
            out_uint.z = in_uint;
            out_uint.w = in_float.z;
        }
#endif
        0x43425844, 0x762dbf5e, 0x2cc83972, 0x60c7aa48, 0xbca6118a, 0x00000001, 0x000001d4, 0x00000003,
        0x0000002c, 0x0000009c, 0x000000e8, 0x4e475349, 0x00000068, 0x00000003, 0x00000008, 0x00000050,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000080f, 0x0000005c, 0x00000000, 0x00000000,
        0x00000001, 0x00000001, 0x00000101, 0x00000061, 0x00000000, 0x00000000, 0x00000003, 0x00000002,
        0x00000407, 0x505f5653, 0x5449534f, 0x004e4f49, 0x544e4955, 0x4f4c4600, 0xab005441, 0x4e47534f,
        0x00000044, 0x00000002, 0x00000008, 0x00000038, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x00000038, 0x00000001, 0x00000000, 0x00000001, 0x00000001, 0x0000000f, 0x545f5653,
        0x45475241, 0xabab0054, 0x52444853, 0x000000e4, 0x00000040, 0x00000039, 0x04002064, 0x00101082,
        0x00000000, 0x00000001, 0x03000862, 0x00101012, 0x00000001, 0x03001062, 0x00101042, 0x00000002,
        0x03000065, 0x001020f2, 0x00000000, 0x03000065, 0x001020f2, 0x00000001, 0x05000056, 0x00102022,
        0x00000000, 0x0010100a, 0x00000001, 0x05000036, 0x00102012, 0x00000000, 0x0010103a, 0x00000000,
        0x05000036, 0x00102042, 0x00000000, 0x0010102a, 0x00000002, 0x05000036, 0x00102082, 0x00000000,
        0x00004001, 0x00000000, 0x0500001c, 0x00102082, 0x00000001, 0x0010102a, 0x00000002, 0x08000036,
        0x00102032, 0x00000001, 0x00004002, 0xdeadbeef, 0x00000000, 0x00000000, 0x00000000, 0x05000036,
        0x00102042, 0x00000001, 0x0010100a, 0x00000001, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps1 = {ps1_code, sizeof(ps1_code)};
    static const DWORD vs2_code[] =
    {
#if 0
        void main(float4 in_position : POSITION,
                float4 in_texcoord0 : TEXCOORD0, float4 in_texcoord1 : TEXCOORD1,
                float4 in_texcoord2 : TEXCOORD2,
                out float4 position : Sv_Position,
                out float2 texcoord0 : TEXCOORD0, out float2 texcoord1 : TEXCOORD1,
                out float4 texcoord2 : TEXCOORD2, out float3 texcoord3 : TEXCOORD3)
        {
            position = in_position;
            texcoord0 = in_texcoord0.yx;
            texcoord1 = in_texcoord0.wz;
            texcoord2 = in_texcoord1;
            texcoord3 = in_texcoord2.yzx;
        }
#endif
        0x43425844, 0x6721613b, 0xb997c7e4, 0x8bc3df4d, 0x813c93b9, 0x00000001, 0x00000224, 0x00000003,
        0x0000002c, 0x000000b0, 0x00000150, 0x4e475349, 0x0000007c, 0x00000004, 0x00000008, 0x00000068,
        0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000f0f, 0x00000071, 0x00000000, 0x00000000,
        0x00000003, 0x00000001, 0x00000f0f, 0x00000071, 0x00000001, 0x00000000, 0x00000003, 0x00000002,
        0x00000f0f, 0x00000071, 0x00000002, 0x00000000, 0x00000003, 0x00000003, 0x0000070f, 0x49534f50,
        0x4e4f4954, 0x58455400, 0x524f4f43, 0xabab0044, 0x4e47534f, 0x00000098, 0x00000005, 0x00000008,
        0x00000080, 0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x0000008c, 0x00000000,
        0x00000000, 0x00000003, 0x00000001, 0x00000c03, 0x0000008c, 0x00000001, 0x00000000, 0x00000003,
        0x00000001, 0x0000030c, 0x0000008c, 0x00000002, 0x00000000, 0x00000003, 0x00000002, 0x0000000f,
        0x0000008c, 0x00000003, 0x00000000, 0x00000003, 0x00000003, 0x00000807, 0x505f7653, 0x7469736f,
        0x006e6f69, 0x43584554, 0x44524f4f, 0xababab00, 0x52444853, 0x000000cc, 0x00010040, 0x00000033,
        0x0300005f, 0x001010f2, 0x00000000, 0x0300005f, 0x001010f2, 0x00000001, 0x0300005f, 0x001010f2,
        0x00000002, 0x0300005f, 0x00101072, 0x00000003, 0x04000067, 0x001020f2, 0x00000000, 0x00000001,
        0x03000065, 0x00102032, 0x00000001, 0x03000065, 0x001020c2, 0x00000001, 0x03000065, 0x001020f2,
        0x00000002, 0x03000065, 0x00102072, 0x00000003, 0x05000036, 0x001020f2, 0x00000000, 0x00101e46,
        0x00000000, 0x05000036, 0x001020f2, 0x00000001, 0x00101b16, 0x00000001, 0x05000036, 0x001020f2,
        0x00000002, 0x00101e46, 0x00000002, 0x05000036, 0x00102072, 0x00000003, 0x00101496, 0x00000003,
        0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE vs2 = {vs2_code, sizeof(vs2_code)};
    static const DWORD ps2_code[] =
    {
#if 0
        void main(float4 position : Sv_Position,
                float2 texcoord0 : TEXCOORD0, float2 texcoord1 : TEXCOORD1,
                float4 texcoord2 : TEXCOORD2, float3 texcoord3 : TEXCOORD3,
                out float4 target0 : Sv_Target0, out uint4 target1 : SV_Target1)
        {
            target0.x = texcoord0.x + texcoord0.y;
            target0.y = texcoord1.x;
            target0.z = texcoord3.z;
            target0.w = texcoord1.y;

            target1.x = texcoord2.x;
            target1.y = texcoord2.y;
            target1.w = texcoord2.w;
            target1.z = 0;
        }
#endif
        0x43425844, 0xa6c0df60, 0x5bf34683, 0xa0093595, 0x98cca724, 0x00000001, 0x000001e8, 0x00000003,
        0x0000002c, 0x000000cc, 0x00000120, 0x4e475349, 0x00000098, 0x00000005, 0x00000008, 0x00000080,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x0000008c, 0x00000000, 0x00000000,
        0x00000003, 0x00000001, 0x00000303, 0x0000008c, 0x00000001, 0x00000000, 0x00000003, 0x00000001,
        0x00000c0c, 0x0000008c, 0x00000002, 0x00000000, 0x00000003, 0x00000002, 0x00000b0f, 0x0000008c,
        0x00000003, 0x00000000, 0x00000003, 0x00000003, 0x00000407, 0x505f7653, 0x7469736f, 0x006e6f69,
        0x43584554, 0x44524f4f, 0xababab00, 0x4e47534f, 0x0000004c, 0x00000002, 0x00000008, 0x00000038,
        0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x0000000f, 0x00000042, 0x00000001, 0x00000000,
        0x00000001, 0x00000001, 0x0000000f, 0x545f7653, 0x65677261, 0x56530074, 0x7261545f, 0x00746567,
        0x52444853, 0x000000c0, 0x00000040, 0x00000030, 0x03001062, 0x00101032, 0x00000001, 0x03001062,
        0x001010c2, 0x00000001, 0x03001062, 0x001010b2, 0x00000002, 0x03001062, 0x00101042, 0x00000003,
        0x03000065, 0x001020f2, 0x00000000, 0x03000065, 0x001020f2, 0x00000001, 0x07000000, 0x00102012,
        0x00000000, 0x0010101a, 0x00000001, 0x0010100a, 0x00000001, 0x05000036, 0x001020a2, 0x00000000,
        0x00101ea6, 0x00000001, 0x05000036, 0x00102042, 0x00000000, 0x0010102a, 0x00000003, 0x0500001c,
        0x001020b2, 0x00000001, 0x00101c46, 0x00000002, 0x05000036, 0x00102042, 0x00000001, 0x00004001,
        0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps2 = {ps2_code, sizeof(ps2_code)};
    static const DWORD ps3_code[] =
    {
#if 0
        void main(float4 position : Sv_Position,
                float2 texcoord0 : TEXCOORD0, float2 texcoord1 : TEXCOORD1,
                float4 texcoord2 : TEXCOORD2, float3 texcoord3 : TEXCOORD3,
                out float4 target0 : Sv_Target0, out uint4 target1 : SV_Target1)
        {
            target0.x = texcoord0.x;
            target0.y = texcoord1.y;
            target0.z = texcoord3.z;
            target0.w = texcoord3.z;

            target1.x = texcoord2.x;
            target1.y = 0;
            target1.w = texcoord2.w;
            target1.z = 0;
        }
#endif
        0x43425844, 0x2df3a11d, 0x885fc859, 0x332d922b, 0xf8e01020, 0x00000001, 0x000001d8, 0x00000003,
        0x0000002c, 0x000000cc, 0x00000120, 0x4e475349, 0x00000098, 0x00000005, 0x00000008, 0x00000080,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x0000008c, 0x00000000, 0x00000000,
        0x00000003, 0x00000001, 0x00000103, 0x0000008c, 0x00000001, 0x00000000, 0x00000003, 0x00000001,
        0x0000080c, 0x0000008c, 0x00000002, 0x00000000, 0x00000003, 0x00000002, 0x0000090f, 0x0000008c,
        0x00000003, 0x00000000, 0x00000003, 0x00000003, 0x00000407, 0x505f7653, 0x7469736f, 0x006e6f69,
        0x43584554, 0x44524f4f, 0xababab00, 0x4e47534f, 0x0000004c, 0x00000002, 0x00000008, 0x00000038,
        0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x0000000f, 0x00000042, 0x00000001, 0x00000000,
        0x00000001, 0x00000001, 0x0000000f, 0x545f7653, 0x65677261, 0x56530074, 0x7261545f, 0x00746567,
        0x52444853, 0x000000b0, 0x00000040, 0x0000002c, 0x03001062, 0x00101012, 0x00000001, 0x03001062,
        0x00101082, 0x00000001, 0x03001062, 0x00101092, 0x00000002, 0x03001062, 0x00101042, 0x00000003,
        0x03000065, 0x001020f2, 0x00000000, 0x03000065, 0x001020f2, 0x00000001, 0x05000036, 0x00102032,
        0x00000000, 0x001010c6, 0x00000001, 0x05000036, 0x001020c2, 0x00000000, 0x00101aa6, 0x00000003,
        0x0500001c, 0x00102092, 0x00000001, 0x00101c06, 0x00000002, 0x08000036, 0x00102062, 0x00000001,
        0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps3 = {ps3_code, sizeof(ps3_code)};
    /* position.xyw */
    static const DWORD ps4_code[] =
    {
#if 0
        void main(float4 position : Sv_Position,
                float2 texcoord0 : TEXCOORD0, float2 texcoord1 : TEXCOORD1,
                float4 texcoord2 : TEXCOORD2, float3 texcoord3 : TEXCOORD3,
                out float4 target0 : Sv_Target0, out uint4 target1 : SV_Target1)
        {
            if (all(position.xy < float2(64, 64)))
                target0 = float4(0, 1, 0, 1);
            else
                target0 = float4(0, 0, 0, 0);

            target1.xyzw = 0;
            target1.y = position.w;
        }
#endif
        0x43425844, 0x6dac90a1, 0x518a6b0a, 0x393cc320, 0x5f6fbe5e, 0x00000001, 0x00000204, 0x00000003,
        0x0000002c, 0x000000cc, 0x00000120, 0x4e475349, 0x00000098, 0x00000005, 0x00000008, 0x00000080,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x00000b0f, 0x0000008c, 0x00000000, 0x00000000,
        0x00000003, 0x00000001, 0x00000003, 0x0000008c, 0x00000001, 0x00000000, 0x00000003, 0x00000001,
        0x0000000c, 0x0000008c, 0x00000002, 0x00000000, 0x00000003, 0x00000002, 0x0000000f, 0x0000008c,
        0x00000003, 0x00000000, 0x00000003, 0x00000003, 0x00000007, 0x505f7653, 0x7469736f, 0x006e6f69,
        0x43584554, 0x44524f4f, 0xababab00, 0x4e47534f, 0x0000004c, 0x00000002, 0x00000008, 0x00000038,
        0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x0000000f, 0x00000042, 0x00000001, 0x00000000,
        0x00000001, 0x00000001, 0x0000000f, 0x545f7653, 0x65677261, 0x56530074, 0x7261545f, 0x00746567,
        0x52444853, 0x000000dc, 0x00000040, 0x00000037, 0x04002064, 0x001010b2, 0x00000000, 0x00000001,
        0x03000065, 0x001020f2, 0x00000000, 0x03000065, 0x001020f2, 0x00000001, 0x02000068, 0x00000001,
        0x0a000031, 0x00100032, 0x00000000, 0x00101046, 0x00000000, 0x00004002, 0x42800000, 0x42800000,
        0x00000000, 0x00000000, 0x07000001, 0x00100012, 0x00000000, 0x0010001a, 0x00000000, 0x0010000a,
        0x00000000, 0x0a000001, 0x001020f2, 0x00000000, 0x00100006, 0x00000000, 0x00004002, 0x00000000,
        0x3f800000, 0x00000000, 0x3f800000, 0x0500001c, 0x00102022, 0x00000001, 0x0010103a, 0x00000000,
        0x08000036, 0x001020d2, 0x00000001, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
        0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps4 = {ps4_code, sizeof(ps4_code)};
#if 0
    struct ps_data
    {
        float4 position : SV_Position;
        float4 color : COLOR;
        float3 color1 : COLOR1;
        float color2 : COLOR2;
    };

    ps_data vs_main(float4 position : POSITION)
    {
        ps_data o;
        o.position = position;
        o.color = float4(0, 1, 0, 1);
        o.color1 = (float3)0.5;
        o.color2 = 0.25;
        return o;
    }

    float4 ps_main(ps_data i) : SV_Target
    {
        return float4(i.color.rgb, i.color2);
    }
#endif
    static const DWORD vs5_code[] =
    {
        0x43425844, 0xc3e1b9fc, 0xb99e43ef, 0x9a2a6dfc, 0xad719e68, 0x00000001, 0x00000190, 0x00000003,
        0x0000002c, 0x00000060, 0x000000e4, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000f0f, 0x49534f50, 0x4e4f4954, 0xababab00,
        0x4e47534f, 0x0000007c, 0x00000004, 0x00000008, 0x00000068, 0x00000000, 0x00000001, 0x00000003,
        0x00000000, 0x0000000f, 0x00000074, 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x0000000f,
        0x00000074, 0x00000001, 0x00000000, 0x00000003, 0x00000002, 0x00000807, 0x00000074, 0x00000002,
        0x00000000, 0x00000003, 0x00000002, 0x00000708, 0x505f5653, 0x7469736f, 0x006e6f69, 0x4f4c4f43,
        0xabab0052, 0x58454853, 0x000000a4, 0x00010050, 0x00000029, 0x0100086a, 0x0300005f, 0x001010f2,
        0x00000000, 0x04000067, 0x001020f2, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000001,
        0x03000065, 0x00102072, 0x00000002, 0x03000065, 0x00102082, 0x00000002, 0x05000036, 0x001020f2,
        0x00000000, 0x00101e46, 0x00000000, 0x08000036, 0x001020f2, 0x00000001, 0x00004002, 0x00000000,
        0x3f800000, 0x00000000, 0x3f800000, 0x08000036, 0x001020f2, 0x00000002, 0x00004002, 0x3f000000,
        0x3f000000, 0x3f000000, 0x3e800000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE vs5 = {vs5_code, sizeof(vs5_code)};
    static const DWORD ps5_code[] =
    {
        0x43425844, 0x285bf397, 0xbc07e078, 0xc4e528e3, 0x74efea4d, 0x00000001, 0x00000148, 0x00000003,
        0x0000002c, 0x000000b0, 0x000000e4, 0x4e475349, 0x0000007c, 0x00000004, 0x00000008, 0x00000068,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x00000074, 0x00000000, 0x00000000,
        0x00000003, 0x00000001, 0x0000070f, 0x00000074, 0x00000001, 0x00000000, 0x00000003, 0x00000002,
        0x00000007, 0x00000074, 0x00000002, 0x00000000, 0x00000003, 0x00000002, 0x00000808, 0x505f5653,
        0x7469736f, 0x006e6f69, 0x4f4c4f43, 0xabab0052, 0x4e47534f, 0x0000002c, 0x00000001, 0x00000008,
        0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x0000000f, 0x545f5653, 0x65677261,
        0xabab0074, 0x58454853, 0x0000005c, 0x00000050, 0x00000017, 0x0100086a, 0x03001062, 0x00101072,
        0x00000001, 0x03001062, 0x00101082, 0x00000002, 0x03000065, 0x001020f2, 0x00000000, 0x05000036,
        0x00102072, 0x00000000, 0x00101246, 0x00000001, 0x05000036, 0x00102082, 0x00000000, 0x0010103a,
        0x00000002, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps5 = {ps5_code, sizeof(ps5_code)};
    static const D3D12_INPUT_ELEMENT_DESC layout_desc[] =
    {
        {"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT,       0,  0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
        {"UINT",     0, DXGI_FORMAT_R32G32B32A32_UINT,  0, 16, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
        {"TEXCOORD", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 32, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
        {"TEXCOORD", 1, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 48, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
        {"TEXCOORD", 2, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 64, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
    };
    static const struct
    {
        struct vec4 position;
        struct uvec4 u;
        struct vec4 t0;
        struct vec4 t1;
        struct vec4 t2;
    }
    quad[] =
    {
        {{-1.0f, -1.0f}, {1, 2, 3, 4}, {3.0f, 3.0f, 8.0f, 4.0f}, {9.0f, 5.0f, 3.0f, 1.0f}, {7.0f, 2.0f, 5.0f}},
        {{-1.0f,  1.0f}, {1, 2, 3, 4}, {3.0f, 3.0f, 8.0f, 4.0f}, {9.0f, 5.0f, 3.0f, 1.0f}, {7.0f, 2.0f, 5.0f}},
        {{ 1.0f, -1.0f}, {1, 2, 3, 4}, {3.0f, 3.0f, 8.0f, 4.0f}, {9.0f, 5.0f, 3.0f, 1.0f}, {7.0f, 2.0f, 5.0f}},
        {{ 1.0f,  1.0f}, {1, 2, 3, 4}, {3.0f, 3.0f, 8.0f, 4.0f}, {9.0f, 5.0f, 3.0f, 1.0f}, {7.0f, 2.0f, 5.0f}},
    };
    static const struct
    {
        const D3D12_SHADER_BYTECODE *vs;
        const D3D12_SHADER_BYTECODE *ps;
        const struct vec4 expected_vec4;
        const struct uvec4 expected_uvec4;
    }
    tests[] =
    {
        {&vs1, &ps1, {1.0f, 2.0f, 3.0f, 0.00f}, {0xdeadbeef, 0, 2, 3}},
        {&vs2, &ps2, {6.0f, 4.0f, 7.0f, 8.00f}, {         9, 5, 0, 1}},
        {&vs2, &ps3, {3.0f, 8.0f, 7.0f, 7.00f}, {         9, 0, 0, 1}},
        {&vs2, &ps4, {0.0f, 1.0f, 0.0f, 1.00f}, {         0, 1, 0, 0}},
        {&vs5, &ps5, {0.0f, 1.0f, 0.0f, 0.25f}, {         0, 1, 0, 0}},
    };

    memset(&desc, 0, sizeof(desc));
    desc.rt_format = DXGI_FORMAT_R32G32B32A32_FLOAT;
    desc.rt_descriptor_count = 2;
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    command_list = context.list;
    queue = context.queue;

    context.root_signature = create_empty_root_signature(context.device,
            D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);

    input_layout.pInputElementDescs = layout_desc;
    input_layout.NumElements = ARRAY_SIZE(layout_desc);
    init_pipeline_state_desc(&pso_desc, context.root_signature, desc.rt_format, NULL, NULL, &input_layout);
    pso_desc.NumRenderTargets = 2;
    pso_desc.RTVFormats[1] = DXGI_FORMAT_R32G32B32A32_UINT;

    rtvs[0] = context.rtv;
    rtvs[1] = get_cpu_rtv_handle(&context, context.rtv_heap, 1);
    desc.rt_format = pso_desc.RTVFormats[1];
    create_render_target(&context, &desc, &uint_render_target, &rtvs[1]);

    vb = create_upload_buffer(context.device, sizeof(quad), quad);

    vbv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vb);
    vbv.StrideInBytes = sizeof(*quad);
    vbv.SizeInBytes = sizeof(quad);

    for (i = 0; i < ARRAY_SIZE(tests); ++i)
    {
        vkd3d_test_push_context("Test %u", i);

        pso_desc.VS = *tests[i].vs;
        pso_desc.PS = *tests[i].ps;
        hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
                &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
        ok(hr == S_OK, "Failed to create graphics pipeline state, hr %#x.\n", hr);

        if (i)
        {
            reset_command_list(command_list, context.allocator);
            transition_resource_state(command_list, context.render_target,
                    D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
            transition_resource_state(command_list, uint_render_target,
                    D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
        }

        ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 2, &context.rtv, true, NULL);
        ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
        ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
        ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, 1, &vbv);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
        ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 1, 0, 0);

        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
        check_sub_resource_vec4(context.render_target, 0, queue, command_list, &tests[i].expected_vec4, 0);
        reset_command_list(command_list, context.allocator);
        transition_resource_state(command_list, uint_render_target,
                D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
        check_sub_resource_uvec4(uint_render_target, 0, queue, command_list, &tests[i].expected_uvec4);

        ID3D12PipelineState_Release(context.pipeline_state);
        context.pipeline_state = NULL;

        vkd3d_test_pop_context();
    }

    ID3D12Resource_Release(vb);
    ID3D12Resource_Release(uint_render_target);
    destroy_test_context(&context);
}

static void check_descriptor_range_(const char *file, unsigned int line, const D3D12_DESCRIPTOR_RANGE *range,
        const D3D12_DESCRIPTOR_RANGE *expected_range)
{
    ok_(file, line)(range->RangeType == expected_range->RangeType,
            "Got range type %#x, expected %#x.\n", range->RangeType, expected_range->RangeType);
    ok_(file, line)(range->NumDescriptors == expected_range->NumDescriptors,
            "Got descriptor count %u, expected %u.\n", range->NumDescriptors, expected_range->NumDescriptors);
    ok_(file, line)(range->BaseShaderRegister == expected_range->BaseShaderRegister,
            "Got base shader register %u, expected %u.\n",
            range->BaseShaderRegister, expected_range->BaseShaderRegister);
    ok_(file, line)(range->RegisterSpace == expected_range->RegisterSpace,
            "Got register space %u, expected %u.\n", range->RegisterSpace, expected_range->RegisterSpace);
    ok_(file, line)(range->OffsetInDescriptorsFromTableStart == expected_range->OffsetInDescriptorsFromTableStart,
            "Got offset %u, expected %u.\n", range->OffsetInDescriptorsFromTableStart,
            expected_range->OffsetInDescriptorsFromTableStart);
}

static void check_descriptor_range1_(const char *file, unsigned int line, const D3D12_DESCRIPTOR_RANGE1 *range,
        const D3D12_DESCRIPTOR_RANGE1 *expected_range, bool converted)
{
    unsigned int expected_flags = converted
            ? D3D12_DESCRIPTOR_RANGE_FLAG_DESCRIPTORS_VOLATILE | D3D12_DESCRIPTOR_RANGE_FLAG_DATA_VOLATILE
            : expected_range->Flags;

    ok_(file, line)(range->RangeType == expected_range->RangeType,
            "Got range type %#x, expected %#x.\n", range->RangeType, expected_range->RangeType);
    ok_(file, line)(range->NumDescriptors == expected_range->NumDescriptors,
            "Got descriptor count %u, expected %u.\n", range->NumDescriptors, expected_range->NumDescriptors);
    ok_(file, line)(range->BaseShaderRegister == expected_range->BaseShaderRegister,
            "Got base shader register %u, expected %u.\n",
            range->BaseShaderRegister, expected_range->BaseShaderRegister);
    ok_(file, line)(range->RegisterSpace == expected_range->RegisterSpace,
            "Got register space %u, expected %u.\n", range->RegisterSpace, expected_range->RegisterSpace);
    ok_(file, line)(range->Flags == expected_flags,
            "Got descriptor range flags %#x, expected %#x.\n", range->Flags, expected_flags);
    ok_(file, line)(range->OffsetInDescriptorsFromTableStart == expected_range->OffsetInDescriptorsFromTableStart,
            "Got offset %u, expected %u.\n", range->OffsetInDescriptorsFromTableStart,
            expected_range->OffsetInDescriptorsFromTableStart);
}

static void check_root_parameter_(const char *file, unsigned int line, const D3D12_ROOT_PARAMETER *parameter,
        const D3D12_ROOT_PARAMETER *expected_parameter)
{
    const D3D12_ROOT_DESCRIPTOR *descriptor, *expected_descriptor;
    const D3D12_ROOT_DESCRIPTOR_TABLE *table, *expected_table;
    const D3D12_ROOT_CONSTANTS *constants, *expected_constants;
    unsigned int i;

    ok_(file, line)(parameter->ParameterType == expected_parameter->ParameterType,
            "Got type %#x, expected %#x.\n", parameter->ParameterType, expected_parameter->ParameterType);
    if (parameter->ParameterType != expected_parameter->ParameterType)
        return;

    switch (parameter->ParameterType)
    {
        case D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE:
            table = &parameter->DescriptorTable;
            expected_table = &expected_parameter->DescriptorTable;
            ok_(file, line)(table->NumDescriptorRanges == expected_table->NumDescriptorRanges,
                    "Got range count %u, expected %u.\n",
                    table->NumDescriptorRanges, expected_table->NumDescriptorRanges);
            if (table->NumDescriptorRanges == expected_table->NumDescriptorRanges)
            {
                for (i = 0; i < table->NumDescriptorRanges; ++i)
                    check_descriptor_range_(file, line, &table->pDescriptorRanges[i],
                            &expected_table->pDescriptorRanges[i]);
            }
            break;
        case D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS:
            constants = &parameter->Constants;
            expected_constants = &expected_parameter->Constants;
            ok_(file, line)(constants->ShaderRegister == expected_constants->ShaderRegister,
                    "Got shader register %u, expected %u.\n",
                    constants->ShaderRegister, expected_constants->ShaderRegister);
            ok_(file, line)(constants->RegisterSpace == expected_constants->RegisterSpace,
                    "Got register space %u, expected %u.\n",
                    constants->RegisterSpace, expected_constants->RegisterSpace);
            ok_(file, line)(constants->Num32BitValues == expected_constants->Num32BitValues,
                    "Got 32-bit value count %u, expected %u.\n",
                    constants->Num32BitValues, expected_constants->Num32BitValues);
            break;
        case D3D12_ROOT_PARAMETER_TYPE_CBV:
        case D3D12_ROOT_PARAMETER_TYPE_SRV:
        case D3D12_ROOT_PARAMETER_TYPE_UAV:
            descriptor = &parameter->Descriptor;
            expected_descriptor = &expected_parameter->Descriptor;
            ok_(file, line)(descriptor->ShaderRegister == expected_descriptor->ShaderRegister,
                    "Got shader register %u, expected %u.\n",
                    descriptor->ShaderRegister, expected_descriptor->ShaderRegister);
            ok_(file, line)(descriptor->RegisterSpace == expected_descriptor->RegisterSpace,
                    "Got register space %u, expected %u.\n",
                    descriptor->RegisterSpace, expected_descriptor->RegisterSpace);
            break;
        default:
            trace("Unhandled type %#x.\n", parameter->ParameterType);
    }

    ok_(file, line)(parameter->ShaderVisibility == expected_parameter->ShaderVisibility,
            "Got shader visibility %#x, expected %#x.\n",
            parameter->ShaderVisibility, expected_parameter->ShaderVisibility);
}

static void check_root_parameter1_(const char *file, unsigned int line, const D3D12_ROOT_PARAMETER1 *parameter,
        const D3D12_ROOT_PARAMETER1 *expected_parameter, bool converted)
{
    const D3D12_ROOT_DESCRIPTOR1 *descriptor, *expected_descriptor;
    const D3D12_ROOT_DESCRIPTOR_TABLE1 *table, *expected_table;
    const D3D12_ROOT_CONSTANTS *constants, *expected_constants;
    unsigned int expected_flags;
    unsigned int i;

    ok_(file, line)(parameter->ParameterType == expected_parameter->ParameterType,
            "Got type %#x, expected %#x.\n", parameter->ParameterType, expected_parameter->ParameterType);
    if (parameter->ParameterType != expected_parameter->ParameterType)
        return;

    switch (parameter->ParameterType)
    {
        case D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE:
            table = &parameter->DescriptorTable;
            expected_table = &expected_parameter->DescriptorTable;
            ok_(file, line)(table->NumDescriptorRanges == expected_table->NumDescriptorRanges,
                    "Got range count %u, expected %u.\n",
                    table->NumDescriptorRanges, expected_table->NumDescriptorRanges);
            if (table->NumDescriptorRanges == expected_table->NumDescriptorRanges)
            {
                for (i = 0; i < table->NumDescriptorRanges; ++i)
                    check_descriptor_range1_(file, line, &table->pDescriptorRanges[i],
                            &expected_table->pDescriptorRanges[i], converted);
            }
            break;
        case D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS:
            constants = &parameter->Constants;
            expected_constants = &expected_parameter->Constants;
            ok_(file, line)(constants->ShaderRegister == expected_constants->ShaderRegister,
                    "Got shader register %u, expected %u.\n",
                    constants->ShaderRegister, expected_constants->ShaderRegister);
            ok_(file, line)(constants->RegisterSpace == expected_constants->RegisterSpace,
                    "Got register space %u, expected %u.\n",
                    constants->RegisterSpace, expected_constants->RegisterSpace);
            ok_(file, line)(constants->Num32BitValues == expected_constants->Num32BitValues,
                    "Got 32-bit value count %u, expected %u.\n",
                    constants->Num32BitValues, expected_constants->Num32BitValues);
            break;
        case D3D12_ROOT_PARAMETER_TYPE_CBV:
        case D3D12_ROOT_PARAMETER_TYPE_SRV:
        case D3D12_ROOT_PARAMETER_TYPE_UAV:
            descriptor = &parameter->Descriptor;
            expected_descriptor = &expected_parameter->Descriptor;
            ok_(file, line)(descriptor->ShaderRegister == expected_descriptor->ShaderRegister,
                    "Got shader register %u, expected %u.\n",
                    descriptor->ShaderRegister, expected_descriptor->ShaderRegister);
            ok_(file, line)(descriptor->RegisterSpace == expected_descriptor->RegisterSpace,
                    "Got register space %u, expected %u.\n",
                    descriptor->RegisterSpace, expected_descriptor->RegisterSpace);
            expected_flags = converted ? D3D12_ROOT_DESCRIPTOR_FLAG_DATA_VOLATILE : expected_descriptor->Flags;
            ok_(file, line)(descriptor->Flags == expected_flags,
                    "Got root descriptor flags %#x, expected %#x.\n",
                    descriptor->Flags, expected_flags);
            break;
        default:
            trace("Unhandled type %#x.\n", parameter->ParameterType);
    }

    ok_(file, line)(parameter->ShaderVisibility == expected_parameter->ShaderVisibility,
            "Got shader visibility %#x, expected %#x.\n",
            parameter->ShaderVisibility, expected_parameter->ShaderVisibility);
}

static void check_static_sampler_(const char *file, unsigned int line, const D3D12_STATIC_SAMPLER_DESC *sampler,
        const D3D12_STATIC_SAMPLER_DESC *expected_sampler)
{
    ok_(file, line)(sampler->Filter == expected_sampler->Filter,
            "Got filter %#x, expected %#x.\n", sampler->Filter, expected_sampler->Filter);
    ok_(file, line)(sampler->AddressU == expected_sampler->AddressU,
            "Got address U %#x, expected %#x.\n", sampler->AddressU, expected_sampler->AddressU);
    ok_(file, line)(sampler->AddressV == expected_sampler->AddressV,
            "Got address V %#x, expected %#x.\n", sampler->AddressV, expected_sampler->AddressV);
    ok_(file, line)(sampler->AddressW == expected_sampler->AddressW,
            "Got address W %#x, expected %#x.\n", sampler->AddressW, expected_sampler->AddressW);
    ok_(file, line)(sampler->MipLODBias == expected_sampler->MipLODBias,
            "Got mip LOD bias %.8e, expected %.8e.\n", sampler->MipLODBias, expected_sampler->MipLODBias);
    ok_(file, line)(sampler->MaxAnisotropy == expected_sampler->MaxAnisotropy,
            "Got max anisotropy %u, expected %u.\n", sampler->MaxAnisotropy, expected_sampler->MaxAnisotropy);
    ok_(file, line)(sampler->ComparisonFunc == expected_sampler->ComparisonFunc,
            "Got comparison func %#x, expected %#x.\n", sampler->ComparisonFunc, expected_sampler->ComparisonFunc);
    ok_(file, line)(sampler->BorderColor == expected_sampler->BorderColor,
            "Got border color %#x, expected %#x.\n", sampler->BorderColor, expected_sampler->BorderColor);
    ok_(file, line)(sampler->MinLOD == expected_sampler->MinLOD,
            "Got min LOD %.8e, expected %.8e.\n", sampler->MinLOD, expected_sampler->MinLOD);
    ok_(file, line)(sampler->MaxLOD == expected_sampler->MaxLOD,
            "Got max LOD %.8e, expected %.8e.\n", sampler->MaxLOD, expected_sampler->MaxLOD);
    ok_(file, line)(sampler->ShaderRegister == expected_sampler->ShaderRegister,
            "Got shader register %u, expected %u.\n", sampler->ShaderRegister, expected_sampler->ShaderRegister);
    ok_(file, line)(sampler->RegisterSpace == expected_sampler->RegisterSpace,
            "Got register space %u, expected %u.\n", sampler->RegisterSpace, expected_sampler->RegisterSpace);
    ok_(file, line)(sampler->ShaderVisibility == expected_sampler->ShaderVisibility,
            "Got shader visibility %#x, expected %#x.\n",
            sampler->ShaderVisibility, expected_sampler->ShaderVisibility);
}

#define check_root_signature_desc(desc, expected) check_root_signature_desc_(__FILE__, __LINE__, desc, expected)
static void check_root_signature_desc_(const char *file, unsigned int line, const D3D12_ROOT_SIGNATURE_DESC *desc,
        const D3D12_ROOT_SIGNATURE_DESC *expected_desc)
{
    unsigned int i;

    ok_(file, line)(desc->NumParameters == expected_desc->NumParameters,
            "Got parameter count %u, expected %u.\n",
            desc->NumParameters, expected_desc->NumParameters);
    if (!expected_desc->pParameters)
    {
        ok_(file, line)(!desc->pParameters, "Got unexpected parameters %p.\n", desc->pParameters);
    }
    else if (desc->NumParameters == expected_desc->NumParameters)
    {
        for (i = 0; i < desc->NumParameters; ++i)
            check_root_parameter_(file, line, &desc->pParameters[i], &expected_desc->pParameters[i]);
    }
    ok_(file, line)(desc->NumStaticSamplers == expected_desc->NumStaticSamplers,
            "Got static sampler count %u, expected %u.\n",
            desc->NumStaticSamplers, expected_desc->NumStaticSamplers);
    if (!expected_desc->pStaticSamplers)
    {
        ok_(file, line)(!desc->pStaticSamplers, "Got unexpected static samplers %p.\n", desc->pStaticSamplers);
    }
    else if (desc->NumStaticSamplers == expected_desc->NumStaticSamplers)
    {
        for (i = 0; i < desc->NumStaticSamplers; ++i)
            check_static_sampler_(file, line, &desc->pStaticSamplers[i], &expected_desc->pStaticSamplers[i]);
    }
    ok_(file, line)(desc->Flags == expected_desc->Flags, "Got flags %#x, expected %#x.\n",
            desc->Flags, expected_desc->Flags);
}

#define check_root_signature_desc1(a, b, c) check_root_signature_desc1_(__FILE__, __LINE__, a, b, c)
static void check_root_signature_desc1_(const char *file, unsigned int line, const D3D12_ROOT_SIGNATURE_DESC1 *desc,
        const D3D12_ROOT_SIGNATURE_DESC1 *expected_desc, bool converted)
{
    unsigned int i;

    ok_(file, line)(desc->NumParameters == expected_desc->NumParameters,
            "Got parameter count %u, expected %u.\n",
            desc->NumParameters, expected_desc->NumParameters);
    if (!expected_desc->pParameters)
    {
        ok_(file, line)(!desc->pParameters, "Got unexpected parameters %p.\n", desc->pParameters);
    }
    else if (desc->NumParameters == expected_desc->NumParameters)
    {
        for (i = 0; i < desc->NumParameters; ++i)
            check_root_parameter1_(file, line, &desc->pParameters[i], &expected_desc->pParameters[i], converted);
    }
    ok_(file, line)(desc->NumStaticSamplers == expected_desc->NumStaticSamplers,
            "Got static sampler count %u, expected %u.\n",
            desc->NumStaticSamplers, expected_desc->NumStaticSamplers);
    if (!expected_desc->pStaticSamplers)
    {
        ok_(file, line)(!desc->pStaticSamplers, "Got unexpected static samplers %p.\n", desc->pStaticSamplers);
    }
    else if (desc->NumStaticSamplers == expected_desc->NumStaticSamplers)
    {
        for (i = 0; i < desc->NumStaticSamplers; ++i)
            check_static_sampler_(file, line, &desc->pStaticSamplers[i], &expected_desc->pStaticSamplers[i]);
    }
    ok_(file, line)(desc->Flags == expected_desc->Flags, "Got flags %#x, expected %#x.\n",
            desc->Flags, expected_desc->Flags);
}

#define check_root_signature_deserialization(a, b, c) check_root_signature_deserialization_(__FILE__, __LINE__, a, b, c)
static void check_root_signature_deserialization_(const char *file, unsigned int line,
        const D3D12_SHADER_BYTECODE *code, const D3D12_ROOT_SIGNATURE_DESC *expected_desc,
        const D3D12_ROOT_SIGNATURE_DESC1 *expected_desc1)
{
    const D3D12_VERSIONED_ROOT_SIGNATURE_DESC *versioned_desc, *versioned_desc2;
    ID3D12VersionedRootSignatureDeserializer *versioned_deserializer;
    ID3D12RootSignatureDeserializer *deserializer;
    const D3D12_ROOT_SIGNATURE_DESC *desc;
    ULONG refcount;
    HRESULT hr;

    if (!code->BytecodeLength)
        return;

    hr = D3D12CreateRootSignatureDeserializer(code->pShaderBytecode, code->BytecodeLength,
            &IID_ID3D12RootSignatureDeserializer, (void **)&deserializer);
    ok_(file, line)(hr == S_OK, "Failed to create deserializer, hr %#x.\n", hr);

    desc = ID3D12RootSignatureDeserializer_GetRootSignatureDesc(deserializer);
    ok(desc, "Got NULL root signature desc.\n");
    check_root_signature_desc_(file, line, desc, expected_desc);

    refcount = ID3D12RootSignatureDeserializer_Release(deserializer);
    ok_(file, line)(!refcount, "ID3D12RootSignatureDeserializer has %u references left.\n", (unsigned int)refcount);

    if (!pfn_D3D12CreateVersionedRootSignatureDeserializer)
        return;

    hr = pfn_D3D12CreateVersionedRootSignatureDeserializer(code->pShaderBytecode, code->BytecodeLength,
            &IID_ID3D12VersionedRootSignatureDeserializer, (void **)&versioned_deserializer);
    ok_(file, line)(hr == S_OK, "Failed to create versioned deserializer, hr %#x.\n", hr);

    versioned_desc = ID3D12VersionedRootSignatureDeserializer_GetUnconvertedRootSignatureDesc(versioned_deserializer);
    ok(versioned_desc, "Got NULL root signature desc.\n");
    ok(versioned_desc->Version == D3D_ROOT_SIGNATURE_VERSION_1_0, "Got unexpected version %#x.\n", versioned_desc->Version);
    check_root_signature_desc_(file, line, &versioned_desc->Desc_1_0, expected_desc);

    hr = ID3D12VersionedRootSignatureDeserializer_GetRootSignatureDescAtVersion(versioned_deserializer,
            D3D_ROOT_SIGNATURE_VERSION_1_0, &versioned_desc2);
    ok_(file, line)(hr == S_OK, "Failed to get root signature 1.0, hr %#x.\n", hr);
    ok_(file, line)(versioned_desc2 == versioned_desc, "Got unexpected pointer %p.\n", versioned_desc2);

    hr = ID3D12VersionedRootSignatureDeserializer_GetRootSignatureDescAtVersion(versioned_deserializer,
            D3D_ROOT_SIGNATURE_VERSION_1_1, &versioned_desc);
    ok_(file, line)(hr == S_OK, "Failed to get root signature 1.0, hr %#x.\n", hr);
    ok(versioned_desc, "Got NULL root signature desc.\n");
    ok(versioned_desc->Version == D3D_ROOT_SIGNATURE_VERSION_1_1, "Got unexpected version %#x.\n", versioned_desc->Version);
    check_root_signature_desc1_(file, line, &versioned_desc->Desc_1_1, expected_desc1, true);

    refcount = ID3D12VersionedRootSignatureDeserializer_Release(versioned_deserializer);
    ok_(file, line)(!refcount, "ID3D12VersionedRootSignatureDeserializer has %u references left.\n",
            (unsigned int)refcount);
}

#define check_root_signature_deserialization1(a, b, c) \
        check_root_signature_deserialization1_(__FILE__, __LINE__, a, b, c)
static void check_root_signature_deserialization1_(const char *file, unsigned int line,
        const D3D12_SHADER_BYTECODE *code, const D3D12_ROOT_SIGNATURE_DESC *expected_desc,
        const D3D12_ROOT_SIGNATURE_DESC1 *expected_desc1)
{
    const D3D12_VERSIONED_ROOT_SIGNATURE_DESC *versioned_desc, *versioned_desc2;
    ID3D12VersionedRootSignatureDeserializer *versioned_deserializer;
    ID3D12RootSignatureDeserializer *deserializer;
    const D3D12_ROOT_SIGNATURE_DESC *desc;
    ULONG refcount;
    HRESULT hr;

    hr = pfn_D3D12CreateVersionedRootSignatureDeserializer(code->pShaderBytecode, code->BytecodeLength,
            &IID_ID3D12VersionedRootSignatureDeserializer, (void **)&versioned_deserializer);
    ok_(file, line)(hr == S_OK, "Failed to create deserializer, hr %#x.\n", hr);

    versioned_desc = ID3D12VersionedRootSignatureDeserializer_GetUnconvertedRootSignatureDesc(versioned_deserializer);
    ok(versioned_desc, "Got NULL root signature desc.\n");
    ok(versioned_desc->Version == D3D_ROOT_SIGNATURE_VERSION_1_1, "Got unexpected version %#x.\n", versioned_desc->Version);
    check_root_signature_desc1_(file, line, &versioned_desc->Desc_1_1, expected_desc1, false);

    hr = ID3D12VersionedRootSignatureDeserializer_GetRootSignatureDescAtVersion(versioned_deserializer,
            D3D_ROOT_SIGNATURE_VERSION_1_1, &versioned_desc2);
    ok_(file, line)(hr == S_OK, "Failed to get root signature 1.1, hr %#x.\n", hr);
    ok_(file, line)(versioned_desc2 == versioned_desc, "Got unexpected pointer %p.\n", versioned_desc2);

    hr = ID3D12VersionedRootSignatureDeserializer_GetRootSignatureDescAtVersion(versioned_deserializer,
            D3D_ROOT_SIGNATURE_VERSION_1_0, &versioned_desc);
    ok_(file, line)(hr == S_OK, "Failed to get root signature 1.0, hr %#x.\n", hr);
    ok(versioned_desc, "Got NULL root signature desc.\n");
    ok(versioned_desc->Version == D3D_ROOT_SIGNATURE_VERSION_1_0, "Got unexpected version %#x.\n", versioned_desc->Version);
    check_root_signature_desc_(file, line, &versioned_desc->Desc_1_0, expected_desc);

    refcount = ID3D12VersionedRootSignatureDeserializer_Release(versioned_deserializer);
    ok_(file, line)(!refcount, "ID3D12VersionedRootSignatureDeserializer has %u references left.\n",
            (unsigned int)refcount);

    hr = D3D12CreateRootSignatureDeserializer(code->pShaderBytecode, code->BytecodeLength,
            &IID_ID3D12RootSignatureDeserializer, (void **)&deserializer);
    ok_(file, line)(hr == S_OK, "Failed to create deserializer, hr %#x.\n", hr);

    desc = ID3D12RootSignatureDeserializer_GetRootSignatureDesc(deserializer);
    ok(desc, "Got NULL root signature desc.\n");
    check_root_signature_desc_(file, line, desc, expected_desc);

    refcount = ID3D12RootSignatureDeserializer_Release(deserializer);
    ok_(file, line)(!refcount, "ID3D12RootSignatureDeserializer has %u references left.\n", (unsigned int)refcount);
}

#define check_root_signature_serialization(a, b) check_root_signature_serialization_(__FILE__, __LINE__, a, b)
static void check_root_signature_serialization_(const char *file, unsigned int line,
        const D3D12_SHADER_BYTECODE *bytecode, const D3D12_ROOT_SIGNATURE_DESC *desc)
{
    const DWORD *code = bytecode->pShaderBytecode;
    ID3DBlob *blob, *error_blob;
    DWORD *blob_buffer;
    size_t blob_size;
    unsigned int i;
    HRESULT hr;

    if (!bytecode->BytecodeLength)
        return;

    error_blob = (ID3DBlob *)0xdeadbeef;
    hr = D3D12SerializeRootSignature(desc, D3D_ROOT_SIGNATURE_VERSION_1_0, &blob, &error_blob);
    ok_(file, line)(hr == S_OK, "Failed to serialize root signature, hr %#x.\n", hr);
    ok_(file, line)(!error_blob, "Got unexpected error blob %p.\n", error_blob);

    blob_buffer = ID3D10Blob_GetBufferPointer(blob);
    blob_size = ID3D10Blob_GetBufferSize(blob);
    ok_(file, line)(blob_size == bytecode->BytecodeLength, "Got size %u, expected %u.\n",
            (unsigned int)blob_size, (unsigned int)bytecode->BytecodeLength);

    for (i = 0; i < bytecode->BytecodeLength / sizeof(*code); ++i)
    {
        ok_(file, line)(blob_buffer[i] == code[i], "Got dword %#x, expected %#x at %u.\n",
                (unsigned int)blob_buffer[i], (unsigned int)code[i], i);
    }

    ID3D10Blob_Release(blob);
}

#define check_root_signature_serialization1(a, b) check_root_signature_serialization1_(__FILE__, __LINE__, a, b)
static void check_root_signature_serialization1_(const char *file, unsigned int line,
        const D3D12_SHADER_BYTECODE *bytecode, const D3D12_ROOT_SIGNATURE_DESC1 *desc)
{
    D3D12_VERSIONED_ROOT_SIGNATURE_DESC versioned_desc;
    const DWORD *code = bytecode->pShaderBytecode;
    ID3DBlob *blob, *error_blob;
    DWORD *blob_buffer;
    size_t blob_size;
    unsigned int i;
    HRESULT hr;

    versioned_desc.Version = D3D_ROOT_SIGNATURE_VERSION_1_1;
    versioned_desc.Desc_1_1 = *desc;

    error_blob = (ID3DBlob *)0xdeadbeef;
    hr = pfn_D3D12SerializeVersionedRootSignature(&versioned_desc, &blob, &error_blob);
    ok_(file, line)(hr == S_OK, "Failed to serialize root signature, hr %#x.\n", hr);
    ok_(file, line)(!error_blob, "Got unexpected error blob %p.\n", error_blob);

    blob_buffer = ID3D10Blob_GetBufferPointer(blob);
    blob_size = ID3D10Blob_GetBufferSize(blob);
    ok_(file, line)(blob_size == bytecode->BytecodeLength, "Got size %u, expected %u.\n",
            (unsigned int)blob_size, (unsigned int)bytecode->BytecodeLength);

    for (i = 0; i < bytecode->BytecodeLength / sizeof(*code); ++i)
    {
        ok_(file, line)(blob_buffer[i] == code[i], "Got dword %#x, expected %#x at %u.\n",
                (unsigned int)blob_buffer[i], (unsigned int)code[i], i);
    }

    ID3D10Blob_Release(blob);
}

static void test_root_signature_byte_code(void)
{
    ID3D12VersionedRootSignatureDeserializer *versioned_deserializer;
    ID3D12RootSignatureDeserializer *deserializer;
    ID3DBlob *blob;
    unsigned int i;
    ULONG refcount;
    HRESULT hr;

#if 0
    #define RS ""
#endif
    /* /T rootsig_1_0 /E RS */
    static const DWORD empty_rootsig[] =
    {
        0x43425844, 0xd64afc1d, 0x5dc27735, 0x9edacb4a, 0x6bd8a7fa, 0x00000001, 0x00000044, 0x00000001,
        0x00000024, 0x30535452, 0x00000018, 0x00000001, 0x00000000, 0x00000018, 0x00000000, 0x00000018,
        0x00000000,
    };
    /* /T rootsig_1_1 /E RS */
    static const DWORD empty_rootsig1[] =
    {
        0x43425844, 0x791882cb, 0x83c1db39, 0x327edc93, 0x3163085b, 0x00000001, 0x00000044, 0x00000001,
        0x00000024, 0x30535452, 0x00000018, 0x00000002, 0x00000000, 0x00000018, 0x00000000, 0x00000018,
        0x00000000,
    };
    static const D3D12_ROOT_SIGNATURE_DESC empty_rootsig_desc =
    {
        .Flags = 0,
    };
    static const D3D12_ROOT_SIGNATURE_DESC1 empty_rootsig_desc1 =
    {
        .Flags = 0,
    };

#if 0
    #define RS "RootFlags(ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT)"
#endif
    static const DWORD ia_rootsig[] =
    {
        0x43425844, 0x05bbd62e, 0xc74d3646, 0xde1407a5, 0x0d99273d, 0x00000001, 0x00000044, 0x00000001,
        0x00000024, 0x30535452, 0x00000018, 0x00000001, 0x00000000, 0x00000018, 0x00000000, 0x00000018,
        0x00000001,
    };
    static const DWORD ia_rootsig1[] =
    {
        0x43425844, 0x1e922238, 0xa7743a59, 0x652c0188, 0xe999b061, 0x00000001, 0x00000044, 0x00000001,
        0x00000024, 0x30535452, 0x00000018, 0x00000002, 0x00000000, 0x00000018, 0x00000000, 0x00000018,
        0x00000001,
    };
    static const D3D12_ROOT_SIGNATURE_DESC ia_rootsig_desc =
    {
        .Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT,
    };
    static const D3D12_ROOT_SIGNATURE_DESC1 ia_rootsig_desc1 =
    {
        .Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT,
    };

#if 0
    #define RS "RootFlags(DENY_PIXEL_SHADER_ROOT_ACCESS)"
#endif
    static const DWORD deny_ps_rootsig[] =
    {
        0x43425844, 0xfad3a4ce, 0xf246286e, 0xeaa9e176, 0x278d5137, 0x00000001, 0x00000044, 0x00000001,
        0x00000024, 0x30535452, 0x00000018, 0x00000001, 0x00000000, 0x00000018, 0x00000000, 0x00000018,
        0x00000020,
    };
    static const DWORD deny_ps_rootsig1[] =
    {
        0x43425844, 0xca541ae8, 0x791dbcaa, 0xe8a61219, 0x697a84c7, 0x00000001, 0x00000044, 0x00000001,
        0x00000024, 0x30535452, 0x00000018, 0x00000002, 0x00000000, 0x00000018, 0x00000000, 0x00000018,
        0x00000020,
    };
    static const D3D12_ROOT_SIGNATURE_DESC deny_ps_rootsig_desc =
    {
        .Flags = D3D12_ROOT_SIGNATURE_FLAG_DENY_PIXEL_SHADER_ROOT_ACCESS,
    };
    static const D3D12_ROOT_SIGNATURE_DESC1 deny_ps_rootsig_desc1 =
    {
        .Flags = D3D12_ROOT_SIGNATURE_FLAG_DENY_PIXEL_SHADER_ROOT_ACCESS,
    };

#if 0
    #define RS "CBV(b3, space = 0)"
#endif
    static const DWORD cbv_rootsig[] =
    {
        0x43425844, 0x8dc5087e, 0x5cb9bf0d, 0x2e465ae3, 0x6291e0e0, 0x00000001, 0x00000058, 0x00000001,
        0x00000024, 0x30535452, 0x0000002c, 0x00000001, 0x00000001, 0x00000018, 0x00000000, 0x0000002c,
        0x00000000, 0x00000002, 0x00000000, 0x00000024, 0x00000003, 0x00000000,

    };
    static const DWORD cbv_rootsig1[] =
    {
        0x43425844, 0x66f3e4ad, 0x9938583c, 0x4eaf4733, 0x7940ab73, 0x00000001, 0x0000005c, 0x00000001,
        0x00000024, 0x30535452, 0x00000030, 0x00000002, 0x00000001, 0x00000018, 0x00000000, 0x00000030,
        0x00000000, 0x00000002, 0x00000000, 0x00000024, 0x00000003, 0x00000000, 0x00000000,
    };
    static const D3D12_ROOT_PARAMETER cbv_parameters[] =
    {
        {D3D12_ROOT_PARAMETER_TYPE_CBV, .Descriptor = {3, 0}},
    };
    static const D3D12_ROOT_SIGNATURE_DESC cbv_rootsig_desc =
    {
        .NumParameters = ARRAY_SIZE(cbv_parameters),
        .pParameters = cbv_parameters,
    };
    static const D3D12_ROOT_PARAMETER1 cbv_parameters1[] =
    {
        {D3D12_ROOT_PARAMETER_TYPE_CBV, .Descriptor = {3, 0}},
    };
    static const D3D12_ROOT_SIGNATURE_DESC1 cbv_rootsig_desc1 =
    {
        .NumParameters = ARRAY_SIZE(cbv_parameters1),
        .pParameters = cbv_parameters1,
    };

#if 0
    #define RS "CBV(b4, space = 1, visibility = SHADER_VISIBILITY_GEOMETRY)"
#endif
    static const DWORD cbv2_rootsig[] =
    {
        0x43425844, 0x6d4cfb48, 0xbfecaa8d, 0x379ff9c3, 0x0cc56997, 0x00000001, 0x00000058, 0x00000001,
        0x00000024, 0x30535452, 0x0000002c, 0x00000001, 0x00000001, 0x00000018, 0x00000000, 0x0000002c,
        0x00000000, 0x00000002, 0x00000004, 0x00000024, 0x00000004, 0x00000001,
    };
    static DWORD cbv2_rootsig1[] =
    {
        0x43425844, 0x8450397e, 0x4e136d61, 0xb4fe3b44, 0xc7223872, 0x00000001, 0x0000005c, 0x00000001,
        0x00000024, 0x30535452, 0x00000030, 0x00000002, 0x00000001, 0x00000018, 0x00000000, 0x00000030,
        0x00000000, 0x00000002, 0x00000004, 0x00000024, 0x00000004, 0x00000001, 0x00000000,
    };
    static const D3D12_ROOT_PARAMETER cbv2_parameters[] =
    {
        {D3D12_ROOT_PARAMETER_TYPE_CBV, .Descriptor = {4, 1}, D3D12_SHADER_VISIBILITY_GEOMETRY},
    };
    static const D3D12_ROOT_SIGNATURE_DESC cbv2_rootsig_desc =
    {
        .NumParameters = ARRAY_SIZE(cbv2_parameters),
        .pParameters = cbv2_parameters,
    };
    static const D3D12_ROOT_PARAMETER1 cbv2_parameters1[] =
    {
        {D3D12_ROOT_PARAMETER_TYPE_CBV, .Descriptor = {4, 1}, D3D12_SHADER_VISIBILITY_GEOMETRY},
    };
    static const D3D12_ROOT_SIGNATURE_DESC1 cbv2_rootsig_desc1 =
    {
        .NumParameters = ARRAY_SIZE(cbv2_parameters1),
        .pParameters = cbv2_parameters1,
    };

#if 0
    #define RS "RootFlags(DENY_VERTEX_SHADER_ROOT_ACCESS), SRV(t13)"
#endif
    static const DWORD srv_rootsig[] =
    {
        0x43425844, 0xbc00e5e0, 0xffff2fd3, 0x85c2d405, 0xa61db5e5, 0x00000001, 0x00000058, 0x00000001,
        0x00000024, 0x30535452, 0x0000002c, 0x00000001, 0x00000001, 0x00000018, 0x00000000, 0x0000002c,
        0x00000002, 0x00000003, 0x00000000, 0x00000024, 0x0000000d, 0x00000000,
    };
    static const DWORD srv_rootsig1[] =
    {
        0x43425844, 0xe79f4ac0, 0x1ac0829e, 0x94fddf9d, 0xd83d8bbf, 0x00000001, 0x0000005c, 0x00000001,
        0x00000024, 0x30535452, 0x00000030, 0x00000002, 0x00000001, 0x00000018, 0x00000000, 0x00000030,
        0x00000002, 0x00000003, 0x00000000, 0x00000024, 0x0000000d, 0x00000000, 0x00000000,
    };
    static const D3D12_ROOT_PARAMETER srv_parameters[] =
    {
        {D3D12_ROOT_PARAMETER_TYPE_SRV, .Descriptor = {13}},
    };
    static const D3D12_ROOT_SIGNATURE_DESC srv_rootsig_desc =
    {
        .NumParameters = ARRAY_SIZE(srv_parameters),
        .pParameters = srv_parameters,
        .Flags = D3D12_ROOT_SIGNATURE_FLAG_DENY_VERTEX_SHADER_ROOT_ACCESS,
    };
    static const D3D12_ROOT_PARAMETER1 srv_parameters1[] =
    {
        {D3D12_ROOT_PARAMETER_TYPE_SRV, .Descriptor = {13}},
    };
    static const D3D12_ROOT_SIGNATURE_DESC1 srv_rootsig_desc1 =
    {
        .NumParameters = ARRAY_SIZE(srv_parameters1),
        .pParameters = srv_parameters1,
        .Flags = D3D12_ROOT_SIGNATURE_FLAG_DENY_VERTEX_SHADER_ROOT_ACCESS,
    };

#if 0
    #define RS "UAV(u6)"
#endif
    static const DWORD uav_rootsig[] =
    {
        0x43425844, 0xf873c52c, 0x69f5cbea, 0xaf6bc9f4, 0x2ccf8b54, 0x00000001, 0x00000058, 0x00000001,
        0x00000024, 0x30535452, 0x0000002c, 0x00000001, 0x00000001, 0x00000018, 0x00000000, 0x0000002c,
        0x00000000, 0x00000004, 0x00000000, 0x00000024, 0x00000006, 0x00000000,
    };
    static const DWORD uav_rootsig1[] =
    {
        0x43425844, 0xbd670c62, 0x5c35651b, 0xfb9b9bd1, 0x8a4dddde, 0x00000001, 0x0000005c, 0x00000001,
        0x00000024, 0x30535452, 0x00000030, 0x00000002, 0x00000001, 0x00000018, 0x00000000, 0x00000030,
        0x00000000, 0x00000004, 0x00000000, 0x00000024, 0x00000006, 0x00000000, 0x00000000,
    };
    static const D3D12_ROOT_PARAMETER uav_parameters[] =
    {
        {D3D12_ROOT_PARAMETER_TYPE_UAV, .Descriptor = {6}},
    };
    static const D3D12_ROOT_SIGNATURE_DESC uav_rootsig_desc =
    {
        .NumParameters = ARRAY_SIZE(uav_parameters),
        .pParameters = uav_parameters,
    };
    static const D3D12_ROOT_PARAMETER1 uav_parameters1[] =
    {
        {D3D12_ROOT_PARAMETER_TYPE_UAV, .Descriptor = {6}},
    };
    static const D3D12_ROOT_SIGNATURE_DESC1 uav_rootsig_desc1 =
    {
        .NumParameters = ARRAY_SIZE(uav_parameters1),
        .pParameters = uav_parameters1,
    };

#if 0
    #define RS "CBV(b4, space = 1, visibility = SHADER_VISIBILITY_VERTEX), " \
            "SRV(t13, flags = DATA_STATIC), " \
            "UAV(u6, flags = DATA_STATIC_WHILE_SET_AT_EXECUTE)"
#endif
    static const DWORD root_descriptors_rootsig1[] =
    {
        0x43425844, 0x8ddedbbe, 0xbcfea259, 0x6b35bfbb, 0x23e1de24, 0x00000001, 0x0000008c, 0x00000001,
        0x00000024, 0x30535452, 0x00000060, 0x00000002, 0x00000003, 0x00000018, 0x00000000, 0x00000060,
        0x00000000, 0x00000002, 0x00000001, 0x0000003c, 0x00000003, 0x00000000, 0x00000048, 0x00000004,
        0x00000000, 0x00000054, 0x00000004, 0x00000001, 0x00000000, 0x0000000d, 0x00000000, 0x00000008,
        0x00000006, 0x00000000, 0x00000004,
    };
    static const D3D12_ROOT_PARAMETER root_descriptors_parameters[] =
    {
        {D3D12_ROOT_PARAMETER_TYPE_CBV, .Descriptor = {4, 1}, D3D12_SHADER_VISIBILITY_VERTEX},
        {D3D12_ROOT_PARAMETER_TYPE_SRV, .Descriptor = {13}},
        {D3D12_ROOT_PARAMETER_TYPE_UAV, .Descriptor = {6}},
    };
    static const D3D12_ROOT_SIGNATURE_DESC root_descriptors_desc =
    {
        .NumParameters = ARRAY_SIZE(root_descriptors_parameters),
        .pParameters = root_descriptors_parameters,
    };
    static const D3D12_ROOT_PARAMETER1 root_descriptors_parameters1[] =
    {
        {D3D12_ROOT_PARAMETER_TYPE_CBV, .Descriptor = {4, 1}, D3D12_SHADER_VISIBILITY_VERTEX},
        {D3D12_ROOT_PARAMETER_TYPE_SRV, .Descriptor = {13, 0, D3D12_ROOT_DESCRIPTOR_FLAG_DATA_STATIC}},
        {D3D12_ROOT_PARAMETER_TYPE_UAV, .Descriptor = {6, 0, D3D12_ROOT_DESCRIPTOR_FLAG_DATA_STATIC_WHILE_SET_AT_EXECUTE}},
    };
    static const D3D12_ROOT_SIGNATURE_DESC1 root_descriptors_desc1 =
    {
        .NumParameters = ARRAY_SIZE(root_descriptors_parameters1),
        .pParameters = root_descriptors_parameters1,
    };

#if 0
    #define RS "RootConstants(num32BitConstants=3, b4), " \
            "RootConstants(num32BitConstants=4, b5, space = 3)"
#endif
    static const DWORD constants_rootsig[] =
    {
        0x43425844, 0xbc015590, 0xa9a4a345, 0x7e446850, 0x2be05281, 0x00000001, 0x00000074, 0x00000001,
        0x00000024, 0x30535452, 0x00000048, 0x00000001, 0x00000002, 0x00000018, 0x00000000, 0x00000048,
        0x00000000, 0x00000001, 0x00000000, 0x00000030, 0x00000001, 0x00000000, 0x0000003c, 0x00000004,
        0x00000000, 0x00000003, 0x00000005, 0x00000003, 0x00000004,
    };
    static const DWORD constants_rootsig1[] =
    {
        0x43425844, 0xaa6e3eb1, 0x092b0bd3, 0x63af9657, 0xa97a0fe4, 0x00000001, 0x00000074, 0x00000001,
        0x00000024, 0x30535452, 0x00000048, 0x00000002, 0x00000002, 0x00000018, 0x00000000, 0x00000048,
        0x00000000, 0x00000001, 0x00000000, 0x00000030, 0x00000001, 0x00000000, 0x0000003c, 0x00000004,
        0x00000000, 0x00000003, 0x00000005, 0x00000003, 0x00000004,
    };
    static const D3D12_ROOT_PARAMETER constants_parameters[] =
    {
        {D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS, .Constants = {4, 0, 3}},
        {D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS, .Constants = {5, 3, 4}},
    };
    static const D3D12_ROOT_SIGNATURE_DESC constants_rootsig_desc =
    {
        .NumParameters = ARRAY_SIZE(constants_parameters),
        .pParameters = constants_parameters,
    };
    static const D3D12_ROOT_PARAMETER1 constants_parameters1[] =
    {
        {D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS, .Constants = {4, 0, 3}},
        {D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS, .Constants = {5, 3, 4}},
    };
    static const D3D12_ROOT_SIGNATURE_DESC1 constants_rootsig_desc1 =
    {
        .NumParameters = ARRAY_SIZE(constants_parameters1),
        .pParameters = constants_parameters1,
    };

#if 0
    #define RS "DescriptorTable(CBV(b1, space = 7), " \
            "SRV(t16, numDescriptors = 8), " \
            "UAV(u3, numDescriptors = unbounded, offset = 44))"
#endif
    static const DWORD descriptor_table_rootsig[] =
    {
        0x43425844, 0x0f92e563, 0x4766993f, 0x2304e283, 0x14f0d8dc, 0x00000001, 0x00000094, 0x00000001,
        0x00000024, 0x30535452, 0x00000068, 0x00000001, 0x00000001, 0x00000018, 0x00000000, 0x00000068,
        0x00000000, 0x00000000, 0x00000000, 0x00000024, 0x00000003, 0x0000002c, 0x00000002, 0x00000001,
        0x00000001, 0x00000007, 0xffffffff, 0x00000000, 0x00000008, 0x00000010, 0x00000000, 0xffffffff,
        0x00000001, 0xffffffff, 0x00000003, 0x00000000, 0x0000002c,
    };
    static const DWORD descriptor_table_rootsig1[] =
    {
        0x43425844, 0x739302ac, 0x9db37f96, 0x1ad9eec8, 0x7a5d08cb, 0x00000001, 0x000000a0, 0x00000001,
        0x00000024, 0x30535452, 0x00000074, 0x00000002, 0x00000001, 0x00000018, 0x00000000, 0x00000074,
        0x00000000, 0x00000000, 0x00000000, 0x00000024, 0x00000003, 0x0000002c, 0x00000002, 0x00000001,
        0x00000001, 0x00000007, 0x00000000, 0xffffffff, 0x00000000, 0x00000008, 0x00000010, 0x00000000,
        0x00000000, 0xffffffff, 0x00000001, 0xffffffff, 0x00000003, 0x00000000, 0x00000000, 0x0000002c,
    };
    static const D3D12_DESCRIPTOR_RANGE descriptor_ranges[] =
    {
        {D3D12_DESCRIPTOR_RANGE_TYPE_CBV,        1,  1, 7, D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND},
        {D3D12_DESCRIPTOR_RANGE_TYPE_SRV,        8, 16, 0, D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND},
        {D3D12_DESCRIPTOR_RANGE_TYPE_UAV, UINT_MAX,  3, 0,                                   44},
    };
    static const D3D12_ROOT_PARAMETER descriptor_table_parameters[] =
    {
        {D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE,
                .DescriptorTable = {ARRAY_SIZE(descriptor_ranges), descriptor_ranges}},
    };
    static const D3D12_ROOT_SIGNATURE_DESC descriptor_table_rootsig_desc =
    {
        .NumParameters = ARRAY_SIZE(descriptor_table_parameters),
        .pParameters = descriptor_table_parameters,
    };
    static const D3D12_DESCRIPTOR_RANGE1 descriptor_ranges1[] =
    {
        {D3D12_DESCRIPTOR_RANGE_TYPE_CBV,        1,  1, 7, 0, D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND},
        {D3D12_DESCRIPTOR_RANGE_TYPE_SRV,        8, 16, 0, 0, D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND},
        {D3D12_DESCRIPTOR_RANGE_TYPE_UAV, UINT_MAX,  3, 0, 0,                                   44},
    };
    static const D3D12_ROOT_PARAMETER1 descriptor_table_parameters1[] =
    {
        {D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE,
                .DescriptorTable = {ARRAY_SIZE(descriptor_ranges1), descriptor_ranges1}},
    };
    static const D3D12_ROOT_SIGNATURE_DESC1 descriptor_table_rootsig_desc1 =
    {
        .NumParameters = ARRAY_SIZE(descriptor_table_parameters1),
        .pParameters = descriptor_table_parameters1,
    };

#if 0
    #define RS "DescriptorTable(CBV(b1, space = 7, flags = DESCRIPTORS_VOLATILE), " \
            "SRV(t16, numDescriptors = 8, flags = DESCRIPTORS_VOLATILE | DATA_VOLATILE), " \
            "UAV(u3, numDescriptors = unbounded, offset = 44, flags = DATA_STATIC))"
#endif
    static const DWORD descriptor_table_flags_rootsig1[] =
    {
        0x43425844, 0xe77ffa8f, 0xfab552d5, 0x586e15d4, 0x4c186c26, 0x00000001, 0x000000a0, 0x00000001,
        0x00000024, 0x30535452, 0x00000074, 0x00000002, 0x00000001, 0x00000018, 0x00000000, 0x00000074,
        0x00000000, 0x00000000, 0x00000000, 0x00000024, 0x00000003, 0x0000002c, 0x00000002, 0x00000001,
        0x00000001, 0x00000007, 0x00000001, 0xffffffff, 0x00000000, 0x00000008, 0x00000010, 0x00000000,
        0x00000003, 0xffffffff, 0x00000001, 0xffffffff, 0x00000003, 0x00000000, 0x00000008, 0x0000002c,
    };
    static const D3D12_DESCRIPTOR_RANGE1 descriptor_ranges1_flags[] =
    {
        {D3D12_DESCRIPTOR_RANGE_TYPE_CBV,        1,  1, 7,
                D3D12_DESCRIPTOR_RANGE_FLAG_DESCRIPTORS_VOLATILE, D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND},
        {D3D12_DESCRIPTOR_RANGE_TYPE_SRV,        8, 16, 0,
                D3D12_DESCRIPTOR_RANGE_FLAG_DESCRIPTORS_VOLATILE | D3D12_DESCRIPTOR_RANGE_FLAG_DATA_VOLATILE,
                D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND},
        {D3D12_DESCRIPTOR_RANGE_TYPE_UAV, UINT_MAX,  3, 0,
                D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC, 44},
    };
    static const D3D12_ROOT_PARAMETER1 descriptor_table_parameters1_flags[] =
    {
        {D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE,
                .DescriptorTable = {ARRAY_SIZE(descriptor_ranges1_flags), descriptor_ranges1_flags}},
    };
    static const D3D12_ROOT_SIGNATURE_DESC1 descriptor_table_flags_rootsig_desc1 =
    {
        .NumParameters = ARRAY_SIZE(descriptor_table_parameters1_flags),
        .pParameters = descriptor_table_parameters1_flags,
    };

#if 0
    #define RS "StaticSampler(s4)"
#endif
    static const DWORD default_static_sampler_rootsig[] =
    {
        0x43425844, 0x2876b8ff, 0x935aaa0d, 0x5d2d344a, 0xe002147c, 0x00000001, 0x00000078, 0x00000001,
        0x00000024, 0x30535452, 0x0000004c, 0x00000001, 0x00000000, 0x00000018, 0x00000001, 0x00000018,
        0x00000000, 0x00000055, 0x00000001, 0x00000001, 0x00000001, 0x00000000, 0x00000010, 0x00000004,
        0x00000002, 0x00000000, 0x7f7fffff, 0x00000004, 0x00000000, 0x00000000,
    };
    static const DWORD default_static_sampler_rootsig1[] =
    {
        0x43425844, 0x52b07945, 0x997c0a1e, 0xe4efb9e9, 0x0378e2d4, 0x00000001, 0x00000078, 0x00000001,
        0x00000024, 0x30535452, 0x0000004c, 0x00000002, 0x00000000, 0x00000018, 0x00000001, 0x00000018,
        0x00000000, 0x00000055, 0x00000001, 0x00000001, 0x00000001, 0x00000000, 0x00000010, 0x00000004,
        0x00000002, 0x00000000, 0x7f7fffff, 0x00000004, 0x00000000, 0x00000000,
    };
    static const D3D12_STATIC_SAMPLER_DESC default_static_sampler_desc =
    {
        .Filter = D3D12_FILTER_ANISOTROPIC,
        .AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP,
        .AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP,
        .AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP,
        .MaxAnisotropy = 16,
        .ComparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL,
        .BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE,
        .MaxLOD = D3D12_FLOAT32_MAX,
        .ShaderRegister = 4,
    };
    static const D3D12_ROOT_SIGNATURE_DESC default_static_sampler_rootsig_desc =
    {
        .NumStaticSamplers = 1,
        .pStaticSamplers = &default_static_sampler_desc,
    };
    static const D3D12_ROOT_SIGNATURE_DESC1 default_static_sampler_rootsig_desc1 =
    {
        .NumStaticSamplers = 1,
        .pStaticSamplers = &default_static_sampler_desc,
    };

#if 0
    #define RS "StaticSampler(s0, filter = FILTER_MIN_MAG_MIP_POINT, " \
            "addressV = TEXTURE_ADDRESS_CLAMP, visibility = SHADER_VISIBILITY_PIXEL), " \
            "StaticSampler(s0, filter = FILTER_MIN_MAG_POINT_MIP_LINEAR, " \
            "AddressW = TEXTURE_ADDRESS_BORDER, MipLODBias = 1, maxLod = 10, " \
            "borderColor = STATIC_BORDER_COLOR_OPAQUE_BLACK, space = 3)"
#endif
    static const DWORD static_samplers_rootsig[] =
    {
        0x43425844, 0x52ed526c, 0x892c2d7c, 0xb8ab1123, 0x7e3a727d, 0x00000001, 0x000000ac, 0x00000001,
        0x00000024, 0x30535452, 0x00000080, 0x00000001, 0x00000000, 0x00000018, 0x00000002, 0x00000018,
        0x00000000, 0x00000000, 0x00000001, 0x00000003, 0x00000001, 0x00000000, 0x00000010, 0x00000004,
        0x00000002, 0x00000000, 0x7f7fffff, 0x00000000, 0x00000000, 0x00000005, 0x00000001, 0x00000001,
        0x00000001, 0x00000004, 0x3f800000, 0x00000010, 0x00000004, 0x00000001, 0x00000000, 0x41200000,
        0x00000000, 0x00000003, 0x00000000,
    };
    static const DWORD static_samplers_rootsig1[] =
    {
        0x43425844, 0xcf44eb9e, 0xdbeaed6b, 0xb8d52b6f, 0x0be01c3b, 0x00000001, 0x000000ac, 0x00000001,
        0x00000024, 0x30535452, 0x00000080, 0x00000002, 0x00000000, 0x00000018, 0x00000002, 0x00000018,
        0x00000000, 0x00000000, 0x00000001, 0x00000003, 0x00000001, 0x00000000, 0x00000010, 0x00000004,
        0x00000002, 0x00000000, 0x7f7fffff, 0x00000000, 0x00000000, 0x00000005, 0x00000001, 0x00000001,
        0x00000001, 0x00000004, 0x3f800000, 0x00000010, 0x00000004, 0x00000001, 0x00000000, 0x41200000,
        0x00000000, 0x00000003, 0x00000000,
    };
    static const D3D12_STATIC_SAMPLER_DESC static_sampler_descs[] =
    {
        {
            .Filter = D3D12_FILTER_MIN_MAG_MIP_POINT,
            .AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP,
            .AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP,
            .AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP,
            .MaxAnisotropy = 16,
            .ComparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL,
            .BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE,
            .MaxLOD = D3D12_FLOAT32_MAX,
            .ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL,
        },
        {
            .Filter = D3D12_FILTER_MIN_MAG_POINT_MIP_LINEAR,
            .AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP,
            .AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP,
            .AddressW = D3D12_TEXTURE_ADDRESS_MODE_BORDER,
            .MipLODBias = 1.0f,
            .MaxAnisotropy = 16,
            .ComparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL,
            .BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK,
            .MaxLOD = 10.0f,
            .RegisterSpace = 3,
        }
    };
    static const D3D12_ROOT_SIGNATURE_DESC static_samplers_rootsig_desc =
    {
        .NumStaticSamplers = ARRAY_SIZE(static_sampler_descs),
        .pStaticSamplers = static_sampler_descs,
    };
    static const D3D12_ROOT_SIGNATURE_DESC1 static_samplers_rootsig_desc1 =
    {
        .NumStaticSamplers = ARRAY_SIZE(static_sampler_descs),
        .pStaticSamplers = static_sampler_descs,
    };

    static const struct test
    {
        D3D12_SHADER_BYTECODE code;
        D3D12_SHADER_BYTECODE code1;
        const D3D12_ROOT_SIGNATURE_DESC *desc;
        const D3D12_ROOT_SIGNATURE_DESC1 *desc1;
    }
    tests[] =
    {
        {
            {empty_rootsig, sizeof(empty_rootsig)},
            {empty_rootsig1, sizeof(empty_rootsig1)},
            &empty_rootsig_desc, &empty_rootsig_desc1,
        },
        {
            {ia_rootsig, sizeof(ia_rootsig)},
            {ia_rootsig1, sizeof(ia_rootsig1)},
            &ia_rootsig_desc, &ia_rootsig_desc1,
        },
        {
            {deny_ps_rootsig, sizeof(deny_ps_rootsig)},
            {deny_ps_rootsig1, sizeof(deny_ps_rootsig1)},
            &deny_ps_rootsig_desc, &deny_ps_rootsig_desc1,
        },
        {
            {cbv_rootsig, sizeof(cbv_rootsig)},
            {cbv_rootsig1, sizeof(cbv_rootsig1)},
            &cbv_rootsig_desc, &cbv_rootsig_desc1,
        },
        {
            {cbv2_rootsig, sizeof(cbv2_rootsig)},
            {cbv2_rootsig1, sizeof(cbv2_rootsig1)},
            &cbv2_rootsig_desc, &cbv2_rootsig_desc1,
        },
        {
            {srv_rootsig, sizeof(srv_rootsig)},
            {srv_rootsig1, sizeof(srv_rootsig1)},
            &srv_rootsig_desc, &srv_rootsig_desc1,
        },
        {
            {uav_rootsig, sizeof(uav_rootsig)},
            {uav_rootsig1, sizeof(uav_rootsig1)},
            &uav_rootsig_desc, &uav_rootsig_desc1,
        },
        {
            {NULL},
            {root_descriptors_rootsig1, sizeof(root_descriptors_rootsig1)},
            &root_descriptors_desc, &root_descriptors_desc1,
        },
        {
            {constants_rootsig, sizeof(constants_rootsig)},
            {constants_rootsig1, sizeof(constants_rootsig1)},
            &constants_rootsig_desc, &constants_rootsig_desc1,
        },
        {
            {descriptor_table_rootsig, sizeof(descriptor_table_rootsig)},
            {descriptor_table_rootsig1, sizeof(descriptor_table_rootsig1)},
            &descriptor_table_rootsig_desc, &descriptor_table_rootsig_desc1,
        },
        {
            {NULL},
            {descriptor_table_flags_rootsig1, sizeof(descriptor_table_flags_rootsig1)},
            &descriptor_table_rootsig_desc, &descriptor_table_flags_rootsig_desc1,
        },
        {
            {default_static_sampler_rootsig, sizeof(default_static_sampler_rootsig)},
            {default_static_sampler_rootsig1, sizeof(default_static_sampler_rootsig1)},
            &default_static_sampler_rootsig_desc, &default_static_sampler_rootsig_desc1,
        },
        {
            {static_samplers_rootsig, sizeof(static_samplers_rootsig)},
            {static_samplers_rootsig1, sizeof(static_samplers_rootsig1)},
            &static_samplers_rootsig_desc, &static_samplers_rootsig_desc1,
        },
    };

    hr = D3D12CreateRootSignatureDeserializer(empty_rootsig, sizeof(empty_rootsig),
            &IID_IUnknown, (void **)&deserializer);
    ok(hr == E_NOINTERFACE, "Got unexpected hr %#x.\n", hr);
    hr = D3D12CreateRootSignatureDeserializer(empty_rootsig, sizeof(empty_rootsig),
            &IID_ID3D12VersionedRootSignatureDeserializer, (void **)&deserializer);
    ok(hr == E_NOINTERFACE, "Got unexpected hr %#x.\n", hr);

    hr = D3D12CreateRootSignatureDeserializer(empty_rootsig, sizeof(empty_rootsig),
            &IID_ID3D12RootSignatureDeserializer, (void **)&deserializer);
    ok(hr == S_OK, "Failed to create deserializer, hr %#x.\n", hr);

    check_interface(deserializer, &IID_IUnknown, false);
    check_interface(deserializer, &IID_ID3D12RootSignatureDeserializer, true);
    check_interface(deserializer, &IID_ID3D12VersionedRootSignatureDeserializer, false);
    check_interface(deserializer, &IID_ID3D12Object, false);
    check_interface(deserializer, &IID_ID3D12DeviceChild, false);
    check_interface(deserializer, &IID_ID3D12Pageable, false);

    refcount = ID3D12RootSignatureDeserializer_Release(deserializer);
    ok(!refcount, "ID3D12RootSignatureDeserializer has %u references left.\n", (unsigned int)refcount);

    for (i = 0; i < ARRAY_SIZE(tests); ++i)
    {
        const struct test *t = &tests[i];

        vkd3d_test_push_context("Test %u", i);

        check_root_signature_deserialization(&t->code, t->desc, t->desc1);
        check_root_signature_serialization(&t->code, t->desc);

        blob = (ID3DBlob *)0xdeadbeef;
        hr = D3D12SerializeRootSignature(t->desc, D3D_ROOT_SIGNATURE_VERSION_1_1, &blob, NULL);
        ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
        ok(blob == (ID3DBlob *)0xdeadbeef, "Got unexpected blob %p.\n", blob);

        if (pfn_D3D12CreateVersionedRootSignatureDeserializer)
        {
            check_root_signature_deserialization1(&t->code1, t->desc, t->desc1);
            check_root_signature_serialization1(&t->code1, t->desc1);
        }

        vkd3d_test_pop_context();
    }

    if (!pfn_D3D12CreateVersionedRootSignatureDeserializer)
    {
        skip("D3D12CreateVersionedRootSignatureDeserializer is not available.\n");
        return;
    }

    hr = pfn_D3D12CreateVersionedRootSignatureDeserializer(empty_rootsig, sizeof(empty_rootsig),
            &IID_IUnknown, (void **)&versioned_deserializer);
    ok(hr == E_NOINTERFACE, "Got unexpected hr %#x.\n", hr);
    hr = pfn_D3D12CreateVersionedRootSignatureDeserializer(empty_rootsig, sizeof(empty_rootsig),
            &IID_ID3D12RootSignatureDeserializer, (void **)&versioned_deserializer);
    ok(hr == E_NOINTERFACE, "Got unexpected hr %#x.\n", hr);

    versioned_deserializer = (ID3D12VersionedRootSignatureDeserializer *)0xdeadbeef;
    hr = pfn_D3D12CreateVersionedRootSignatureDeserializer(empty_rootsig, 0,
            &IID_ID3D12VersionedRootSignatureDeserializer, (void **)&versioned_deserializer);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    ok(!versioned_deserializer, "Got unexpected versioned_deserializer %p.\n", versioned_deserializer);

    versioned_deserializer = (ID3D12VersionedRootSignatureDeserializer *)0xdeadbeef;
    hr = pfn_D3D12CreateVersionedRootSignatureDeserializer(NULL, 0,
            &IID_ID3D12VersionedRootSignatureDeserializer, (void **)&versioned_deserializer);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    ok(!versioned_deserializer, "Got unexpected versioned_deserializer %p.\n", versioned_deserializer);

    versioned_deserializer = (ID3D12VersionedRootSignatureDeserializer *)0xdeadbeef;
    i = 0xdeadbeef;
    hr = pfn_D3D12CreateVersionedRootSignatureDeserializer(&i, sizeof(i),
            &IID_ID3D12VersionedRootSignatureDeserializer, (void **)&versioned_deserializer);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    ok(!versioned_deserializer, "Got unexpected versioned_deserializer %p.\n", versioned_deserializer);

    hr = pfn_D3D12CreateVersionedRootSignatureDeserializer(empty_rootsig, sizeof(empty_rootsig),
            &IID_ID3D12VersionedRootSignatureDeserializer, (void **)&versioned_deserializer);
    ok(hr == S_OK, "Failed to create deserializer, hr %#x.\n", hr);

    check_interface(versioned_deserializer, &IID_IUnknown, false);
    check_interface(versioned_deserializer, &IID_ID3D12RootSignatureDeserializer, false);
    check_interface(versioned_deserializer, &IID_ID3D12VersionedRootSignatureDeserializer, true);
    check_interface(versioned_deserializer, &IID_ID3D12Object, false);
    check_interface(versioned_deserializer, &IID_ID3D12DeviceChild, false);
    check_interface(versioned_deserializer, &IID_ID3D12Pageable, false);

    refcount = ID3D12VersionedRootSignatureDeserializer_Release(versioned_deserializer);
    ok(!refcount, "ID3D12VersionedRootSignatureDeserializer has %u references left.\n", (unsigned int)refcount);
}

static void test_cs_constant_buffer(void)
{
    D3D12_CPU_DESCRIPTOR_HANDLE cpu_descriptor_handle;
    D3D12_GPU_DESCRIPTOR_HANDLE gpu_descriptor_handle;
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    D3D12_DESCRIPTOR_RANGE descriptor_ranges[1];
    D3D12_UNORDERED_ACCESS_VIEW_DESC uav_desc;
    ID3D12GraphicsCommandList *command_list;
    D3D12_ROOT_PARAMETER root_parameters[2];
    ID3D12DescriptorHeap *descriptor_heap;
    D3D12_DESCRIPTOR_HEAP_DESC heap_desc;
    ID3D12RootSignature *root_signature;
    ID3D12PipelineState *pipeline_state;
    struct d3d12_resource_readback rb;
    ID3D12Resource *resource, *cb;
    unsigned int descriptor_size;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Device *device;
    unsigned int i;
    float value;
    HRESULT hr;

    static const DWORD cs_code[] =
    {
#if 0
        cbuffer cb : register(b7)
        {
            float value;
        };

        RWBuffer<float> buffer;

        [numthreads(32, 1, 1)]
        void main(uint3 group_id : SV_groupID, uint group_index : SV_GroupIndex)
        {
            uint global_index = 32 * group_id.x + group_index;
            buffer[global_index] = value;
        }
#endif
        0x43425844, 0xbcbca6fb, 0x0bd883e5, 0x8e0848ea, 0xaf152cfd, 0x00000001, 0x000000e8, 0x00000003,
        0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x00000094, 0x00050050, 0x00000025, 0x0100086a,
        0x04000059, 0x00208e46, 0x00000007, 0x00000001, 0x0400089c, 0x0011e000, 0x00000000, 0x00005555,
        0x0200005f, 0x00024000, 0x0200005f, 0x00021012, 0x02000068, 0x00000001, 0x0400009b, 0x00000020,
        0x00000001, 0x00000001, 0x07000023, 0x00100012, 0x00000000, 0x0002100a, 0x00004001, 0x00000020,
        0x0002400a, 0x080000a4, 0x0011e0f2, 0x00000000, 0x00100006, 0x00000000, 0x00208006, 0x00000007,
        0x00000000, 0x0100003e,
    };

    if (!init_compute_test_context(&context))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    value = 2.0f;
    cb = create_upload_buffer(context.device, sizeof(value), &value);

    descriptor_ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
    descriptor_ranges[0].NumDescriptors = 4;
    descriptor_ranges[0].BaseShaderRegister = 0;
    descriptor_ranges[0].RegisterSpace = 0;
    descriptor_ranges[0].OffsetInDescriptorsFromTableStart = 0;
    root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameters[0].DescriptorTable.NumDescriptorRanges = 1;
    root_parameters[0].DescriptorTable.pDescriptorRanges = descriptor_ranges;
    root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    root_parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
    root_parameters[1].Descriptor.ShaderRegister = 7;
    root_parameters[1].Descriptor.RegisterSpace = 0;
    root_parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    root_signature_desc.NumParameters = 2;
    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(device, &root_signature_desc, &root_signature);
    ok(SUCCEEDED(hr), "Failed to create root signature, hr %#x.\n", hr);

    pipeline_state = create_compute_pipeline_state(device, root_signature,
            shader_bytecode(cs_code, sizeof(cs_code)));

    heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
    heap_desc.NumDescriptors = 4;
    heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
    heap_desc.NodeMask = 0;
    hr = ID3D12Device_CreateDescriptorHeap(device, &heap_desc,
            &IID_ID3D12DescriptorHeap, (void **)&descriptor_heap);
    ok(SUCCEEDED(hr), "Failed to create descriptor heap, hr %#x.\n", hr);

    descriptor_size = ID3D12Device_GetDescriptorHandleIncrementSize(device,
            D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);

    cpu_descriptor_handle = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(descriptor_heap);
    gpu_descriptor_handle = ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(descriptor_heap);

    resource = create_default_buffer(device, 64 * sizeof(float),
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

    uav_desc.Format = DXGI_FORMAT_R32_FLOAT;
    uav_desc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
    uav_desc.Buffer.FirstElement = 0;
    uav_desc.Buffer.NumElements = 64;
    uav_desc.Buffer.StructureByteStride = 0;
    uav_desc.Buffer.CounterOffsetInBytes = 0;
    uav_desc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_NONE;
    ID3D12Device_CreateUnorderedAccessView(device, resource, NULL, &uav_desc, cpu_descriptor_handle);
    /* For tier 1 hardware all descriptors must be populated. */
    for (i = 1; i < heap_desc.NumDescriptors; ++i)
    {
        cpu_descriptor_handle.ptr += descriptor_size;
        ID3D12Device_CreateUnorderedAccessView(device, NULL, NULL, &uav_desc, cpu_descriptor_handle);
    }

    ID3D12GraphicsCommandList_SetComputeRootSignature(command_list, root_signature);
    ID3D12GraphicsCommandList_SetComputeRootConstantBufferView(command_list, 1,
            ID3D12Resource_GetGPUVirtualAddress(cb));
    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &descriptor_heap);
    ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(command_list, 0, gpu_descriptor_handle);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, pipeline_state);
    ID3D12GraphicsCommandList_Dispatch(command_list, 2, 1, 1);

    transition_sub_resource_state(command_list, resource, 0,
            D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_buffer_readback_with_command_list(resource, uav_desc.Format, &rb, queue, command_list);
    check_readback_data_float(&rb.rb, NULL, 2.0f, 0);
    release_resource_readback(&rb);

    value = 6.0f;
    update_buffer_data(cb, 0, sizeof(value), &value);

    reset_command_list(command_list, context.allocator);
    transition_sub_resource_state(command_list, resource, 0,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

    ID3D12GraphicsCommandList_SetPipelineState(command_list, pipeline_state);
    ID3D12GraphicsCommandList_SetComputeRootSignature(command_list, root_signature);
    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &descriptor_heap);
    ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(command_list, 0, gpu_descriptor_handle);
    ID3D12GraphicsCommandList_SetComputeRootConstantBufferView(command_list, 1,
            ID3D12Resource_GetGPUVirtualAddress(cb));
    ID3D12GraphicsCommandList_Dispatch(command_list, 2, 1, 1);

    transition_sub_resource_state(command_list, resource, 0,
            D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_buffer_readback_with_command_list(resource, uav_desc.Format, &rb, queue, command_list);
    check_readback_data_float(&rb.rb, NULL, 6.0f, 0);
    release_resource_readback(&rb);

    ID3D12Resource_Release(cb);
    ID3D12Resource_Release(resource);
    ID3D12RootSignature_Release(root_signature);
    ID3D12PipelineState_Release(pipeline_state);
    ID3D12DescriptorHeap_Release(descriptor_heap);
    destroy_test_context(&context);
}

static void test_constant_buffer_relative_addressing(void)
{
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    D3D12_ROOT_PARAMETER root_parameters[2];
    ID3D12GraphicsCommandList *command_list;
    struct d3d12_resource_readback rb;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Resource *uav, *cb;
    ID3D12Device *device;
    unsigned int i;
    HRESULT hr;

    static const DWORD cs_code[] =
    {
#if 0
        cbuffer b0
        {
            uint4 pad;
            uint4 data[4];
        };

        RWByteAddressBuffer u0;

        [numthreads(4, 1, 1)]
        void main(uint tid : SV_GroupThreadID)
        {
            uint location = 4 * tid;
            u0.Store4(4 * location, data[tid]);
        }
#endif
        0x43425844, 0x759a28a0, 0xdd34cd41, 0x73702692, 0x739a66ea, 0x00000001, 0x000000f0, 0x00000003,
        0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x0000009c, 0x00050050, 0x00000027, 0x0100086a,
        0x04000859, 0x00208e46, 0x00000000, 0x00000005, 0x0300009d, 0x0011e000, 0x00000000, 0x0200005f,
        0x00022012, 0x02000068, 0x00000001, 0x0400009b, 0x00000004, 0x00000001, 0x00000001, 0x06000029,
        0x00100012, 0x00000000, 0x0002200a, 0x00004001, 0x00000004, 0x04000036, 0x00100022, 0x00000000,
        0x0002200a, 0x0a0000a6, 0x0011e0f2, 0x00000000, 0x0010000a, 0x00000000, 0x06208e46, 0x00000000,
        0x00000001, 0x0010001a, 0x00000000, 0x0100003e,
    };
    static const struct uvec4 cb_data[] =
    {
        {0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef},
        {1, 2, 3, 4},
        {4, 4, 9, 8},
        {4, 5, 6, 7},
        {6, 0, 6, 0},
    };

    if (!init_compute_test_context(&context))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    cb = create_upload_buffer(context.device, sizeof(cb_data), &cb_data);

    root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV;
    root_parameters[0].Descriptor.ShaderRegister = 0;
    root_parameters[0].Descriptor.RegisterSpace = 0;
    root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    root_parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
    root_parameters[1].Descriptor.ShaderRegister = 0;
    root_parameters[1].Descriptor.RegisterSpace = 0;
    root_parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    root_signature_desc.NumParameters = 2;
    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(device, &root_signature_desc, &context.root_signature);
    ok(SUCCEEDED(hr), "Failed to create root signature, hr %#x.\n", hr);

    context.pipeline_state = create_compute_pipeline_state(device, context.root_signature,
            shader_bytecode(cs_code, sizeof(cs_code)));

    uav = create_default_buffer(device, 16 * sizeof(uint32_t),
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

    ID3D12GraphicsCommandList_SetComputeRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetComputeRootUnorderedAccessView(command_list, 0,
            ID3D12Resource_GetGPUVirtualAddress(uav));
    ID3D12GraphicsCommandList_SetComputeRootConstantBufferView(command_list, 1,
            ID3D12Resource_GetGPUVirtualAddress(cb));
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_Dispatch(command_list, 1, 1, 1);

    transition_sub_resource_state(command_list, uav, 0,
            D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_buffer_readback_with_command_list(uav, DXGI_FORMAT_R32_UINT, &rb, queue, command_list);
    for (i = 0; i < rb.rb.width; ++i)
    {
        unsigned int got = get_readback_uint(&rb.rb, i, 0, 0);
        const unsigned int *expected = &cb_data[1].x;
        ok(got == expected[i], "Got %#x, expected %#x at %u.\n", got, expected[i], i);
    }
    release_resource_readback(&rb);

    ID3D12Resource_Release(cb);
    ID3D12Resource_Release(uav);
    destroy_test_context(&context);
}

static void test_immediate_constant_buffer(void)
{
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    ID3D12GraphicsCommandList *command_list;
    struct test_context_desc desc;
    struct test_context context;
    unsigned int index[4] = {0};
    ID3D12CommandQueue *queue;
    ID3D12Resource *cb;
    unsigned int i;

    static const DWORD ps_code[] =
    {
#if 0
        uint index;

        static const int int_array[6] =
        {
            310, 111, 212, -513, -318, 0,
        };

        static const uint uint_array[6] =
        {
            2, 7, 0x7f800000, 0xff800000, 0x7fc00000, 0
        };

        static const float float_array[6] =
        {
            76, 83.5f, 0.5f, 0.75f, -0.5f, 0.0f,
        };

        float4 main() : SV_Target
        {
            return float4(int_array[index], uint_array[index], float_array[index], 1.0f);
        }
#endif
        0x43425844, 0xbad068da, 0xd631ea3c, 0x41648374, 0x3ccd0120, 0x00000001, 0x00000184, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x52444853, 0x0000010c, 0x00000040, 0x00000043,
        0x00001835, 0x0000001a, 0x00000136, 0x00000002, 0x42980000, 0x00000000, 0x0000006f, 0x00000007,
        0x42a70000, 0x00000000, 0x000000d4, 0x7f800000, 0x3f000000, 0x00000000, 0xfffffdff, 0xff800000,
        0x3f400000, 0x00000000, 0xfffffec2, 0x7fc00000, 0xbf000000, 0x00000000, 0x00000000, 0x00000000,
        0x00000000, 0x00000000, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2,
        0x00000000, 0x02000068, 0x00000001, 0x05000036, 0x00102082, 0x00000000, 0x00004001, 0x3f800000,
        0x06000036, 0x00100012, 0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x06000056, 0x00102022,
        0x00000000, 0x0090901a, 0x0010000a, 0x00000000, 0x0600002b, 0x00102012, 0x00000000, 0x0090900a,
        0x0010000a, 0x00000000, 0x06000036, 0x00102042, 0x00000000, 0x0090902a, 0x0010000a, 0x00000000,
        0x0100003e,
    };
    static const unsigned int MAX_CB_SIZE = D3D12_REQ_IMMEDIATE_CONSTANT_BUFFER_ELEMENT_COUNT * sizeof(struct vec4);
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};
    static struct vec4 expected_result[] =
    {
        { 310.0f,          2.0f, 76.00f, 1.0f},
        { 111.0f,          7.0f, 83.50f, 1.0f},
        { 212.0f, 2139095040.0f,  0.50f, 1.0f},
        {-513.0f, 4286578688.0f,  0.75f, 1.0f},
        {-318.0f, 2143289344.0f, -0.50f, 1.0f},
        {   0.0f,          0.0f,  0.0f,  1.0f},
    };

    memset(&desc, 0, sizeof(desc));
    desc.rt_format = DXGI_FORMAT_R32G32B32A32_FLOAT;
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    command_list = context.list;
    queue = context.queue;

    context.root_signature = create_cb_root_signature(context.device,
            0, D3D12_SHADER_VISIBILITY_PIXEL, D3D12_ROOT_SIGNATURE_FLAG_NONE);
    context.pipeline_state = create_pipeline_state(context.device,
            context.root_signature, desc.rt_format, NULL, &ps, NULL);

    cb = create_upload_buffer(context.device, 2 * MAX_CB_SIZE, NULL);

    for (i = 0; i < ARRAY_SIZE(expected_result); ++i)
    {
        *index = i;
        update_buffer_data(cb, 0, sizeof(index), index);

        if (i)
            transition_resource_state(command_list, context.render_target,
                    D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

        ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
        ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 0,
                ID3D12Resource_GetGPUVirtualAddress(cb));
        ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
        ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
        ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
        check_sub_resource_vec4(context.render_target, 0, queue, command_list, &expected_result[i], 0);

        reset_command_list(command_list, context.allocator);
    }

    ID3D12Resource_Release(cb);
    destroy_test_context(&context);
}

static void test_root_constants(void)
{
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    static const unsigned int constants[4] = {0, 1, 0, 2};

    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    D3D12_ROOT_PARAMETER root_parameters[3];
    ID3D12GraphicsCommandList *command_list;
    struct vec4 vs_cb_color, ps_cb_color;
    struct test_context_desc desc;
    struct test_context context;
    struct vec4 expected_result;
    ID3D12CommandQueue *queue;
    HRESULT hr;

    static const DWORD ps_uint_constant_code[] =
    {
#if 0
        uint4 constants;

        float4 main() : SV_Target
        {
            return (float4)constants;
        }
#endif
        0x43425844, 0xf744186d, 0x6805439a, 0x491c3625, 0xe3e4053c, 0x00000001, 0x000000bc, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000044, 0x00000050, 0x00000011,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x06000056, 0x001020f2, 0x00000000, 0x00208e46, 0x00000000, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_uint_constant = {ps_uint_constant_code, sizeof(ps_uint_constant_code)};
    static const DWORD vs_color_code[] =
    {
#if 0
        float4 constant_color;

        void main(uint id : SV_VertexID,
                out float4 position : SV_Position, out float4 color : COLOR)
        {
            float2 coords = float2((id << 1) & 2, id & 2);
            position = float4(coords * float2(2, -2) + float2(-1, 1), 0, 1);
            color = constant_color;
        }
#endif
        0x43425844, 0x7c3173fb, 0xdd990625, 0x290ad676, 0x50b41793, 0x00000001, 0x000001e0, 0x00000003,
        0x0000002c, 0x00000060, 0x000000b4, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000006, 0x00000001, 0x00000000, 0x00000101, 0x565f5653, 0x65747265, 0x00444978,
        0x4e47534f, 0x0000004c, 0x00000002, 0x00000008, 0x00000038, 0x00000000, 0x00000001, 0x00000003,
        0x00000000, 0x0000000f, 0x00000044, 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x0000000f,
        0x505f5653, 0x7469736f, 0x006e6f69, 0x4f4c4f43, 0xabab0052, 0x58454853, 0x00000124, 0x00010050,
        0x00000049, 0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x04000060, 0x00101012,
        0x00000000, 0x00000006, 0x04000067, 0x001020f2, 0x00000000, 0x00000001, 0x03000065, 0x001020f2,
        0x00000001, 0x02000068, 0x00000001, 0x0b00008c, 0x00100012, 0x00000000, 0x00004001, 0x00000001,
        0x00004001, 0x00000001, 0x0010100a, 0x00000000, 0x00004001, 0x00000000, 0x07000001, 0x00100042,
        0x00000000, 0x0010100a, 0x00000000, 0x00004001, 0x00000002, 0x05000056, 0x00100032, 0x00000000,
        0x00100086, 0x00000000, 0x0f000032, 0x00102032, 0x00000000, 0x00100046, 0x00000000, 0x00004002,
        0x40000000, 0xc0000000, 0x00000000, 0x00000000, 0x00004002, 0xbf800000, 0x3f800000, 0x00000000,
        0x00000000, 0x08000036, 0x001020c2, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000,
        0x3f800000, 0x06000036, 0x001020f2, 0x00000001, 0x00208e46, 0x00000000, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE vs_color = {vs_color_code, sizeof(vs_color_code)};
    static const DWORD ps_color_code[] =
    {
#if 0
        float4 color;

        float4 main(float4 position : SV_POSITION, float4 in_color : COLOR) : SV_Target
        {
            if (any(color != in_color))
                return float4(0.0f, 0.0f, 1.0f, 1.0f);
            return in_color;
        }
#endif
        0x43425844, 0xb1e305a3, 0x962c4d64, 0x6b2c5515, 0x4fb4f524, 0x00000001, 0x0000019c, 0x00000003,
        0x0000002c, 0x00000080, 0x000000b4, 0x4e475349, 0x0000004c, 0x00000002, 0x00000008, 0x00000038,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x00000044, 0x00000000, 0x00000000,
        0x00000003, 0x00000001, 0x00000f0f, 0x505f5653, 0x5449534f, 0x004e4f49, 0x4f4c4f43, 0xabab0052,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x000000e0, 0x00000050,
        0x00000038, 0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03001062, 0x001010f2,
        0x00000001, 0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000001, 0x08000039, 0x001000f2,
        0x00000000, 0x00208e46, 0x00000000, 0x00000000, 0x00101e46, 0x00000001, 0x0700003c, 0x00100032,
        0x00000000, 0x00100ae6, 0x00000000, 0x00100046, 0x00000000, 0x0700003c, 0x00100012, 0x00000000,
        0x0010001a, 0x00000000, 0x0010000a, 0x00000000, 0x0304001f, 0x0010000a, 0x00000000, 0x08000036,
        0x001020f2, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x3f800000, 0x3f800000, 0x0100003e,
        0x01000015, 0x05000036, 0x001020f2, 0x00000000, 0x00101e46, 0x00000001, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_color = {ps_color_code, sizeof(ps_color_code)};
    static const DWORD vs_mix_code[] =
    {
#if 0
        cbuffer shared_cb
        {
            uint token;
            uint op;
        };

        cbuffer vs_cb
        {
            float4 padding;
            float4 vs_color;
        };

        void main(uint id : SV_VertexID,
                out float4 position : SV_Position, out float4 color : COLOR,
                out uint vs_token : TOKEN)
        {
            float2 coords = float2((id << 1) & 2, id & 2);
            position = float4(coords * float2(2, -2) + float2(-1, 1), 0, 1);
            color = vs_color;
            vs_token = token;
        }
#endif
        0x43425844, 0xb5bc00c3, 0x6b5041fe, 0xd55d1d86, 0x34a2a229, 0x00000001, 0x00000230, 0x00000003,
        0x0000002c, 0x00000060, 0x000000d0, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000006, 0x00000001, 0x00000000, 0x00000101, 0x565f5653, 0x65747265, 0x00444978,
        0x4e47534f, 0x00000068, 0x00000003, 0x00000008, 0x00000050, 0x00000000, 0x00000001, 0x00000003,
        0x00000000, 0x0000000f, 0x0000005c, 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x0000000f,
        0x00000062, 0x00000000, 0x00000000, 0x00000001, 0x00000002, 0x00000e01, 0x505f5653, 0x7469736f,
        0x006e6f69, 0x4f4c4f43, 0x4f540052, 0x004e454b, 0x58454853, 0x00000158, 0x00010050, 0x00000056,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x04000059, 0x00208e46, 0x00000001,
        0x00000002, 0x04000060, 0x00101012, 0x00000000, 0x00000006, 0x04000067, 0x001020f2, 0x00000000,
        0x00000001, 0x03000065, 0x001020f2, 0x00000001, 0x03000065, 0x00102012, 0x00000002, 0x02000068,
        0x00000001, 0x0b00008c, 0x00100012, 0x00000000, 0x00004001, 0x00000001, 0x00004001, 0x00000001,
        0x0010100a, 0x00000000, 0x00004001, 0x00000000, 0x07000001, 0x00100042, 0x00000000, 0x0010100a,
        0x00000000, 0x00004001, 0x00000002, 0x05000056, 0x00100032, 0x00000000, 0x00100086, 0x00000000,
        0x0f000032, 0x00102032, 0x00000000, 0x00100046, 0x00000000, 0x00004002, 0x40000000, 0xc0000000,
        0x00000000, 0x00000000, 0x00004002, 0xbf800000, 0x3f800000, 0x00000000, 0x00000000, 0x08000036,
        0x001020c2, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x3f800000, 0x06000036,
        0x001020f2, 0x00000001, 0x00208e46, 0x00000001, 0x00000001, 0x06000036, 0x00102012, 0x00000002,
        0x0020800a, 0x00000000, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE vs_mix = {vs_mix_code, sizeof(vs_mix_code)};
    static const DWORD ps_mix_code[] =
    {
#if 0
        cbuffer shared_cb
        {
            uint token;
            uint op;
        };

        cbuffer ps_cb
        {
            float4 ps_color;
        };

        float4 main(float4 position : SV_POSITION, float4 vs_color : COLOR,
                uint vs_token : TOKEN) : SV_Target
        {
            if (token != vs_token)
                return (float4)1.0f;

            switch (op)
            {
                case 0: return vs_color;
                case 1: return ps_color;
                case 2: return vs_color * ps_color;
                default: return (float4)0.0f;
            }
        }
#endif
        0x43425844, 0x128ef4ce, 0xa1c46517, 0x34ca76f3, 0x3c7d6112, 0x00000001, 0x00000240, 0x00000003,
        0x0000002c, 0x0000009c, 0x000000d0, 0x4e475349, 0x00000068, 0x00000003, 0x00000008, 0x00000050,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x0000005c, 0x00000000, 0x00000000,
        0x00000003, 0x00000001, 0x00000f0f, 0x00000062, 0x00000000, 0x00000000, 0x00000001, 0x00000002,
        0x00000101, 0x505f5653, 0x5449534f, 0x004e4f49, 0x4f4c4f43, 0x4f540052, 0x004e454b, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000168, 0x00000050, 0x0000005a,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x04000059, 0x00208e46, 0x00000001,
        0x00000001, 0x03001062, 0x001010f2, 0x00000001, 0x03000862, 0x00101012, 0x00000002, 0x03000065,
        0x001020f2, 0x00000000, 0x02000068, 0x00000001, 0x08000027, 0x00100012, 0x00000000, 0x0020800a,
        0x00000000, 0x00000000, 0x0010100a, 0x00000002, 0x0304001f, 0x0010000a, 0x00000000, 0x08000036,
        0x001020f2, 0x00000000, 0x00004002, 0x3f800000, 0x3f800000, 0x3f800000, 0x3f800000, 0x0100003e,
        0x01000015, 0x0400004c, 0x0020801a, 0x00000000, 0x00000000, 0x03000006, 0x00004001, 0x00000000,
        0x05000036, 0x001020f2, 0x00000000, 0x00101e46, 0x00000001, 0x0100003e, 0x03000006, 0x00004001,
        0x00000001, 0x06000036, 0x001020f2, 0x00000000, 0x00208e46, 0x00000001, 0x00000000, 0x0100003e,
        0x03000006, 0x00004001, 0x00000002, 0x08000038, 0x001020f2, 0x00000000, 0x00101e46, 0x00000001,
        0x00208e46, 0x00000001, 0x00000000, 0x0100003e, 0x0100000a, 0x08000036, 0x001020f2, 0x00000000,
        0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0100003e, 0x01000017, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_mix = {ps_mix_code, sizeof(ps_mix_code)};

    memset(&desc, 0, sizeof(desc));
    desc.rt_format = DXGI_FORMAT_R32G32B32A32_FLOAT;
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    command_list = context.list;
    queue = context.queue;

    context.root_signature = create_32bit_constants_root_signature(context.device,
            0, ARRAY_SIZE(constants), D3D12_SHADER_VISIBILITY_ALL);
    context.pipeline_state = create_pipeline_state(context.device,
            context.root_signature, desc.rt_format, NULL, &ps_uint_constant, NULL);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 0,
            ARRAY_SIZE(constants), constants, 0);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    expected_result.x = constants[0];
    expected_result.y = constants[1];
    expected_result.z = constants[2];
    expected_result.w = constants[3];
    check_sub_resource_vec4(context.render_target, 0, queue, command_list, &expected_result, 0);

    reset_command_list(command_list, context.allocator);

    ID3D12PipelineState_Release(context.pipeline_state);
    ID3D12RootSignature_Release(context.root_signature);

    root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
    root_parameters[0].Constants.ShaderRegister = 0;
    root_parameters[0].Constants.RegisterSpace = 0;
    root_parameters[0].Constants.Num32BitValues = 4;
    root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;
    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_PIXEL;
    root_signature_desc.NumParameters = 2;
    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, &context.root_signature);
    ok(SUCCEEDED(hr), "Failed to create root signature, hr %#x.\n", hr);
    context.pipeline_state = create_pipeline_state(context.device,
            context.root_signature, desc.rt_format, &vs_color, &ps_color, NULL);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    vs_cb_color = ps_cb_color = expected_result = (struct vec4){0.0f, 1.0f, 0.0f, 1.0f};
    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 0, 4, &vs_cb_color.x, 0);
    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 1, 4, &ps_cb_color.x, 0);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_vec4(context.render_target, 0, queue, command_list, &expected_result, 0);

    reset_command_list(command_list, context.allocator);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    vs_cb_color = (struct vec4){0.0f, 1.0f, 0.0f, 1.0f};
    ps_cb_color = (struct vec4){1.0f, 1.0f, 1.0f, 1.0f};
    expected_result = (struct vec4){0.0f, 0.0f, 1.0f, 1.0f};
    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 0, 4, &vs_cb_color.x, 0);
    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 1, 4, &ps_cb_color.x, 0);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_vec4(context.render_target, 0, queue, command_list, &expected_result, 0);

    reset_command_list(command_list, context.allocator);

    ID3D12PipelineState_Release(context.pipeline_state);
    ID3D12RootSignature_Release(context.root_signature);

    root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
    root_parameters[0].Constants.ShaderRegister = 1;
    root_parameters[0].Constants.RegisterSpace = 0;
    root_parameters[0].Constants.Num32BitValues = 8;
    root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;
    root_parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
    root_parameters[1].Constants.ShaderRegister = 1;
    root_parameters[1].Constants.RegisterSpace = 0;
    root_parameters[1].Constants.Num32BitValues = 4;
    root_parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
    root_parameters[2].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
    root_parameters[2].Constants.ShaderRegister = 0;
    root_parameters[2].Constants.RegisterSpace = 0;
    root_parameters[2].Constants.Num32BitValues = 2;
    root_parameters[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    root_signature_desc.NumParameters = 3;
    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, &context.root_signature);
    ok(SUCCEEDED(hr), "Failed to create root signature, hr %#x.\n", hr);
    context.pipeline_state = create_pipeline_state(context.device,
            context.root_signature, desc.rt_format, &vs_mix, &ps_mix, NULL);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    vs_cb_color = expected_result = (struct vec4){0.0f, 1.0f, 0.0f, 1.0f};
    ps_cb_color = (struct vec4){1.0f, 1.0f, 1.0f, 1.0f};
    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 0, 4, &vs_cb_color.x, 4);
    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 1, 4, &ps_cb_color.x, 0);
    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstant(command_list, 2, 0xdeadbeef, 0);
    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstant(command_list, 2, 0, 1);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_vec4(context.render_target, 0, queue, command_list, &expected_result, 0);

    reset_command_list(command_list, context.allocator);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    vs_cb_color = (struct vec4){0.0f, 1.0f, 0.0f, 1.0f};
    ps_cb_color = expected_result = (struct vec4){1.0f, 1.0f, 1.0f, 1.0f};
    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 0, 4, &vs_cb_color.x, 4);
    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 1, 4, &ps_cb_color.x, 0);
    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstant(command_list, 2, 0xdeadbeef, 0);
    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstant(command_list, 2, 1, 1);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_vec4(context.render_target, 0, queue, command_list, &expected_result, 0);

    reset_command_list(command_list, context.allocator);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    vs_cb_color = (struct vec4){0.5f, 1.0f, 0.5f, 1.0f};
    ps_cb_color = (struct vec4){0.5f, 0.7f, 1.0f, 1.0f};
    expected_result = (struct vec4){0.25f, 0.7f, 0.5f, 1.0f};
    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 0, 4, &vs_cb_color.x, 4);
    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 1, 4, &ps_cb_color.x, 0);
    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstant(command_list, 2, 0xdeadbeef, 0);
    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstant(command_list, 2, 2, 1);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_vec4(context.render_target, 0, queue, command_list, &expected_result, 0);

    destroy_test_context(&context);
}

static void test_sample_instructions(void)
{
    ID3D12DescriptorHeap *heap, *sampler_heap, *heaps[2];
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    D3D12_DESCRIPTOR_RANGE descriptor_range[2];
    D3D12_ROOT_PARAMETER root_parameters[3];
    ID3D12GraphicsCommandList *command_list;
    D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle;
    D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle;
    struct d3d12_resource_readback rb;
    D3D12_SAMPLER_DESC sampler_desc;
    struct test_context_desc desc;
    struct test_context context;
    unsigned int x_step, y_step;
    ID3D12CommandQueue *queue;
    ID3D12Resource *texture;
    unsigned int i, x = 0, y;
    ID3D12Device *device;
    HRESULT hr;

    static const DWORD ps_sample_code[] =
    {
#if 0
        Texture2D t;
        SamplerState s;

        float4 main(float4 position : SV_POSITION) : SV_Target
        {
            float2 p;

            p.x = position.x / 640.0f;
            p.y = position.y / 480.0f;
            return t.Sample(s, p);
        }
#endif
        0x43425844, 0xd48f8d1c, 0x91689a9a, 0x99683e50, 0xae5e3efd, 0x00000001, 0x00000140, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000030f, 0x505f5653, 0x5449534f, 0x004e4f49,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x000000a4, 0x00000050,
        0x00000029, 0x0100086a, 0x0300005a, 0x00106000, 0x00000000, 0x04001858, 0x00107000, 0x00000000,
        0x00005555, 0x04002064, 0x00101032, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x02000068, 0x00000001, 0x0a000038, 0x00100032, 0x00000000, 0x00101046, 0x00000000, 0x00004002,
        0x3acccccd, 0x3b088889, 0x00000000, 0x00000000, 0x8b000045, 0x800000c2, 0x00155543, 0x001020f2,
        0x00000000, 0x00100046, 0x00000000, 0x00107e46, 0x00000000, 0x00106000, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_sample = {ps_sample_code, sizeof(ps_sample_code)};
    static const DWORD ps_sample_b_code[] =
    {
#if 0
        Texture2D t;
        SamplerState s;

        float bias;

        float4 main(float4 position : SV_POSITION) : SV_Target
        {
            float2 p;

            p.x = position.x / 640.0f;
            p.y = position.y / 480.0f;
            return t.SampleBias(s, p, bias);
        }
#endif
        0x43425844, 0xc39b0686, 0x8244a7fc, 0x14c0b97a, 0x2900b3b7, 0x00000001, 0x00000150, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000030f, 0x505f5653, 0x5449534f, 0x004e4f49,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x52444853, 0x000000b4, 0x00000040,
        0x0000002d, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x0300005a, 0x00106000, 0x00000000,
        0x04001858, 0x00107000, 0x00000000, 0x00005555, 0x04002064, 0x00101032, 0x00000000, 0x00000001,
        0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000001, 0x0a000038, 0x00100032, 0x00000000,
        0x00101046, 0x00000000, 0x00004002, 0x3acccccd, 0x3b088889, 0x00000000, 0x00000000, 0x0c00004a,
        0x001020f2, 0x00000000, 0x00100046, 0x00000000, 0x00107e46, 0x00000000, 0x00106000, 0x00000000,
        0x0020800a, 0x00000000, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_sample_b = {ps_sample_b_code, sizeof(ps_sample_b_code)};
    static const DWORD ps_sample_d_code[] =
    {
#if 0
        Texture2D t;
        SamplerState s;

        float4 dd;

        float4 main(float4 position : SV_POSITION) : SV_Target
        {
            float2 p;

            p.x = position.x / 640.0f;
            p.y = position.y / 480.0f;
            return t.SampleGrad(s, p, float2(dd.x, dd.y), float2(dd.z, dd.w));
       }
#endif
        0x43425844, 0xecc423bc, 0x3742699c, 0xf08f6dd7, 0x9976ad55, 0x00000001, 0x00000168, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000030f, 0x505f5653, 0x5449534f, 0x004e4f49,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x000000cc, 0x00000050,
        0x00000033, 0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x0300005a, 0x00106000,
        0x00000000, 0x04001858, 0x00107000, 0x00000000, 0x00005555, 0x04002064, 0x00101032, 0x00000000,
        0x00000001, 0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000001, 0x0a000038, 0x00100032,
        0x00000000, 0x00101046, 0x00000000, 0x00004002, 0x3acccccd, 0x3b088889, 0x00000000, 0x00000000,
        0x91000049, 0x800000c2, 0x00155543, 0x001020f2, 0x00000000, 0x00100046, 0x00000000, 0x00107e46,
        0x00000000, 0x00106000, 0x00000000, 0x00208046, 0x00000000, 0x00000000, 0x00208ae6, 0x00000000,
        0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_sample_d = {ps_sample_d_code, sizeof(ps_sample_d_code)};
    static const DWORD ps_sample_l_code[] =
    {
#if 0
        Texture2D t;
        SamplerState s;

        float level;

        float4 main(float4 position : SV_POSITION) : SV_Target
        {
            float2 p;

            p.x = position.x / 640.0f;
            p.y = position.y / 480.0f;
            return t.SampleLevel(s, p, level);
        }
#endif
        0x43425844, 0x61e05d85, 0x2a7300fb, 0x0a83706b, 0x889d1683, 0x00000001, 0x00000150, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000030f, 0x505f5653, 0x5449534f, 0x004e4f49,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x52444853, 0x000000b4, 0x00000040,
        0x0000002d, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x0300005a, 0x00106000, 0x00000000,
        0x04001858, 0x00107000, 0x00000000, 0x00005555, 0x04002064, 0x00101032, 0x00000000, 0x00000001,
        0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000001, 0x0a000038, 0x00100032, 0x00000000,
        0x00101046, 0x00000000, 0x00004002, 0x3acccccd, 0x3b088889, 0x00000000, 0x00000000, 0x0c000048,
        0x001020f2, 0x00000000, 0x00100046, 0x00000000, 0x00107e46, 0x00000000, 0x00106000, 0x00000000,
        0x0020800a, 0x00000000, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_sample_l = {ps_sample_l_code, sizeof(ps_sample_l_code)};
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    static const unsigned int r8g8b8a8_data[] =
    {
        0xff0000ff, 0xff00ffff, 0xff00ff00, 0xffffff00,
        0xffff0000, 0xffff00ff, 0xff000000, 0xff7f7f7f,
        0xffffffff, 0xffffffff, 0xffffffff, 0xff000000,
        0xffffffff, 0xff000000, 0xff000000, 0xff000000,
    };
    static const uint8_t a8_data[] =
    {
        0x00, 0xff, 0x7f, 0xf0,
        0x0f, 0x11, 0x00, 0x00,
        0xff, 0xf0, 0x0f, 0xff,
        0xfa, 0xfe, 0xaa, 0xcc,
    };
    static const unsigned int a8_expected_data[] =
    {
        0x00000000, 0xff000000, 0x7f000000, 0xf0000000,
        0x0f000000, 0x11000000, 0x00000000, 0x00000000,
        0xff000000, 0xf0000000, 0x0f000000, 0xff000000,
        0xfa000000, 0xfe000000, 0xaa000000, 0xcc000000,
    };
    static const unsigned int rgba_level_0[] =
    {
        0xff0000ff, 0xff00ffff, 0xff00ff00, 0xffffff00,
        0xffff0000, 0xffff00ff, 0xff000000, 0xff7f7f7f,
        0xffffffff, 0xffffffff, 0xffffffff, 0xff000000,
        0xffffffff, 0xff000000, 0xff000000, 0xff000000,
    };
    static const unsigned int rgba_level_1[] =
    {
        0xffffffff, 0xff0000ff,
        0xff000000, 0xff00ff00,
    };
    static const unsigned int rgba_level_2[] =
    {
        0xffff0000,
    };
    static const unsigned int level_1_colors[] =
    {
        0xffffffff, 0xffffffff, 0xff0000ff, 0xff0000ff,
        0xffffffff, 0xffffffff, 0xff0000ff, 0xff0000ff,
        0xff000000, 0xff000000, 0xff00ff00, 0xff00ff00,
        0xff000000, 0xff000000, 0xff00ff00, 0xff00ff00,
    };
    static const unsigned int level_2_colors[] =
    {
        0xffff0000, 0xffff0000, 0xffff0000, 0xffff0000,
        0xffff0000, 0xffff0000, 0xffff0000, 0xffff0000,
        0xffff0000, 0xffff0000, 0xffff0000, 0xffff0000,
        0xffff0000, 0xffff0000, 0xffff0000, 0xffff0000,
    };
    static const unsigned int lerp_1_2_colors[] =
    {
        0xffff7f7f, 0xffff7f7f, 0xff7f007f, 0xff7f007f,
        0xffff7f7f, 0xffff7f7f, 0xff7f007f, 0xff7f007f,
        0xff7f0000, 0xff7f0000, 0xff7f7f00, 0xff7f7f00,
        0xff7f0000, 0xff7f0000, 0xff7f7f00, 0xff7f7f00,
    };
    struct texture
    {
        unsigned int width;
        unsigned int height;
        unsigned int miplevel_count;
        unsigned int array_size;
        DXGI_FORMAT format;
        D3D12_SUBRESOURCE_DATA data[3];
    };
    static const struct texture r8g8b8a8_texture =
    {
        4, 4, 1, 1, DXGI_FORMAT_R8G8B8A8_UNORM,
        {
            {r8g8b8a8_data, 4 * sizeof(*r8g8b8a8_data), 16 * sizeof(*r8g8b8a8_data)},
        },
    };
    static const struct texture a8_texture =
    {
        4, 4, 1, 1, DXGI_FORMAT_A8_UNORM,
        {
            {a8_data, 4 * sizeof(*a8_data), 16 * sizeof(*a8_data)},
        },
    };
    static const struct texture rgba_texture =
    {
        4, 4, 3, 1, DXGI_FORMAT_R8G8B8A8_UNORM,
        {
            {rgba_level_0, 4 * sizeof(*rgba_level_0), 0},
            {rgba_level_1, 2 * sizeof(*rgba_level_1), 0},
            {rgba_level_2,     sizeof(*rgba_level_2), 0},
        },
    };
    static const struct
    {
        const D3D12_SHADER_BYTECODE *ps_code;
        const struct texture *texture;
        D3D12_FILTER filter;
        float lod_bias;
        float min_lod;
        float max_lod;
        float ps_constants[4];
        const unsigned int *expected_data;
        bool bug_on_mvk;
        bool todo_on_llvmpipe;
    }
    tests[] =
    {
#define MIP_MAX      D3D12_FLOAT32_MAX
#define POINT        D3D12_FILTER_MIN_MAG_MIP_POINT
#define POINT_LINEAR D3D12_FILTER_MIN_MAG_POINT_MIP_LINEAR
        {&ps_sample,   &r8g8b8a8_texture,    POINT, 0.0f, 0.0f, MIP_MAX, {0.0f},                   r8g8b8a8_data},
        {&ps_sample,   &a8_texture,          POINT, 0.0f, 0.0f, MIP_MAX, {0.0f},                   a8_expected_data},
        {&ps_sample_b, &r8g8b8a8_texture,    POINT, 0.0f, 0.0f, MIP_MAX, {0.0f},                   r8g8b8a8_data},
        {&ps_sample_b, &a8_texture,          POINT, 0.0f, 0.0f, MIP_MAX, {0.0f},                   a8_expected_data},
        {&ps_sample_b, &rgba_texture,        POINT, 0.0f, 0.0f, MIP_MAX, {0.0f},                   rgba_level_0},
        {&ps_sample_b, &rgba_texture,        POINT, 8.0f, 0.0f, MIP_MAX, {0.0f},                   level_1_colors, true},
        {&ps_sample_b, &rgba_texture,        POINT, 0.0f, 0.0f, MIP_MAX, {8.0f},                   level_1_colors},
        {&ps_sample_b, &rgba_texture,        POINT, 0.0f, 0.0f, MIP_MAX, {8.4f},                   level_1_colors},
        {&ps_sample_b, &rgba_texture,        POINT, 0.0f, 0.0f, MIP_MAX, {8.5f},                   level_2_colors},
        {&ps_sample_b, &rgba_texture,        POINT, 0.0f, 0.0f, MIP_MAX, {9.0f},                   level_2_colors},
        {&ps_sample_b, &rgba_texture,        POINT, 0.0f, 0.0f, 2.0f,    {1.0f},                   rgba_level_0},
        {&ps_sample_b, &rgba_texture,        POINT, 0.0f, 0.0f, 2.0f,    {9.0f},                   level_2_colors},
        {&ps_sample_b, &rgba_texture,        POINT, 0.0f, 0.0f, 1.0f,    {9.0f},                   level_1_colors},
        {&ps_sample_b, &rgba_texture,        POINT, 0.0f, 0.0f, 0.0f,    {9.0f},                   rgba_level_0},
        {&ps_sample_d, &r8g8b8a8_texture,    POINT, 0.0f, 0.0f, MIP_MAX, {0.0f, 0.0f, 0.0f, 0.0f}, r8g8b8a8_data},
        {&ps_sample_d, &a8_texture,          POINT, 0.0f, 0.0f, MIP_MAX, {0.0f, 0.0f, 0.0f, 0.0f}, a8_expected_data},
        {&ps_sample_d, &rgba_texture,        POINT, 0.0f, 0.0f, MIP_MAX, {0.0f, 0.0f, 0.0f, 0.0f}, rgba_level_0},
        {&ps_sample_d, &rgba_texture,        POINT, 0.0f, 0.0f, MIP_MAX, {0.3f, 0.0f, 0.0f, 0.0f}, rgba_level_0},
        {&ps_sample_d, &rgba_texture,        POINT, 0.0f, 0.0f, MIP_MAX, {0.4f, 0.0f, 0.0f, 0.0f}, level_1_colors},
        {&ps_sample_d, &rgba_texture,        POINT, 0.0f, 0.0f, MIP_MAX, {1.0f, 0.0f, 0.0f, 0.0f}, level_2_colors},
        {&ps_sample_l, &rgba_texture,        POINT, 0.0f, 0.0f, MIP_MAX, {-1.0f},                  rgba_level_0},
        {&ps_sample_l, &rgba_texture,        POINT, 0.0f, 0.0f, MIP_MAX, {0.0f},                   rgba_level_0},
        {&ps_sample_l, &rgba_texture,        POINT, 0.0f, 0.0f, MIP_MAX, {0.4f},                   rgba_level_0},
        /* For POINT filtering, Direct3D specifies mip level selection
         * essentially as "⌊λ + ½⌋" (7.18.10 "Mipmap Selection" in the
         * Direct3D 11.3 Functional Specification). OpenGL and Vulkan allow
         * both "⌈λ + ½⌉ - 1" and "⌊λ + ½⌋", with the former being preferred.
         * Perhaps unsurprisingly, most desktop GPUs implement the Direct3D
         * behaviour. Current versions of llvmpipe seem to implement the
         * preferred OpenGL/Vulkan behaviour. */
        {&ps_sample_l, &rgba_texture,        POINT, 0.0f, 0.0f, MIP_MAX, {0.5f},                   level_1_colors, true, true},
        {&ps_sample_l, &rgba_texture,        POINT, 0.0f, 0.0f, MIP_MAX, {0.6f},                   level_1_colors},
        {&ps_sample_l, &rgba_texture,        POINT, 0.0f, 0.0f, MIP_MAX, {1.0f},                   level_1_colors},
        {&ps_sample_l, &rgba_texture,        POINT, 0.0f, 0.0f, MIP_MAX, {1.4f},                   level_1_colors},
        {&ps_sample_l, &rgba_texture,        POINT, 0.0f, 0.0f, MIP_MAX, {1.5f},                   level_2_colors, true},
        {&ps_sample_l, &rgba_texture,        POINT, 0.0f, 0.0f, MIP_MAX, {1.6f},                   level_2_colors},
        {&ps_sample_l, &rgba_texture,        POINT, 0.0f, 0.0f, MIP_MAX, {2.0f},                   level_2_colors},
        {&ps_sample_l, &rgba_texture,        POINT, 0.0f, 0.0f, MIP_MAX, {3.0f},                   level_2_colors},
        {&ps_sample_l, &rgba_texture,        POINT, 0.0f, 0.0f, MIP_MAX, {4.0f},                   level_2_colors},
        {&ps_sample_l, &rgba_texture, POINT_LINEAR, 0.0f, 0.0f, MIP_MAX, {1.5f},                   lerp_1_2_colors},
        {&ps_sample_l, &rgba_texture, POINT_LINEAR, 2.0f, 0.0f, MIP_MAX, {-2.0f},                  rgba_level_0},
        {&ps_sample_l, &rgba_texture, POINT_LINEAR, 2.0f, 0.0f, MIP_MAX, {-1.0f},                  level_1_colors, true},
        {&ps_sample_l, &rgba_texture, POINT_LINEAR, 2.0f, 0.0f, MIP_MAX, {0.0f},                   level_2_colors, true},
        {&ps_sample_l, &rgba_texture, POINT_LINEAR, 2.0f, 0.0f, MIP_MAX, {1.0f},                   level_2_colors, true},
        {&ps_sample_l, &rgba_texture, POINT_LINEAR, 2.0f, 0.0f, MIP_MAX, {1.5f},                   level_2_colors, true},
        {&ps_sample_l, &rgba_texture, POINT_LINEAR, 2.0f, 2.0f, 2.0f,    {-9.0f},                  level_2_colors},
        {&ps_sample_l, &rgba_texture, POINT_LINEAR, 2.0f, 2.0f, 2.0f,    {-1.0f},                  level_2_colors},
        {&ps_sample_l, &rgba_texture, POINT_LINEAR, 2.0f, 2.0f, 2.0f,    {0.0f},                   level_2_colors},
        {&ps_sample_l, &rgba_texture, POINT_LINEAR, 2.0f, 2.0f, 2.0f,    {1.0f},                   level_2_colors},
        {&ps_sample_l, &rgba_texture, POINT_LINEAR, 2.0f, 2.0f, 2.0f,    {9.0f},                   level_2_colors},
        {&ps_sample_l, &rgba_texture,        POINT, 2.0f, 2.0f, 2.0f,    {-9.0f},                  level_2_colors},
        {&ps_sample_l, &rgba_texture,        POINT, 2.0f, 2.0f, 2.0f,    {-1.0f},                  level_2_colors},
        {&ps_sample_l, &rgba_texture,        POINT, 2.0f, 2.0f, 2.0f,    {0.0f},                   level_2_colors},
        {&ps_sample_l, &rgba_texture,        POINT, 2.0f, 2.0f, 2.0f,    {1.0f},                   level_2_colors},
        {&ps_sample_l, &rgba_texture,        POINT, 2.0f, 2.0f, 2.0f,    {9.0f},                   level_2_colors},
#undef MIP_MAX
#undef POINT
#undef POINT_LINEAR
    };

    memset(&desc, 0, sizeof(desc));
    desc.rt_width = 640;
    desc.rt_height = 480;
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    descriptor_range[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
    descriptor_range[0].NumDescriptors = 1;
    descriptor_range[0].BaseShaderRegister = 0;
    descriptor_range[0].RegisterSpace = 0;
    descriptor_range[0].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[0];
    root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;

    descriptor_range[1].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER;
    descriptor_range[1].NumDescriptors = 1;
    descriptor_range[1].BaseShaderRegister = 0;
    descriptor_range[1].RegisterSpace = 0;
    descriptor_range[1].OffsetInDescriptorsFromTableStart = 0;
    root_parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameters[1].DescriptorTable.NumDescriptorRanges = 1;
    root_parameters[1].DescriptorTable.pDescriptorRanges = &descriptor_range[1];
    root_parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;

    root_parameters[2].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
    root_parameters[2].Constants.ShaderRegister = 0;
    root_parameters[2].Constants.RegisterSpace = 0;
    root_parameters[2].Constants.Num32BitValues = ARRAY_SIZE(tests->ps_constants);
    root_parameters[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;

    memset(&root_signature_desc, 0, sizeof(root_signature_desc));
    root_signature_desc.NumParameters = ARRAY_SIZE(root_parameters);
    root_signature_desc.pParameters = root_parameters;
    hr = create_root_signature(device, &root_signature_desc, &context.root_signature);
    ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr);

    heap = create_gpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1);
    cpu_handle = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(heap);
    gpu_handle = ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(heap);

    sampler_heap = create_gpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, 1);

    for (i = 0; i < ARRAY_SIZE(tests); ++i)
    {
        unsigned int color = 0;
        bool fail = false;

        vkd3d_test_push_context("Test %u", i);

        memset(&sampler_desc, 0, sizeof(sampler_desc));
        sampler_desc.Filter = tests[i].filter;
        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 = tests[i].lod_bias;
        sampler_desc.MinLOD = tests[i].min_lod;
        sampler_desc.MaxLOD = tests[i].max_lod;
        ID3D12Device_CreateSampler(device, &sampler_desc, get_cpu_sampler_handle(&context, sampler_heap, 0));

        if (context.pipeline_state)
            ID3D12PipelineState_Release(context.pipeline_state);
        context.pipeline_state = create_pipeline_state(device, context.root_signature,
                context.render_target_desc.Format, NULL, tests[i].ps_code, NULL);

        texture = create_default_texture2d(device, tests[i].texture->width, tests[i].texture->height,
                tests[i].texture->array_size, tests[i].texture->miplevel_count, tests[i].texture->format,
                D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST);
        upload_texture_data(texture, tests[i].texture->data,
                tests[i].texture->miplevel_count * tests[i].texture->array_size, queue, command_list);
        reset_command_list(command_list, context.allocator);
        transition_resource_state(command_list, texture,
                D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);

        ID3D12Device_CreateShaderResourceView(device, texture, NULL, cpu_handle);

        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

        ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
        ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
        heaps[0] = heap; heaps[1] = sampler_heap;
        ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, ARRAY_SIZE(heaps), heaps);
        ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0, gpu_handle);
        ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 1,
                get_gpu_sampler_handle(&context, sampler_heap, 0));
        ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
        ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
        ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 2,
                ARRAY_SIZE(tests[i].ps_constants), tests[i].ps_constants, 0);
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);

        x_step = desc.rt_width / tests[i].texture->width;
        y_step = desc.rt_height / tests[i].texture->height;
        get_resource_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
        for (y = 0; y < tests[i].texture->height; ++y)
        {
            for (x = 0; x < tests[i].texture->width; ++x)
            {
                color = get_readback_uint(&rb.rb, x * x_step + x_step / 2, y * y_step + y_step / 2, 0);
                if (!compare_color(color, tests[i].expected_data[tests[i].texture->width * y + x], 1))
                {
                    fail = true;
                    break;
                }
            }

            if (fail)
                break;
        }
        bug_if(tests[i].bug_on_mvk && is_mvk_device(device))
        todo_if(tests[i].todo_on_llvmpipe && is_llvmpipe_device(device))
        ok(!fail, "Got color 0x%08x, expected 0x%08x at (%u, %u).\n",
                color, tests[i].expected_data[tests[i].texture->width * y + x], x, y);
        release_resource_readback(&rb);

        ID3D12Resource_Release(texture);
        reset_command_list(command_list, context.allocator);
        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

        vkd3d_test_pop_context();
    }

    ID3D12DescriptorHeap_Release(heap);
    ID3D12DescriptorHeap_Release(sampler_heap);
    destroy_test_context(&context);
}

static void test_texture_ld(void)
{
    ID3D12GraphicsCommandList *command_list;
    D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12DescriptorHeap *heap;
    ID3D12CommandQueue *queue;
    ID3D12Resource *texture;
    unsigned int i;

    static const DWORD ps_ld_code[] =
    {
#if 0
        Texture2D t;

        int2 offset;
        uint2 location;

        float4 main() : SV_Target
        {
            switch (offset.x)
            {
                case -1:
                    switch (offset.y)
                    {
                        case -2: return t.Load(uint3(location, 0), int2(-1, -2));
                        case -1: return t.Load(uint3(location, 0), int2(-1, -1));
                        case  0: return t.Load(uint3(location, 0), int2(-1,  0));
                        case  1: return t.Load(uint3(location, 0), int2(-1,  1));
                        case  2: return t.Load(uint3(location, 0), int2(-1,  2));
                    }
                    break;
                case 0:
                    switch (offset.y)
                    {
                        case -2: return t.Load(uint3(location, 0), int2(0, -2));
                        case -1: return t.Load(uint3(location, 0), int2(0, -1));
                        case  0: return t.Load(uint3(location, 0), int2(0,  0));
                        case  1: return t.Load(uint3(location, 0), int2(0,  1));
                        case  2: return t.Load(uint3(location, 0), int2(0,  2));
                    }
                    break;
                case 1:
                    switch (offset.y)
                    {
                        case -2: return t.Load(uint3(location, 0), int2(1, -2));
                        case -1: return t.Load(uint3(location, 0), int2(1, -1));
                        case  0: return t.Load(uint3(location, 0), int2(1,  0));
                        case  1: return t.Load(uint3(location, 0), int2(1,  1));
                        case  2: return t.Load(uint3(location, 0), int2(1,  2));
                    }
                    break;
            }

            return t.Load(uint3(location, 0));
        }
#endif
        0x43425844, 0xe925cc02, 0x43ea9623, 0xb67c6425, 0xb4503305, 0x00000001, 0x00000844, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x000007cc, 0x00000050, 0x000001f3,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x04001858, 0x00107000, 0x00000000,
        0x00005555, 0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000001, 0x0400004c, 0x0020800a,
        0x00000000, 0x00000000, 0x03000006, 0x00004001, 0xffffffff, 0x0400004c, 0x0020801a, 0x00000000,
        0x00000000, 0x03000006, 0x00004001, 0xfffffffe, 0x06000036, 0x00100032, 0x00000000, 0x00208ae6,
        0x00000000, 0x00000000, 0x08000036, 0x001000c2, 0x00000000, 0x00004002, 0x00000000, 0x00000000,
        0x00000000, 0x00000000, 0x8a00002d, 0x8001de01, 0x800000c2, 0x00155543, 0x001020f2, 0x00000000,
        0x00100e46, 0x00000000, 0x00107e46, 0x00000000, 0x0100003e, 0x03000006, 0x00004001, 0xffffffff,
        0x06000036, 0x00100032, 0x00000000, 0x00208ae6, 0x00000000, 0x00000000, 0x08000036, 0x001000c2,
        0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x8a00002d, 0x8001fe01,
        0x800000c2, 0x00155543, 0x001020f2, 0x00000000, 0x00100e46, 0x00000000, 0x00107e46, 0x00000000,
        0x0100003e, 0x03000006, 0x00004001, 0x00000000, 0x06000036, 0x00100032, 0x00000000, 0x00208ae6,
        0x00000000, 0x00000000, 0x08000036, 0x001000c2, 0x00000000, 0x00004002, 0x00000000, 0x00000000,
        0x00000000, 0x00000000, 0x8a00002d, 0x80001e01, 0x800000c2, 0x00155543, 0x001020f2, 0x00000000,
        0x00100e46, 0x00000000, 0x00107e46, 0x00000000, 0x0100003e, 0x03000006, 0x00004001, 0x00000001,
        0x06000036, 0x00100032, 0x00000000, 0x00208ae6, 0x00000000, 0x00000000, 0x08000036, 0x001000c2,
        0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x8a00002d, 0x80003e01,
        0x800000c2, 0x00155543, 0x001020f2, 0x00000000, 0x00100e46, 0x00000000, 0x00107e46, 0x00000000,
        0x0100003e, 0x03000006, 0x00004001, 0x00000002, 0x06000036, 0x00100032, 0x00000000, 0x00208ae6,
        0x00000000, 0x00000000, 0x08000036, 0x001000c2, 0x00000000, 0x00004002, 0x00000000, 0x00000000,
        0x00000000, 0x00000000, 0x8a00002d, 0x80005e01, 0x800000c2, 0x00155543, 0x001020f2, 0x00000000,
        0x00100e46, 0x00000000, 0x00107e46, 0x00000000, 0x0100003e, 0x0100000a, 0x01000002, 0x01000017,
        0x01000002, 0x03000006, 0x00004001, 0x00000000, 0x0400004c, 0x0020801a, 0x00000000, 0x00000000,
        0x03000006, 0x00004001, 0xfffffffe, 0x06000036, 0x00100032, 0x00000000, 0x00208ae6, 0x00000000,
        0x00000000, 0x08000036, 0x001000c2, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000,
        0x00000000, 0x8a00002d, 0x8001c001, 0x800000c2, 0x00155543, 0x001020f2, 0x00000000, 0x00100e46,
        0x00000000, 0x00107e46, 0x00000000, 0x0100003e, 0x03000006, 0x00004001, 0xffffffff, 0x06000036,
        0x00100032, 0x00000000, 0x00208ae6, 0x00000000, 0x00000000, 0x08000036, 0x001000c2, 0x00000000,
        0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x8a00002d, 0x8001e001, 0x800000c2,
        0x00155543, 0x001020f2, 0x00000000, 0x00100e46, 0x00000000, 0x00107e46, 0x00000000, 0x0100003e,
        0x03000006, 0x00004001, 0x00000000, 0x06000036, 0x00100032, 0x00000000, 0x00208ae6, 0x00000000,
        0x00000000, 0x08000036, 0x001000c2, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000,
        0x00000000, 0x8900002d, 0x800000c2, 0x00155543, 0x001020f2, 0x00000000, 0x00100e46, 0x00000000,
        0x00107e46, 0x00000000, 0x0100003e, 0x03000006, 0x00004001, 0x00000001, 0x06000036, 0x00100032,
        0x00000000, 0x00208ae6, 0x00000000, 0x00000000, 0x08000036, 0x001000c2, 0x00000000, 0x00004002,
        0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x8a00002d, 0x80002001, 0x800000c2, 0x00155543,
        0x001020f2, 0x00000000, 0x00100e46, 0x00000000, 0x00107e46, 0x00000000, 0x0100003e, 0x03000006,
        0x00004001, 0x00000002, 0x06000036, 0x00100032, 0x00000000, 0x00208ae6, 0x00000000, 0x00000000,
        0x08000036, 0x001000c2, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
        0x8a00002d, 0x80004001, 0x800000c2, 0x00155543, 0x001020f2, 0x00000000, 0x00100e46, 0x00000000,
        0x00107e46, 0x00000000, 0x0100003e, 0x0100000a, 0x01000002, 0x01000017, 0x01000002, 0x03000006,
        0x00004001, 0x00000001, 0x0400004c, 0x0020801a, 0x00000000, 0x00000000, 0x03000006, 0x00004001,
        0xfffffffe, 0x06000036, 0x00100032, 0x00000000, 0x00208ae6, 0x00000000, 0x00000000, 0x08000036,
        0x001000c2, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x8a00002d,
        0x8001c201, 0x800000c2, 0x00155543, 0x001020f2, 0x00000000, 0x00100e46, 0x00000000, 0x00107e46,
        0x00000000, 0x0100003e, 0x03000006, 0x00004001, 0xffffffff, 0x06000036, 0x00100032, 0x00000000,
        0x00208ae6, 0x00000000, 0x00000000, 0x08000036, 0x001000c2, 0x00000000, 0x00004002, 0x00000000,
        0x00000000, 0x00000000, 0x00000000, 0x8a00002d, 0x8001e201, 0x800000c2, 0x00155543, 0x001020f2,
        0x00000000, 0x00100e46, 0x00000000, 0x00107e46, 0x00000000, 0x0100003e, 0x03000006, 0x00004001,
        0x00000000, 0x06000036, 0x00100032, 0x00000000, 0x00208ae6, 0x00000000, 0x00000000, 0x08000036,
        0x001000c2, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x8a00002d,
        0x80000201, 0x800000c2, 0x00155543, 0x001020f2, 0x00000000, 0x00100e46, 0x00000000, 0x00107e46,
        0x00000000, 0x0100003e, 0x03000006, 0x00004001, 0x00000001, 0x06000036, 0x00100032, 0x00000000,
        0x00208ae6, 0x00000000, 0x00000000, 0x08000036, 0x001000c2, 0x00000000, 0x00004002, 0x00000000,
        0x00000000, 0x00000000, 0x00000000, 0x8a00002d, 0x80002201, 0x800000c2, 0x00155543, 0x001020f2,
        0x00000000, 0x00100e46, 0x00000000, 0x00107e46, 0x00000000, 0x0100003e, 0x03000006, 0x00004001,
        0x00000002, 0x06000036, 0x00100032, 0x00000000, 0x00208ae6, 0x00000000, 0x00000000, 0x08000036,
        0x001000c2, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x8a00002d,
        0x80004201, 0x800000c2, 0x00155543, 0x001020f2, 0x00000000, 0x00100e46, 0x00000000, 0x00107e46,
        0x00000000, 0x0100003e, 0x0100000a, 0x01000002, 0x01000017, 0x01000002, 0x0100000a, 0x01000002,
        0x01000017, 0x06000036, 0x00100032, 0x00000000, 0x00208ae6, 0x00000000, 0x00000000, 0x08000036,
        0x001000c2, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x8900002d,
        0x800000c2, 0x00155543, 0x001020f2, 0x00000000, 0x00100e46, 0x00000000, 0x00107e46, 0x00000000,
        0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_ld = {ps_ld_code, sizeof(ps_ld_code)};
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    static const unsigned int texture_data[] =
    {
        0xff0008ff, 0xff00ffff, 0xff00ff05, 0xffffff01,
        0xffff0007, 0xffff00ff, 0x11111101, 0xff7f7f7f,
        0x44444f44, 0x88888888, 0x22222222, 0xff000002,
        0x66f66666, 0xff000000, 0xff000003, 0x55555555,
    };
    static const D3D12_SUBRESOURCE_DATA resource_data = {&texture_data, sizeof(texture_data) / 4};
    static const struct
    {
        int32_t constants[4];
        unsigned int expected_color;
    }
    tests[] =
    {
        {{ 0,  0, 0, 0}, 0xff0008ff},
        {{ 1,  0, 0, 0}, 0xff00ffff},
        {{ 0,  1, 0, 0}, 0xffff0007},
        {{ 1,  1, 0, 0}, 0xffff00ff},
        {{ 3,  3, 0, 0}, 0xff0008ff},
        {{ 3,  3, 1, 1}, 0xffff00ff},
        {{ 0,  0, 3, 3}, 0x55555555},
        {{-1, -1, 3, 3}, 0x22222222},
        {{-1, -2, 3, 3}, 0x11111101},
        {{ 0, -1, 3, 3}, 0xff000002},
        {{ 0, -2, 3, 3}, 0xff7f7f7f},
        {{ 3,  3, 3, 3}, 0x55555555},
    };

    if (test_options.use_warp_device)
    {
        skip("WARP device is removed when ps_ld is used.\n");
        return;
    }

    memset(&desc, 0, sizeof(desc));
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    command_list = context.list;
    queue = context.queue;

    context.root_signature = create_texture_root_signature(context.device,
            D3D12_SHADER_VISIBILITY_PIXEL, 4, 0);
    context.pipeline_state = create_pipeline_state(context.device,
            context.root_signature, context.render_target_desc.Format, NULL, &ps_ld, NULL);

    heap = create_gpu_descriptor_heap(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1);
    gpu_handle = ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(heap);

    texture = create_default_texture(context.device,
            4, 4, DXGI_FORMAT_R8G8B8A8_UNORM, 0, D3D12_RESOURCE_STATE_COPY_DEST);
    upload_texture_data(texture, &resource_data, 1, queue, command_list);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, texture,
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
    ID3D12Device_CreateShaderResourceView(context.device, texture, NULL,
            ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(heap));

    for (i = 0; i < ARRAY_SIZE(tests); ++i)
    {
        vkd3d_test_push_context("Test %u", i);

        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

        ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
        ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
        ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &heap);
        ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0, gpu_handle);
        ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
        ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
        ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 1,
                ARRAY_SIZE(tests[i].constants), &tests[i].constants, 0);
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
        check_sub_resource_uint(context.render_target, 0, queue, command_list, tests[i].expected_color, 0);

        reset_command_list(command_list, context.allocator);
        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

        vkd3d_test_pop_context();
    }

    ID3D12Resource_Release(texture);
    ID3D12DescriptorHeap_Release(heap);
    destroy_test_context(&context);
}

static void test_gather(void)
{
    struct
    {
        int width, height;
        int offset_x, offset_y;
    } constants;

    ID3D12GraphicsCommandList *command_list;
    D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle;
    D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle;
    struct d3d12_resource_readback rb;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12DescriptorHeap *heap;
    ID3D12CommandQueue *queue;
    ID3D12Resource *texture;
    unsigned int x, y;

    static const DWORD gather4_code[] =
    {
#if 0
        SamplerState s;
        Texture2D<float4> t;

        int2 size;

        float4 main(float4 position : SV_Position) : SV_Target
        {
            return t.Gather(s, position.xy / size);
        }
#endif
        0x43425844, 0xca1ee692, 0xb122f477, 0x8c467d38, 0x0f5a233a, 0x00000001, 0x00000154, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000030f, 0x505f5653, 0x7469736f, 0x006e6f69,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x52444853, 0x000000b8, 0x00000041,
        0x0000002e, 0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x0300005a, 0x00106000,
        0x00000000, 0x04001858, 0x00107000, 0x00000000, 0x00005555, 0x04002064, 0x00101032, 0x00000000,
        0x00000001, 0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000001, 0x0600002b, 0x00100032,
        0x00000000, 0x00208046, 0x00000000, 0x00000000, 0x0700000e, 0x00100032, 0x00000000, 0x00101046,
        0x00000000, 0x00100046, 0x00000000, 0x0900006d, 0x001020f2, 0x00000000, 0x00100046, 0x00000000,
        0x00107e46, 0x00000000, 0x0010600a, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_gather4 = {gather4_code, sizeof(gather4_code)};
    static const DWORD gather4_offset_code[] =
    {
#if 0
        SamplerState s;
        Texture2D<float4> t;

        int2 size;

        float4 main(float4 position : SV_Position) : SV_Target
        {
            return t.Gather(s, position.xy / size, int2(1, 1));
        }
#endif
        0x43425844, 0xe5ab2216, 0x90748ece, 0x7ccf2123, 0x4edbba7c, 0x00000001, 0x00000158, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000030f, 0x505f5653, 0x7469736f, 0x006e6f69,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x52444853, 0x000000bc, 0x00000041,
        0x0000002f, 0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x0300005a, 0x00106000,
        0x00000000, 0x04001858, 0x00107000, 0x00000000, 0x00005555, 0x04002064, 0x00101032, 0x00000000,
        0x00000001, 0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000001, 0x0600002b, 0x00100032,
        0x00000000, 0x00208046, 0x00000000, 0x00000000, 0x0700000e, 0x00100032, 0x00000000, 0x00101046,
        0x00000000, 0x00100046, 0x00000000, 0x8a00006d, 0x00002201, 0x001020f2, 0x00000000, 0x00100046,
        0x00000000, 0x00107e46, 0x00000000, 0x0010600a, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_gather4_offset = {gather4_offset_code, sizeof(gather4_offset_code)};
    static const DWORD gather4_green_code[] =
    {
#if 0
        SamplerState s;
        Texture2D<float4> t;

        int2 size;

        float4 main(float4 position : SV_Position) : SV_Target
        {
            return t.GatherGreen(s, position.xy / size);
        }
#endif
        0x43425844, 0x2b0ad2d9, 0x8ad30b52, 0xc418477f, 0xe5211693, 0x00000001, 0x0000015c, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000030f, 0x505f5653, 0x7469736f, 0x006e6f69,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x000000c0, 0x00000050,
        0x00000030, 0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x0300005a, 0x00106000,
        0x00000000, 0x04001858, 0x00107000, 0x00000000, 0x00005555, 0x04002064, 0x00101032, 0x00000000,
        0x00000001, 0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000001, 0x0600002b, 0x00100032,
        0x00000000, 0x00208046, 0x00000000, 0x00000000, 0x0700000e, 0x00100032, 0x00000000, 0x00101046,
        0x00000000, 0x00100046, 0x00000000, 0x8b00006d, 0x800000c2, 0x00155543, 0x001020f2, 0x00000000,
        0x00100046, 0x00000000, 0x00107e46, 0x00000000, 0x0010601a, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_gather4_green = {gather4_green_code, sizeof(gather4_green_code)};
    static const DWORD gather4_po_code[] =
    {
#if 0
        SamplerState s;
        Texture2D<float4> t;

        int2 size;
        int2 offset;

        float4 main(float4 position : SV_Position) : SV_Target
        {
            return t.Gather(s, position.xy / size, offset);
        }
#endif
        0x43425844, 0xe19bdd35, 0x44514fb3, 0xfaa8727f, 0xc1092da0, 0x00000001, 0x00000168, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000030f, 0x505f5653, 0x7469736f, 0x006e6f69,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x000000cc, 0x00000050,
        0x00000033, 0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x0300005a, 0x00106000,
        0x00000000, 0x04001858, 0x00107000, 0x00000000, 0x00005555, 0x04002064, 0x00101032, 0x00000000,
        0x00000001, 0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000001, 0x0600002b, 0x00100032,
        0x00000000, 0x00208046, 0x00000000, 0x00000000, 0x0700000e, 0x00100032, 0x00000000, 0x00101046,
        0x00000000, 0x00100046, 0x00000000, 0x8e00007f, 0x800000c2, 0x00155543, 0x001020f2, 0x00000000,
        0x00100046, 0x00000000, 0x00208ae6, 0x00000000, 0x00000000, 0x00107e46, 0x00000000, 0x0010600a,
        0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_gather4_po = {gather4_po_code, sizeof(gather4_po_code)};
    static const struct vec4 texture_data[] =
    {
        {0.0f, 0.0f}, {1.0f, 1.0f}, {2.0f, 2.0f}, {3.0f, 3.0f},
        {4.0f, 0.1f}, {5.0f, 1.1f}, {6.0f, 2.1f}, {7.0f, 3.1f},
        {8.0f, 0.2f}, {9.0f, 1.2f}, {0.5f, 2.2f}, {1.5f, 3.2f},
        {2.5f, 0.3f}, {3.5f, 1.3f}, {4.5f, 2.3f}, {5.5f, 3.3f},
    };
    static const struct vec4 expected_gather4[] =
    {
        {4.0f, 5.0f, 1.0f, 0.0f}, {5.0f, 6.0f, 2.0f, 1.0f}, {6.0f, 7.0f, 3.0f, 2.0f}, {7.0f, 7.0f, 3.0f, 3.0f},
        {8.0f, 9.0f, 5.0f, 4.0f}, {9.0f, 0.5f, 6.0f, 5.0f}, {0.5f, 1.5f, 7.0f, 6.0f}, {1.5f, 1.5f, 7.0f, 7.0f},
        {2.5f, 3.5f, 9.0f, 8.0f}, {3.5f, 4.5f, 0.5f, 9.0f}, {4.5f, 5.5f, 1.5f, 0.5f}, {5.5f, 5.5f, 1.5f, 1.5f},
        {2.5f, 3.5f, 3.5f, 2.5f}, {3.5f, 4.5f, 4.5f, 3.5f}, {4.5f, 5.5f, 5.5f, 4.5f}, {5.5f, 5.5f, 5.5f, 5.5f},
    };
    static const struct vec4 expected_gather4_offset[] =
    {
        {9.0f, 0.5f, 6.0f, 5.0f}, {0.5f, 1.5f, 7.0f, 6.0f}, {1.5f, 1.5f, 7.0f, 7.0f}, {1.5f, 1.5f, 7.0f, 7.0f},
        {3.5f, 4.5f, 0.5f, 9.0f}, {4.5f, 5.5f, 1.5f, 0.5f}, {5.5f, 5.5f, 1.5f, 1.5f}, {5.5f, 5.5f, 1.5f, 1.5f},
        {3.5f, 4.5f, 4.5f, 3.5f}, {4.5f, 5.5f, 5.5f, 4.5f}, {5.5f, 5.5f, 5.5f, 5.5f}, {5.5f, 5.5f, 5.5f, 5.5f},
        {3.5f, 4.5f, 4.5f, 3.5f}, {4.5f, 5.5f, 5.5f, 4.5f}, {5.5f, 5.5f, 5.5f, 5.5f}, {5.5f, 5.5f, 5.5f, 5.5f},
    };
    static const struct vec4 expected_gather4_green[] =
    {
        {0.1f, 1.1f, 1.0f, 0.0f}, {1.1f, 2.1f, 2.0f, 1.0f}, {2.1f, 3.1f, 3.0f, 2.0f}, {3.1f, 3.1f, 3.0f, 3.0f},
        {0.2f, 1.2f, 1.1f, 0.1f}, {1.2f, 2.2f, 2.1f, 1.1f}, {2.2f, 3.2f, 3.1f, 2.1f}, {3.2f, 3.2f, 3.1f, 3.1f},
        {0.3f, 1.3f, 1.2f, 0.2f}, {1.3f, 2.3f, 2.2f, 1.2f}, {2.3f, 3.3f, 3.2f, 2.2f}, {3.3f, 3.3f, 3.2f, 3.2f},
        {0.3f, 1.3f, 1.3f, 0.3f}, {1.3f, 2.3f, 2.3f, 1.3f}, {2.3f, 3.3f, 3.3f, 2.3f}, {3.3f, 3.3f, 3.3f, 3.3f},
    };
    static const struct vec4 white = {1.0f, 1.0f, 1.0f, 1.0f};
    static const D3D12_SUBRESOURCE_DATA resource_data = {&texture_data, sizeof(texture_data) / 4};

    memset(&desc, 0, sizeof(desc));
    desc.rt_width = 4;
    desc.rt_height = 4;
    desc.rt_format = DXGI_FORMAT_R32G32B32A32_FLOAT;
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    command_list = context.list;
    queue = context.queue;

    context.root_signature = create_texture_root_signature(context.device,
            D3D12_SHADER_VISIBILITY_PIXEL, 4, 0);

    heap = create_gpu_descriptor_heap(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1);
    cpu_handle = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(heap);
    gpu_handle = ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(heap);

    texture = create_default_texture(context.device, 4, 4, DXGI_FORMAT_R32G32B32A32_FLOAT,
            0, D3D12_RESOURCE_STATE_COPY_DEST);
    ID3D12Device_CreateShaderResourceView(context.device, texture, NULL, cpu_handle);
    upload_texture_data(texture, &resource_data, 1, queue, command_list);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, texture,
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);

    constants.width = 4;
    constants.height = 4;
    constants.offset_x = 1;
    constants.offset_y = 1;

    /* ps_gather4 */
    context.pipeline_state = create_pipeline_state(context.device,
            context.root_signature, desc.rt_format, NULL, &ps_gather4, NULL);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, (const float *)&white, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &heap);
    ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0, gpu_handle);
    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 1, 4, &constants.width, 0);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_resource_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
    for (y = 0; y < rb.rb.height; ++y)
    {
        for (x = 0; x < rb.rb.width; ++x)
        {
            const struct vec4 *expected = &expected_gather4[y * rb.rb.width + x];
            const struct vec4 *got = get_readback_vec4(&rb.rb, x, y);
            ok(compare_vec4(got, expected, 0),
                    "Got {%.8e, %.8e, %.8e, %.8e}, expected {%.8e, %.8e, %.8e, %.8e}.\n",
                    got->x, got->y, got->z, got->w, expected->x, expected->y, expected->z, expected->w);
        }
    }
    release_resource_readback(&rb);

    ID3D12PipelineState_Release(context.pipeline_state);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    /* ps_gather4_offset */
    context.pipeline_state = create_pipeline_state(context.device,
            context.root_signature, desc.rt_format, NULL, &ps_gather4_offset, NULL);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, (const float *)&white, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &heap);
    ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0, gpu_handle);
    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 1, 4, &constants.width, 0);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_resource_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
    for (y = 0; y < rb.rb.height; ++y)
    {
        for (x = 0; x < rb.rb.width; ++x)
        {
            const struct vec4 *expected = &expected_gather4_offset[y * rb.rb.width + x];
            const struct vec4 *got = get_readback_vec4(&rb.rb, x, y);
            ok(compare_vec4(got, expected, 0),
                    "Got {%.8e, %.8e, %.8e, %.8e}, expected {%.8e, %.8e, %.8e, %.8e}.\n",
                    got->x, got->y, got->z, got->w, expected->x, expected->y, expected->z, expected->w);
        }
    }
    release_resource_readback(&rb);

    ID3D12PipelineState_Release(context.pipeline_state);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    /* ps_gather4_green */
    context.pipeline_state = create_pipeline_state(context.device,
            context.root_signature, desc.rt_format, NULL, &ps_gather4_green, NULL);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, (const float *)&white, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &heap);
    ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0, gpu_handle);
    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 1, 4, &constants.width, 0);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_resource_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
    for (y = 0; y < rb.rb.height; ++y)
    {
        for (x = 0; x < rb.rb.width; ++x)
        {
            const struct vec4 *expected = &expected_gather4_green[y * rb.rb.width + x];
            const struct vec4 *got = get_readback_vec4(&rb.rb, x, y);
            ok(compare_vec4(got, expected, 0),
                    "Got {%.8e, %.8e, %.8e, %.8e}, expected {%.8e, %.8e, %.8e, %.8e}.\n",
                    got->x, got->y, got->z, got->w, expected->x, expected->y, expected->z, expected->w);
        }
    }
    release_resource_readback(&rb);

    ID3D12PipelineState_Release(context.pipeline_state);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    /* ps_gather4_po */
    context.pipeline_state = create_pipeline_state(context.device,
            context.root_signature, desc.rt_format, NULL, &ps_gather4_po, NULL);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, (const float *)&white, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &heap);
    ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0, gpu_handle);
    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 1, 4, &constants.width, 0);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_resource_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
    for (y = 0; y < rb.rb.height; ++y)
    {
        for (x = 0; x < rb.rb.width; ++x)
        {
            const struct vec4 *expected = &expected_gather4_offset[y * rb.rb.width + x];
            const struct vec4 *got = get_readback_vec4(&rb.rb, x, y);
            ok(compare_vec4(got, expected, 0),
                    "Got {%.8e, %.8e, %.8e, %.8e}, expected {%.8e, %.8e, %.8e, %.8e}.\n",
                    got->x, got->y, got->z, got->w, expected->x, expected->y, expected->z, expected->w);
        }
    }
    release_resource_readback(&rb);

    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    constants.offset_x = 0;
    constants.offset_y = 0;

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, (const float *)&white, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &heap);
    ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0, gpu_handle);
    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 1, 4, &constants.width, 0);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_resource_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
    for (y = 0; y < rb.rb.height; ++y)
    {
        for (x = 0; x < rb.rb.width; ++x)
        {
            const struct vec4 *expected = &expected_gather4[y * rb.rb.width + x];
            const struct vec4 *got = get_readback_vec4(&rb.rb, x, y);
            ok(compare_vec4(got, expected, 0),
                    "Got {%.8e, %.8e, %.8e, %.8e}, expected {%.8e, %.8e, %.8e, %.8e}.\n",
                    got->x, got->y, got->z, got->w, expected->x, expected->y, expected->z, expected->w);
        }
    }
    release_resource_readback(&rb);

    ID3D12Resource_Release(texture);
    ID3D12DescriptorHeap_Release(heap);
    destroy_test_context(&context);
}

static void test_gather_c(void)
{
    struct
    {
        int width, height;
        int offset_x, offset_y;
        float d_ref;
    } constants;

    D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc;
    ID3D12GraphicsCommandList *command_list;
    D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle;
    D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle;
    struct d3d12_resource_readback rb;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12DescriptorHeap *heap;
    ID3D12CommandQueue *queue;
    ID3D12Resource *texture;
    unsigned int x, y;

    static const DWORD gather4_c_code[] =
    {
#if 0
        SamplerComparisonState s;
        Texture2D<float4> t;

        int2 size;
        int2 offset;
        float d_ref;

        float4 main(float4 position : SV_Position) : SV_Target
        {
            return t.GatherCmp(s, position.xy / size, d_ref);
        }
#endif
        0x43425844, 0xd3d04479, 0x901e9208, 0x7074fd0c, 0xbcadb2da, 0x00000001, 0x00000168, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000030f, 0x505f5653, 0x7469736f, 0x006e6f69,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x000000cc, 0x00000050,
        0x00000033, 0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000002, 0x0300085a, 0x00106000,
        0x00000000, 0x04001858, 0x00107000, 0x00000000, 0x00005555, 0x04002064, 0x00101032, 0x00000000,
        0x00000001, 0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000001, 0x0600002b, 0x00100032,
        0x00000000, 0x00208046, 0x00000000, 0x00000000, 0x0700000e, 0x00100032, 0x00000000, 0x00101046,
        0x00000000, 0x00100046, 0x00000000, 0x8e00007e, 0x800000c2, 0x00155543, 0x001020f2, 0x00000000,
        0x00100046, 0x00000000, 0x00107e46, 0x00000000, 0x0010600a, 0x00000000, 0x0020800a, 0x00000000,
        0x00000001, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_gather4_c = {gather4_c_code, sizeof(gather4_c_code)};
    static const DWORD gather4_po_c_code[] =
    {
#if 0
        SamplerComparisonState s;
        Texture2D<float4> t;

        int2 size;
        int2 offset;
        float d_ref;

        float4 main(float4 position : SV_Position) : SV_Target
        {
            return t.GatherCmp(s, position.xy / size, d_ref, offset);
        }
#endif
        0x43425844, 0x501de13e, 0x472d2d20, 0x6df0fee4, 0xef27d9e6, 0x00000001, 0x00000174, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000030f, 0x505f5653, 0x7469736f, 0x006e6f69,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x000000d8, 0x00000050,
        0x00000036, 0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000002, 0x0300085a, 0x00106000,
        0x00000000, 0x04001858, 0x00107000, 0x00000000, 0x00005555, 0x04002064, 0x00101032, 0x00000000,
        0x00000001, 0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000001, 0x0600002b, 0x00100032,
        0x00000000, 0x00208046, 0x00000000, 0x00000000, 0x0700000e, 0x00100032, 0x00000000, 0x00101046,
        0x00000000, 0x00100046, 0x00000000, 0x91000080, 0x800000c2, 0x00155543, 0x001020f2, 0x00000000,
        0x00100046, 0x00000000, 0x00208ae6, 0x00000000, 0x00000000, 0x00107e46, 0x00000000, 0x0010600a,
        0x00000000, 0x0020800a, 0x00000000, 0x00000001, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_gather4_po_c = {gather4_po_c_code, sizeof(gather4_po_c_code)};
    static const float texture_data[] =
    {
        0.0f, 1.0f, 0.20f, 0.30f,
        0.4f, 0.5f, 0.60f, 0.70f,
        0.8f, 0.9f, 0.50f, 0.15f,
        0.2f, 0.3f, 0.45f, 0.55f,
    };
    static const struct vec4 expected_gather4_c[] =
    {
        {0.0f, 1.0f, 1.0f, 0.0f}, {1.0f, 1.0f, 0.0f, 1.0f}, {1.0f, 1.0f, 0.0f, 0.0f}, {1.0f, 1.0f, 0.0f, 0.0f},
        {1.0f, 1.0f, 1.0f, 0.0f}, {1.0f, 1.0f, 1.0f, 1.0f}, {1.0f, 0.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 1.0f, 1.0f},
        {0.0f, 0.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 1.0f, 1.0f}, {0.0f, 1.0f, 0.0f, 1.0f}, {1.0f, 1.0f, 0.0f, 0.0f},
        {0.0f, 0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 1.0f, 0.0f}, {1.0f, 1.0f, 1.0f, 1.0f},
    };
    static const struct vec4 expected_gather4_po_c[] =
    {
        {1.0f, 1.0f, 1.0f, 1.0f}, {1.0f, 0.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 1.0f, 1.0f},
        {0.0f, 0.0f, 1.0f, 1.0f}, {0.0f, 1.0f, 0.0f, 1.0f}, {1.0f, 1.0f, 0.0f, 0.0f}, {1.0f, 1.0f, 0.0f, 0.0f},
        {0.0f, 0.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 1.0f, 0.0f}, {1.0f, 1.0f, 1.0f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f},
        {0.0f, 0.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 1.0f, 0.0f}, {1.0f, 1.0f, 1.0f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f},
    };
    static const struct vec4 white = {1.0f, 1.0f, 1.0f, 1.0f};
    static const D3D12_SUBRESOURCE_DATA resource_data = {&texture_data, sizeof(texture_data) / 4};
    static const D3D12_STATIC_SAMPLER_DESC sampler_desc =
    {
        .Filter = D3D12_FILTER_COMPARISON_MIN_MAG_MIP_POINT,
        .AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP,
        .AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP,
        .AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP,
        .ComparisonFunc = D3D12_COMPARISON_FUNC_LESS,
        .MaxLOD = D3D12_FLOAT32_MAX,
        .ShaderRegister = 0,
        .RegisterSpace = 0,
        .ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL,
    };

    memset(&desc, 0, sizeof(desc));
    desc.rt_width = 4;
    desc.rt_height = 4;
    desc.rt_format = DXGI_FORMAT_R32G32B32A32_FLOAT;
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    command_list = context.list;
    queue = context.queue;

    context.root_signature = create_texture_root_signature_(__FILE__, __LINE__, context.device,
            D3D12_SHADER_VISIBILITY_PIXEL, 5, 0, &sampler_desc);

    heap = create_gpu_descriptor_heap(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1);
    cpu_handle = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(heap);
    gpu_handle = ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(heap);

    texture = create_default_texture(context.device, 4, 4, DXGI_FORMAT_R32_TYPELESS,
            0, D3D12_RESOURCE_STATE_COPY_DEST);
    upload_texture_data(texture, &resource_data, 1, queue, command_list);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, texture,
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);

    memset(&srv_desc, 0, sizeof(srv_desc));
    srv_desc.Format = DXGI_FORMAT_R32_FLOAT;
    srv_desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
    srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
    srv_desc.Texture2D.MipLevels = 1;
    ID3D12Device_CreateShaderResourceView(context.device, texture, &srv_desc, cpu_handle);

    constants.width = 4;
    constants.height = 4;
    constants.offset_x = 1;
    constants.offset_y = 1;
    constants.d_ref = 0.46f;

    /* ps_gather4_c */
    context.pipeline_state = create_pipeline_state(context.device,
            context.root_signature, desc.rt_format, NULL, &ps_gather4_c, NULL);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, (const float *)&white, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &heap);
    ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0, gpu_handle);
    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 1, 5, &constants.width, 0);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_resource_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
    for (y = 0; y < rb.rb.height; ++y)
    {
        for (x = 0; x < rb.rb.width; ++x)
        {
            const struct vec4 *expected = &expected_gather4_c[y * rb.rb.width + x];
            const struct vec4 *got = get_readback_vec4(&rb.rb, x, y);
            ok(compare_vec4(got, expected, 0),
                    "Got {%.8e, %.8e, %.8e, %.8e}, expected {%.8e, %.8e, %.8e, %.8e}.\n",
                    got->x, got->y, got->z, got->w, expected->x, expected->y, expected->z, expected->w);
        }
    }
    release_resource_readback(&rb);

    ID3D12PipelineState_Release(context.pipeline_state);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    /* ps_gather4_po_c */
    context.pipeline_state = create_pipeline_state(context.device,
            context.root_signature, desc.rt_format, NULL, &ps_gather4_po_c, NULL);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, (const float *)&white, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &heap);
    ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0, gpu_handle);
    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 1, 5, &constants.width, 0);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_resource_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
    for (y = 0; y < rb.rb.height; ++y)
    {
        for (x = 0; x < rb.rb.width; ++x)
        {
            const struct vec4 *expected = &expected_gather4_po_c[y * rb.rb.width + x];
            const struct vec4 *got = get_readback_vec4(&rb.rb, x, y);
            ok(compare_vec4(got, expected, 0),
                    "Got {%.8e, %.8e, %.8e, %.8e}, expected {%.8e, %.8e, %.8e, %.8e}.\n",
                    got->x, got->y, got->z, got->w, expected->x, expected->y, expected->z, expected->w);
        }
    }
    release_resource_readback(&rb);

    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    constants.offset_x = 0;
    constants.offset_y = 0;

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, (const float *)&white, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &heap);
    ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0, gpu_handle);
    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 1, 5, &constants.width, 0);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_resource_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
    for (y = 0; y < rb.rb.height; ++y)
    {
        for (x = 0; x < rb.rb.width; ++x)
        {
            const struct vec4 *expected = &expected_gather4_c[y * rb.rb.width + x];
            const struct vec4 *got = get_readback_vec4(&rb.rb, x, y);
            ok(compare_vec4(got, expected, 0),
                    "Got {%.8e, %.8e, %.8e, %.8e}, expected {%.8e, %.8e, %.8e, %.8e}.\n",
                    got->x, got->y, got->z, got->w, expected->x, expected->y, expected->z, expected->w);
        }
    }
    release_resource_readback(&rb);

    ID3D12Resource_Release(texture);
    ID3D12DescriptorHeap_Release(heap);
    destroy_test_context(&context);
}

static void test_sample_c_lz(void)
{
    struct depth_stencil_resource ds, ds_offset;
    D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc;
    ID3D12GraphicsCommandList *command_list;
    D3D12_DEPTH_STENCIL_VIEW_DESC dsv_desc;
    D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle;
    struct d3d12_resource_readback rb;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12DescriptorHeap *heap;
    ID3D12CommandQueue *queue;
    struct vec4 ps_constant;
    ID3D12Device *device;
    unsigned int i, j;
    RECT rect;

    static const D3D12_STATIC_SAMPLER_DESC sampler_desc =
    {
        .Filter = D3D12_FILTER_COMPARISON_MIN_MAG_MIP_LINEAR,
        .AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP,
        .AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP,
        .AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP,
        .ComparisonFunc = D3D12_COMPARISON_FUNC_GREATER,
        .MaxAnisotropy = 0,
        .BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE,
        .MaxLOD = 10.0f,
    };
    static const float clear_color[] = {0.5f, 0.5f, 0.5f, 0.5f};
    static const DWORD ps_array_code[] =
    {
#if 0
        Texture2DArray t;
        SamplerComparisonState s;

        float ref;
        float layer;

        float4 main(float4 position : SV_Position) : SV_Target
        {
            return t.SampleCmpLevelZero(s, float3(position.x / 640.0f, position.y / 480.0f, layer), ref);
        }
#endif
        0x43425844, 0xfe28b3c3, 0xdd7ef404, 0x8d5874a1, 0x984ff182, 0x00000001, 0x00000180, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000030f, 0x505f5653, 0x7469736f, 0x006e6f69,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x52444853, 0x000000e4, 0x00000041,
        0x00000039, 0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x0300085a, 0x00106000,
        0x00000000, 0x04004058, 0x00107000, 0x00000000, 0x00005555, 0x04002064, 0x00101032, 0x00000000,
        0x00000001, 0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000001, 0x0a000038, 0x00100032,
        0x00000000, 0x00101046, 0x00000000, 0x00004002, 0x3acccccd, 0x3b088889, 0x00000000, 0x00000000,
        0x06000036, 0x00100042, 0x00000000, 0x0020801a, 0x00000000, 0x00000000, 0x0c000047, 0x00100012,
        0x00000000, 0x00100246, 0x00000000, 0x00107006, 0x00000000, 0x00106000, 0x00000000, 0x0020800a,
        0x00000000, 0x00000000, 0x05000036, 0x001020f2, 0x00000000, 0x00100006, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_array = {ps_array_code, sizeof(ps_array_code)};
    static const DWORD ps_array_offset_code[] =
    {
#if 0
        Texture2DArray t;
        SamplerComparisonState s;

        float ref;
        float layer;

        float4 main(float4 position : SV_Position) : SV_Target
        {
            return t.SampleCmpLevelZero(s, float3(position.x / 5120.0f, position.y / 3840.0f, layer), ref,
                    int2(4, 4));
        }
#endif
        0x43425844, 0xdf99ade1, 0x420d8d11, 0x3abe4b31, 0x116badd9, 0x00000001, 0x0000018c, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000030f, 0x505f5653, 0x7469736f, 0x006e6f69,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x000000f0, 0x00000050,
        0x0000003c, 0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x0300085a, 0x00106000,
        0x00000000, 0x04004058, 0x00107000, 0x00000000, 0x00005555, 0x04002064, 0x00101032, 0x00000000,
        0x00000001, 0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000001, 0x0a000038, 0x00100032,
        0x00000000, 0x00101046, 0x00000000, 0x00004002, 0x394ccccd, 0x39888889, 0x00000000, 0x00000000,
        0x06000036, 0x00100042, 0x00000000, 0x0020801a, 0x00000000, 0x00000000, 0x8f000047, 0x80008801,
        0x80000202, 0x00155543, 0x00100012, 0x00000000, 0x00100246, 0x00000000, 0x00107006, 0x00000000,
        0x00106000, 0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x05000036, 0x001020f2, 0x00000000,
        0x00100006, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_array_offset = {ps_array_offset_code, sizeof(ps_array_offset_code)};
    static const DWORD ps_cube_code[] =
    {
#if 0
        TextureCube t;
        SamplerComparisonState s;

        float ref;
        float face;

        float4 main(float4 position : SV_Position) : SV_Target
        {
            float2 p;
            p.x = position.x / 640.0f;
            p.y = position.y / 480.0f;

            float3 coord;
            switch ((uint)face)
            {
                case 0:
                    coord = float3(1.0f, p.x, p.y);
                    break;
                case 1:
                    coord = float3(-1.0f, p.x, p.y);
                    break;
                case 2:
                    coord = float3(p.x, 1.0f, p.y);
                    break;
                case 3:
                    coord = float3(p.x, -1.0f, p.y);
                    break;
                case 4:
                    coord = float3(p.x, p.y, 1.0f);
                    break;
                case 5:
                default:
                    coord = float3(p.x, p.y, -1.0f);
                    break;
            }

            return t.SampleCmpLevelZero(s, coord, ref);
        }
#endif
        0x43425844, 0xde5655e5, 0x1b116fa1, 0xfce9e757, 0x41c28aac, 0x00000001, 0x00000328, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000030f, 0x505f5653, 0x7469736f, 0x006e6f69,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x52444853, 0x0000028c, 0x00000041,
        0x000000a3, 0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x0300085a, 0x00106000,
        0x00000000, 0x04003058, 0x00107000, 0x00000000, 0x00005555, 0x04002064, 0x00101032, 0x00000000,
        0x00000001, 0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000001, 0x0600001c, 0x00100012,
        0x00000000, 0x0020801a, 0x00000000, 0x00000000, 0x0300004c, 0x0010000a, 0x00000000, 0x03000006,
        0x00004001, 0x00000000, 0x05000036, 0x00100012, 0x00000000, 0x00004001, 0x3f800000, 0x0a000038,
        0x00100062, 0x00000000, 0x00101106, 0x00000000, 0x00004002, 0x00000000, 0x3acccccd, 0x3b088889,
        0x00000000, 0x01000002, 0x03000006, 0x00004001, 0x00000001, 0x05000036, 0x00100012, 0x00000000,
        0x00004001, 0xbf800000, 0x0a000038, 0x00100062, 0x00000000, 0x00101106, 0x00000000, 0x00004002,
        0x00000000, 0x3acccccd, 0x3b088889, 0x00000000, 0x01000002, 0x03000006, 0x00004001, 0x00000002,
        0x0a000038, 0x00100052, 0x00000000, 0x00101106, 0x00000000, 0x00004002, 0x3acccccd, 0x00000000,
        0x3b088889, 0x00000000, 0x05000036, 0x00100022, 0x00000000, 0x00004001, 0x3f800000, 0x01000002,
        0x03000006, 0x00004001, 0x00000003, 0x0a000038, 0x00100052, 0x00000000, 0x00101106, 0x00000000,
        0x00004002, 0x3acccccd, 0x00000000, 0x3b088889, 0x00000000, 0x05000036, 0x00100022, 0x00000000,
        0x00004001, 0xbf800000, 0x01000002, 0x03000006, 0x00004001, 0x00000004, 0x0a000038, 0x00100032,
        0x00000000, 0x00101046, 0x00000000, 0x00004002, 0x3acccccd, 0x3b088889, 0x00000000, 0x00000000,
        0x05000036, 0x00100042, 0x00000000, 0x00004001, 0x3f800000, 0x01000002, 0x0100000a, 0x0a000038,
        0x00100032, 0x00000000, 0x00101046, 0x00000000, 0x00004002, 0x3acccccd, 0x3b088889, 0x00000000,
        0x00000000, 0x05000036, 0x00100042, 0x00000000, 0x00004001, 0xbf800000, 0x01000002, 0x01000017,
        0x0c000047, 0x00100012, 0x00000000, 0x00100246, 0x00000000, 0x00107006, 0x00000000, 0x00106000,
        0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x05000036, 0x001020f2, 0x00000000, 0x00100006,
        0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_cube = {ps_cube_code, sizeof(ps_cube_code)};
    static const DWORD ps_cube_array_code[] =
    {
#if 0
        TextureCubeArray t;
        SamplerComparisonState s;

        float ref;
        float layer;

        float4 main(float4 position : SV_Position) : SV_Target
        {
            float2 p;
            p.x = position.x / 640.0f;
            p.y = position.y / 480.0f;

            float3 coord;
            switch ((uint)layer % 6)
            {
                case 0:
                    coord = float3(1.0f, p.x, p.y);
                    break;
                case 1:
                    coord = float3(-1.0f, p.x, p.y);
                    break;
                case 2:
                    coord = float3(p.x, 1.0f, p.y);
                    break;
                case 3:
                    coord = float3(p.x, -1.0f, p.y);
                    break;
                case 4:
                    coord = float3(p.x, p.y, 1.0f);
                    break;
                case 5:
                default:
                    coord = float3(p.x, p.y, -1.0f);
                    break;
            }

            return t.SampleCmpLevelZero(s, float4(coord, (uint)layer / 6), ref);
        }
#endif
        0x43425844, 0xafbb850b, 0x724ab414, 0x56cdc3a8, 0x6b9fd1af, 0x00000001, 0x00000360, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000030f, 0x505f5653, 0x7469736f, 0x006e6f69,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x52444853, 0x000002c4, 0x00000041,
        0x000000b1, 0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x0300085a, 0x00106000,
        0x00000000, 0x04005058, 0x00107000, 0x00000000, 0x00005555, 0x04002064, 0x00101032, 0x00000000,
        0x00000001, 0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000002, 0x0600001c, 0x00100012,
        0x00000000, 0x0020801a, 0x00000000, 0x00000000, 0x0900004e, 0x00100012, 0x00000000, 0x00100012,
        0x00000001, 0x0010000a, 0x00000000, 0x00004001, 0x00000006, 0x0300004c, 0x0010000a, 0x00000001,
        0x03000006, 0x00004001, 0x00000000, 0x05000036, 0x00100012, 0x00000001, 0x00004001, 0x3f800000,
        0x0a000038, 0x00100062, 0x00000001, 0x00101106, 0x00000000, 0x00004002, 0x00000000, 0x3acccccd,
        0x3b088889, 0x00000000, 0x01000002, 0x03000006, 0x00004001, 0x00000001, 0x05000036, 0x00100012,
        0x00000001, 0x00004001, 0xbf800000, 0x0a000038, 0x00100062, 0x00000001, 0x00101106, 0x00000000,
        0x00004002, 0x00000000, 0x3acccccd, 0x3b088889, 0x00000000, 0x01000002, 0x03000006, 0x00004001,
        0x00000002, 0x0a000038, 0x00100052, 0x00000001, 0x00101106, 0x00000000, 0x00004002, 0x3acccccd,
        0x00000000, 0x3b088889, 0x00000000, 0x05000036, 0x00100022, 0x00000001, 0x00004001, 0x3f800000,
        0x01000002, 0x03000006, 0x00004001, 0x00000003, 0x0a000038, 0x00100052, 0x00000001, 0x00101106,
        0x00000000, 0x00004002, 0x3acccccd, 0x00000000, 0x3b088889, 0x00000000, 0x05000036, 0x00100022,
        0x00000001, 0x00004001, 0xbf800000, 0x01000002, 0x03000006, 0x00004001, 0x00000004, 0x0a000038,
        0x00100032, 0x00000001, 0x00101046, 0x00000000, 0x00004002, 0x3acccccd, 0x3b088889, 0x00000000,
        0x00000000, 0x05000036, 0x00100042, 0x00000001, 0x00004001, 0x3f800000, 0x01000002, 0x0100000a,
        0x0a000038, 0x00100032, 0x00000001, 0x00101046, 0x00000000, 0x00004002, 0x3acccccd, 0x3b088889,
        0x00000000, 0x00000000, 0x05000036, 0x00100042, 0x00000001, 0x00004001, 0xbf800000, 0x01000002,
        0x01000017, 0x05000056, 0x00100082, 0x00000001, 0x0010000a, 0x00000000, 0x0c000047, 0x00100012,
        0x00000000, 0x00100e46, 0x00000001, 0x00107006, 0x00000000, 0x00106000, 0x00000000, 0x0020800a,
        0x00000000, 0x00000000, 0x05000036, 0x001020f2, 0x00000000, 0x00100006, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_cube_array = {ps_cube_array_code, sizeof(ps_cube_array_code)};
    static const float depth_values[] =
    {
        1.0f, 0.0f, 0.5f, 0.6f, 0.4f, 0.1f,
        0.2f, 0.9f, 0.0f, 1.0f, 0.8f, 0.3f,
    };
    static const struct
    {
        unsigned int layer;
        float d_ref;
        float expected;
    }
    tests[] =
    {
        { 0, 0.5f, 0.0f},
        { 1, 0.5f, 1.0f},
        { 2, 0.5f, 0.0f},
        { 3, 0.5f, 0.0f},
        { 4, 0.5f, 1.0f},
        { 5, 0.5f, 1.0f},
        { 6, 0.5f, 1.0f},
        { 7, 0.5f, 0.0f},
        { 8, 0.5f, 1.0f},
        { 9, 0.5f, 0.0f},
        {10, 0.5f, 0.0f},
        {11, 0.5f, 1.0f},

        {0, 0.0f, 0.0f},
        {1, 0.0f, 0.0f},
        {2, 0.0f, 0.0f},
        {3, 0.0f, 0.0f},
        {4, 0.0f, 0.0f},
        {5, 0.0f, 0.0f},

        {0, 1.0f, 0.0f},
        {1, 1.0f, 1.0f},
        {2, 1.0f, 1.0f},
        {3, 1.0f, 1.0f},
        {4, 1.0f, 1.0f},
        {5, 1.0f, 1.0f},
    };
    static const D3D12_SHADER_BYTECODE * const shaders_2d_array[] =
    {
        &ps_array,
        &ps_array_offset,
    };

    memset(&desc, 0, sizeof(desc));
    desc.rt_width = 640;
    desc.rt_height = 480;
    desc.rt_format = DXGI_FORMAT_R32_FLOAT;
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    context.root_signature = create_texture_root_signature_(__FILE__, __LINE__, device,
            D3D12_SHADER_VISIBILITY_PIXEL, 4, 0, &sampler_desc);

    heap = create_gpu_descriptor_heap(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 6);
    cpu_handle = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(heap);

    init_depth_stencil(&ds, device, 32, 32, ARRAY_SIZE(depth_values), 2,
            DXGI_FORMAT_R32_TYPELESS, DXGI_FORMAT_D32_FLOAT, NULL);
    init_depth_stencil(&ds_offset, device, 32, 32, ARRAY_SIZE(depth_values), 2,
            DXGI_FORMAT_R32_TYPELESS, DXGI_FORMAT_D32_FLOAT, NULL);

    for (i = 0; i < ARRAY_SIZE(depth_values); ++i)
    {
        static const D3D12_RECT rects[] = {{0, 0, 3, 3}, {3, 3, 32, 32}};

        memset(&dsv_desc, 0, sizeof(dsv_desc));
        dsv_desc.Format = DXGI_FORMAT_D32_FLOAT;
        dsv_desc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DARRAY;
        dsv_desc.Texture2DArray.FirstArraySlice = i;
        dsv_desc.Texture2DArray.ArraySize = 1;
        ID3D12Device_CreateDepthStencilView(device, ds.texture, &dsv_desc, ds.dsv_handle);
        ID3D12GraphicsCommandList_ClearDepthStencilView(command_list, ds.dsv_handle,
                D3D12_CLEAR_FLAG_DEPTH, depth_values[i], 0, 0, NULL);
        ID3D12Device_CreateDepthStencilView(device, ds_offset.texture, &dsv_desc, ds_offset.dsv_handle);
        ID3D12GraphicsCommandList_ClearDepthStencilView(command_list, ds_offset.dsv_handle,
                D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 1, &rects[0]);
        ID3D12GraphicsCommandList_ClearDepthStencilView(command_list, ds_offset.dsv_handle,
                D3D12_CLEAR_FLAG_DEPTH, depth_values[i], 0, 1, &rects[1]);

        dsv_desc.Texture2DArray.MipSlice = 1;
        ID3D12Device_CreateDepthStencilView(device, ds.texture, &dsv_desc, ds.dsv_handle);
        ID3D12GraphicsCommandList_ClearDepthStencilView(command_list, ds.dsv_handle,
                D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, NULL);
    }

    /* 2D array texture */
    memset(&srv_desc, 0, sizeof(srv_desc));
    srv_desc.Format = DXGI_FORMAT_R32_FLOAT;
    srv_desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
    srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY;
    srv_desc.Texture2DArray.MostDetailedMip = 0;
    srv_desc.Texture2DArray.MipLevels = 2;
    srv_desc.Texture2DArray.FirstArraySlice = 0;
    srv_desc.Texture2DArray.ArraySize = ARRAY_SIZE(depth_values);
    ID3D12Device_CreateShaderResourceView(context.device, ds.texture, &srv_desc, cpu_handle);
    ID3D12Device_CreateShaderResourceView(context.device, ds_offset.texture, &srv_desc,
            get_cpu_descriptor_handle(&context, heap, 1));

    transition_resource_state(command_list, ds.texture,
            D3D12_RESOURCE_STATE_DEPTH_WRITE, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
    transition_resource_state(command_list, ds_offset.texture,
            D3D12_RESOURCE_STATE_DEPTH_WRITE, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);

    memset(&ps_constant, 0, sizeof(ps_constant));
    for (j = 0; j < ARRAY_SIZE(shaders_2d_array); ++j)
    {
        context.pipeline_state = create_pipeline_state(context.device, context.root_signature,
                context.render_target_desc.Format, NULL, shaders_2d_array[j], NULL);

        for (i = 0; i < ARRAY_SIZE(tests); ++i)
        {
            vkd3d_test_push_context("test %u%s", i, j ? " offset" : "");

            ps_constant.x = tests[i].d_ref;
            ps_constant.y = tests[i].layer;

            ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, clear_color, 0, NULL);
            ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
            ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
            ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
            ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &heap);
            ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list,
                    0, get_gpu_descriptor_handle(&context, heap, j));
            ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 1, 4, &ps_constant.x, 0);
            ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
            ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
            ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
            ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

            transition_resource_state(command_list, context.render_target,
                    D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);

            check_sub_resource_float(context.render_target, 0, queue, command_list, tests[i].expected, 2);

            reset_command_list(command_list, context.allocator);
            transition_resource_state(command_list, context.render_target,
                    D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

            vkd3d_test_pop_context();
        }
        ID3D12PipelineState_Release(context.pipeline_state);
    }

    /* cube texture */
    context.pipeline_state = create_pipeline_state(context.device,
            context.root_signature, context.render_target_desc.Format, NULL, &ps_cube, NULL);

    memset(&srv_desc, 0, sizeof(srv_desc));
    srv_desc.Format = DXGI_FORMAT_R32_FLOAT;
    srv_desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
    srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE;
    srv_desc.TextureCube.MostDetailedMip = 0;
    srv_desc.TextureCube.MipLevels = 2;
    ID3D12Device_CreateShaderResourceView(context.device, ds.texture, &srv_desc, cpu_handle);

    for (i = 0; i < ARRAY_SIZE(tests); ++i)
    {
        if (tests[i].layer >= 6)
            continue;

        vkd3d_test_push_context("test %u", i);

        ps_constant.x = tests[i].d_ref;
        ps_constant.y = tests[i].layer;

        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, clear_color, 0, NULL);
        ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
        ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
        ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &heap);
        ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0,
                    get_gpu_descriptor_handle(&context, heap, 0));
        ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 1, 4, &ps_constant.x, 0);
        ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
        ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);

        get_resource_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
        /* Avoid testing values affected by seamless cube map filtering. */
        set_rect(&rect, 100, 100, 540, 380);
        check_readback_data_float(&rb.rb, &rect, tests[i].expected, 2);
        release_resource_readback(&rb);

        reset_command_list(command_list, context.allocator);
        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

        vkd3d_test_pop_context();
    }

    ID3D12PipelineState_Release(context.pipeline_state);

    /* cube array texture */
    context.pipeline_state = create_pipeline_state(context.device,
            context.root_signature, context.render_target_desc.Format, NULL, &ps_cube_array, NULL);

    memset(&srv_desc, 0, sizeof(srv_desc));
    srv_desc.Format = DXGI_FORMAT_R32_FLOAT;
    srv_desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
    srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBEARRAY;
    srv_desc.TextureCubeArray.MostDetailedMip = 0;
    srv_desc.TextureCubeArray.MipLevels = 2;
    srv_desc.TextureCubeArray.First2DArrayFace = 0;
    srv_desc.TextureCubeArray.NumCubes = ARRAY_SIZE(depth_values) / 6;
    ID3D12Device_CreateShaderResourceView(context.device, ds.texture, &srv_desc, cpu_handle);

    for (i = 0; i < ARRAY_SIZE(tests); ++i)
    {
        if (is_qualcomm_device(device))
        {
            skip("Sampling a cube texture crashes on Qualcomm.\n");
            break;
        }

        vkd3d_test_push_context("test %u", i);

        ps_constant.x = tests[i].d_ref;
        ps_constant.y = tests[i].layer;

        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, clear_color, 0, NULL);
        ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
        ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
        ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &heap);
        ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0,
                    get_gpu_descriptor_handle(&context, heap, 0));
        ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 1, 4, &ps_constant.x, 0);
        ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
        ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);

        get_resource_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
        /* Avoid testing values affected by seamless cube map filtering. */
        set_rect(&rect, 100, 100, 540, 380);
        check_readback_data_float(&rb.rb, &rect, tests[i].expected, 2);
        release_resource_readback(&rb);

        reset_command_list(command_list, context.allocator);
        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

        vkd3d_test_pop_context();
    }

    destroy_depth_stencil(&ds_offset);
    destroy_depth_stencil(&ds);
    ID3D12DescriptorHeap_Release(heap);
    destroy_test_context(&context);
}

static void test_cube_maps(void)
{
    unsigned int i, j, sub_resource_idx, sub_resource_count;
    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
    D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc;
    ID3D12GraphicsCommandList *command_list;
    D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle;
    D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle;
    D3D12_SUBRESOURCE_DATA *texture_data;
    const D3D12_SHADER_BYTECODE *ps;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12DescriptorHeap *heap;
    ID3D12CommandQueue *queue;
    ID3D12PipelineState *pso;
    ID3D12Resource *texture;
    float *data;
    HRESULT hr;

    struct
    {
        unsigned int face;
        unsigned int level;
        unsigned int cube;
    } constants;

    const unsigned int texture_size = 64;
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    static const DWORD ps_cube_code[] =
    {
#if 0
        TextureCube t;
        SamplerState s;

        uint face;
        uint level;

        float4 main(float4 position : SV_POSITION) : SV_Target
        {
            float2 p;
            p.x = position.x / 640.0f;
            p.y = position.y / 480.0f;

            float3 coord;
            switch (face)
            {
                case 0:
                    coord = float3(1.0f, p.x, p.y);
                    break;
                case 1:
                    coord = float3(-1.0f, p.x, p.y);
                    break;
                case 2:
                    coord = float3(p.x, 1.0f, p.y);
                    break;
                case 3:
                    coord = float3(p.x, -1.0f, p.y);
                    break;
                case 4:
                    coord = float3(p.x, p.y, 1.0f);
                    break;
                case 5:
                default:
                    coord = float3(p.x, p.y, -1.0f);
                    break;
            }
            return t.SampleLevel(s, coord, level);
        }
#endif
        0x43425844, 0x039aee18, 0xfd630453, 0xb884cf0f, 0x10100744, 0x00000001, 0x00000310, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000030f, 0x505f5653, 0x5449534f, 0x004e4f49,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x52444853, 0x00000274, 0x00000040,
        0x0000009d, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x0300005a, 0x00106000, 0x00000000,
        0x04003058, 0x00107000, 0x00000000, 0x00005555, 0x04002064, 0x00101032, 0x00000000, 0x00000001,
        0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000001, 0x0400004c, 0x0020800a, 0x00000000,
        0x00000000, 0x03000006, 0x00004001, 0x00000000, 0x05000036, 0x00100012, 0x00000000, 0x00004001,
        0x3f800000, 0x0a000038, 0x00100062, 0x00000000, 0x00101106, 0x00000000, 0x00004002, 0x00000000,
        0x3acccccd, 0x3b088889, 0x00000000, 0x01000002, 0x03000006, 0x00004001, 0x00000001, 0x05000036,
        0x00100012, 0x00000000, 0x00004001, 0xbf800000, 0x0a000038, 0x00100062, 0x00000000, 0x00101106,
        0x00000000, 0x00004002, 0x00000000, 0x3acccccd, 0x3b088889, 0x00000000, 0x01000002, 0x03000006,
        0x00004001, 0x00000002, 0x0a000038, 0x00100052, 0x00000000, 0x00101106, 0x00000000, 0x00004002,
        0x3acccccd, 0x00000000, 0x3b088889, 0x00000000, 0x05000036, 0x00100022, 0x00000000, 0x00004001,
        0x3f800000, 0x01000002, 0x03000006, 0x00004001, 0x00000003, 0x0a000038, 0x00100052, 0x00000000,
        0x00101106, 0x00000000, 0x00004002, 0x3acccccd, 0x00000000, 0x3b088889, 0x00000000, 0x05000036,
        0x00100022, 0x00000000, 0x00004001, 0xbf800000, 0x01000002, 0x03000006, 0x00004001, 0x00000004,
        0x0a000038, 0x00100032, 0x00000000, 0x00101046, 0x00000000, 0x00004002, 0x3acccccd, 0x3b088889,
        0x00000000, 0x00000000, 0x05000036, 0x00100042, 0x00000000, 0x00004001, 0x3f800000, 0x01000002,
        0x0100000a, 0x0a000038, 0x00100032, 0x00000000, 0x00101046, 0x00000000, 0x00004002, 0x3acccccd,
        0x3b088889, 0x00000000, 0x00000000, 0x05000036, 0x00100042, 0x00000000, 0x00004001, 0xbf800000,
        0x01000002, 0x01000017, 0x06000056, 0x00100082, 0x00000000, 0x0020801a, 0x00000000, 0x00000000,
        0x0b000048, 0x001020f2, 0x00000000, 0x00100246, 0x00000000, 0x00107e46, 0x00000000, 0x00106000,
        0x00000000, 0x0010003a, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_cube = {ps_cube_code, sizeof(ps_cube_code)};
    static const DWORD ps_cube_array_code[] =
    {
#if 0
        TextureCubeArray t;
        SamplerState s;

        uint face;
        uint level;
        uint cube;

        float4 main(float4 position : SV_POSITION) : SV_Target
        {
            float2 p;
            p.x = position.x / 640.0f;
            p.y = position.y / 480.0f;

            float3 coord;
            switch (face)
            {
                case 0:
                    coord = float3(1.0f, p.x, p.y);
                    break;
                case 1:
                    coord = float3(-1.0f, p.x, p.y);
                    break;
                case 2:
                    coord = float3(p.x, 1.0f, p.y);
                    break;
                case 3:
                    coord = float3(p.x, -1.0f, p.y);
                    break;
                case 4:
                    coord = float3(p.x, p.y, 1.0f);
                    break;
                case 5:
                default:
                    coord = float3(p.x, p.y, -1.0f);
                    break;
            }
            return t.SampleLevel(s, float4(coord, cube), level);
        }
#endif
        0x43425844, 0xb8d5b94a, 0xdb4be034, 0x183aed19, 0xad4af415, 0x00000001, 0x00000328, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000030f, 0x505f5653, 0x5449534f, 0x004e4f49,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x52444853, 0x0000028c, 0x00000041,
        0x000000a3, 0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x0300005a, 0x00106000,
        0x00000000, 0x04005058, 0x00107000, 0x00000000, 0x00005555, 0x04002064, 0x00101032, 0x00000000,
        0x00000001, 0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000002, 0x0400004c, 0x0020800a,
        0x00000000, 0x00000000, 0x03000006, 0x00004001, 0x00000000, 0x05000036, 0x00100012, 0x00000000,
        0x00004001, 0x3f800000, 0x0a000038, 0x00100062, 0x00000000, 0x00101106, 0x00000000, 0x00004002,
        0x00000000, 0x3acccccd, 0x3b088889, 0x00000000, 0x01000002, 0x03000006, 0x00004001, 0x00000001,
        0x05000036, 0x00100012, 0x00000000, 0x00004001, 0xbf800000, 0x0a000038, 0x00100062, 0x00000000,
        0x00101106, 0x00000000, 0x00004002, 0x00000000, 0x3acccccd, 0x3b088889, 0x00000000, 0x01000002,
        0x03000006, 0x00004001, 0x00000002, 0x0a000038, 0x00100052, 0x00000000, 0x00101106, 0x00000000,
        0x00004002, 0x3acccccd, 0x00000000, 0x3b088889, 0x00000000, 0x05000036, 0x00100022, 0x00000000,
        0x00004001, 0x3f800000, 0x01000002, 0x03000006, 0x00004001, 0x00000003, 0x0a000038, 0x00100052,
        0x00000000, 0x00101106, 0x00000000, 0x00004002, 0x3acccccd, 0x00000000, 0x3b088889, 0x00000000,
        0x05000036, 0x00100022, 0x00000000, 0x00004001, 0xbf800000, 0x01000002, 0x03000006, 0x00004001,
        0x00000004, 0x0a000038, 0x00100032, 0x00000000, 0x00101046, 0x00000000, 0x00004002, 0x3acccccd,
        0x3b088889, 0x00000000, 0x00000000, 0x05000036, 0x00100042, 0x00000000, 0x00004001, 0x3f800000,
        0x01000002, 0x0100000a, 0x0a000038, 0x00100032, 0x00000000, 0x00101046, 0x00000000, 0x00004002,
        0x3acccccd, 0x3b088889, 0x00000000, 0x00000000, 0x05000036, 0x00100042, 0x00000000, 0x00004001,
        0xbf800000, 0x01000002, 0x01000017, 0x06000056, 0x00100032, 0x00000001, 0x00208a66, 0x00000000,
        0x00000000, 0x05000036, 0x00100082, 0x00000000, 0x0010000a, 0x00000001, 0x0b000048, 0x001020f2,
        0x00000000, 0x00100e46, 0x00000000, 0x00107e46, 0x00000000, 0x00106000, 0x00000000, 0x0010001a,
        0x00000001, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_cube_array = {ps_cube_array_code, sizeof(ps_cube_array_code)};
    static const struct ps_test
    {
        const D3D12_SHADER_BYTECODE *ps;
        unsigned int miplevel_count;
        unsigned int array_size;
        unsigned int cube_count;
    }
    ps_tests[] =
    {
        {&ps_cube, 1, 6, 1},
        {&ps_cube, 2, 6, 1},
        {&ps_cube, 3, 6, 1},
        {&ps_cube, 3, 6, ~0u},

        {&ps_cube_array, 1, 12, 2},
        {&ps_cube_array, 1, 12, ~0u},
        {&ps_cube_array, 2, 12, 2},
        {&ps_cube_array, 3, 12, 2},
        {&ps_cube_array, 3, 12, ~0u},
    };

    memset(&desc, 0, sizeof(desc));
    desc.rt_width = 640;
    desc.rt_height = 480;
    desc.rt_format = DXGI_FORMAT_R32_FLOAT;
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    command_list = context.list;
    queue = context.queue;

    context.root_signature = create_texture_root_signature(context.device,
            D3D12_SHADER_VISIBILITY_PIXEL, 3, 0);

    init_pipeline_state_desc(&pso_desc, context.root_signature,
            context.render_target_desc.Format, NULL, NULL, NULL);

    heap = create_gpu_descriptor_heap(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1);
    cpu_handle = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(heap);
    gpu_handle = ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(heap);

    ps = NULL;
    pso = NULL;
    for (i = 0; i < ARRAY_SIZE(ps_tests); ++i)
    {
        const struct ps_test *test = &ps_tests[i];

        if (ps != test->ps)
        {
            if (pso)
                ID3D12PipelineState_Release(pso);

            ps = test->ps;
            pso_desc.PS = *test->ps;
            hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
                    &IID_ID3D12PipelineState, (void **)&pso);
            ok(hr == S_OK, "Failed to create graphics pipeline state, hr %#x.\n", hr);
        }

        texture = create_default_texture2d(context.device, texture_size, texture_size,
                test->array_size, test->miplevel_count, DXGI_FORMAT_R32_FLOAT,
                D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST);
        srv_desc.Format = DXGI_FORMAT_R32_FLOAT;
        srv_desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
        if (ps == &ps_cube)
        {
            srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE;
            srv_desc.TextureCube.MostDetailedMip = 0;
            srv_desc.TextureCube.MipLevels = test->miplevel_count;
            srv_desc.TextureCube.ResourceMinLODClamp = 0.0f;
        }
        else
        {
            srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBEARRAY;
            srv_desc.TextureCubeArray.MostDetailedMip = 0;
            srv_desc.TextureCubeArray.MipLevels = test->miplevel_count;
            srv_desc.TextureCubeArray.First2DArrayFace = 0;
            srv_desc.TextureCubeArray.NumCubes = test->cube_count;
            srv_desc.TextureCubeArray.ResourceMinLODClamp = 0.0f;
        }
        ID3D12Device_CreateShaderResourceView(context.device, texture, &srv_desc, cpu_handle);

        sub_resource_count = test->array_size * test->miplevel_count;
        texture_data = calloc(sub_resource_count, sizeof(*texture_data));
        ok(texture_data, "Failed to allocate memory.\n");
        for (sub_resource_idx = 0; sub_resource_idx < sub_resource_count; ++sub_resource_idx)
        {
            data = calloc(texture_size * texture_size, sizeof(*data));
            ok(data, "Failed to allocate memory.\n");
            for (j = 0; j < texture_size * texture_size; ++j)
                data[j] = sub_resource_idx;

            texture_data[sub_resource_idx].pData = data;
            texture_data[sub_resource_idx].RowPitch = texture_size * sizeof(*data);
            texture_data[sub_resource_idx].SlicePitch = 0;
        }
        upload_texture_data(texture, texture_data, sub_resource_count, queue, command_list);
        for (sub_resource_idx = 0; sub_resource_idx < sub_resource_count; ++sub_resource_idx)
            free((void *)texture_data[sub_resource_idx].pData);
        free(texture_data);

        reset_command_list(command_list, context.allocator);
        transition_resource_state(command_list, texture,
                D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);

        for (sub_resource_idx = 0; sub_resource_idx < sub_resource_count; ++sub_resource_idx)
        {
            constants.face = (sub_resource_idx / test->miplevel_count) % 6;
            constants.level = sub_resource_idx % test->miplevel_count;
            constants.cube = (sub_resource_idx / test->miplevel_count) / 6;

            ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
            ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
            ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
            ID3D12GraphicsCommandList_SetPipelineState(command_list, pso);
            ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &heap);
            ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0, gpu_handle);
            ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 1, 3, &constants.face, 0);
            ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
            ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
            ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
            ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

            transition_resource_state(command_list, context.render_target,
                    D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);

            check_sub_resource_float(context.render_target, 0, queue, command_list, sub_resource_idx, 0);

            reset_command_list(command_list, context.allocator);
            transition_resource_state(command_list, context.render_target,
                    D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
        }

        ID3D12Resource_Release(texture);
    }
    ID3D12PipelineState_Release(pso);

    ID3D12DescriptorHeap_Release(heap);
    destroy_test_context(&context);
}

static void test_multisample_array_texture(void)
{
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    D3D12_ROOT_PARAMETER root_parameters[3];
    D3D12_DESCRIPTOR_RANGE descriptor_range;
    ID3D12GraphicsCommandList *command_list;
    D3D12_RENDER_TARGET_VIEW_DESC rtv_desc;
    D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle;
    D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle;
    D3D12_HEAP_PROPERTIES heap_properties;
    struct d3d12_resource_readback rb;
    D3D12_RESOURCE_DESC resource_desc;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12DescriptorHeap *heap;
    ID3D12Resource *uav_buffer;
    ID3D12CommandQueue *queue;
    ID3D12Resource *texture;
    ID3D12Device *device;
    unsigned int i;
    HRESULT hr;

    static const DWORD ps_code[] =
    {
#if 0
        Texture2DMSArray<float4> t;

        RWByteAddressBuffer u;

        uint layer;
        uint sample_index;

        uint offset;

        float4 main() : SV_Target
        {
            uint width, height, elements, samples;
            t.GetDimensions(width, height, elements, samples);
            u.Store4(offset, uint4(width, height, elements, samples));
            return t.Load(int3(0, 0, layer), sample_index);
        }
#endif
        0x43425844, 0xb1457478, 0xd475e3dd, 0xda1eb41d, 0x66075d2b, 0x00000001, 0x0000017c, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000104, 0x00000050, 0x00000041,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x04004858, 0x00107000, 0x00000000,
        0x00005555, 0x0300009d, 0x0011e000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000, 0x02000068,
        0x00000001, 0x8900103d, 0x80000242, 0x00155543, 0x00100072, 0x00000000, 0x00004001, 0x00000000,
        0x00107e46, 0x00000000, 0x0500086f, 0x00100082, 0x00000000, 0x0010700a, 0x00000000, 0x080000a6,
        0x0011e0f2, 0x00000001, 0x0020802a, 0x00000000, 0x00000000, 0x00100e46, 0x00000000, 0x05000036,
        0x00100012, 0x00000000, 0x00004001, 0x00000000, 0x06000036, 0x00100042, 0x00000000, 0x0020800a,
        0x00000000, 0x00000000, 0x8c00002e, 0x80000242, 0x00155543, 0x001020f2, 0x00000000, 0x00100206,
        0x00000000, 0x00107e46, 0x00000000, 0x0020801a, 0x00000000, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    static const struct vec4 colors[] =
    {
        {1.0f, 0.0f, 0.0f, 1.0f},
        {0.0f, 1.0f, 0.0f, 1.0f},
        {0.0f, 0.0f, 1.0f, 1.0f},

        {0.0f, 1.0f, 1.0f, 1.0f},
        {1.0f, 0.0f, 1.0f, 1.0f},
        {1.0f, 1.0f, 0.0f, 1.0f},

        {0.5f, 0.5f, 0.5f, 1.0f},
        {0.5f, 0.5f, 0.5f, 0.5f},
    };
    static const struct
    {
        struct
        {
            unsigned int layer;
            unsigned int sample_index;
            unsigned int offset;
        } constants;
        unsigned int expected_color;
    }
    tests[] =
    {
        {{0, 0,  0}, 0xff0000ff},
        {{0, 1,  0}, 0xff0000ff},
        {{0, 2,  0}, 0xff0000ff},
        {{0, 3,  0}, 0xff0000ff},

        {{1, 0, 16}, 0xff00ff00},
        {{2, 1, 32}, 0xffff0000},
        {{3, 2, 32}, 0xffffff00},
        {{4, 3, 32}, 0xffff00ff},
        {{5, 0, 32}, 0xff00ffff},
        {{6, 0, 32}, 0xff7f7f7f},
        {{7, 0, 32}, 0x7f7f7f7f},
    };

    memset(&desc, 0, sizeof(desc));
    desc.rt_descriptor_count = 2;
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    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_PIXEL;
    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 = 3;
    root_parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
    root_parameters[2].ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV;
    root_parameters[2].Descriptor.ShaderRegister = 1;
    root_parameters[2].Descriptor.RegisterSpace = 0;
    root_parameters[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;

    memset(&root_signature_desc, 0, sizeof(root_signature_desc));
    root_signature_desc.NumParameters = ARRAY_SIZE(root_parameters);
    root_signature_desc.pParameters = root_parameters;
    root_signature_desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_NONE;
    hr = create_root_signature(device, &root_signature_desc, &context.root_signature);
    ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr);

    context.pipeline_state = create_pipeline_state(context.device,
            context.root_signature, context.render_target_desc.Format, NULL, &ps, NULL);

    heap = create_gpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1);

    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_TEXTURE2D;
    resource_desc.Width = 32;
    resource_desc.Height = 32;
    resource_desc.DepthOrArraySize = ARRAY_SIZE(colors);
    resource_desc.MipLevels = 1;
    resource_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    resource_desc.SampleDesc.Count = 4;
    resource_desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
    hr = ID3D12Device_CreateCommittedResource(device, &heap_properties, D3D12_HEAP_FLAG_NONE,
            &resource_desc, D3D12_RESOURCE_STATE_RENDER_TARGET, NULL, &IID_ID3D12Resource, (void **)&texture);
    ok(hr == S_OK, "Failed to create texture, hr %#x.\n", hr);

    cpu_handle = get_cpu_rtv_handle(&context, context.rtv_heap, 1);

    for (i = 0; i < ARRAY_SIZE(colors); ++i)
    {
        memset(&rtv_desc, 0, sizeof(rtv_desc));
        rtv_desc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DMSARRAY;
        rtv_desc.Format = resource_desc.Format;
        rtv_desc.Texture2DMSArray.FirstArraySlice = i;
        rtv_desc.Texture2DMSArray.ArraySize = 1;
        ID3D12Device_CreateRenderTargetView(device, texture, &rtv_desc, cpu_handle);

        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, cpu_handle, (const float *)&colors[i], 0, NULL);
    }

    transition_resource_state(command_list, texture,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);

    cpu_handle = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(heap);
    gpu_handle = ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(heap);

    ID3D12Device_CreateShaderResourceView(device, texture, NULL, cpu_handle);

    uav_buffer = create_default_buffer(device, 4096,
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

    for (i = 0; i < ARRAY_SIZE(tests); ++i)
    {
        vkd3d_test_push_context("Test %u", i);

        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
        ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
        ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
        ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &heap);
        ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0, gpu_handle);
        ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 1, 3, &tests[i].constants.layer, 0);
        ID3D12GraphicsCommandList_SetGraphicsRootUnorderedAccessView(command_list,
                2, ID3D12Resource_GetGPUVirtualAddress(uav_buffer));
        ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
        ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
        check_sub_resource_uint(context.render_target, 0, queue, command_list, tests[i].expected_color, 1);

        reset_command_list(command_list, context.allocator);
        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

        vkd3d_test_pop_context();
    }

    transition_resource_state(command_list, uav_buffer,
            D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);

    get_buffer_readback_with_command_list(uav_buffer, DXGI_FORMAT_R32G32B32A32_UINT, &rb, queue, command_list);
    for (i = 0; i < 2; ++i)
    {
        const struct uvec4 *v = get_readback_uvec4(&rb.rb, i, 0);
        ok(v->x == resource_desc.Width, "Got unexpected width %u.\n", v->x);
        ok(v->y == resource_desc.Height, "Got unexpected height %u.\n", v->y);
        ok(v->z == resource_desc.DepthOrArraySize, "Got unexpected array size %u.\n", v->z);
        ok(v->w == resource_desc.SampleDesc.Count, "Got unexpected sample count %u.\n", v->w);
    }
    release_resource_readback(&rb);

    ID3D12Resource_Release(texture);
    ID3D12Resource_Release(uav_buffer);
    ID3D12DescriptorHeap_Release(heap);
    destroy_test_context(&context);
}

static void test_resinfo(void)
{
    D3D12_SHADER_RESOURCE_VIEW_DESC *current_srv_desc, srv_desc;
    const D3D12_SHADER_BYTECODE *current_ps;
    ID3D12GraphicsCommandList *command_list;
    D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle;
    D3D12_HEAP_PROPERTIES heap_properties;
    D3D12_RESOURCE_DESC resource_desc;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12DescriptorHeap *heap;
    ID3D12CommandQueue *queue;
    ID3D12Resource *texture;
    struct uvec4 constant;
    ID3D12Device *device;
    unsigned int i, type;
    HRESULT hr;

    static const DWORD ps_2d_code[] =
    {
#if 0
        Texture2D t;

        uint type;
        uint level;

        float4 main() : SV_TARGET
        {
            if (!type)
            {
                float width, height, miplevels;
                t.GetDimensions(level, width, height, miplevels);
                return float4(width, height, miplevels, 0);
            }
            else
            {
                uint width, height, miplevels;
                t.GetDimensions(level, width, height, miplevels);
                return float4(width, height, miplevels, 0);
            }
        }
#endif
        0x43425844, 0x9c2db58d, 0x7218d757, 0x23255414, 0xaa86938e, 0x00000001, 0x00000168, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x45475241, 0xabab0054, 0x52444853, 0x000000f0, 0x00000040, 0x0000003c,
        0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x04001858, 0x00107000, 0x00000000, 0x00005555,
        0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000001, 0x0400001f, 0x0020800a, 0x00000000,
        0x00000000, 0x0800003d, 0x001000f2, 0x00000000, 0x0020801a, 0x00000000, 0x00000000, 0x00107e46,
        0x00000000, 0x05000036, 0x00102072, 0x00000000, 0x00100346, 0x00000000, 0x05000036, 0x00102082,
        0x00000000, 0x00004001, 0x00000000, 0x0100003e, 0x01000012, 0x0800103d, 0x001000f2, 0x00000000,
        0x0020801a, 0x00000000, 0x00000000, 0x00107e46, 0x00000000, 0x05000056, 0x00102072, 0x00000000,
        0x00100346, 0x00000000, 0x05000036, 0x00102082, 0x00000000, 0x00004001, 0x00000000, 0x0100003e,
        0x01000015, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_2d = {ps_2d_code, sizeof(ps_2d_code)};
    static const DWORD ps_2d_array_code[] =
    {
#if 0
        Texture2DArray t;

        uint type;
        uint level;

        float4 main() : SV_TARGET
        {
            if (!type)
            {
                float width, height, elements, miplevels;
                t.GetDimensions(level, width, height, elements, miplevels);
                return float4(width, height, elements, miplevels);
            }
            else
            {
                uint width, height, elements, miplevels;
                t.GetDimensions(level, width, height, elements, miplevels);
                return float4(width, height, elements, miplevels);
            }
        }
#endif
        0x43425844, 0x92cd8789, 0x38e359ac, 0xd65ab502, 0xa018a5ae, 0x00000001, 0x0000012c, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x45475241, 0xabab0054, 0x52444853, 0x000000b4, 0x00000040, 0x0000002d,
        0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x04004058, 0x00107000, 0x00000000, 0x00005555,
        0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000001, 0x0400001f, 0x0020800a, 0x00000000,
        0x00000000, 0x0800003d, 0x001020f2, 0x00000000, 0x0020801a, 0x00000000, 0x00000000, 0x00107e46,
        0x00000000, 0x0100003e, 0x01000012, 0x0800103d, 0x001000f2, 0x00000000, 0x0020801a, 0x00000000,
        0x00000000, 0x00107e46, 0x00000000, 0x05000056, 0x001020f2, 0x00000000, 0x00100e46, 0x00000000,
        0x0100003e, 0x01000015, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_2d_array = {ps_2d_array_code, sizeof(ps_2d_array_code)};
    static const DWORD ps_3d_code[] =
    {
#if 0
        Texture3D t;

        uint type;
        uint level;

        float4 main() : SV_TARGET
        {
            if (!type)
            {
                float width, height, depth, miplevels;
                t.GetDimensions(level, width, height, depth, miplevels);
                return float4(width, height, depth, miplevels);
            }
            else
            {
                uint width, height, depth, miplevels;
                t.GetDimensions(level, width, height, depth, miplevels);
                return float4(width, height, depth, miplevels);
            }
        }
#endif
        0x43425844, 0xac1f73b9, 0x2bce1322, 0x82c599e6, 0xbff0d681, 0x00000001, 0x0000012c, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x45475241, 0xabab0054, 0x52444853, 0x000000b4, 0x00000040, 0x0000002d,
        0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x04002858, 0x00107000, 0x00000000, 0x00005555,
        0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000001, 0x0400001f, 0x0020800a, 0x00000000,
        0x00000000, 0x0800003d, 0x001020f2, 0x00000000, 0x0020801a, 0x00000000, 0x00000000, 0x00107e46,
        0x00000000, 0x0100003e, 0x01000012, 0x0800103d, 0x001000f2, 0x00000000, 0x0020801a, 0x00000000,
        0x00000000, 0x00107e46, 0x00000000, 0x05000056, 0x001020f2, 0x00000000, 0x00100e46, 0x00000000,
        0x0100003e, 0x01000015, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_3d = {ps_3d_code, sizeof(ps_3d_code)};
    static const DWORD ps_cube_code[] =
    {
#if 0
        TextureCube t;

        uint type;
        uint level;

        float4 main() : SV_TARGET
        {
            if (!type)
            {
                float width, height, miplevels;
                t.GetDimensions(level, width, height, miplevels);
                return float4(width, height, miplevels, 0);
            }
            else
            {
                uint width, height, miplevels;
                t.GetDimensions(level, width, height, miplevels);
                return float4(width, height, miplevels, 0);
            }
        }
#endif
        0x43425844, 0x795eb161, 0xb8291400, 0xcc531086, 0x2a8143ce, 0x00000001, 0x00000168, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x45475241, 0xabab0054, 0x52444853, 0x000000f0, 0x00000040, 0x0000003c,
        0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x04003058, 0x00107000, 0x00000000, 0x00005555,
        0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000001, 0x0400001f, 0x0020800a, 0x00000000,
        0x00000000, 0x0800003d, 0x001000f2, 0x00000000, 0x0020801a, 0x00000000, 0x00000000, 0x00107e46,
        0x00000000, 0x05000036, 0x00102072, 0x00000000, 0x00100346, 0x00000000, 0x05000036, 0x00102082,
        0x00000000, 0x00004001, 0x00000000, 0x0100003e, 0x01000012, 0x0800103d, 0x001000f2, 0x00000000,
        0x0020801a, 0x00000000, 0x00000000, 0x00107e46, 0x00000000, 0x05000056, 0x00102072, 0x00000000,
        0x00100346, 0x00000000, 0x05000036, 0x00102082, 0x00000000, 0x00004001, 0x00000000, 0x0100003e,
        0x01000015, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_cube = {ps_cube_code, sizeof(ps_cube_code)};
    static const DWORD ps_cube_array_code[] =
    {
#if 0
        TextureCubeArray t;

        uint type;
        uint level;

        float4 main() : SV_TARGET
        {
            if (!type)
            {
                float width, height, elements, miplevels;
                t.GetDimensions(level, width, height, elements, miplevels);
                return float4(width, height, miplevels, 0);
            }
            else
            {
                uint width, height, elements, miplevels;
                t.GetDimensions(level, width, height, elements, miplevels);
                return float4(width, height, miplevels, 0);
            }
        }
#endif
        0x43425844, 0x894d136f, 0xa1f5c746, 0xd771ac09, 0x6914e044, 0x00000001, 0x0000016c, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x45475241, 0xabab0054, 0x52444853, 0x000000f4, 0x00000041, 0x0000003d,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x04005058, 0x00107000, 0x00000000,
        0x00005555, 0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000001, 0x0400001f, 0x0020800a,
        0x00000000, 0x00000000, 0x0800003d, 0x00100072, 0x00000000, 0x0020801a, 0x00000000, 0x00000000,
        0x00107b46, 0x00000000, 0x05000036, 0x00102072, 0x00000000, 0x00100246, 0x00000000, 0x05000036,
        0x00102082, 0x00000000, 0x00004001, 0x00000000, 0x0100003e, 0x01000012, 0x0800103d, 0x00100072,
        0x00000000, 0x0020801a, 0x00000000, 0x00000000, 0x00107b46, 0x00000000, 0x05000056, 0x00102072,
        0x00000000, 0x00100246, 0x00000000, 0x05000036, 0x00102082, 0x00000000, 0x00004001, 0x00000000,
        0x0100003e, 0x01000015, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_cube_array = {ps_cube_array_code, sizeof(ps_cube_array_code)};
    static const struct ps_test
    {
        const D3D12_SHADER_BYTECODE *ps;
        struct
        {
            unsigned int width;
            unsigned int height;
            unsigned int depth;
            unsigned int miplevel_count;
            unsigned int array_size;
            unsigned int cube_count;
        } texture_desc;
        unsigned int miplevel;
        struct vec4 expected_result;
    }
    ps_tests[] =
    {
        {&ps_2d, {64, 64, 1, 1, 1, 0}, 0, {64.0f, 64.0f, 1.0f, 0.0f}},
        {&ps_2d, {32, 16, 1, 3, 1, 0}, 0, {32.0f, 16.0f, 3.0f, 0.0f}},
        {&ps_2d, {32, 16, 1, 3, 1, 0}, 1, {16.0f,  8.0f, 3.0f, 0.0f}},
        {&ps_2d, {32, 16, 1, 3, 1, 0}, 2, { 8.0f,  4.0f, 3.0f, 0.0f}},

        {&ps_2d_array, {64, 64, 1, 1, 6, 0}, 0, {64.0f, 64.0f, 6.0f, 1.0f}},
        {&ps_2d_array, {32, 16, 1, 3, 9, 0}, 0, {32.0f, 16.0f, 9.0f, 3.0f}},
        {&ps_2d_array, {32, 16, 1, 3, 7, 0}, 1, {16.0f,  8.0f, 7.0f, 3.0f}},
        {&ps_2d_array, {32, 16, 1, 3, 3, 0}, 2, { 8.0f,  4.0f, 3.0f, 3.0f}},

        {&ps_3d, {64, 64, 2, 1, 1, 0}, 0, {64.0f, 64.0f, 2.0f, 1.0f}},
        {&ps_3d, {64, 64, 2, 2, 1, 0}, 1, {32.0f, 32.0f, 1.0f, 2.0f}},
        {&ps_3d, {64, 64, 4, 1, 1, 0}, 0, {64.0f, 64.0f, 4.0f, 1.0f}},
        {&ps_3d, {64, 64, 4, 2, 1, 0}, 1, {32.0f, 32.0f, 2.0f, 2.0f}},
        {&ps_3d, { 8,  8, 8, 1, 1, 0}, 0, { 8.0f,  8.0f, 8.0f, 1.0f}},
        {&ps_3d, { 8,  8, 8, 4, 1, 0}, 0, { 8.0f,  8.0f, 8.0f, 4.0f}},
        {&ps_3d, { 8,  8, 8, 4, 1, 0}, 1, { 4.0f,  4.0f, 4.0f, 4.0f}},
        {&ps_3d, { 8,  8, 8, 4, 1, 0}, 2, { 2.0f,  2.0f, 2.0f, 4.0f}},
        {&ps_3d, { 8,  8, 8, 4, 1, 0}, 3, { 1.0f,  1.0f, 1.0f, 4.0f}},

        {&ps_cube, { 4,  4, 1, 1, 6, 1}, 0, { 4.0f,  4.0f, 1.0f, 0.0f}},
        {&ps_cube, {32, 32, 1, 1, 6, 1}, 0, {32.0f, 32.0f, 1.0f, 0.0f}},
        {&ps_cube, {32, 32, 1, 3, 6, 1}, 0, {32.0f, 32.0f, 3.0f, 0.0f}},
        {&ps_cube, {32, 32, 1, 3, 6, 1}, 1, {16.0f, 16.0f, 3.0f, 0.0f}},
        {&ps_cube, {32, 32, 1, 3, 6, 1}, 2, { 8.0f,  8.0f, 3.0f, 0.0f}},

        {&ps_cube_array, { 4,  4, 1, 1, 12, 2}, 0, { 4.0f,  4.0f, 1.0f, 0.0f}},
        {&ps_cube_array, {32, 32, 1, 1, 12, 2}, 0, {32.0f, 32.0f, 1.0f, 0.0f}},
        {&ps_cube_array, {32, 32, 1, 3, 12, 2}, 0, {32.0f, 32.0f, 3.0f, 0.0f}},
    };

    memset(&desc, 0, sizeof(desc));
    desc.rt_width = desc.rt_height = 64;
    desc.rt_format = DXGI_FORMAT_R32G32B32A32_FLOAT;
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    context.root_signature = create_texture_root_signature(context.device,
            D3D12_SHADER_VISIBILITY_PIXEL, 4, 0);

    heap = create_gpu_descriptor_heap(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 6);
    cpu_handle = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(heap);

    current_ps = NULL;
    for (i = 0; i < ARRAY_SIZE(ps_tests); ++i)
    {
        const struct ps_test *test = &ps_tests[i];

        vkd3d_test_push_context("test %u", i);

        if (current_ps != test->ps)
        {
            if (context.pipeline_state)
                ID3D12PipelineState_Release(context.pipeline_state);

            current_ps = test->ps;

            context.pipeline_state = create_pipeline_state(context.device,
                    context.root_signature, context.render_target_desc.Format, NULL, current_ps, NULL);
        }

        memset(&heap_properties, 0, sizeof(heap_properties));
        heap_properties.Type = D3D12_HEAP_TYPE_DEFAULT;
        resource_desc.Dimension = test->texture_desc.depth != 1
                ? D3D12_RESOURCE_DIMENSION_TEXTURE3D : D3D12_RESOURCE_DIMENSION_TEXTURE2D;
        resource_desc.Alignment = 0;
        resource_desc.Width = test->texture_desc.width;
        resource_desc.Height = test->texture_desc.height;
        resource_desc.DepthOrArraySize = test->texture_desc.depth != 1
                ? test->texture_desc.depth : test->texture_desc.array_size;
        resource_desc.MipLevels = test->texture_desc.miplevel_count;
        resource_desc.Format = DXGI_FORMAT_R8_UNORM;
        resource_desc.SampleDesc.Count = 1;
        resource_desc.SampleDesc.Quality = 0;
        resource_desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
        resource_desc.Flags = 0;
        hr = ID3D12Device_CreateCommittedResource(device,
                &heap_properties, D3D12_HEAP_FLAG_NONE, &resource_desc,
                D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, NULL,
                &IID_ID3D12Resource, (void **)&texture);
        ok(hr == S_OK, "Failed to create texture, hr %#x.\n", hr);

        current_srv_desc = NULL;
        if (test->texture_desc.cube_count)
        {
            current_srv_desc = &srv_desc;
            srv_desc.Format = resource_desc.Format;
            srv_desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
            if (test->texture_desc.cube_count > 1)
            {
                srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBEARRAY;
                srv_desc.TextureCubeArray.MostDetailedMip = 0;
                srv_desc.TextureCubeArray.MipLevels = resource_desc.MipLevels;
                srv_desc.TextureCubeArray.First2DArrayFace = 0;
                srv_desc.TextureCubeArray.NumCubes = test->texture_desc.cube_count;
                srv_desc.TextureCubeArray.ResourceMinLODClamp = 0.0f;
            }
            else
            {
                srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE;
                srv_desc.TextureCube.MostDetailedMip = 0;
                srv_desc.TextureCube.MipLevels = resource_desc.MipLevels;
                srv_desc.TextureCube.ResourceMinLODClamp = 0.0f;
            }
        }
        ID3D12Device_CreateShaderResourceView(context.device, texture, current_srv_desc, cpu_handle);

        for (type = 0; type < 2; ++type)
        {
            vkd3d_test_push_context("type %u", type);

            memset(&constant, 0, sizeof(constant));
            constant.x = type;
            constant.y = test->miplevel;

            ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
            ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
            ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
            ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
            ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
            ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
            ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &heap);
            ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0,
                    get_gpu_descriptor_handle(&context, heap, 0));
            ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 1, 4, &constant.x, 0);
            ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

            transition_resource_state(command_list, context.render_target,
                    D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);

            check_sub_resource_vec4(context.render_target, 0, queue, command_list, &test->expected_result, 0);

            reset_command_list(command_list, context.allocator);
            transition_resource_state(command_list, context.render_target,
                    D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

            vkd3d_test_pop_context();
        }

        ID3D12Resource_Release(texture);

        vkd3d_test_pop_context();
    }

    ID3D12DescriptorHeap_Release(heap);
    destroy_test_context(&context);
}

static void test_srv_component_mapping(void)
{
    D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc;
    D3D12_SUBRESOURCE_DATA subresource_data;
    ID3D12GraphicsCommandList *command_list;
    D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle;
    D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12DescriptorHeap *heap;
    ID3D12CommandQueue *queue;
    ID3D12Resource *texture;
    uint32_t expected_color;
    unsigned int i, j;

    static const DWORD ps_code[] =
    {
#if 0
        Texture2D t;
        SamplerState s;

        float4 main(float4 position : SV_POSITION) : SV_Target
        {
            float2 p;

            p.x = position.x / 32.0f;
            p.y = position.y / 32.0f;
            return t.Sample(s, p);
        }
#endif
        0x43425844, 0x7a0c3929, 0x75ff3ca4, 0xccb318b2, 0xe6965b4c, 0x00000001, 0x00000140, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000030f, 0x505f5653, 0x5449534f, 0x004e4f49,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x000000a4, 0x00000050,
        0x00000029, 0x0100086a, 0x0300005a, 0x00106000, 0x00000000, 0x04001858, 0x00107000, 0x00000000,
        0x00005555, 0x04002064, 0x00101032, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x02000068, 0x00000001, 0x0a000038, 0x00100032, 0x00000000, 0x00101046, 0x00000000, 0x00004002,
        0x3d000000, 0x3d000000, 0x00000000, 0x00000000, 0x8b000045, 0x800000c2, 0x00155543, 0x001020f2,
        0x00000000, 0x00100046, 0x00000000, 0x00107e46, 0x00000000, 0x00106000, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    static const uint32_t r8g8b8a8_data = 0x39495969;
    static const uint16_t r8g8_data = 0xaabb;
    static const uint8_t a8_data = 0x11;
    static const uint8_t r8_data = 0xfe;
    static const struct
    {
        const char *name;
        DXGI_FORMAT format;
        const void *data;
        uint32_t color;
    }
    tests[] =
    {
        {"R8G8B8A8", DXGI_FORMAT_R8G8B8A8_UNORM, &r8g8b8a8_data, 0x39495969},
        {"R8G8",     DXGI_FORMAT_R8G8_UNORM,     &r8g8_data,     0xff00aabb},
        {"R8",       DXGI_FORMAT_R8_UNORM,       &r8_data,       0xff0000fe},
        {"A8",       DXGI_FORMAT_A8_UNORM,       &a8_data,       0x11000000},
    };
    static const struct
    {
        unsigned int mapping;
        unsigned int r_shift;
        unsigned int g_shift;
        unsigned int b_shift;
        unsigned int a_shift;
        uint32_t forced_mask;
        uint32_t forced_color;
    }
    component_mappings[] =
    {
        {
            D3D12_ENCODE_SHADER_4_COMPONENT_MAPPING(
                    D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_0,
                    D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_0,
                    D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_0,
                    D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_0),
            0, 0, 0, 0, 0xffffffff, 0x00000000,
        },
        {
            D3D12_ENCODE_SHADER_4_COMPONENT_MAPPING(
                    D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_1,
                    D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_1,
                    D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_1,
                    D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_1),
            0, 0, 0, 0, 0xffffffff, 0xffffffff,
        },
        {
            D3D12_ENCODE_SHADER_4_COMPONENT_MAPPING(
                    D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_1,
                    D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_0,
                    D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_0,
                    D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_1),
            0, 0, 0, 0, 0xffffffff, 0xff0000ff,
        },
        {
            D3D12_ENCODE_SHADER_4_COMPONENT_MAPPING(
                    D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_1,
                    D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_0,
                    D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_1,
                    D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_0),
            0, 0, 0, 0, 0xffffffff, 0x00ff00ff,
        },

        {
            D3D12_ENCODE_SHADER_4_COMPONENT_MAPPING(
                    D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_0,
                    D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_1,
                    D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_2,
                    D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_3),
            0, 8, 16, 24, 0x00000000, 0x00000000,
        },
        {
            D3D12_ENCODE_SHADER_4_COMPONENT_MAPPING(
                    D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_3,
                    D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_2,
                    D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_1,
                    D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_0),
            24, 16, 8, 0, 0x00000000, 0x00000000,
        },

        {
            D3D12_ENCODE_SHADER_4_COMPONENT_MAPPING(
                    D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_0,
                    D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_1,
                    D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_2,
                    D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_0),
            0, 8, 16, 24, 0xff000000, 0x00000000,
        },

        {
            D3D12_ENCODE_SHADER_4_COMPONENT_MAPPING(
                    D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_0,
                    D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_0,
                    D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_0,
                    D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_0),
            0, 0, 0, 0, 0x00000000, 0x00000000,
        },
        {
            D3D12_ENCODE_SHADER_4_COMPONENT_MAPPING(
                    D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_1,
                    D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_1,
                    D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_1,
                    D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_1),
            8, 8, 8, 8, 0x00000000, 0x00000000,
        },
        {
            D3D12_ENCODE_SHADER_4_COMPONENT_MAPPING(
                    D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_2,
                    D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_2,
                    D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_2,
                    D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_2),
            16, 16, 16, 16, 0x00000000, 0x00000000,
        },
        {
            D3D12_ENCODE_SHADER_4_COMPONENT_MAPPING(
                    D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_3,
                    D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_3,
                    D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_3,
                    D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_3),
            24, 24, 24, 24, 0x00000000, 0x00000000,
        },
    };

    memset(&desc, 0, sizeof(desc));
    desc.rt_width = desc.rt_height = 32;
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    command_list = context.list;
    queue = context.queue;

    context.root_signature = create_texture_root_signature(context.device,
            D3D12_SHADER_VISIBILITY_PIXEL, 0, 0);
    context.pipeline_state = create_pipeline_state(context.device,
            context.root_signature, context.render_target_desc.Format, NULL, &ps, NULL);

    heap = create_gpu_descriptor_heap(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1);
    cpu_handle = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(heap);
    gpu_handle = ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(heap);

    for (i = 0; i < ARRAY_SIZE(tests); ++i)
    {
        vkd3d_test_push_context("Test %s", tests[i].name);

        texture = create_default_texture(context.device, 1, 1,
                tests[i].format, 0, D3D12_RESOURCE_STATE_COPY_DEST);
        subresource_data.pData = tests[i].data;
        subresource_data.RowPitch = format_size(tests[i].format);
        subresource_data.SlicePitch = subresource_data.RowPitch;
        upload_texture_data(texture, &subresource_data, 1, queue, command_list);
        reset_command_list(command_list, context.allocator);

        transition_resource_state(command_list, texture,
                D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);

        for (j = 0; j < ARRAY_SIZE(component_mappings); ++j)
        {
            vkd3d_test_push_context("component mapping %#x", component_mappings[j].mapping);

            memset(&srv_desc, 0, sizeof(srv_desc));
            srv_desc.Format = tests[i].format;
            srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
            srv_desc.Shader4ComponentMapping = component_mappings[j].mapping;
            srv_desc.Texture2D.MipLevels = 1;
            ID3D12Device_CreateShaderResourceView(context.device, texture, &srv_desc, cpu_handle);

            ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

            ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
            ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
            ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
            ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &heap);
            ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0, gpu_handle);
            ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
            ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
            ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
            ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

            transition_resource_state(command_list, context.render_target,
                    D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
            expected_color = 0;
            expected_color |= ((tests[i].color >> component_mappings[j].r_shift) & 0xff) << 0;
            expected_color |= ((tests[i].color >> component_mappings[j].g_shift) & 0xff) << 8;
            expected_color |= ((tests[i].color >> component_mappings[j].b_shift) & 0xff) << 16;
            expected_color |= ((tests[i].color >> component_mappings[j].a_shift) & 0xff) << 24;
            expected_color &= ~component_mappings[j].forced_mask;
            expected_color |= component_mappings[j].forced_color & component_mappings[j].forced_mask;
            check_sub_resource_uint(context.render_target, 0, queue, command_list, expected_color, 0);

            reset_command_list(command_list, context.allocator);
            transition_resource_state(command_list, context.render_target,
                    D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

            vkd3d_test_pop_context();
        }

        ID3D12Resource_Release(texture);

        vkd3d_test_pop_context();
    }

    ID3D12DescriptorHeap_Release(heap);
    destroy_test_context(&context);
}

static void test_descriptor_tables(void)
{
    ID3D12DescriptorHeap *heap, *sampler_heap, *wrong_heap, *wrong_sampler_heap, *heaps[2];
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    D3D12_DESCRIPTOR_RANGE descriptor_range[4];
    D3D12_CONSTANT_BUFFER_VIEW_DESC cbv_desc;
    D3D12_ROOT_PARAMETER root_parameters[3];
    ID3D12GraphicsCommandList *command_list;
    D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle;
    D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle;
    ID3D12Resource *cb, *textures[4];
    unsigned int i, descriptor_size;
    D3D12_SAMPLER_DESC sampler_desc;
    struct test_context_desc desc;
    D3D12_SUBRESOURCE_DATA data;
    struct test_context context;
    ID3D12CommandQueue *queue;
    HRESULT hr;

    static const DWORD ps_code[] =
    {
#if 0
        Texture2D t0;
        Texture2D t1;
        Texture2D t2;
        Texture2D t3;
        SamplerState s0;

        cbuffer cb0
        {
            float4 c;
        };

        float4 main(float4 position : SV_POSITION) : SV_Target
        {
            float2 p = float2(position.x / 32.0f, position.y / 32.0f);

            return c.x * t0.Sample(s0, p) + c.y * t1.Sample(s0, p)
                    + c.z * t2.Sample(s0, p) + c.w * t3.Sample(s0, p);
        }
#endif
        0x43425844, 0xf848ef5f, 0x4da3fe0c, 0x776883a0, 0x6b3f0297, 0x00000001, 0x0000029c, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000030f, 0x505f5653, 0x5449534f, 0x004e4f49,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000200, 0x00000050,
        0x00000080, 0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x0300005a, 0x00106000,
        0x00000000, 0x04001858, 0x00107000, 0x00000000, 0x00005555, 0x04001858, 0x00107000, 0x00000001,
        0x00005555, 0x04001858, 0x00107000, 0x00000002, 0x00005555, 0x04001858, 0x00107000, 0x00000003,
        0x00005555, 0x04002064, 0x00101032, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x02000068, 0x00000003, 0x0a000038, 0x00100032, 0x00000000, 0x00101046, 0x00000000, 0x00004002,
        0x3d000000, 0x3d000000, 0x00000000, 0x00000000, 0x8b000045, 0x800000c2, 0x00155543, 0x001000f2,
        0x00000001, 0x00100046, 0x00000000, 0x00107e46, 0x00000001, 0x00106000, 0x00000000, 0x08000038,
        0x001000f2, 0x00000001, 0x00100e46, 0x00000001, 0x00208556, 0x00000000, 0x00000000, 0x8b000045,
        0x800000c2, 0x00155543, 0x001000f2, 0x00000002, 0x00100046, 0x00000000, 0x00107e46, 0x00000000,
        0x00106000, 0x00000000, 0x0a000032, 0x001000f2, 0x00000001, 0x00208006, 0x00000000, 0x00000000,
        0x00100e46, 0x00000002, 0x00100e46, 0x00000001, 0x8b000045, 0x800000c2, 0x00155543, 0x001000f2,
        0x00000002, 0x00100046, 0x00000000, 0x00107e46, 0x00000002, 0x00106000, 0x00000000, 0x8b000045,
        0x800000c2, 0x00155543, 0x001000f2, 0x00000000, 0x00100046, 0x00000000, 0x00107e46, 0x00000003,
        0x00106000, 0x00000000, 0x0a000032, 0x001000f2, 0x00000001, 0x00208aa6, 0x00000000, 0x00000000,
        0x00100e46, 0x00000002, 0x00100e46, 0x00000001, 0x0a000032, 0x001020f2, 0x00000000, 0x00208ff6,
        0x00000000, 0x00000000, 0x00100e46, 0x00000000, 0x00100e46, 0x00000001, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    static const struct vec4 constant = {0.1f, 0.2f, 0.3f, 0.1f};
    static const unsigned int texture_data[4] = {0xff0000ff, 0xff00ff00, 0xffff0000, 0xffffff00};

    memset(&desc, 0, sizeof(desc));
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    command_list = context.list;
    queue = context.queue;

    cb = create_upload_buffer(context.device, sizeof(constant), &constant.x);

    descriptor_range[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
    descriptor_range[0].NumDescriptors = 2;
    descriptor_range[0].BaseShaderRegister = 0;
    descriptor_range[0].RegisterSpace = 0;
    descriptor_range[0].OffsetInDescriptorsFromTableStart = 1;
    root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameters[0].DescriptorTable.NumDescriptorRanges = 1;
    root_parameters[0].DescriptorTable.pDescriptorRanges = &descriptor_range[0];
    root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;

    descriptor_range[1].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER;
    descriptor_range[1].NumDescriptors = 1;
    descriptor_range[1].BaseShaderRegister = 0;
    descriptor_range[1].RegisterSpace = 0;
    descriptor_range[1].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
    root_parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameters[1].DescriptorTable.NumDescriptorRanges = 1;
    root_parameters[1].DescriptorTable.pDescriptorRanges = &descriptor_range[1];
    root_parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;

    descriptor_range[2].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
    descriptor_range[2].NumDescriptors = 2;
    descriptor_range[2].BaseShaderRegister = 2;
    descriptor_range[2].RegisterSpace = 0;
    descriptor_range[2].OffsetInDescriptorsFromTableStart = 0;
    descriptor_range[3].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_CBV;
    descriptor_range[3].NumDescriptors = 1;
    descriptor_range[3].BaseShaderRegister = 0;
    descriptor_range[3].RegisterSpace = 0;
    descriptor_range[3].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
    root_parameters[2].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameters[2].DescriptorTable.NumDescriptorRanges = 2;
    root_parameters[2].DescriptorTable.pDescriptorRanges = &descriptor_range[2];
    root_parameters[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;

    memset(&root_signature_desc, 0, sizeof(root_signature_desc));
    root_signature_desc.NumParameters = 3;
    root_signature_desc.pParameters = root_parameters;
    hr = create_root_signature(context.device, &root_signature_desc, &context.root_signature);
    ok(SUCCEEDED(hr), "Failed to create root signature, hr %#x.\n", hr);

    context.pipeline_state = create_pipeline_state(context.device,
            context.root_signature, context.render_target_desc.Format, NULL, &ps, NULL);

    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;

    heap = create_gpu_descriptor_heap(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 6);
    sampler_heap = create_gpu_descriptor_heap(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, 1);
    wrong_heap = create_gpu_descriptor_heap(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 6);
    wrong_sampler_heap = create_gpu_descriptor_heap(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, 1);

    descriptor_size = ID3D12Device_GetDescriptorHandleIncrementSize(context.device,
            D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);

    for (i = 0; i < ARRAY_SIZE(textures); ++i)
    {
        textures[i] = create_default_texture(context.device,
                1, 1, DXGI_FORMAT_R8G8B8A8_UNORM, 0, D3D12_RESOURCE_STATE_COPY_DEST);
        data.pData = &texture_data[i];
        data.RowPitch = sizeof(texture_data[i]);
        data.SlicePitch = data.RowPitch;
        upload_texture_data(textures[i], &data, 1, queue, command_list);
        reset_command_list(command_list, context.allocator);
    }

    for (i = 0; i < ARRAY_SIZE(textures); ++i)
        transition_resource_state(command_list, textures[i],
                D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);

    cpu_handle = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(heap);
    cpu_handle.ptr += descriptor_size;
    /* t0-t3 */
    for (i = 0; i < ARRAY_SIZE(textures); ++i)
    {
        ID3D12Device_CreateShaderResourceView(context.device, textures[i], NULL, cpu_handle);
        cpu_handle.ptr += descriptor_size;
    }
    /* cbv0 */
    cbv_desc.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(cb);
    cbv_desc.SizeInBytes = align(sizeof(constant), D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT);
    ID3D12Device_CreateConstantBufferView(context.device, &cbv_desc, cpu_handle);

    cpu_handle = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(sampler_heap);
    /* s0 */
    ID3D12Device_CreateSampler(context.device, &sampler_desc, cpu_handle);

    gpu_handle = ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(heap);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    heaps[0] = wrong_heap; heaps[1] = wrong_sampler_heap;
    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, ARRAY_SIZE(heaps), heaps);
    ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0, gpu_handle);
    ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 1,
            ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(sampler_heap));
    gpu_handle.ptr += 3 * descriptor_size;
    ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 2, gpu_handle);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xb2664c19, 2);

    ID3D12Resource_Release(cb);
    for (i = 0; i < ARRAY_SIZE(textures); ++i)
        ID3D12Resource_Release(textures[i]);
    ID3D12DescriptorHeap_Release(heap);
    ID3D12DescriptorHeap_Release(sampler_heap);
    ID3D12DescriptorHeap_Release(wrong_heap);
    ID3D12DescriptorHeap_Release(wrong_sampler_heap);
    destroy_test_context(&context);
}

/* Tests overlapping descriptor heap ranges for SRV and UAV descriptor tables.
 * Only descriptors used by the pipeline have to be valid.
 */
static void test_descriptor_tables_overlapping_bindings(void)
{
    ID3D12Resource *input_buffers[2], *output_buffers[2];
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    D3D12_DESCRIPTOR_RANGE descriptor_range[2];
    D3D12_UNORDERED_ACCESS_VIEW_DESC uav_desc;
    D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc;
    D3D12_ROOT_PARAMETER root_parameters[3];
    ID3D12GraphicsCommandList *command_list;
    struct d3d12_resource_readback rb;
    struct test_context context;
    ID3D12DescriptorHeap *heap;
    ID3D12CommandQueue *queue;
    ID3D12Device *device;
    unsigned int i;
    HRESULT hr;

    static const DWORD cs_code[] =
    {
#if 0
        ByteAddressBuffer t0;
        ByteAddressBuffer t4 : register(t4);

        RWByteAddressBuffer u0;
        RWByteAddressBuffer u2 : register(u2);

        uint size;
        uint size2;

        [numthreads(1, 1, 1)]
        void main()
        {
            uint i;
            for (i = 0; i < size; ++i)
                u0.Store(4 * i, t0.Load(4 *i));
            for (i = 0; i < size2; ++i)
                u2.Store(4 * i, t4.Load(4 * i));
        }
#endif
        0x43425844, 0x8d2646b7, 0xeb60d9ee, 0x33ccd6ed, 0x5557e649, 0x00000001, 0x0000023c, 0x00000003,
        0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x000001e8, 0x00050050, 0x0000007a, 0x0100086a,
        0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x030000a1, 0x00107000, 0x00000000, 0x030000a1,
        0x00107000, 0x00000004, 0x0300009d, 0x0011e000, 0x00000000, 0x0300009d, 0x0011e000, 0x00000002,
        0x02000068, 0x00000001, 0x0400009b, 0x00000001, 0x00000001, 0x00000001, 0x05000036, 0x00100012,
        0x00000000, 0x00004001, 0x00000000, 0x01000030, 0x08000050, 0x00100022, 0x00000000, 0x0010000a,
        0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x03040003, 0x0010001a, 0x00000000, 0x07000029,
        0x00100022, 0x00000000, 0x0010000a, 0x00000000, 0x00004001, 0x00000002, 0x890000a5, 0x800002c2,
        0x00199983, 0x00100042, 0x00000000, 0x0010001a, 0x00000000, 0x00107006, 0x00000000, 0x070000a6,
        0x0011e012, 0x00000000, 0x0010001a, 0x00000000, 0x0010002a, 0x00000000, 0x0700001e, 0x00100012,
        0x00000000, 0x0010000a, 0x00000000, 0x00004001, 0x00000001, 0x01000016, 0x05000036, 0x00100012,
        0x00000000, 0x00004001, 0x00000000, 0x01000030, 0x08000050, 0x00100022, 0x00000000, 0x0010000a,
        0x00000000, 0x0020801a, 0x00000000, 0x00000000, 0x03040003, 0x0010001a, 0x00000000, 0x07000029,
        0x00100022, 0x00000000, 0x0010000a, 0x00000000, 0x00004001, 0x00000002, 0x890000a5, 0x800002c2,
        0x00199983, 0x00100042, 0x00000000, 0x0010001a, 0x00000000, 0x00107006, 0x00000004, 0x070000a6,
        0x0011e012, 0x00000002, 0x0010001a, 0x00000000, 0x0010002a, 0x00000000, 0x0700001e, 0x00100012,
        0x00000000, 0x0010000a, 0x00000000, 0x00004001, 0x00000001, 0x01000016, 0x0100003e,
    };
    static const uint32_t buffer_data[] = {0xdeadbabe};
    static const uint32_t buffer_data2[] = {0, 1, 2, 3, 4, 5};

    if (!init_compute_test_context(&context))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    descriptor_range[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
    descriptor_range[0].NumDescriptors = 10;
    descriptor_range[0].BaseShaderRegister = 0;
    descriptor_range[0].RegisterSpace = 0;
    descriptor_range[0].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[0];
    root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;

    descriptor_range[1].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
    descriptor_range[1].NumDescriptors = 10;
    descriptor_range[1].BaseShaderRegister = 0;
    descriptor_range[1].RegisterSpace = 0;
    descriptor_range[1].OffsetInDescriptorsFromTableStart = 0;
    root_parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameters[1].DescriptorTable.NumDescriptorRanges = 1;
    root_parameters[1].DescriptorTable.pDescriptorRanges = &descriptor_range[1];
    root_parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;

    root_parameters[2].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
    root_parameters[2].Constants.ShaderRegister = 0;
    root_parameters[2].Constants.RegisterSpace = 0;
    root_parameters[2].Constants.Num32BitValues = 2;
    root_parameters[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;

    memset(&root_signature_desc, 0, sizeof(root_signature_desc));
    root_signature_desc.NumParameters = 3;
    root_signature_desc.pParameters = root_parameters;
    hr = create_root_signature(device, &root_signature_desc, &context.root_signature);
    ok(SUCCEEDED(hr), "Failed to create root signature, hr %#x.\n", hr);

    context.pipeline_state = create_compute_pipeline_state(device, context.root_signature,
            shader_bytecode(cs_code, sizeof(cs_code)));

    heap = create_gpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 30);

    input_buffers[0] = create_default_buffer(device, sizeof(buffer_data),
            D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST);
    upload_buffer_data(input_buffers[0], 0, sizeof(buffer_data), &buffer_data, queue, command_list);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, input_buffers[0],
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);

    input_buffers[1] = create_default_buffer(device, sizeof(buffer_data2),
            D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST);
    upload_buffer_data(input_buffers[1], 0, sizeof(buffer_data2), &buffer_data2, queue, command_list);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, input_buffers[1],
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);

    output_buffers[0] = create_default_buffer(device, sizeof(buffer_data),
              D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
    output_buffers[1] = create_default_buffer(device, sizeof(buffer_data2),
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

    memset(&uav_desc, 0, sizeof(uav_desc));
    uav_desc.Format = DXGI_FORMAT_R32_TYPELESS;
    uav_desc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
    uav_desc.Buffer.FirstElement = 0;
    uav_desc.Buffer.NumElements = ARRAY_SIZE(buffer_data);
    uav_desc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_RAW;
    ID3D12Device_CreateUnorderedAccessView(device, output_buffers[0], NULL, &uav_desc,
            get_cpu_descriptor_handle(&context, heap, 0)); /* u0 */
    uav_desc.Buffer.NumElements = ARRAY_SIZE(buffer_data2);
    ID3D12Device_CreateUnorderedAccessView(device, output_buffers[1], NULL, &uav_desc,
            get_cpu_descriptor_handle(&context, heap, 2)); /* u2 */

    memset(&srv_desc, 0, sizeof(srv_desc));
    srv_desc.Format = DXGI_FORMAT_R32_TYPELESS;
    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 = ARRAY_SIZE(buffer_data);
    srv_desc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_RAW;
    ID3D12Device_CreateShaderResourceView(device, input_buffers[0], &srv_desc,
            get_cpu_descriptor_handle(&context, heap, 3)); /* t0 */
    srv_desc.Buffer.NumElements = ARRAY_SIZE(buffer_data2);
    ID3D12Device_CreateShaderResourceView(device, input_buffers[1], &srv_desc,
            get_cpu_descriptor_handle(&context, heap, 7)); /* t4 */

    ID3D12GraphicsCommandList_SetComputeRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &heap);
    ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(command_list, 0,
            get_gpu_descriptor_handle(&context, heap, 3));
    ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(command_list, 1,
            get_gpu_descriptor_handle(&context, heap, 0));
    ID3D12GraphicsCommandList_SetComputeRoot32BitConstant(command_list, 2,
            ARRAY_SIZE(buffer_data), 0);
    ID3D12GraphicsCommandList_SetComputeRoot32BitConstant(command_list, 2,
            ARRAY_SIZE(buffer_data2), 1);
    ID3D12GraphicsCommandList_Dispatch(command_list, 1, 1, 1);

    for (i = 0; i < ARRAY_SIZE(output_buffers); ++i)
    {
        transition_resource_state(command_list, output_buffers[i],
                D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
    }

    get_buffer_readback_with_command_list(output_buffers[0], DXGI_FORMAT_R32_UINT, &rb, queue, command_list);
    for (i = 0; i < ARRAY_SIZE(buffer_data); ++i)
    {
        unsigned int value = get_readback_uint(&rb.rb, i, 0, 0);
        ok(value == buffer_data[i], "Got %#x, expected %#x.\n", value, buffer_data[i]);
    }
    release_resource_readback(&rb);
    reset_command_list(command_list, context.allocator);
    get_buffer_readback_with_command_list(output_buffers[1], DXGI_FORMAT_R32_UINT, &rb, queue, command_list);
    for (i = 0; i < ARRAY_SIZE(buffer_data2); ++i)
    {
        unsigned int value = get_readback_uint(&rb.rb, i, 0, 0);
        ok(value == buffer_data2[i], "Got %#x, expected %#x.\n", value, buffer_data2[i]);
    }
    release_resource_readback(&rb);

    for (i = 0; i < ARRAY_SIZE(input_buffers); ++i)
        ID3D12Resource_Release(input_buffers[i]);
    for (i = 0; i < ARRAY_SIZE(output_buffers); ++i)
        ID3D12Resource_Release(output_buffers[i]);
    ID3D12DescriptorHeap_Release(heap);
    destroy_test_context(&context);
}

static void test_update_root_descriptors(void)
{
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    D3D12_GPU_VIRTUAL_ADDRESS cb_va, uav_va;
    D3D12_ROOT_PARAMETER root_parameters[3];
    ID3D12GraphicsCommandList *command_list;
    ID3D12RootSignature *root_signature;
    ID3D12PipelineState *pipeline_state;
    struct d3d12_resource_readback rb;
    ID3D12Resource *resource, *cb;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Device *device;
    unsigned int i;
    HRESULT hr;

    static const DWORD cs_code[] =
    {
#if 0
        cbuffer cb
        {
            unsigned int offset;
            unsigned int value;
        };

        RWByteAddressBuffer b;

        [numthreads(1, 1, 1)]
        void main()
        {
            b.Store(4 * offset, value);
        }
#endif
        0x43425844, 0xaadc5460, 0x88c27e90, 0x2acacf4e, 0x4e06019a, 0x00000001, 0x000000d8, 0x00000003,
        0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x00000084, 0x00050050, 0x00000021, 0x0100086a,
        0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x0300009d, 0x0011e000, 0x00000000, 0x02000068,
        0x00000001, 0x0400009b, 0x00000001, 0x00000001, 0x00000001, 0x08000029, 0x00100012, 0x00000000,
        0x0020800a, 0x00000000, 0x00000000, 0x00004001, 0x00000002, 0x080000a6, 0x0011e012, 0x00000000,
        0x0010000a, 0x00000000, 0x0020801a, 0x00000000, 0x00000000, 0x0100003e,
    };
    struct
    {
        uint32_t offset;
        uint32_t value;
        uint32_t uav_offset;
        uint8_t padding[D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT - 3 * sizeof(uint32_t)];
    }
    input[] =
    {
        {0, 4,  0},
        {2, 6,  0},
        {0, 5, 64},
        {7, 2, 64},
    };

    if (!init_compute_test_context(&context))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    cb = create_upload_buffer(context.device, sizeof(input), input);
    cb_va = ID3D12Resource_GetGPUVirtualAddress(cb);

    resource = create_default_buffer(device, 512,
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
    uav_va = ID3D12Resource_GetGPUVirtualAddress(resource);

    root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
    root_parameters[0].Descriptor.ShaderRegister = 0;
    root_parameters[0].Descriptor.RegisterSpace = 0;
    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_SRV;
    root_parameters[2].Descriptor.ShaderRegister = 0;
    root_parameters[2].Descriptor.RegisterSpace = 0;
    root_parameters[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    memset(&root_signature_desc, 0, sizeof(root_signature_desc));
    root_signature_desc.NumParameters = 3;
    root_signature_desc.pParameters = root_parameters;
    hr = create_root_signature(device, &root_signature_desc, &root_signature);
    ok(SUCCEEDED(hr), "Failed to create root signature, hr %#x.\n", hr);

    pipeline_state = create_compute_pipeline_state(device, root_signature,
            shader_bytecode(cs_code, sizeof(cs_code)));

    ID3D12GraphicsCommandList_SetPipelineState(command_list, pipeline_state);
    ID3D12GraphicsCommandList_SetComputeRootSignature(command_list, root_signature);
    ID3D12GraphicsCommandList_SetComputeRootConstantBufferView(command_list, 0, 0);
    ID3D12GraphicsCommandList_SetComputeRootUnorderedAccessView(command_list, 1, 0);
    ID3D12GraphicsCommandList_SetComputeRootShaderResourceView(command_list, 2, 0);
    for (i = 0; i < ARRAY_SIZE(input); ++i)
    {
        ID3D12GraphicsCommandList_SetComputeRootConstantBufferView(command_list,
                0, cb_va + i * sizeof(*input));
        if (!i || input[i - 1].uav_offset != input[i].uav_offset)
            ID3D12GraphicsCommandList_SetComputeRootUnorderedAccessView(command_list,
                    1, uav_va + input[i].uav_offset * sizeof(uint32_t));
        ID3D12GraphicsCommandList_Dispatch(command_list, 1, 1, 1);
    }

    transition_sub_resource_state(command_list, resource, 0,
            D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);

    get_buffer_readback_with_command_list(resource, DXGI_FORMAT_R32_UINT, &rb, queue, command_list);
    for (i = 0; i < ARRAY_SIZE(input); ++i)
    {
        unsigned int offset = input[i].uav_offset + input[i].offset;
        unsigned int value = get_readback_uint(&rb.rb, offset, 0, 0);
        ok(value == input[i].value, "Got %#x, expected %#x.\n", value, input[i].value);
    }
    release_resource_readback(&rb);

    ID3D12Resource_Release(cb);
    ID3D12Resource_Release(resource);
    ID3D12RootSignature_Release(root_signature);
    ID3D12PipelineState_Release(pipeline_state);
    destroy_test_context(&context);
}

static void test_update_descriptor_tables(void)
{
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    D3D12_DESCRIPTOR_RANGE descriptor_range;
    ID3D12GraphicsCommandList *command_list;
    D3D12_STATIC_SAMPLER_DESC sampler_desc;
    ID3D12DescriptorHeap *heap, *cpu_heap;
    D3D12_DESCRIPTOR_HEAP_DESC heap_desc;
    D3D12_ROOT_PARAMETER root_parameter;
    struct d3d12_resource_readback rb;
    struct test_context_desc desc;
    D3D12_SUBRESOURCE_DATA data;
    struct test_context context;
    ID3D12Resource *textures[3];
    ID3D12CommandQueue *queue;
    unsigned int i;
    D3D12_BOX box;
    HRESULT hr;
    RECT rect;

    static const DWORD ps_code[] =
    {
#if 0
        Texture2D t0;
        Texture2D t1;
        SamplerState s;

        float4 main(float4 position : SV_POSITION) : SV_Target
        {
            float2 p = (position.x / 32.0f, position.x / 32.0f);
            return float4(t0.Sample(s, p).r, t1.Sample(s, p).r, 0, 1);
        }
#endif
        0x43425844, 0x5c19caa6, 0xd4fadb4f, 0xc9d6831e, 0x563b68b7, 0x00000001, 0x000001a4, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000010f, 0x505f5653, 0x5449534f, 0x004e4f49,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000108, 0x00000050,
        0x00000042, 0x0100086a, 0x0300005a, 0x00106000, 0x00000000, 0x04001858, 0x00107000, 0x00000000,
        0x00005555, 0x04001858, 0x00107000, 0x00000001, 0x00005555, 0x04002064, 0x00101012, 0x00000000,
        0x00000001, 0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000001, 0x07000038, 0x00100012,
        0x00000000, 0x0010100a, 0x00000000, 0x00004001, 0x3d000000, 0x8b000045, 0x800000c2, 0x00155543,
        0x00100022, 0x00000000, 0x00100006, 0x00000000, 0x00107e16, 0x00000000, 0x00106000, 0x00000000,
        0x8b000045, 0x800000c2, 0x00155543, 0x00100012, 0x00000000, 0x00100006, 0x00000000, 0x00107e46,
        0x00000001, 0x00106000, 0x00000000, 0x05000036, 0x00102032, 0x00000000, 0x00100516, 0x00000000,
        0x08000036, 0x001020c2, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x3f800000,
        0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    static const float texture_data[] = {0.5f, 0.25f, 0.1f};

    memset(&desc, 0, sizeof(desc));
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    command_list = context.list;
    queue = context.queue;

    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.ShaderRegister = 0;
    sampler_desc.RegisterSpace = 0;
    sampler_desc.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;

    descriptor_range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
    descriptor_range.NumDescriptors = 2;
    descriptor_range.BaseShaderRegister = 0;
    descriptor_range.RegisterSpace = 0;
    descriptor_range.OffsetInDescriptorsFromTableStart = 0;
    root_parameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameter.DescriptorTable.NumDescriptorRanges = 1;
    root_parameter.DescriptorTable.pDescriptorRanges = &descriptor_range;
    root_parameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;

    memset(&root_signature_desc, 0, sizeof(root_signature_desc));
    root_signature_desc.NumParameters = 1;
    root_signature_desc.pParameters = &root_parameter;
    root_signature_desc.NumStaticSamplers = 1;
    root_signature_desc.pStaticSamplers = &sampler_desc;
    hr = create_root_signature(context.device, &root_signature_desc, &context.root_signature);
    ok(SUCCEEDED(hr), "Failed to create root signature, hr %#x.\n", hr);

    context.pipeline_state = create_pipeline_state(context.device,
            context.root_signature, context.render_target_desc.Format, NULL, &ps, NULL);

    memset(&heap_desc, 0, sizeof(heap_desc));
    heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
    heap_desc.NumDescriptors = 4;
    heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
    hr = ID3D12Device_CreateDescriptorHeap(context.device, &heap_desc,
            &IID_ID3D12DescriptorHeap, (void **)&heap);
    ok(SUCCEEDED(hr), "Failed to create descriptor heap, hr %#x.\n", hr);

    heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
    hr = ID3D12Device_CreateDescriptorHeap(context.device, &heap_desc,
            &IID_ID3D12DescriptorHeap, (void **)&cpu_heap);
    ok(SUCCEEDED(hr), "Failed to create descriptor heap, hr %#x.\n", hr);

    for (i = 0; i < ARRAY_SIZE(textures); ++i)
    {
        textures[i] = create_default_texture(context.device, 1, 1, DXGI_FORMAT_R32_FLOAT,
                0, D3D12_RESOURCE_STATE_COPY_DEST);
        data.pData = &texture_data[i];
        data.RowPitch = sizeof(texture_data[i]);
        data.SlicePitch = data.RowPitch;
        upload_texture_data(textures[i], &data, 1, queue, command_list);
        reset_command_list(command_list, context.allocator);
        transition_resource_state(command_list, textures[i],
                D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
    }

    for (i = 0; i < heap_desc.NumDescriptors; ++i)
    {
        ID3D12Device_CreateShaderResourceView(context.device, textures[2], NULL,
                get_cpu_descriptor_handle(&context, heap, i));
    }
    for (i = 0; i < ARRAY_SIZE(textures); ++i)
    {
        ID3D12Device_CreateShaderResourceView(context.device, textures[i], NULL,
                get_cpu_descriptor_handle(&context, cpu_heap, i));
    }

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &heap);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);

    set_rect(&rect, 0, 0, 16, 32);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &rect);
    ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0,
            get_gpu_descriptor_handle(&context, heap, 0));
    ID3D12Device_CopyDescriptorsSimple(context.device, 2,
            get_cpu_sampler_handle(&context, heap, 0),
            get_cpu_sampler_handle(&context, cpu_heap, 0),
            D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    set_rect(&rect, 16, 0, 32, 32);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &rect);
    ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0,
            get_gpu_descriptor_handle(&context, heap, 2));
    ID3D12Device_CreateShaderResourceView(context.device, textures[1], NULL,
            get_cpu_descriptor_handle(&context, heap, 2));
    ID3D12Device_CreateShaderResourceView(context.device, textures[0], NULL,
            get_cpu_descriptor_handle(&context, heap, 3));
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_resource_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
    set_box(&box, 0, 0, 0, 16, 32, 1);
    check_readback_data_uint(&rb.rb, &box, 0xff00407f, 1);
    set_box(&box, 16, 0, 0, 32, 32, 1);
    check_readback_data_uint(&rb.rb, &box, 0xff007f40, 1);
    release_resource_readback(&rb);

    for (i = 0; i < ARRAY_SIZE(textures); ++i)
        ID3D12Resource_Release(textures[i]);
    ID3D12DescriptorHeap_Release(cpu_heap);
    ID3D12DescriptorHeap_Release(heap);
    destroy_test_context(&context);
}

/* This requires the Vulkan descriptor indexing extension and Vulkan-backed
 * descriptor heaps. Vulkan doesn't allow updating descriptor sets after the
 * vkCmdBindDescriptorSets() command is recorded unless the update-after-bind
 * feature of descriptor indexing is used.
 */
static void test_update_descriptor_heap_after_closing_command_list(void)
{
    ID3D12Resource *red_texture, *green_texture;
    D3D12_RESOURCE_BINDING_TIER binding_tier;
    ID3D12GraphicsCommandList *command_list;
    D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle;
    ID3D12DescriptorHeap *cpu_heap, *heap;
    D3D12_SUBRESOURCE_DATA texture_data;
    struct d3d12_resource_readback rb;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12CommandQueue *queue;
    unsigned int value;
    HRESULT hr;

    static const DWORD ps_code[] =
    {
#if 0
        Texture2D t;
        SamplerState s;

        float4 main(float4 position : SV_POSITION) : SV_Target
        {
            float2 p;

            p.x = position.x / 32.0f;
            p.y = position.y / 32.0f;
            return t.Sample(s, p);
        }
#endif
        0x43425844, 0x7a0c3929, 0x75ff3ca4, 0xccb318b2, 0xe6965b4c, 0x00000001, 0x00000140, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000030f, 0x505f5653, 0x5449534f, 0x004e4f49,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x000000a4, 0x00000050,
        0x00000029, 0x0100086a, 0x0300005a, 0x00106000, 0x00000000, 0x04001858, 0x00107000, 0x00000000,
        0x00005555, 0x04002064, 0x00101032, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x02000068, 0x00000001, 0x0a000038, 0x00100032, 0x00000000, 0x00101046, 0x00000000, 0x00004002,
        0x3d000000, 0x3d000000, 0x00000000, 0x00000000, 0x8b000045, 0x800000c2, 0x00155543, 0x001020f2,
        0x00000000, 0x00100046, 0x00000000, 0x00107e46, 0x00000000, 0x00106000, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    static const unsigned int red_data[] = {0xff0000ff};
    static const unsigned int green_data[] = {0xff00ff00};

    memset(&desc, 0, sizeof(desc));
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    command_list = context.list;
    queue = context.queue;

    binding_tier = get_resource_binding_tier(context.device);

    context.root_signature = create_texture_root_signature(context.device,
            D3D12_SHADER_VISIBILITY_PIXEL, 0, 0);
    context.pipeline_state = create_pipeline_state(context.device,
            context.root_signature, context.render_target_desc.Format, NULL, &ps, NULL);

    heap = create_gpu_descriptor_heap(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1);
    cpu_handle = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(heap);

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

    red_texture = create_default_texture(context.device, 1, 1, DXGI_FORMAT_R8G8B8A8_UNORM,
            0, D3D12_RESOURCE_STATE_COPY_DEST);
    texture_data.pData = red_data;
    texture_data.RowPitch = sizeof(*red_data);
    texture_data.SlicePitch = texture_data.RowPitch;
    upload_texture_data(red_texture, &texture_data, 1, queue, command_list);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, red_texture,
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);

    green_texture = create_default_texture(context.device, 1, 1, DXGI_FORMAT_R8G8B8A8_UNORM,
            0, D3D12_RESOURCE_STATE_COPY_DEST);
    texture_data.pData = green_data;
    upload_texture_data(green_texture, &texture_data, 1, queue, command_list);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, green_texture,
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);

    ID3D12Device_CreateShaderResourceView(context.device, red_texture, NULL,
            get_cpu_descriptor_handle(&context, cpu_heap, 0));
    ID3D12Device_CopyDescriptorsSimple(context.device, 1,
            get_cpu_sampler_handle(&context, heap, 0),
            get_cpu_sampler_handle(&context, cpu_heap, 0),
            D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &heap);
    ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0,
            ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(heap));
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    hr = ID3D12GraphicsCommandList_Close(command_list);
    ok(SUCCEEDED(hr), "Failed to close command list, hr %#x.\n", hr);

    /* Update the descriptor heap used by the closed command list. */
    ID3D12Device_CreateShaderResourceView(context.device, green_texture, NULL, cpu_handle);

    exec_command_list(queue, command_list);
    wait_queue_idle(context.device, queue);
    reset_command_list(command_list, context.allocator);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_resource_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
    value = get_readback_uint(&rb.rb, 0, 0, 0);
    todo_if(binding_tier < D3D12_RESOURCE_BINDING_TIER_3)
    ok(value == 0xff00ff00, "Got unexpected value %#x.\n", value);
    release_resource_readback(&rb);

    ID3D12DescriptorHeap_Release(cpu_heap);
    ID3D12DescriptorHeap_Release(heap);
    ID3D12Resource_Release(green_texture);
    ID3D12Resource_Release(red_texture);
    destroy_test_context(&context);
}

static void test_update_compute_descriptor_tables(void)
{
    struct cb_data
    {
        struct uvec4 srv_size[2];
        struct uvec4 uav_size[2];
        /* Constant buffers need to be padded to 256 bytes. */
        char pad[D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT - 2 * sizeof(struct uvec4[2])];
    };

    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    ID3D12PipelineState *buffer_pso, *texture_pso;
    D3D12_DESCRIPTOR_RANGE descriptor_ranges[4];
    D3D12_UNORDERED_ACCESS_VIEW_DESC uav_desc;
    D3D12_CONSTANT_BUFFER_VIEW_DESC cbv_desc;
    D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc;
    ID3D12GraphicsCommandList *command_list;
    D3D12_ROOT_PARAMETER root_parameters[5];
    D3D12_SUBRESOURCE_DATA subresource_data;
    ID3D12Resource *buffer_cb, *texture_cb;
    ID3D12DescriptorHeap *descriptor_heap;
    struct d3d12_resource_readback rb;
    ID3D12Resource *output_buffers[2];
    ID3D12Resource *input_buffers[5];
    ID3D12Resource *textures[3];
    struct test_context context;
    ID3D12CommandQueue *queue;
    struct cb_data cb_data;
    ID3D12Device *device;
    unsigned int i;
    uint32_t data;
    HRESULT hr;

    static const DWORD cs_buffer_code[] =
    {
#if 0
        uint offset;

        RWByteAddressBuffer u0 : register(u0);

        cbuffer cb0 : register(b0)
        {
            uint4 srv_size[2];
            uint4 uav_size[2];
        };

        Buffer<uint> t0 : register(t0);
        Buffer<uint> t1 : register(t1);

        RWBuffer<uint> u4 : register(u4);
        RWBuffer<uint> u7 : register(u7);

        [numthreads(1, 1, 1)]
        void main()
        {
            uint x, result, byte_offset = offset;

            for (x = 0, result = 0; x < srv_size[0].x; ++x)
                result += t0.Load(x);
            u0.Store(byte_offset, result);
            byte_offset += 4;

            for (x = 0, result = 0; x < srv_size[1].x; ++x)
                result += t1.Load(x);
            u0.Store(byte_offset, result);
            byte_offset += 4;

            for (x = 0, result = 0; x < uav_size[0].x; ++x)
                result += u4[x];
            u0.Store(byte_offset, result);
            byte_offset += 4;

            for (x = 0, result = 0; x < uav_size[1].x; ++x)
                result += u7[x];
            u0.Store(byte_offset, result);
        }
#endif
        0x43425844, 0xb3d9f052, 0xcc3f0310, 0xd18f8515, 0xccabd8f6, 0x00000001, 0x00000404, 0x00000003,
        0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x000003b0, 0x00050050, 0x000000ec, 0x0100086a,
        0x04000059, 0x00208e46, 0x00000001, 0x00000001, 0x04000059, 0x00208e46, 0x00000000, 0x00000004,
        0x04000858, 0x00107000, 0x00000000, 0x00004444, 0x04000858, 0x00107000, 0x00000001, 0x00004444,
        0x0300009d, 0x0011e000, 0x00000000, 0x0400089c, 0x0011e000, 0x00000004, 0x00004444, 0x0400089c,
        0x0011e000, 0x00000007, 0x00004444, 0x02000068, 0x00000002, 0x0400009b, 0x00000001, 0x00000001,
        0x00000001, 0x08000036, 0x00100032, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000,
        0x00000000, 0x01000030, 0x08000050, 0x00100012, 0x00000001, 0x0010001a, 0x00000000, 0x0020800a,
        0x00000000, 0x00000000, 0x03040003, 0x0010000a, 0x00000001, 0x8900002d, 0x80000042, 0x00111103,
        0x00100012, 0x00000001, 0x00100556, 0x00000000, 0x00107e46, 0x00000000, 0x0700001e, 0x00100012,
        0x00000000, 0x0010000a, 0x00000000, 0x0010000a, 0x00000001, 0x0700001e, 0x00100022, 0x00000000,
        0x0010001a, 0x00000000, 0x00004001, 0x00000001, 0x01000016, 0x08000036, 0x00100032, 0x00000001,
        0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x01000030, 0x08000050, 0x00100042,
        0x00000001, 0x0010000a, 0x00000001, 0x0020800a, 0x00000000, 0x00000001, 0x03040003, 0x0010002a,
        0x00000001, 0x8900002d, 0x80000042, 0x00111103, 0x00100042, 0x00000001, 0x00100006, 0x00000001,
        0x00107c96, 0x00000001, 0x0700001e, 0x00100022, 0x00000001, 0x0010001a, 0x00000001, 0x0010002a,
        0x00000001, 0x0700001e, 0x00100012, 0x00000001, 0x0010000a, 0x00000001, 0x00004001, 0x00000001,
        0x01000016, 0x05000036, 0x00100022, 0x00000000, 0x0010001a, 0x00000001, 0x08000036, 0x00100032,
        0x00000001, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x01000030, 0x08000050,
        0x00100042, 0x00000001, 0x0010000a, 0x00000001, 0x0020800a, 0x00000000, 0x00000002, 0x03040003,
        0x0010002a, 0x00000001, 0x890000a3, 0x80000042, 0x00111103, 0x00100042, 0x00000001, 0x00100006,
        0x00000001, 0x0011ec96, 0x00000004, 0x0700001e, 0x00100022, 0x00000001, 0x0010001a, 0x00000001,
        0x0010002a, 0x00000001, 0x0700001e, 0x00100012, 0x00000001, 0x0010000a, 0x00000001, 0x00004001,
        0x00000001, 0x01000016, 0x05000036, 0x00100042, 0x00000000, 0x0010001a, 0x00000001, 0x08000036,
        0x00100032, 0x00000001, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x01000030,
        0x08000050, 0x00100042, 0x00000001, 0x0010000a, 0x00000001, 0x0020800a, 0x00000000, 0x00000003,
        0x03040003, 0x0010002a, 0x00000001, 0x890000a3, 0x80000042, 0x00111103, 0x00100042, 0x00000001,
        0x00100006, 0x00000001, 0x0011ec96, 0x00000007, 0x0700001e, 0x00100022, 0x00000001, 0x0010001a,
        0x00000001, 0x0010002a, 0x00000001, 0x0700001e, 0x00100012, 0x00000001, 0x0010000a, 0x00000001,
        0x00004001, 0x00000001, 0x01000016, 0x05000036, 0x00100082, 0x00000000, 0x0010001a, 0x00000001,
        0x080000a6, 0x0011e0f2, 0x00000000, 0x0020800a, 0x00000001, 0x00000000, 0x00100e46, 0x00000000,
        0x0100003e,
    };
    static const DWORD cs_texture_code[] =
    {
#if 0
        uint offset;

        RWByteAddressBuffer u0 : register(u0);

        cbuffer cb0 : register(b0)
        {
            uint4 srv_size[2];
            uint4 uav_size[2];
        };

        Texture2D<uint> t0 : register(t0);
        Texture2D<uint> t1 : register(t1);

        RWBuffer<uint> u4 : register(u4);
        RWTexture2D<uint> u6 : register(u6);

        [numthreads(1, 1, 1)]
        void main()
        {
            uint x, y, result, byte_offset = offset;

            for (y = 0, result = 0; y < srv_size[0].y; ++y)
            {
                for (x = 0; x < srv_size[0].x; ++x)
                    result += t0.Load(int3(x, y, 0));
            }
            u0.Store(byte_offset, result);
            byte_offset += 4;

            for (y = 0, result = 0; y < srv_size[1].y; ++y)
            {
                for (x = 0; x < srv_size[1].x; ++x)
                    result += t1.Load(int3(x, y, 0));
            }
            u0.Store(byte_offset, result);
            byte_offset += 4;

            for (x = 0, result = 0; x < uav_size[0].x; ++x)
                result += u4[x];
            u0.Store(byte_offset, result);
            byte_offset += 4;

            for (y = 0, result = 0; y < uav_size[1].y; ++y)
            {
                for (x = 0; x < uav_size[1].x; ++x)
                    result += u6[uint2(x, y)];
            }
            u0.Store(byte_offset, result);
        }
#endif
        0x43425844, 0x3f0f012e, 0xfb75f6aa, 0xb87ffe68, 0xf25f9ee6, 0x00000001, 0x00000650, 0x00000003,
        0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x000005fc, 0x00050050, 0x0000017f, 0x0100086a,
        0x04000059, 0x00208e46, 0x00000001, 0x00000001, 0x04000059, 0x00208e46, 0x00000000, 0x00000004,
        0x04001858, 0x00107000, 0x00000000, 0x00004444, 0x04001858, 0x00107000, 0x00000001, 0x00004444,
        0x0300009d, 0x0011e000, 0x00000000, 0x0400089c, 0x0011e000, 0x00000004, 0x00004444, 0x0400189c,
        0x0011e000, 0x00000006, 0x00004444, 0x02000068, 0x00000004, 0x0400009b, 0x00000001, 0x00000001,
        0x00000001, 0x08000036, 0x001000c2, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000,
        0x00000000, 0x05000036, 0x00100012, 0x00000001, 0x00004001, 0x00000000, 0x05000036, 0x00100012,
        0x00000002, 0x00004001, 0x00000000, 0x01000030, 0x08000050, 0x00100022, 0x00000001, 0x0010000a,
        0x00000001, 0x0020801a, 0x00000000, 0x00000000, 0x03040003, 0x0010001a, 0x00000001, 0x05000036,
        0x00100022, 0x00000000, 0x0010000a, 0x00000001, 0x05000036, 0x00100012, 0x00000003, 0x00004001,
        0x00000000, 0x05000036, 0x00100022, 0x00000003, 0x0010000a, 0x00000002, 0x01000030, 0x08000050,
        0x00100022, 0x00000001, 0x0010000a, 0x00000003, 0x0020800a, 0x00000000, 0x00000000, 0x03040003,
        0x0010001a, 0x00000001, 0x05000036, 0x00100012, 0x00000000, 0x0010000a, 0x00000003, 0x8900002d,
        0x800000c2, 0x00111103, 0x00100012, 0x00000000, 0x00100e46, 0x00000000, 0x00107e46, 0x00000000,
        0x0700001e, 0x00100022, 0x00000003, 0x0010001a, 0x00000003, 0x0010000a, 0x00000000, 0x0700001e,
        0x00100012, 0x00000003, 0x0010000a, 0x00000003, 0x00004001, 0x00000001, 0x01000016, 0x05000036,
        0x00100012, 0x00000002, 0x0010001a, 0x00000003, 0x0700001e, 0x00100012, 0x00000001, 0x0010000a,
        0x00000001, 0x00004001, 0x00000001, 0x01000016, 0x08000036, 0x001000c2, 0x00000000, 0x00004002,
        0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x05000036, 0x00100012, 0x00000001, 0x00004001,
        0x00000000, 0x05000036, 0x00100022, 0x00000002, 0x00004001, 0x00000000, 0x01000030, 0x08000050,
        0x00100022, 0x00000001, 0x0010000a, 0x00000001, 0x0020801a, 0x00000000, 0x00000001, 0x03040003,
        0x0010001a, 0x00000001, 0x05000036, 0x00100022, 0x00000000, 0x0010000a, 0x00000001, 0x05000036,
        0x00100012, 0x00000003, 0x00004001, 0x00000000, 0x05000036, 0x00100022, 0x00000003, 0x0010001a,
        0x00000002, 0x01000030, 0x08000050, 0x00100022, 0x00000001, 0x0010000a, 0x00000003, 0x0020800a,
        0x00000000, 0x00000001, 0x03040003, 0x0010001a, 0x00000001, 0x05000036, 0x00100012, 0x00000000,
        0x0010000a, 0x00000003, 0x8900002d, 0x800000c2, 0x00111103, 0x00100012, 0x00000000, 0x00100e46,
        0x00000000, 0x00107e46, 0x00000001, 0x0700001e, 0x00100022, 0x00000003, 0x0010001a, 0x00000003,
        0x0010000a, 0x00000000, 0x0700001e, 0x00100012, 0x00000003, 0x0010000a, 0x00000003, 0x00004001,
        0x00000001, 0x01000016, 0x05000036, 0x00100022, 0x00000002, 0x0010001a, 0x00000003, 0x0700001e,
        0x00100012, 0x00000001, 0x0010000a, 0x00000001, 0x00004001, 0x00000001, 0x01000016, 0x08000036,
        0x00100032, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x01000030,
        0x08000050, 0x00100042, 0x00000000, 0x0010000a, 0x00000000, 0x0020800a, 0x00000000, 0x00000002,
        0x03040003, 0x0010002a, 0x00000000, 0x890000a3, 0x80000042, 0x00111103, 0x00100042, 0x00000000,
        0x00100006, 0x00000000, 0x0011ec96, 0x00000004, 0x0700001e, 0x00100022, 0x00000000, 0x0010001a,
        0x00000000, 0x0010002a, 0x00000000, 0x0700001e, 0x00100012, 0x00000000, 0x0010000a, 0x00000000,
        0x00004001, 0x00000001, 0x01000016, 0x05000036, 0x00100042, 0x00000002, 0x0010001a, 0x00000000,
        0x05000036, 0x00100012, 0x00000000, 0x00004001, 0x00000000, 0x05000036, 0x00100082, 0x00000002,
        0x00004001, 0x00000000, 0x01000030, 0x08000050, 0x00100022, 0x00000000, 0x0010000a, 0x00000000,
        0x0020801a, 0x00000000, 0x00000003, 0x03040003, 0x0010001a, 0x00000000, 0x05000036, 0x001000e2,
        0x00000001, 0x00100006, 0x00000000, 0x05000036, 0x00100012, 0x00000003, 0x00004001, 0x00000000,
        0x05000036, 0x00100022, 0x00000003, 0x0010003a, 0x00000002, 0x01000030, 0x08000050, 0x00100022,
        0x00000000, 0x0010000a, 0x00000003, 0x0020800a, 0x00000000, 0x00000003, 0x03040003, 0x0010001a,
        0x00000000, 0x05000036, 0x00100012, 0x00000001, 0x0010000a, 0x00000003, 0x890000a3, 0x800000c2,
        0x00111103, 0x00100022, 0x00000000, 0x00100e46, 0x00000001, 0x0011ee16, 0x00000006, 0x0700001e,
        0x00100022, 0x00000003, 0x0010001a, 0x00000003, 0x0010001a, 0x00000000, 0x0700001e, 0x00100012,
        0x00000003, 0x0010000a, 0x00000003, 0x00004001, 0x00000001, 0x01000016, 0x05000036, 0x00100082,
        0x00000002, 0x0010001a, 0x00000003, 0x0700001e, 0x00100012, 0x00000000, 0x0010000a, 0x00000000,
        0x00004001, 0x00000001, 0x01000016, 0x080000a6, 0x0011e0f2, 0x00000000, 0x0020800a, 0x00000001,
        0x00000000, 0x00100e46, 0x00000002, 0x0100003e,
    };
    static const uint32_t buffer0_data[] = {1, 2, 3, 1};
    static const uint32_t buffer1_data[] = {10, 20, 30, 10};
    static const uint32_t buffer2_data[] = {100, 200, 300, 200};
    static const uint32_t buffer3_data[] = {1000, 2000, 2000, 2000};
    static const uint32_t buffer4_data[] = {0, 0, 0, 0};
    static const uint32_t texture0_data[4][4] =
    {
        {1, 0, 0, 0},
        {10000, 100, 1000, 10000},
        {0, 0, 0, 2},
        {0, 30000, 10000, 10},
    };
    static const uint32_t texture1_data[4][4] =
    {
        {6, 0, 0, 0},
        {600, 0, 1000, 60000},
        {0, 40, 0, 0},
        {0, 30000, 0, 0},
    };
    static const uint32_t texture2_data[4][4] =
    {
        {1, 1, 1, 1},
        {2, 2, 2, 2},
        {3, 3, 3, 3},
        {4, 4, 4, 4},
    };
    static const uint32_t expected_output0[] = {7, 70, 800, 7000, 70, 0, 800, 7000, 61113, 91646, 800, 40};
    static const uint32_t expected_output1[] = {61113, 91646, 800, 40, 7, 70, 800, 7000};

    if (!init_compute_test_context(&context))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
    root_parameters[0].Constants.ShaderRegister = 1;
    root_parameters[0].Constants.RegisterSpace = 0;
    root_parameters[0].Constants.Num32BitValues = 1;
    root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    descriptor_ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
    descriptor_ranges[0].NumDescriptors = 1;
    descriptor_ranges[0].BaseShaderRegister = 0;
    descriptor_ranges[0].RegisterSpace = 0;
    descriptor_ranges[0].OffsetInDescriptorsFromTableStart = 0;
    root_parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameters[1].DescriptorTable.NumDescriptorRanges = 1;
    root_parameters[1].DescriptorTable.pDescriptorRanges = &descriptor_ranges[0];
    root_parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    descriptor_ranges[1].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
    descriptor_ranges[1].NumDescriptors = 2;
    descriptor_ranges[1].BaseShaderRegister = 0;
    descriptor_ranges[1].RegisterSpace = 0;
    descriptor_ranges[1].OffsetInDescriptorsFromTableStart = 0;
    root_parameters[2].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameters[2].DescriptorTable.NumDescriptorRanges = 1;
    root_parameters[2].DescriptorTable.pDescriptorRanges = &descriptor_ranges[1];
    root_parameters[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    descriptor_ranges[2].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
    descriptor_ranges[2].NumDescriptors = 4;
    descriptor_ranges[2].BaseShaderRegister = 4;
    descriptor_ranges[2].RegisterSpace = 0;
    descriptor_ranges[2].OffsetInDescriptorsFromTableStart = 0;
    root_parameters[3].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameters[3].DescriptorTable.NumDescriptorRanges = 1;
    root_parameters[3].DescriptorTable.pDescriptorRanges = &descriptor_ranges[2];
    root_parameters[3].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    descriptor_ranges[3].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_CBV;
    descriptor_ranges[3].NumDescriptors = 1;
    descriptor_ranges[3].BaseShaderRegister = 0;
    descriptor_ranges[3].RegisterSpace = 0;
    descriptor_ranges[3].OffsetInDescriptorsFromTableStart = 0;
    root_parameters[4].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameters[4].DescriptorTable.NumDescriptorRanges = 1;
    root_parameters[4].DescriptorTable.pDescriptorRanges = &descriptor_ranges[3];
    root_parameters[4].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    root_signature_desc.NumParameters = 5;
    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(device, &root_signature_desc, &context.root_signature);
    ok(SUCCEEDED(hr), "Failed to create root signature, hr %#x.\n", hr);

    buffer_pso = create_compute_pipeline_state(device, context.root_signature,
            shader_bytecode(cs_buffer_code, sizeof(cs_buffer_code)));
    texture_pso = create_compute_pipeline_state(device, context.root_signature,
            shader_bytecode(cs_texture_code, sizeof(cs_texture_code)));

    for (i = 0; i < ARRAY_SIZE(output_buffers); ++i)
    {
        output_buffers[i] = create_default_buffer(device, 1024,
                D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
    }

    input_buffers[0] = create_default_buffer(device, sizeof(buffer0_data),
            D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST);
    upload_buffer_data(input_buffers[0], 0, sizeof(buffer0_data), buffer0_data, queue, command_list);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, input_buffers[0],
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);

    input_buffers[1] = create_default_buffer(device, sizeof(buffer1_data),
            D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST);
    upload_buffer_data(input_buffers[1], 0, sizeof(buffer1_data), buffer1_data, queue, command_list);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, input_buffers[1],
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);

    input_buffers[2] = create_default_buffer(device, sizeof(buffer2_data),
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_DEST);
    upload_buffer_data(input_buffers[2], 0, sizeof(buffer2_data), buffer2_data, queue, command_list);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, input_buffers[2],
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

    input_buffers[3] = create_default_buffer(device, sizeof(buffer3_data),
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_DEST);
    upload_buffer_data(input_buffers[3], 0, sizeof(buffer3_data), buffer3_data, queue, command_list);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, input_buffers[3],
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

    input_buffers[4] = create_default_buffer(device, sizeof(buffer4_data),
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_DEST);
    upload_buffer_data(input_buffers[4], 0, sizeof(buffer4_data), buffer4_data, queue, command_list);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, input_buffers[4],
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);

    textures[0] = create_default_texture(context.device,
            4, 4, DXGI_FORMAT_R32_UINT, 0, D3D12_RESOURCE_STATE_COPY_DEST);
    subresource_data.pData = texture0_data;
    subresource_data.RowPitch = sizeof(*texture0_data);
    subresource_data.SlicePitch = subresource_data.RowPitch;
    upload_texture_data(textures[0], &subresource_data, 1, queue, command_list);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, textures[0],
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);

    textures[1] = create_default_texture(context.device,
            4, 4, DXGI_FORMAT_R32_UINT, 0, D3D12_RESOURCE_STATE_COPY_DEST);
    subresource_data.pData = texture1_data;
    subresource_data.RowPitch = sizeof(*texture1_data);
    subresource_data.SlicePitch = subresource_data.RowPitch;
    upload_texture_data(textures[1], &subresource_data, 1, queue, command_list);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, textures[1],
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);

    textures[2] = create_default_texture(context.device, 4, 4, DXGI_FORMAT_R32_UINT,
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_DEST);
    subresource_data.pData = texture2_data;
    subresource_data.RowPitch = sizeof(*texture2_data);
    subresource_data.SlicePitch = subresource_data.RowPitch;
    upload_texture_data(textures[2], &subresource_data, 1, queue, command_list);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, textures[2],
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

    memset(&cb_data, 0, sizeof(cb_data));
    cb_data.srv_size[0].x = ARRAY_SIZE(buffer0_data);
    cb_data.srv_size[1].x = ARRAY_SIZE(buffer1_data);
    cb_data.uav_size[0].x = ARRAY_SIZE(buffer2_data);
    cb_data.uav_size[1].x = ARRAY_SIZE(buffer3_data);
    buffer_cb = create_upload_buffer(device, sizeof(cb_data), &cb_data);

    memset(&cb_data, 0, sizeof(cb_data));
    cb_data.srv_size[0].x = 4;
    cb_data.srv_size[0].y = 4;
    cb_data.srv_size[1].x = 4;
    cb_data.srv_size[1].y = 4;
    cb_data.uav_size[0].x = ARRAY_SIZE(buffer2_data);
    cb_data.uav_size[1].x = 4;
    cb_data.uav_size[1].y = 4;
    texture_cb = create_upload_buffer(device, sizeof(cb_data), &cb_data);

    descriptor_heap = create_gpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 30);

    memset(&srv_desc, 0, sizeof(srv_desc));
    srv_desc.Format = DXGI_FORMAT_R32_UINT;
    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 = ARRAY_SIZE(buffer0_data);
    ID3D12Device_CreateShaderResourceView(device, input_buffers[0], &srv_desc,
            get_cpu_descriptor_handle(&context, descriptor_heap, 0));
    srv_desc.Buffer.NumElements = ARRAY_SIZE(buffer1_data);
    ID3D12Device_CreateShaderResourceView(device, input_buffers[1], &srv_desc,
            get_cpu_descriptor_handle(&context, descriptor_heap, 1));

    ID3D12Device_CreateShaderResourceView(device, input_buffers[1], &srv_desc,
            get_cpu_descriptor_handle(&context, descriptor_heap, 6));
    srv_desc.Buffer.NumElements = ARRAY_SIZE(buffer4_data);
    ID3D12Device_CreateShaderResourceView(device, input_buffers[4], &srv_desc,
            get_cpu_descriptor_handle(&context, descriptor_heap, 7));

    memset(&uav_desc, 0, sizeof(uav_desc));
    uav_desc.Format = DXGI_FORMAT_R32_UINT;
    uav_desc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
    uav_desc.Buffer.FirstElement = 0;
    uav_desc.Buffer.NumElements = ARRAY_SIZE(buffer2_data);
    ID3D12Device_CreateUnorderedAccessView(device, input_buffers[2], NULL, &uav_desc,
            get_cpu_descriptor_handle(&context, descriptor_heap, 2));
    ID3D12Device_CreateUnorderedAccessView(device, input_buffers[2], NULL, &uav_desc,
            get_cpu_descriptor_handle(&context, descriptor_heap, 12));
    uav_desc.Buffer.NumElements = ARRAY_SIZE(buffer3_data);
    ID3D12Device_CreateUnorderedAccessView(device, input_buffers[3], NULL, &uav_desc,
            get_cpu_descriptor_handle(&context, descriptor_heap, 5));

    ID3D12Device_CreateShaderResourceView(device, textures[0], NULL,
            get_cpu_descriptor_handle(&context, descriptor_heap, 10));
    ID3D12Device_CreateShaderResourceView(device, textures[1], NULL,
            get_cpu_descriptor_handle(&context, descriptor_heap, 11));

    ID3D12Device_CreateUnorderedAccessView(device, textures[2], NULL, NULL,
            get_cpu_descriptor_handle(&context, descriptor_heap, 14));

    cbv_desc.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(buffer_cb);
    cbv_desc.SizeInBytes = align(sizeof(cb_data), D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT);
    ID3D12Device_CreateConstantBufferView(context.device, &cbv_desc,
            get_cpu_descriptor_handle(&context, descriptor_heap, 8));

    cbv_desc.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(texture_cb);
    cbv_desc.SizeInBytes = align(sizeof(cb_data), D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT);
    ID3D12Device_CreateConstantBufferView(context.device, &cbv_desc,
            get_cpu_descriptor_handle(&context, descriptor_heap, 9));

    memset(&uav_desc, 0, sizeof(uav_desc));
    uav_desc.Format = DXGI_FORMAT_R32_TYPELESS;
    uav_desc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
    uav_desc.Buffer.FirstElement = 0;
    uav_desc.Buffer.NumElements = 256;
    uav_desc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_RAW;
    ID3D12Device_CreateUnorderedAccessView(device, output_buffers[0], NULL, &uav_desc,
            get_cpu_descriptor_handle(&context, descriptor_heap, 20));
    ID3D12Device_CreateUnorderedAccessView(device, output_buffers[1], NULL, &uav_desc,
            get_cpu_descriptor_handle(&context, descriptor_heap, 21));

    ID3D12GraphicsCommandList_SetComputeRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &descriptor_heap);

    ID3D12GraphicsCommandList_SetPipelineState(command_list, buffer_pso);

    ID3D12GraphicsCommandList_SetComputeRoot32BitConstant(command_list, 0, 0 /* offset */, 0);
    ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(command_list,
            1, get_gpu_descriptor_handle(&context, descriptor_heap, 20)); /* u0 */
    ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(command_list,
            2, get_gpu_descriptor_handle(&context, descriptor_heap, 0)); /* t1-t2 */
    ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(command_list,
            3, get_gpu_descriptor_handle(&context, descriptor_heap, 2)); /* u4-u7 */
    ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(command_list,
            4, get_gpu_descriptor_handle(&context, descriptor_heap, 8)); /* b0 */
    ID3D12GraphicsCommandList_Dispatch(command_list, 1, 1, 1);

    ID3D12GraphicsCommandList_SetComputeRoot32BitConstant(command_list, 0, 16 /* offset */, 0);
    ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(command_list,
            2, get_gpu_descriptor_handle(&context, descriptor_heap, 6));  /* t1-t2 */
    ID3D12GraphicsCommandList_Dispatch(command_list, 1, 1, 1);

    ID3D12GraphicsCommandList_SetPipelineState(command_list, texture_pso);

    transition_resource_state(command_list, input_buffers[4],
            D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
    ID3D12GraphicsCommandList_SetComputeRoot32BitConstant(command_list, 0, 32 /* offset */, 0);
    ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(command_list,
            2, get_gpu_descriptor_handle(&context, descriptor_heap, 10)); /* t1-t2 */
    ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(command_list,
            3, get_gpu_descriptor_handle(&context, descriptor_heap, 12)); /* u4-u7 */
    ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(command_list,
            4, get_gpu_descriptor_handle(&context, descriptor_heap, 9)); /* b0 */
    ID3D12GraphicsCommandList_Dispatch(command_list, 1, 1, 1);

    ID3D12GraphicsCommandList_SetComputeRoot32BitConstant(command_list, 0, 0 /* offset */, 0);
    ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(command_list,
            1, get_gpu_descriptor_handle(&context, descriptor_heap, 21)); /* u0 */
    ID3D12GraphicsCommandList_Dispatch(command_list, 1, 1, 1);

    ID3D12GraphicsCommandList_SetPipelineState(command_list, buffer_pso);

    ID3D12GraphicsCommandList_SetComputeRoot32BitConstant(command_list, 0, 16 /* offset */, 0);
    ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(command_list,
            2, get_gpu_descriptor_handle(&context, descriptor_heap, 0)); /* t1-t2 */
    ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(command_list,
            3, get_gpu_descriptor_handle(&context, descriptor_heap, 2)); /* u4-u7 */
    ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(command_list,
            4, get_gpu_descriptor_handle(&context, descriptor_heap, 8)); /* b0 */
    ID3D12GraphicsCommandList_Dispatch(command_list, 1, 1, 1);

    transition_sub_resource_state(command_list, output_buffers[0], 0,
            D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_buffer_readback_with_command_list(output_buffers[0], DXGI_FORMAT_R32_UINT, &rb, queue, command_list);
    for (i = 0; i < ARRAY_SIZE(expected_output0); ++i)
    {
        data = get_readback_uint(&rb.rb, i, 0, 0);
        ok(data == expected_output0[i], "Got %#x, expected %#x at %u.\n", data, expected_output0[i], i);
    }
    release_resource_readback(&rb);

    reset_command_list(command_list, context.allocator);
    transition_sub_resource_state(command_list, output_buffers[1], 0,
            D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_buffer_readback_with_command_list(output_buffers[1], DXGI_FORMAT_R32_UINT, &rb, queue, command_list);
    for (i = 0; i < ARRAY_SIZE(expected_output1); ++i)
    {
        data = get_readback_uint(&rb.rb, i, 0, 0);
        ok(data == expected_output1[i], "Got %#x, expected %#x at %u.\n", data, expected_output1[i], i);
    }
    release_resource_readback(&rb);

    ID3D12Resource_Release(buffer_cb);
    ID3D12Resource_Release(texture_cb);
    for (i = 0; i < ARRAY_SIZE(input_buffers); ++i)
        ID3D12Resource_Release(input_buffers[i]);
    for (i = 0; i < ARRAY_SIZE(textures); ++i)
        ID3D12Resource_Release(textures[i]);
    for (i = 0; i < ARRAY_SIZE(output_buffers); ++i)
        ID3D12Resource_Release(output_buffers[i]);
    ID3D12PipelineState_Release(buffer_pso);
    ID3D12PipelineState_Release(texture_pso);
    ID3D12DescriptorHeap_Release(descriptor_heap);
    destroy_test_context(&context);
}

static void test_update_descriptor_tables_after_root_signature_change(void)
{
    ID3D12RootSignature *root_signature, *root_signature2;
    ID3D12PipelineState *pipeline_state, *pipeline_state2;
    ID3D12DescriptorHeap *heap, *sampler_heap, *heaps[2];
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    D3D12_DESCRIPTOR_RANGE descriptor_range[4];
    D3D12_ROOT_PARAMETER root_parameters[3];
    ID3D12GraphicsCommandList *command_list;
    D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle;
    unsigned int i, descriptor_size;
    D3D12_SAMPLER_DESC sampler_desc;
    struct test_context_desc desc;
    ID3D12Resource *textures[2];
    D3D12_SUBRESOURCE_DATA data;
    struct test_context context;
    ID3D12CommandQueue *queue;
    HRESULT hr;

    static const DWORD ps_code[] =
    {
#if 0
        Texture2D t;
        SamplerState s;

        float4 main(float4 position : SV_POSITION) : SV_Target
        {
            float2 p;

            p.x = position.x / 32.0f;
            p.y = position.y / 32.0f;
            return t.Sample(s, p);
        }
#endif
        0x43425844, 0x7a0c3929, 0x75ff3ca4, 0xccb318b2, 0xe6965b4c, 0x00000001, 0x00000140, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000030f, 0x505f5653, 0x5449534f, 0x004e4f49,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x000000a4, 0x00000050,
        0x00000029, 0x0100086a, 0x0300005a, 0x00106000, 0x00000000, 0x04001858, 0x00107000, 0x00000000,
        0x00005555, 0x04002064, 0x00101032, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x02000068, 0x00000001, 0x0a000038, 0x00100032, 0x00000000, 0x00101046, 0x00000000, 0x00004002,
        0x3d000000, 0x3d000000, 0x00000000, 0x00000000, 0x8b000045, 0x800000c2, 0x00155543, 0x001020f2,
        0x00000000, 0x00100046, 0x00000000, 0x00107e46, 0x00000000, 0x00106000, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    static const unsigned int texture_data[] = {0xff00ff00, 0xff0000ff};

    memset(&desc, 0, sizeof(desc));
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    command_list = context.list;
    queue = context.queue;

    descriptor_range[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
    descriptor_range[0].NumDescriptors = 2;
    descriptor_range[0].BaseShaderRegister = 0;
    descriptor_range[0].RegisterSpace = 0;
    descriptor_range[0].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[0];
    root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;

    descriptor_range[1].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER;
    descriptor_range[1].NumDescriptors = 1;
    descriptor_range[1].BaseShaderRegister = 0;
    descriptor_range[1].RegisterSpace = 0;
    descriptor_range[1].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
    root_parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameters[1].DescriptorTable.NumDescriptorRanges = 1;
    root_parameters[1].DescriptorTable.pDescriptorRanges = &descriptor_range[1];
    root_parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;

    descriptor_range[2].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
    descriptor_range[2].NumDescriptors = 2;
    descriptor_range[2].BaseShaderRegister = 2;
    descriptor_range[2].RegisterSpace = 0;
    descriptor_range[2].OffsetInDescriptorsFromTableStart = 0;
    descriptor_range[3].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_CBV;
    descriptor_range[3].NumDescriptors = 1;
    descriptor_range[3].BaseShaderRegister = 0;
    descriptor_range[3].RegisterSpace = 0;
    descriptor_range[3].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
    root_parameters[2].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameters[2].DescriptorTable.NumDescriptorRanges = 2;
    root_parameters[2].DescriptorTable.pDescriptorRanges = &descriptor_range[2];
    root_parameters[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;

    memset(&root_signature_desc, 0, sizeof(root_signature_desc));
    root_signature_desc.NumParameters = ARRAY_SIZE(root_parameters);
    root_signature_desc.pParameters = root_parameters;
    hr = create_root_signature(context.device, &root_signature_desc, &root_signature);
    ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr);
    root_signature_desc.NumParameters = ARRAY_SIZE(root_parameters) - 1;
    hr = create_root_signature(context.device, &root_signature_desc, &root_signature2);
    ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr);

    pipeline_state = create_pipeline_state(context.device,
            root_signature, context.render_target_desc.Format, NULL, &ps, NULL);
    pipeline_state2 = create_pipeline_state(context.device,
            root_signature2, context.render_target_desc.Format, NULL, &ps, NULL);

    heap = create_gpu_descriptor_heap(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 6);
    sampler_heap = create_gpu_descriptor_heap(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, 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;
    ID3D12Device_CreateSampler(context.device, &sampler_desc, get_cpu_descriptor_handle(&context, sampler_heap, 0));

    descriptor_size = ID3D12Device_GetDescriptorHandleIncrementSize(context.device,
            D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);

    for (i = 0; i < ARRAY_SIZE(textures); ++i)
    {
        textures[i] = create_default_texture(context.device,
                1, 1, DXGI_FORMAT_R8G8B8A8_UNORM, 0, D3D12_RESOURCE_STATE_COPY_DEST);
        data.pData = &texture_data[i];
        data.RowPitch = sizeof(texture_data[i]);
        data.SlicePitch = data.RowPitch;
        upload_texture_data(textures[i], &data, 1, queue, command_list);
        reset_command_list(command_list, context.allocator);
    }

    cpu_handle = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(heap);
    for (i = 0; i < ARRAY_SIZE(textures); ++i)
    {
        transition_resource_state(command_list, textures[i],
                D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
        ID3D12Device_CreateShaderResourceView(context.device, textures[i], NULL, cpu_handle);
        cpu_handle.ptr += descriptor_size;
    }
    for (; i < 6; ++i)
    {
        ID3D12Device_CreateShaderResourceView(context.device, textures[1], NULL, cpu_handle);
        cpu_handle.ptr += descriptor_size;
    }

    heaps[0] = heap; heaps[1] = sampler_heap;
    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, ARRAY_SIZE(heaps), heaps);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, pipeline_state);

    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);

    ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0,
            get_gpu_descriptor_handle(&context, heap, 0));
    ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 1,
            ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(sampler_heap));
    ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 2,
            get_gpu_descriptor_handle(&context, heap, 2));

    ID3D12GraphicsCommandList_SetPipelineState(command_list, pipeline_state2);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, root_signature2);

    ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0,
            get_gpu_descriptor_handle(&context, heap, 0));
    ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 1,
            ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(sampler_heap));

    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);

    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, ARRAY_SIZE(heaps), heaps);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, pipeline_state2);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, root_signature2);

    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);

    ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0,
            get_gpu_descriptor_handle(&context, heap, 0));
    ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 1,
            ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(sampler_heap));

    ID3D12GraphicsCommandList_SetPipelineState(command_list, pipeline_state);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, root_signature);

    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);

    ID3D12PipelineState_Release(pipeline_state);
    ID3D12PipelineState_Release(pipeline_state2);
    ID3D12RootSignature_Release(root_signature);
    ID3D12RootSignature_Release(root_signature2);
    for (i = 0; i < ARRAY_SIZE(textures); ++i)
        ID3D12Resource_Release(textures[i]);
    ID3D12DescriptorHeap_Release(heap);
    ID3D12DescriptorHeap_Release(sampler_heap);
    destroy_test_context(&context);
}

static void test_copy_descriptors(void)
{
    struct data
    {
        unsigned int u[3];
        float f;
    };

    ID3D12DescriptorHeap *cpu_heap, *cpu_sampler_heap, *cpu_sampler_heap2;
    D3D12_CPU_DESCRIPTOR_HANDLE dst_handles[4], src_handles[4];
    ID3D12DescriptorHeap *heap, *sampler_heap, *heaps[2];
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    D3D12_DESCRIPTOR_RANGE descriptor_ranges[5];
    UINT dst_range_sizes[4], src_range_sizes[4];
    D3D12_UNORDERED_ACCESS_VIEW_DESC uav_desc;
    D3D12_CONSTANT_BUFFER_VIEW_DESC cbv_desc;
    D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc;
    ID3D12GraphicsCommandList *command_list;
    D3D12_ROOT_PARAMETER root_parameters[4];
    D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle;
    struct d3d12_resource_readback rb;
    ID3D12Resource *t[7], *u[3], *cb;
    struct depth_stencil_resource ds;
    D3D12_SAMPLER_DESC sampler_desc;
    struct test_context_desc desc;
    unsigned int descriptor_size;
    D3D12_SUBRESOURCE_DATA data;
    struct test_context context;
    ID3D12CommandQueue *queue;
    unsigned int sampler_size;
    ID3D12Device *device;
    unsigned int *result;
    unsigned int i;
    HRESULT hr;

    static const DWORD cs_code[] =
    {
#if 0
        struct data
        {
            uint3 u;
            float f;
        };

        cbuffer cb0
        {
            float f;
        };

        cbuffer cb1
        {
            uint u;
        };

        cbuffer cb2
        {
            int i;
        };

        SamplerState s0;
        SamplerState s1;
        SamplerState s2;
        SamplerComparisonState s3;

        Texture2D t0;
        Texture2D<uint> t1;
        Texture2D<int> t2;
        Buffer<float> t3;
        StructuredBuffer<float> t4;
        ByteAddressBuffer t5;
        Texture2D t6;

        RWByteAddressBuffer u0;
        RWStructuredBuffer<data> u1;

        RWByteAddressBuffer u2;

        [numthreads(1, 1, 1)]
        void main()
        {
            u2.Store(0 * 4, f);
            u2.Store(1 * 4, u);
            u2.Store(2 * 4, i);
            u2.Store(3 * 4, 0);

            u2.Store4( 4 * 4, t0.SampleLevel(s0, (float2)0, 0));
            u2.Store4( 8 * 4, t0.SampleLevel(s1, (float2)0, 0));
            u2.Store4(12 * 4, t0.SampleLevel(s2, (float2)0, 0));

            u2.Store(16 * 4, t1.Load((int3)0));
            u2.Store(17 * 4, t2.Load((int3)0));
            u2.Store(18 * 4, t3.Load(0));
            u2.Store(19 * 4, t4[0]);

            u2.Store4(20 * 4, t5.Load4(0));

            u2.Store4(24 * 4, t6.SampleCmpLevelZero(s3, (float2)0, 0.6f));
            u2.Store4(28 * 4, t6.SampleCmpLevelZero(s3, (float2)0, 0.4f));

            u2.Store2(32 * 4, u0.Load2(0));
            u2.Store2(34 * 4, u0.Load2(8));

            u2.Store3(36 * 4, u1[0].u);
            u2.Store4(39 * 4, u1[0].f);

            u2.Store(43 * 4, 0xdeadbeef);
        }
#endif
        0x43425844, 0x52d2c2d3, 0xaf60e190, 0xb897944f, 0x4a6a6653, 0x00000001, 0x00000650, 0x00000003,
        0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x000005fc, 0x00050050, 0x0000017f, 0x0100086a,
        0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x04000059, 0x00208e46, 0x00000001, 0x00000001,
        0x04000059, 0x00208e46, 0x00000002, 0x00000001, 0x0300005a, 0x00106000, 0x00000000, 0x0300005a,
        0x00106000, 0x00000001, 0x0300005a, 0x00106000, 0x00000002, 0x0300085a, 0x00106000, 0x00000003,
        0x04001858, 0x00107000, 0x00000000, 0x00005555, 0x04001858, 0x00107000, 0x00000001, 0x00004444,
        0x04001858, 0x00107000, 0x00000002, 0x00003333, 0x04000858, 0x00107000, 0x00000003, 0x00005555,
        0x040000a2, 0x00107000, 0x00000004, 0x00000004, 0x030000a1, 0x00107000, 0x00000005, 0x04001858,
        0x00107000, 0x00000006, 0x00005555, 0x0300009d, 0x0011e000, 0x00000000, 0x0400009e, 0x0011e000,
        0x00000001, 0x00000010, 0x0300009d, 0x0011e000, 0x00000002, 0x02000068, 0x00000002, 0x0400009b,
        0x00000001, 0x00000001, 0x00000001, 0x0600001c, 0x00100012, 0x00000000, 0x0020800a, 0x00000000,
        0x00000000, 0x06000036, 0x00100022, 0x00000000, 0x0020800a, 0x00000001, 0x00000000, 0x06000036,
        0x00100042, 0x00000000, 0x0020800a, 0x00000002, 0x00000000, 0x05000036, 0x00100082, 0x00000000,
        0x00004001, 0x00000000, 0x070000a6, 0x0011e0f2, 0x00000002, 0x00004001, 0x00000000, 0x00100e46,
        0x00000000, 0x90000048, 0x800000c2, 0x00155543, 0x001000f2, 0x00000000, 0x00004002, 0x00000000,
        0x00000000, 0x00000000, 0x00000000, 0x00107e46, 0x00000000, 0x00106000, 0x00000000, 0x00004001,
        0x00000000, 0x0500001c, 0x001000f2, 0x00000000, 0x00100e46, 0x00000000, 0x070000a6, 0x0011e0f2,
        0x00000002, 0x00004001, 0x00000010, 0x00100e46, 0x00000000, 0x90000048, 0x800000c2, 0x00155543,
        0x001000f2, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00107e46,
        0x00000000, 0x00106000, 0x00000001, 0x00004001, 0x00000000, 0x0500001c, 0x001000f2, 0x00000000,
        0x00100e46, 0x00000000, 0x070000a6, 0x0011e0f2, 0x00000002, 0x00004001, 0x00000020, 0x00100e46,
        0x00000000, 0x90000048, 0x800000c2, 0x00155543, 0x001000f2, 0x00000000, 0x00004002, 0x00000000,
        0x00000000, 0x00000000, 0x00000000, 0x00107e46, 0x00000000, 0x00106000, 0x00000002, 0x00004001,
        0x00000000, 0x0500001c, 0x001000f2, 0x00000000, 0x00100e46, 0x00000000, 0x070000a6, 0x0011e0f2,
        0x00000002, 0x00004001, 0x00000030, 0x00100e46, 0x00000000, 0x8c00002d, 0x80000042, 0x00155543,
        0x00100012, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00107e46,
        0x00000003, 0x0500001c, 0x00100042, 0x00000000, 0x0010000a, 0x00000000, 0x8b0000a7, 0x80002302,
        0x00199983, 0x00100012, 0x00000001, 0x00004001, 0x00000000, 0x00004001, 0x00000000, 0x00107006,
        0x00000004, 0x0500001c, 0x00100082, 0x00000000, 0x0010000a, 0x00000001, 0x8c00002d, 0x800000c2,
        0x00111103, 0x00100012, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
        0x00107e46, 0x00000001, 0x8c00002d, 0x800000c2, 0x000cccc3, 0x00100022, 0x00000000, 0x00004002,
        0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00107e16, 0x00000002, 0x070000a6, 0x0011e0f2,
        0x00000002, 0x00004001, 0x00000040, 0x00100e46, 0x00000000, 0x890000a5, 0x800002c2, 0x00199983,
        0x001000f2, 0x00000000, 0x00004001, 0x00000000, 0x00107e46, 0x00000005, 0x070000a6, 0x0011e0f2,
        0x00000002, 0x00004001, 0x00000050, 0x00100e46, 0x00000000, 0x90000047, 0x800000c2, 0x00155543,
        0x00100012, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00107006,
        0x00000006, 0x00106000, 0x00000003, 0x00004001, 0x3f19999a, 0x0500001c, 0x00100012, 0x00000000,
        0x0010000a, 0x00000000, 0x070000a6, 0x0011e0f2, 0x00000002, 0x00004001, 0x00000060, 0x00100006,
        0x00000000, 0x90000047, 0x800000c2, 0x00155543, 0x00100012, 0x00000000, 0x00004002, 0x00000000,
        0x00000000, 0x00000000, 0x00000000, 0x00107006, 0x00000006, 0x00106000, 0x00000003, 0x00004001,
        0x3ecccccd, 0x0500001c, 0x00100012, 0x00000000, 0x0010000a, 0x00000000, 0x070000a6, 0x0011e0f2,
        0x00000002, 0x00004001, 0x00000070, 0x00100006, 0x00000000, 0x890000a5, 0x800002c2, 0x00199983,
        0x00100032, 0x00000000, 0x00004001, 0x00000000, 0x0011e046, 0x00000000, 0x890000a5, 0x800002c2,
        0x00199983, 0x001000c2, 0x00000000, 0x00004001, 0x00000008, 0x0011e406, 0x00000000, 0x070000a6,
        0x0011e0f2, 0x00000002, 0x00004001, 0x00000080, 0x00100e46, 0x00000000, 0x8b0000a7, 0x80008302,
        0x00199983, 0x001000f2, 0x00000000, 0x00004001, 0x00000000, 0x00004001, 0x00000000, 0x0011ee46,
        0x00000001, 0x070000a6, 0x0011e072, 0x00000002, 0x00004001, 0x00000090, 0x00100246, 0x00000000,
        0x0500001c, 0x00100012, 0x00000000, 0x0010003a, 0x00000000, 0x070000a6, 0x0011e0f2, 0x00000002,
        0x00004001, 0x0000009c, 0x00100006, 0x00000000, 0x070000a6, 0x0011e012, 0x00000002, 0x00004001,
        0x000000ac, 0x00004001, 0xdeadbeef, 0x0100003e,
    };
    static const float cb0_data = 10.0f;
    static const UINT cb1_data = 11;
    static const INT cb2_data = -1;
    static const struct vec4 t0_data = {1.0f, 2.0f, 3.0f, 4.0f};
    static const UINT t1_data = 111;
    static const INT t2_data = 222;
    static const float t3_data = 333.3f;
    static const float t4_data = 44.44f;
    static const struct uvec4 t5_data = {50, 51, 52, 53};
    static const struct uvec4 u0_data = {10, 20, 30, 40};
    static const struct data u1_data = {{5, 6, 7}, 10.0f};

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

    descriptor_size = ID3D12Device_GetDescriptorHandleIncrementSize(device,
            D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
    sampler_size = ID3D12Device_GetDescriptorHandleIncrementSize(device,
            D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);

    cpu_sampler_heap = create_cpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, 2);
    cpu_sampler_heap2 = create_cpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, 2);
    sampler_heap = create_gpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, 4);

    cpu_heap = create_cpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 30);
    heap = create_gpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 30);

    /* create samplers */
    cpu_handle = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(cpu_sampler_heap);
    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;
    ID3D12Device_CreateSampler(context.device, &sampler_desc, cpu_handle);
    sampler_desc.Filter = D3D12_FILTER_COMPARISON_MIN_MAG_MIP_POINT;
    sampler_desc.ComparisonFunc = D3D12_COMPARISON_FUNC_GREATER;
    cpu_handle.ptr += sampler_size;
    ID3D12Device_CreateSampler(context.device, &sampler_desc, cpu_handle);

    /* create CBVs */
    cpu_handle = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(cpu_heap);
    cb = create_upload_buffer(context.device,
            3 * D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT, NULL);
    update_buffer_data(cb, 0, sizeof(cb0_data), &cb0_data);
    update_buffer_data(cb, D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT, sizeof(cb1_data), &cb1_data);
    update_buffer_data(cb, 2 * D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT, sizeof(cb2_data), &cb2_data);
    cbv_desc.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(cb);
    cbv_desc.SizeInBytes = D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT;
    for (i = 0; i < 3; ++i)
    {
        ID3D12Device_CreateConstantBufferView(context.device, &cbv_desc, cpu_handle);
        cbv_desc.BufferLocation += D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT;
        cpu_handle.ptr += descriptor_size;
    }

    /* create SRVs */
    cpu_handle = get_cpu_descriptor_handle(&context, cpu_heap, 10);

    t[0] = create_default_texture(context.device,
            1, 1, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D12_RESOURCE_STATE_COPY_DEST);
    data.pData = &t0_data;
    data.RowPitch = sizeof(t0_data);
    data.SlicePitch = data.RowPitch;
    upload_texture_data(t[0], &data, 1, queue, command_list);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, t[0],
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);

    t[1] = create_default_texture(context.device,
            1, 1, DXGI_FORMAT_R32_UINT, 0, D3D12_RESOURCE_STATE_COPY_DEST);
    data.pData = &t1_data;
    data.RowPitch = sizeof(t1_data);
    data.SlicePitch = data.RowPitch;
    upload_texture_data(t[1], &data, 1, queue, command_list);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, t[1],
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);

    t[2] = create_default_texture(context.device,
            1, 1, DXGI_FORMAT_R32_SINT, 0, D3D12_RESOURCE_STATE_COPY_DEST);
    data.pData = &t2_data;
    data.RowPitch = sizeof(t2_data);
    data.SlicePitch = data.RowPitch;
    upload_texture_data(t[2], &data, 1, queue, command_list);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, t[2],
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);

    t[3] = create_default_buffer(device, sizeof(t3_data),
            D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST);
    upload_buffer_data(t[3], 0, sizeof(t3_data), &t3_data, queue, command_list);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, t[3],
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);

    t[4] = create_default_buffer(device, sizeof(t4_data),
            D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST);
    upload_buffer_data(t[4], 0, sizeof(t4_data), &t4_data, queue, command_list);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, t[4],
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);

    t[5] = create_default_buffer(device, sizeof(t5_data),
            D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST);
    upload_buffer_data(t[5], 0, sizeof(t5_data), &t5_data, queue, command_list);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, t[5],
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);

    init_depth_stencil(&ds, device, 32, 32, 1, 1, DXGI_FORMAT_R32_TYPELESS, DXGI_FORMAT_D32_FLOAT, NULL);
    t[6] = ds.texture;
    ID3D12Resource_AddRef(t[6]);
    ID3D12GraphicsCommandList_ClearDepthStencilView(command_list, ds.dsv_handle,
            D3D12_CLEAR_FLAG_DEPTH, 0.5f, 0, 0, NULL);
    transition_resource_state(command_list, t[6],
            D3D12_RESOURCE_STATE_DEPTH_WRITE, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);

    for (i = 0; i < 3; ++i)
    {
        ID3D12Device_CreateShaderResourceView(device, t[i], NULL, cpu_handle);
        cpu_handle.ptr += descriptor_size;
    }

    memset(&srv_desc, 0, sizeof(srv_desc));
    srv_desc.Format = DXGI_FORMAT_R32_FLOAT;
    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 = 1;
    ID3D12Device_CreateShaderResourceView(device, t[3], &srv_desc, cpu_handle);
    cpu_handle.ptr += descriptor_size;

    srv_desc.Format = DXGI_FORMAT_UNKNOWN;
    srv_desc.Buffer.StructureByteStride = sizeof(t4_data);
    ID3D12Device_CreateShaderResourceView(device, t[4], &srv_desc, cpu_handle);
    cpu_handle.ptr += descriptor_size;

    srv_desc.Format = DXGI_FORMAT_R32_TYPELESS;
    srv_desc.Buffer.NumElements = 4;
    srv_desc.Buffer.StructureByteStride = 0;
    srv_desc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_RAW;
    ID3D12Device_CreateShaderResourceView(device, t[5], &srv_desc, cpu_handle);
    cpu_handle.ptr += descriptor_size;

    memset(&srv_desc, 0, sizeof(srv_desc));
    srv_desc.Format = DXGI_FORMAT_R32_FLOAT;
    srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
    srv_desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
    srv_desc.Texture2D.MipLevels = 1;
    ID3D12Device_CreateShaderResourceView(device, t[6], &srv_desc, cpu_handle);

    /* create UAVs */
    cpu_handle = get_cpu_descriptor_handle(&context, cpu_heap, 20);

    u[0] = create_default_buffer(device, sizeof(u0_data),
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_DEST);
    upload_buffer_data(u[0], 0, sizeof(u0_data), &u0_data, queue, command_list);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, u[0],
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
    u[1] = create_default_buffer(device, sizeof(struct uvec4),
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_DEST);
    upload_buffer_data(u[1], 0, sizeof(u1_data), &u1_data, queue, command_list);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, u[0],
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
    u[2] = create_default_buffer(device, 44 * 4,
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

    memset(&uav_desc, 0, sizeof(uav_desc));
    uav_desc.Format = DXGI_FORMAT_R32_TYPELESS;
    uav_desc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
    uav_desc.Buffer.FirstElement = 0;
    uav_desc.Buffer.NumElements = 4;
    uav_desc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_RAW;
    ID3D12Device_CreateUnorderedAccessView(device, u[0], NULL, &uav_desc, cpu_handle);
    cpu_handle.ptr += descriptor_size;

    uav_desc.Format = DXGI_FORMAT_UNKNOWN;
    uav_desc.Buffer.NumElements = 1;
    uav_desc.Buffer.StructureByteStride = sizeof(u1_data);
    uav_desc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_NONE;
    ID3D12Device_CreateUnorderedAccessView(device, u[1], NULL, &uav_desc, cpu_handle);
    cpu_handle.ptr += descriptor_size;

    uav_desc.Format = DXGI_FORMAT_R32_TYPELESS;
    uav_desc.Buffer.NumElements = 44;
    uav_desc.Buffer.StructureByteStride = 0;
    uav_desc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_RAW;
    ID3D12Device_CreateUnorderedAccessView(device, u[2], NULL, &uav_desc, cpu_handle);

    /* root signature */
    descriptor_ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_CBV;
    descriptor_ranges[0].NumDescriptors = 3;
    descriptor_ranges[0].BaseShaderRegister = 0;
    descriptor_ranges[0].RegisterSpace = 0;
    descriptor_ranges[0].OffsetInDescriptorsFromTableStart = 0;
    root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameters[0].DescriptorTable.NumDescriptorRanges = 1;
    root_parameters[0].DescriptorTable.pDescriptorRanges = &descriptor_ranges[0];
    root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    descriptor_ranges[1].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER;
    descriptor_ranges[1].NumDescriptors = 4;
    descriptor_ranges[1].BaseShaderRegister = 0;
    descriptor_ranges[1].RegisterSpace = 0;
    descriptor_ranges[1].OffsetInDescriptorsFromTableStart = 0;
    root_parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameters[1].DescriptorTable.NumDescriptorRanges = 1;
    root_parameters[1].DescriptorTable.pDescriptorRanges = &descriptor_ranges[1];
    root_parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    descriptor_ranges[2].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
    descriptor_ranges[2].NumDescriptors = 7;
    descriptor_ranges[2].BaseShaderRegister = 0;
    descriptor_ranges[2].RegisterSpace = 0;
    descriptor_ranges[2].OffsetInDescriptorsFromTableStart = 0;
    root_parameters[2].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameters[2].DescriptorTable.NumDescriptorRanges = 1;
    root_parameters[2].DescriptorTable.pDescriptorRanges = &descriptor_ranges[2];
    root_parameters[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    descriptor_ranges[3].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
    descriptor_ranges[3].NumDescriptors = 2;
    descriptor_ranges[3].BaseShaderRegister = 0;
    descriptor_ranges[3].RegisterSpace = 0;
    descriptor_ranges[3].OffsetInDescriptorsFromTableStart = 0;
    descriptor_ranges[4].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
    descriptor_ranges[4].NumDescriptors = 1;
    descriptor_ranges[4].BaseShaderRegister = 2;
    descriptor_ranges[4].RegisterSpace = 0;
    descriptor_ranges[4].OffsetInDescriptorsFromTableStart = 2;
    root_parameters[3].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameters[3].DescriptorTable.NumDescriptorRanges = 2;
    root_parameters[3].DescriptorTable.pDescriptorRanges = &descriptor_ranges[3];
    root_parameters[3].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    memset(&root_signature_desc, 0, sizeof(root_signature_desc));
    root_signature_desc.NumParameters = 4;
    root_signature_desc.pParameters = root_parameters;
    hr = create_root_signature(device, &root_signature_desc, &context.root_signature);
    ok(SUCCEEDED(hr), "Failed to create root signature, hr %#x.\n", hr);

    context.pipeline_state = create_compute_pipeline_state(device, context.root_signature,
            shader_bytecode(cs_code, sizeof(cs_code)));

    /* copy descriptors */
    dst_handles[0] = get_cpu_descriptor_handle(&context, heap, 5);
    dst_range_sizes[0] = 2;
    src_handles[0] = get_cpu_descriptor_handle(&context, cpu_heap, 0);
    src_range_sizes[0] = 2;
    /* cb0-cb1 */
    ID3D12Device_CopyDescriptors(device, 1, dst_handles, dst_range_sizes,
            1, src_handles, src_range_sizes, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
    dst_handles[0] = get_cpu_descriptor_handle(&context, heap, 7);
    dst_range_sizes[0] = 1;
    src_handles[0] = get_cpu_descriptor_handle(&context, cpu_heap, 2);
    src_range_sizes[0] = 1;
    /* cb2 */
    ID3D12Device_CopyDescriptors(device, 1, dst_handles, dst_range_sizes,
            1, src_handles, src_range_sizes, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);

    ID3D12Device_CopyDescriptorsSimple(device, 2,
            get_cpu_sampler_handle(&context, cpu_sampler_heap2, 0),
            get_cpu_sampler_handle(&context, cpu_sampler_heap, 0),
            D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);

    dst_handles[0] = get_cpu_sampler_handle(&context, sampler_heap, 0);
    dst_range_sizes[0] = 4;
    src_handles[0] = get_cpu_sampler_handle(&context, cpu_sampler_heap2, 0);
    src_handles[1] = get_cpu_sampler_handle(&context, cpu_sampler_heap2, 0);
    src_handles[2] = get_cpu_sampler_handle(&context, cpu_sampler_heap2, 0);
    src_handles[3] = get_cpu_sampler_handle(&context, cpu_sampler_heap2, 1);
    /* s0-s3 */
    ID3D12Device_CopyDescriptors(device, 1, dst_handles, dst_range_sizes,
            4, src_handles, NULL, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);

    dst_handles[0] = get_cpu_descriptor_handle(&context, heap, 9);
    dst_range_sizes[0] = 4;
    dst_handles[1] = get_cpu_descriptor_handle(&context, heap, 9);
    dst_range_sizes[1] = 0;
    dst_handles[2] = get_cpu_descriptor_handle(&context, heap, 13);
    dst_range_sizes[2] = 3;
    dst_handles[3] = get_cpu_descriptor_handle(&context, heap, 13);
    dst_range_sizes[3] = 0;
    src_handles[0] = get_cpu_descriptor_handle(&context, cpu_heap, 10);
    src_range_sizes[0] = 8;
    /* t0-t6 */
    ID3D12Device_CopyDescriptors(device, 4, dst_handles, dst_range_sizes,
            1, src_handles, src_range_sizes, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);

    /* copy 1 uninitialized descriptor (19) */
    dst_handles[0] = get_cpu_descriptor_handle(&context, heap, 19);
    dst_range_sizes[0] = 2;
    dst_handles[1] = get_cpu_descriptor_handle(&context, heap, 21);
    dst_range_sizes[1] = 1;
    src_handles[0] = get_cpu_descriptor_handle(&context, cpu_heap, 19);
    src_range_sizes[0] = 2;
    src_handles[1] = get_cpu_descriptor_handle(&context, cpu_heap, 21);
    src_range_sizes[1] = 1;
    /* u1-u2 */
    ID3D12Device_CopyDescriptors(device, 2, dst_handles, dst_range_sizes,
            2, src_handles, src_range_sizes, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);

    /* u2 */
    ID3D12Device_CopyDescriptorsSimple(device, 1,
            get_cpu_descriptor_handle(&context, heap, 22),
            get_cpu_descriptor_handle(&context, cpu_heap, 22),
            D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);

    /* range sizes equal to 0 */
    dst_handles[0] = get_cpu_descriptor_handle(&context, heap, 19);
    dst_range_sizes[0] = 0;
    dst_handles[1] = get_cpu_descriptor_handle(&context, heap, 19);
    dst_range_sizes[1] = 0;
    src_handles[0] = get_cpu_descriptor_handle(&context, cpu_heap, 0);
    src_range_sizes[0] = 1;
    src_handles[1] = get_cpu_descriptor_handle(&context, cpu_heap, 0);
    src_range_sizes[1] = 4;
    ID3D12Device_CopyDescriptors(device, 2, dst_handles, dst_range_sizes,
            2, src_handles, src_range_sizes, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
    dst_handles[0] = get_cpu_descriptor_handle(&context, heap, 19);
    dst_range_sizes[0] = 4;
    dst_handles[1] = get_cpu_descriptor_handle(&context, heap, 19);
    dst_range_sizes[1] = 4;
    src_handles[0] = get_cpu_descriptor_handle(&context, cpu_heap, 0);
    src_range_sizes[0] = 0;
    src_handles[1] = get_cpu_descriptor_handle(&context, cpu_heap, 0);
    src_range_sizes[1] = 0;
    ID3D12Device_CopyDescriptors(device, 2, dst_handles, dst_range_sizes,
            2, src_handles, src_range_sizes, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);

    ID3D12GraphicsCommandList_SetComputeRootSignature(command_list, context.root_signature);
    heaps[0] = sampler_heap; heaps[1] = heap;
    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, ARRAY_SIZE(heaps), heaps);
    ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(command_list, 0,
            get_gpu_descriptor_handle(&context, heap, 5));
    ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(command_list, 1,
            get_gpu_sampler_handle(&context, sampler_heap, 0));
    ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(command_list, 2,
            get_gpu_descriptor_handle(&context, heap, 9));
    ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(command_list, 3,
            get_gpu_descriptor_handle(&context, heap, 20));

    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_Dispatch(command_list, 1, 1, 1);

    transition_sub_resource_state(command_list, u[2], 0,
            D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_buffer_readback_with_command_list(u[2], DXGI_FORMAT_R32_UINT, &rb, queue, command_list);
    result = get_readback_data(&rb.rb, 0, 0, 0, sizeof(*result));
    ok(result[ 0] == cb0_data, "Got unexpected value %#x.\n", result[0]);
    ok(result[ 1] == cb1_data, "Got unexpected value %#x.\n", result[1]);
    ok(result[ 2] == cb2_data, "Got unexpected value %#x.\n", result[2]);
    ok(result[ 3] == 0, "Got unexpected value %#x.\n", result[3]);
    ok(result[ 4] == t0_data.x, "Got unexpected value %#x.\n", result[4]);
    ok(result[ 5] == t0_data.y, "Got unexpected value %#x.\n", result[5]);
    ok(result[ 6] == t0_data.z, "Got unexpected value %#x.\n", result[6]);
    ok(result[ 7] == t0_data.w, "Got unexpected value %#x.\n", result[7]);
    ok(result[ 8] == t0_data.x, "Got unexpected value %#x.\n", result[8]);
    ok(result[ 9] == t0_data.y, "Got unexpected value %#x.\n", result[9]);
    ok(result[10] == t0_data.z, "Got unexpected value %#x.\n", result[10]);
    ok(result[11] == t0_data.w, "Got unexpected value %#x.\n", result[11]);
    ok(result[12] == t0_data.x, "Got unexpected value %#x.\n", result[12]);
    ok(result[13] == t0_data.y, "Got unexpected value %#x.\n", result[13]);
    ok(result[14] == t0_data.z, "Got unexpected value %#x.\n", result[14]);
    ok(result[15] == t0_data.w, "Got unexpected value %#x.\n", result[15]);
    ok(result[16] == t1_data, "Got unexpected value %#x.\n", result[16]);
    ok(result[17] == t2_data, "Got unexpected value %#x.\n", result[17]);
    ok(result[18] == (unsigned int)t3_data, "Got unexpected value %#x.\n", result[18]);
    ok(result[19] == (unsigned int)t4_data, "Got unexpected value %#x.\n", result[19]);
    ok(result[20] == t5_data.x, "Got unexpected value %#x.\n", result[20]);
    ok(result[21] == t5_data.y, "Got unexpected value %#x.\n", result[21]);
    ok(result[22] == t5_data.z, "Got unexpected value %#x.\n", result[22]);
    ok(result[23] == t5_data.w, "Got unexpected value %#x.\n", result[23]);
    ok(result[24] == 1, "Got unexpected value %#x.\n", result[24]);
    ok(result[25] == 1, "Got unexpected value %#x.\n", result[25]);
    ok(result[26] == 1, "Got unexpected value %#x.\n", result[26]);
    ok(result[27] == 1, "Got unexpected value %#x.\n", result[27]);
    ok(result[28] == 0, "Got unexpected value %#x.\n", result[28]);
    ok(result[29] == 0, "Got unexpected value %#x.\n", result[29]);
    ok(result[30] == 0, "Got unexpected value %#x.\n", result[30]);
    ok(result[31] == 0, "Got unexpected value %#x.\n", result[31]);
    ok(result[32] == u0_data.x, "Got unexpected value %#x.\n", result[32]);
    ok(result[33] == u0_data.y, "Got unexpected value %#x.\n", result[33]);
    ok(result[34] == u0_data.z, "Got unexpected value %#x.\n", result[34]);
    ok(result[35] == u0_data.w, "Got unexpected value %#x.\n", result[35]);
    ok(result[36] == u1_data.u[0], "Got unexpected value %#x.\n", result[36]);
    ok(result[37] == u1_data.u[1], "Got unexpected value %#x.\n", result[37]);
    ok(result[38] == u1_data.u[2], "Got unexpected value %#x.\n", result[38]);
    ok(result[39] == u1_data.f, "Got unexpected value %#x.\n", result[39]);
    ok(result[40] == u1_data.f, "Got unexpected value %#x.\n", result[40]);
    ok(result[41] == u1_data.f, "Got unexpected value %#x.\n", result[41]);
    ok(result[42] == u1_data.f, "Got unexpected value %#x.\n", result[42]);
    ok(result[43] == 0xdeadbeef, "Got unexpected value %#x.\n", result[43]);
    assert(rb.rb.width == 44);
    release_resource_readback(&rb);

    ID3D12DescriptorHeap_Release(cpu_heap);
    ID3D12DescriptorHeap_Release(cpu_sampler_heap);
    ID3D12DescriptorHeap_Release(cpu_sampler_heap2);
    ID3D12DescriptorHeap_Release(heap);
    ID3D12DescriptorHeap_Release(sampler_heap);
    ID3D12Resource_Release(cb);
    for (i = 0; i < ARRAY_SIZE(t); ++i)
        ID3D12Resource_Release(t[i]);
    for (i = 0; i < ARRAY_SIZE(u); ++i)
        ID3D12Resource_Release(u[i]);
    destroy_depth_stencil(&ds);
    destroy_test_context(&context);
}

static void test_copy_descriptors_range_sizes(void)
{
    D3D12_CPU_DESCRIPTOR_HANDLE dst_handles[1], src_handles[1];
    D3D12_CPU_DESCRIPTOR_HANDLE green_handle, blue_handle;
    ID3D12Resource *green_texture, *blue_texture;
    UINT dst_range_sizes[1], src_range_sizes[1];
    ID3D12GraphicsCommandList *command_list;
    struct d3d12_resource_readback rb;
    ID3D12DescriptorHeap *cpu_heap;
    struct test_context_desc desc;
    D3D12_SUBRESOURCE_DATA data;
    struct test_context context;
    ID3D12DescriptorHeap *heap;
    ID3D12CommandQueue *queue;
    ID3D12Device *device;
    unsigned int i;
    D3D12_BOX box;

    static const DWORD ps_code[] =
    {
#if 0
        Texture2D t;
        SamplerState s;

        float4 main(float4 position : SV_POSITION) : SV_Target
        {
            float2 p;

            p.x = position.x / 32.0f;
            p.y = position.y / 32.0f;
            return t.Sample(s, p);
        }
#endif
        0x43425844, 0x7a0c3929, 0x75ff3ca4, 0xccb318b2, 0xe6965b4c, 0x00000001, 0x00000140, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000030f, 0x505f5653, 0x5449534f, 0x004e4f49,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x000000a4, 0x00000050,
        0x00000029, 0x0100086a, 0x0300005a, 0x00106000, 0x00000000, 0x04001858, 0x00107000, 0x00000000,
        0x00005555, 0x04002064, 0x00101032, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x02000068, 0x00000001, 0x0a000038, 0x00100032, 0x00000000, 0x00101046, 0x00000000, 0x00004002,
        0x3d000000, 0x3d000000, 0x00000000, 0x00000000, 0x8b000045, 0x800000c2, 0x00155543, 0x001020f2,
        0x00000000, 0x00100046, 0x00000000, 0x00107e46, 0x00000000, 0x00106000, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    static const struct vec4 green = {0.0f, 1.0f, 0.0f, 1.0f};
    static const struct vec4 blue = {0.0f, 0.0f, 1.0f, 1.0f};

    memset(&desc, 0, sizeof(desc));
    desc.rt_width = desc.rt_height = 6;
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    cpu_heap = create_cpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 10);
    heap = create_gpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 8);

    green_handle = get_cpu_descriptor_handle(&context, cpu_heap, 0);
    blue_handle = get_cpu_descriptor_handle(&context, cpu_heap, 1);

    green_texture = create_default_texture(context.device,
            1, 1, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D12_RESOURCE_STATE_COPY_DEST);
    data.pData = &green;
    data.RowPitch = sizeof(green);
    data.SlicePitch = data.RowPitch;
    upload_texture_data(green_texture, &data, 1, queue, command_list);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, green_texture,
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
    ID3D12Device_CreateShaderResourceView(device, green_texture, NULL, green_handle);

    blue_texture = create_default_texture(context.device,
            1, 1, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D12_RESOURCE_STATE_COPY_DEST);
    data.pData = &blue;
    data.RowPitch = sizeof(blue);
    data.SlicePitch = data.RowPitch;
    upload_texture_data(blue_texture, &data, 1, queue, command_list);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, blue_texture,
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
    ID3D12Device_CreateShaderResourceView(device, blue_texture, NULL, blue_handle);

    context.root_signature = create_texture_root_signature(context.device,
            D3D12_SHADER_VISIBILITY_PIXEL, 0, 0);
    context.pipeline_state = create_pipeline_state(context.device,
            context.root_signature, context.render_target_desc.Format, NULL, &ps, NULL);

    /* copy descriptors */
    dst_handles[0] = get_cpu_descriptor_handle(&context, heap, 1);
    dst_range_sizes[0] = 1;
    src_handles[0] = blue_handle;
    src_range_sizes[0] = 1;
    ID3D12Device_CopyDescriptors(device, 1, dst_handles, dst_range_sizes,
            1, src_handles, src_range_sizes, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);

    dst_handles[0] = get_cpu_descriptor_handle(&context, heap, 2);
    dst_range_sizes[0] = 1;
    src_handles[0] = green_handle;
    src_range_sizes[0] = 1;
    ID3D12Device_CopyDescriptors(device, 1, dst_handles, dst_range_sizes,
            1, src_handles, src_range_sizes, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);

    dst_handles[0] = get_cpu_descriptor_handle(&context, heap, 3);
    src_handles[0] = blue_handle;
    src_range_sizes[0] = 1;
    ID3D12Device_CopyDescriptors(device, 1, dst_handles, NULL,
            1, src_handles, src_range_sizes, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);

    dst_handles[0] = get_cpu_descriptor_handle(&context, heap, 4);
    src_handles[0] = green_handle;
    src_range_sizes[0] = 1;
    ID3D12Device_CopyDescriptors(device, 1, dst_handles, NULL,
            1, src_handles, src_range_sizes, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);

    dst_handles[0] = get_cpu_descriptor_handle(&context, heap, 5);
    src_handles[0] = blue_handle;
    ID3D12Device_CopyDescriptors(device, 1, dst_handles, NULL,
            1, src_handles, NULL, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);

    dst_handles[0] = get_cpu_descriptor_handle(&context, heap, 0);
    src_handles[0] = green_handle;
    ID3D12Device_CopyDescriptors(device, 1, dst_handles, NULL,
            1, src_handles, NULL, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &heap);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);

    for (i = 0; i < desc.rt_width; ++i)
    {
        ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0,
                get_gpu_descriptor_handle(&context, heap, i));
        set_viewport(&context.viewport, i, 0.0f, 1.0f, desc.rt_height, 0.0f, 1.0f);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);
    }

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);

    get_resource_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
    for (i = 0; i < desc.rt_width; ++i)
    {
        set_box(&box, i, 0, 0, i + 1, desc.rt_height, 1);
        check_readback_data_uint(&rb.rb, &box, i % 2 ? 0xffff0000 : 0xff00ff00, 0);
    }
    release_resource_readback(&rb);

    ID3D12DescriptorHeap_Release(cpu_heap);
    ID3D12DescriptorHeap_Release(heap);
    ID3D12Resource_Release(blue_texture);
    ID3D12Resource_Release(green_texture);
    destroy_test_context(&context);
}

static void test_descriptors_visibility(void)
{
    ID3D12Resource *vs_raw_buffer, *ps_raw_buffer;
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    D3D12_DESCRIPTOR_RANGE descriptor_ranges[2];
    D3D12_STATIC_SAMPLER_DESC sampler_desc[2];
    ID3D12Resource *vs_texture, *ps_texture;
    ID3D12GraphicsCommandList *command_list;
    D3D12_ROOT_PARAMETER root_parameters[6];
    ID3D12Resource *vs_cb, *ps_cb;
    struct test_context_desc desc;
    D3D12_SUBRESOURCE_DATA data;
    struct test_context context;
    ID3D12DescriptorHeap *heap;
    ID3D12CommandQueue *queue;
    ID3D12Device *device;
    HRESULT hr;

    static const DWORD vs_code[] =
    {
#if 0
        ByteAddressBuffer b;
        Texture2D t;
        SamplerState s;

        float4 cb;

        float4 main(uint id : SV_VertexID) : SV_Position
        {
            float2 coords = float2((id << 1) & 2, id & 2);
            uint i;

            if (cb.x != 4.0 || cb.y != 8.0 || cb.z != 16.0 || cb.w != 32.0)
                return (float4)0;

            for (i = 0; i <= 6; ++i)
            {
                if (b.Load(4 * i) != i)
                    return (float4)0;
            }

            if (any(t.SampleLevel(s, (float2)0, 0) != float4(1.0, 1.0, 0.0, 1.0)))
                return (float4)0;

            return float4(coords * float2(2, -2) + float2(-1, 1), 0, 1);
        }
#endif
        0x43425844, 0x046e4d13, 0xd2103a18, 0x8576703b, 0x6f58933a, 0x00000001, 0x0000043c, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000006, 0x00000001, 0x00000000, 0x00000101, 0x565f5653, 0x65747265, 0x00444978,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000001, 0x00000003,
        0x00000000, 0x0000000f, 0x505f5653, 0x7469736f, 0x006e6f69, 0x58454853, 0x000003a0, 0x00010050,
        0x000000e8, 0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x0300005a, 0x00106000,
        0x00000000, 0x030000a1, 0x00107000, 0x00000000, 0x04001858, 0x00107000, 0x00000001, 0x00005555,
        0x04000060, 0x00101012, 0x00000000, 0x00000006, 0x04000067, 0x001020f2, 0x00000000, 0x00000001,
        0x02000068, 0x00000002, 0x0b000039, 0x001000f2, 0x00000000, 0x00208e46, 0x00000000, 0x00000000,
        0x00004002, 0x40800000, 0x41000000, 0x41800000, 0x42000000, 0x0700003c, 0x00100012, 0x00000000,
        0x0010001a, 0x00000000, 0x0010000a, 0x00000000, 0x0700003c, 0x00100012, 0x00000000, 0x0010002a,
        0x00000000, 0x0010000a, 0x00000000, 0x0700003c, 0x00100012, 0x00000000, 0x0010003a, 0x00000000,
        0x0010000a, 0x00000000, 0x0304001f, 0x0010000a, 0x00000000, 0x08000036, 0x001020f2, 0x00000000,
        0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0100003e, 0x01000015, 0x05000036,
        0x00100012, 0x00000000, 0x00004001, 0x00000000, 0x01000030, 0x0700004f, 0x00100022, 0x00000000,
        0x00004001, 0x00000006, 0x0010000a, 0x00000000, 0x03040003, 0x0010001a, 0x00000000, 0x07000029,
        0x00100022, 0x00000000, 0x0010000a, 0x00000000, 0x00004001, 0x00000002, 0x890000a5, 0x800002c2,
        0x00199983, 0x00100022, 0x00000000, 0x0010001a, 0x00000000, 0x00107006, 0x00000000, 0x07000027,
        0x00100022, 0x00000000, 0x0010001a, 0x00000000, 0x0010000a, 0x00000000, 0x0304001f, 0x0010001a,
        0x00000000, 0x08000036, 0x001020f2, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000,
        0x00000000, 0x0100003e, 0x01000015, 0x0700001e, 0x00100012, 0x00000000, 0x0010000a, 0x00000000,
        0x00004001, 0x00000001, 0x01000016, 0x90000048, 0x800000c2, 0x00155543, 0x001000f2, 0x00000000,
        0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00107e46, 0x00000001, 0x00106000,
        0x00000000, 0x00004001, 0x00000000, 0x0a000039, 0x001000f2, 0x00000000, 0x00100e46, 0x00000000,
        0x00004002, 0x3f800000, 0x3f800000, 0x00000000, 0x3f800000, 0x0700003c, 0x00100032, 0x00000000,
        0x00100ae6, 0x00000000, 0x00100046, 0x00000000, 0x0700003c, 0x00100012, 0x00000000, 0x0010001a,
        0x00000000, 0x0010000a, 0x00000000, 0x0304001f, 0x0010000a, 0x00000000, 0x08000036, 0x001020f2,
        0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0100003e, 0x01000015,
        0x0b00008c, 0x00100012, 0x00000000, 0x00004001, 0x00000001, 0x00004001, 0x00000001, 0x0010100a,
        0x00000000, 0x00004001, 0x00000000, 0x07000001, 0x00100022, 0x00000000, 0x0010100a, 0x00000000,
        0x00004001, 0x00000002, 0x05000056, 0x00100032, 0x00000001, 0x00100046, 0x00000000, 0x0f000032,
        0x00102032, 0x00000000, 0x00100046, 0x00000001, 0x00004002, 0x40000000, 0xc0000000, 0x00000000,
        0x00000000, 0x00004002, 0xbf800000, 0x3f800000, 0x00000000, 0x00000000, 0x08000036, 0x001020c2,
        0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x3f800000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE vs = {vs_code, sizeof(vs_code)};
    static const DWORD ps_code[] =
    {
#if 0
        ByteAddressBuffer b;
        Texture2D t;
        SamplerState s;

        float4 cb;

        float4 main(float4 position : SV_POSITION) : SV_Target
        {
            if (cb.x != 1.0 || cb.y != 2.0 || cb.z != 3.0 || cb.w != 4.0)
                return float4(1.0, 0.0, 0.0, 1.0);

            if (b.Load(0) != 2 || b.Load(4) != 4 || b.Load(8) != 8)
                return float4(1.0, 0.0, 0.0, 1.0);

            return t.Sample(s, float2(position.x / 32.0, position.y / 32.0));
        }
#endif
        0x43425844, 0x1b1aafc1, 0xeab215f6, 0x77d65b25, 0x03cbe695, 0x00000001, 0x000002dc, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000030f, 0x505f5653, 0x5449534f, 0x004e4f49,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000240, 0x00000050,
        0x00000090, 0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x0300005a, 0x00106000,
        0x00000000, 0x030000a1, 0x00107000, 0x00000000, 0x04001858, 0x00107000, 0x00000001, 0x00005555,
        0x04002064, 0x00101032, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000, 0x02000068,
        0x00000001, 0x0b000039, 0x001000f2, 0x00000000, 0x00208e46, 0x00000000, 0x00000000, 0x00004002,
        0x3f800000, 0x40000000, 0x40400000, 0x40800000, 0x0700003c, 0x00100012, 0x00000000, 0x0010001a,
        0x00000000, 0x0010000a, 0x00000000, 0x0700003c, 0x00100012, 0x00000000, 0x0010002a, 0x00000000,
        0x0010000a, 0x00000000, 0x0700003c, 0x00100012, 0x00000000, 0x0010003a, 0x00000000, 0x0010000a,
        0x00000000, 0x0304001f, 0x0010000a, 0x00000000, 0x08000036, 0x001020f2, 0x00000000, 0x00004002,
        0x3f800000, 0x00000000, 0x00000000, 0x3f800000, 0x0100003e, 0x01000015, 0x890000a5, 0x800002c2,
        0x00199983, 0x00100072, 0x00000000, 0x00004001, 0x00000000, 0x00107246, 0x00000000, 0x0a000027,
        0x00100072, 0x00000000, 0x00100246, 0x00000000, 0x00004002, 0x00000002, 0x00000004, 0x00000008,
        0x00000000, 0x0700003c, 0x00100012, 0x00000000, 0x0010001a, 0x00000000, 0x0010000a, 0x00000000,
        0x0700003c, 0x00100012, 0x00000000, 0x0010002a, 0x00000000, 0x0010000a, 0x00000000, 0x0304001f,
        0x0010000a, 0x00000000, 0x08000036, 0x001020f2, 0x00000000, 0x00004002, 0x3f800000, 0x00000000,
        0x00000000, 0x3f800000, 0x0100003e, 0x01000015, 0x0a000038, 0x00100032, 0x00000000, 0x00101046,
        0x00000000, 0x00004002, 0x3d000000, 0x3d000000, 0x00000000, 0x00000000, 0x8b000045, 0x800000c2,
        0x00155543, 0x001000f2, 0x00000000, 0x00100046, 0x00000000, 0x00107e46, 0x00000001, 0x00106000,
        0x00000000, 0x05000036, 0x001020f2, 0x00000000, 0x00100e46, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};
    static const struct vec4 vs_cb_data = {4.0f, 8.0f, 16.0f, 32.0f};
    static const struct vec4 ps_cb_data = {1.0f, 2.0f, 3.0f, 4.0f};
    static const uint32_t vs_buffer_data[] = {0, 1, 2, 3, 4, 5, 6};
    static const uint32_t ps_buffer_data[] = {2, 4, 8};
    static const float vs_texture_data[] = {1.0f, 1.0f, 0.0f, 1.0f};
    static const float ps_texture_data[] = {0.0f, 1.0f, 0.0f, 1.0f};
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};

    memset(&desc, 0, sizeof(desc));
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    sampler_desc[0].Filter = D3D12_FILTER_MIN_MAG_MIP_POINT;
    sampler_desc[0].AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
    sampler_desc[0].AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
    sampler_desc[0].AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
    sampler_desc[0].MipLODBias = 0.0f;
    sampler_desc[0].MaxAnisotropy = 0;
    sampler_desc[0].ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER;
    sampler_desc[0].BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE;
    sampler_desc[0].MinLOD = 0.0f;
    sampler_desc[0].MaxLOD = 0.0f;
    sampler_desc[0].ShaderRegister = 0;
    sampler_desc[0].RegisterSpace = 0;
    sampler_desc[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;

    sampler_desc[1] = sampler_desc[0];
    sampler_desc[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;

    root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
    root_parameters[0].Descriptor.ShaderRegister = 0;
    root_parameters[0].Descriptor.RegisterSpace = 0;
    root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;
    root_parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
    root_parameters[1].Descriptor.ShaderRegister = 0;
    root_parameters[1].Descriptor.RegisterSpace = 0;
    root_parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
    root_parameters[2].ParameterType = D3D12_ROOT_PARAMETER_TYPE_SRV;
    root_parameters[2].Descriptor.ShaderRegister = 0;
    root_parameters[2].Descriptor.RegisterSpace = 0;
    root_parameters[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;
    root_parameters[3].ParameterType = D3D12_ROOT_PARAMETER_TYPE_SRV;
    root_parameters[3].Descriptor.ShaderRegister = 0;
    root_parameters[3].Descriptor.RegisterSpace = 0;
    root_parameters[3].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
    descriptor_ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
    descriptor_ranges[0].NumDescriptors = 1;
    descriptor_ranges[0].BaseShaderRegister = 1;
    descriptor_ranges[0].RegisterSpace = 0;
    descriptor_ranges[0].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
    root_parameters[4].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameters[4].DescriptorTable.NumDescriptorRanges = 1;
    root_parameters[4].DescriptorTable.pDescriptorRanges = &descriptor_ranges[0];
    root_parameters[4].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;
    descriptor_ranges[1].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
    descriptor_ranges[1].NumDescriptors = 1;
    descriptor_ranges[1].BaseShaderRegister = 1;
    descriptor_ranges[1].RegisterSpace = 0;
    descriptor_ranges[1].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
    root_parameters[5].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameters[5].DescriptorTable.NumDescriptorRanges = 1;
    root_parameters[5].DescriptorTable.pDescriptorRanges = &descriptor_ranges[1];
    root_parameters[5].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
    memset(&root_signature_desc, 0, sizeof(root_signature_desc));
    root_signature_desc.NumParameters = 6;
    root_signature_desc.pParameters = root_parameters;
    root_signature_desc.NumStaticSamplers = 2;
    root_signature_desc.pStaticSamplers = sampler_desc;
    hr = create_root_signature(device, &root_signature_desc, &context.root_signature);
    ok(SUCCEEDED(hr), "Failed to create root signature, hr %#x.\n", hr);

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

    context.pipeline_state = create_pipeline_state(device,
            context.root_signature, context.render_target_desc.Format,
            &vs, &ps, NULL);

    vs_cb = create_upload_buffer(device, sizeof(vs_cb_data), &vs_cb_data);
    ps_cb = create_upload_buffer(device, sizeof(ps_cb_data), &ps_cb_data);

    vs_raw_buffer = create_upload_buffer(device, sizeof(vs_buffer_data), vs_buffer_data);
    ps_raw_buffer = create_upload_buffer(device, sizeof(ps_buffer_data), ps_buffer_data);

    vs_texture = create_default_texture(device,
            1, 1, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D12_RESOURCE_STATE_COPY_DEST);
    data.pData = vs_texture_data;
    data.RowPitch = sizeof(vs_texture_data);
    data.SlicePitch = data.RowPitch;
    upload_texture_data(vs_texture, &data, 1, queue, command_list);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, vs_texture,
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);

    ps_texture = create_default_texture(device,
            1, 1, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D12_RESOURCE_STATE_COPY_DEST);
    data.pData = ps_texture_data;
    data.RowPitch = sizeof(ps_texture_data);
    data.SlicePitch = data.RowPitch;
    upload_texture_data(ps_texture, &data, 1, queue, command_list);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, ps_texture,
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);

    heap = create_gpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 2);
    ID3D12Device_CreateShaderResourceView(device, vs_texture, NULL,
            get_cpu_descriptor_handle(&context, heap, 0));
    ID3D12Device_CreateShaderResourceView(device, ps_texture, NULL,
            get_cpu_descriptor_handle(&context, heap, 1));

    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &heap);
    ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list,
            0, ID3D12Resource_GetGPUVirtualAddress(vs_cb));
    ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list,
            1, ID3D12Resource_GetGPUVirtualAddress(ps_cb));
    ID3D12GraphicsCommandList_SetGraphicsRootShaderResourceView(command_list,
            2, ID3D12Resource_GetGPUVirtualAddress(vs_raw_buffer));
    ID3D12GraphicsCommandList_SetGraphicsRootShaderResourceView(command_list,
            3, ID3D12Resource_GetGPUVirtualAddress(ps_raw_buffer));
    ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list,
            4, get_gpu_descriptor_handle(&context, heap, 0));
    ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list,
            5, get_gpu_descriptor_handle(&context, heap, 1));

    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);

    ID3D12Resource_Release(vs_cb);
    ID3D12Resource_Release(ps_cb);
    ID3D12Resource_Release(vs_texture);
    ID3D12Resource_Release(ps_texture);
    ID3D12Resource_Release(vs_raw_buffer);
    ID3D12Resource_Release(ps_raw_buffer);
    ID3D12DescriptorHeap_Release(heap);
    destroy_test_context(&context);
}

static void test_create_null_descriptors(void)
{
    D3D12_UNORDERED_ACCESS_VIEW_DESC uav_desc;
    D3D12_CONSTANT_BUFFER_VIEW_DESC cbv_desc;
    D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc;
    D3D12_DESCRIPTOR_HEAP_DESC heap_desc;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12DescriptorHeap *heap;
    ID3D12Device *device;
    HRESULT hr;

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

    heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
    heap_desc.NumDescriptors = 16;
    heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
    heap_desc.NodeMask = 0;
    hr = ID3D12Device_CreateDescriptorHeap(device, &heap_desc,
            &IID_ID3D12DescriptorHeap, (void **)&heap);
    ok(SUCCEEDED(hr), "Failed to create descriptor heap, hr %#x.\n", hr);

    cbv_desc.BufferLocation = 0;
    cbv_desc.SizeInBytes = 0;
    ID3D12Device_CreateConstantBufferView(device, &cbv_desc,
            get_cpu_descriptor_handle(&context, heap, 0));

    memset(&srv_desc, 0, sizeof(srv_desc));
    srv_desc.Format = DXGI_FORMAT_R32_TYPELESS;
    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 = 1;
    srv_desc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_RAW;
    ID3D12Device_CreateShaderResourceView(device, NULL, &srv_desc,
            get_cpu_descriptor_handle(&context, heap, 1));

    memset(&srv_desc, 0, sizeof(srv_desc));
    srv_desc.Format = DXGI_FORMAT_R32_FLOAT;
    srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
    srv_desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
    srv_desc.Texture2D.MipLevels = 1;
    ID3D12Device_CreateShaderResourceView(device, NULL, &srv_desc,
            get_cpu_descriptor_handle(&context, heap, 2));

    memset(&uav_desc, 0, sizeof(uav_desc));
    uav_desc.Format = DXGI_FORMAT_R32_UINT;
    uav_desc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
    uav_desc.Buffer.FirstElement = 0;
    uav_desc.Buffer.NumElements = 1;
    ID3D12Device_CreateUnorderedAccessView(device, NULL, NULL, &uav_desc,
            get_cpu_descriptor_handle(&context, heap, 3));

    memset(&uav_desc, 0, sizeof(uav_desc));
    uav_desc.Format = DXGI_FORMAT_R32_UINT;
    uav_desc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D;
    uav_desc.Texture2D.MipSlice = 0;
    uav_desc.Texture2D.PlaneSlice = 0;
    ID3D12Device_CreateUnorderedAccessView(device, NULL, NULL, &uav_desc,
            get_cpu_descriptor_handle(&context, heap, 3));

    ID3D12DescriptorHeap_Release(heap);
    destroy_test_context(&context);
}

static void test_null_cbv(void)
{
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    D3D12_CONSTANT_BUFFER_VIEW_DESC cbv_desc;
    ID3D12GraphicsCommandList *command_list;
    D3D12_ROOT_PARAMETER root_parameters[2];
    D3D12_DESCRIPTOR_RANGE descriptor_range;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12DescriptorHeap *heap;
    ID3D12CommandQueue *queue;
    ID3D12Device *device;
    unsigned int index;
    HRESULT hr;

    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    static const DWORD ps_code[] =
    {
#if 0
        uint index;

        cbuffer null_cb
        {
            float4 data[1024];
        };

        float4 main() : SV_Target
        {
            return data[index];
        }
#endif
        0x43425844, 0xa69026e2, 0xccf934be, 0x11f0a922, 0x95e9ab51, 0x00000001, 0x000000f0, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000078, 0x00000050, 0x0000001e,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x04000859, 0x00208e46, 0x00000001,
        0x00000400, 0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000001, 0x06000036, 0x00100012,
        0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x07000036, 0x001020f2, 0x00000000, 0x04208e46,
        0x00000001, 0x0010000a, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};

    memset(&desc, 0, sizeof(desc));
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    command_list = context.list;
    queue = context.queue;
    device = context.device;

    descriptor_range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_CBV;
    descriptor_range.NumDescriptors = 1;
    descriptor_range.BaseShaderRegister = 1;
    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_PIXEL;

    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 = 1;
    root_parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;

    memset(&root_signature_desc, 0, sizeof(root_signature_desc));
    root_signature_desc.NumParameters = ARRAY_SIZE(root_parameters);
    root_signature_desc.pParameters = root_parameters;
    hr = create_root_signature(device, &root_signature_desc, &context.root_signature);
    ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr);

    context.pipeline_state = create_pipeline_state(context.device,
            context.root_signature, context.render_target_desc.Format, NULL, &ps, NULL);

    heap = create_gpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 16);

    cbv_desc.BufferLocation = 0;
    cbv_desc.SizeInBytes = 0; /* Size doesn't appear to matter for NULL CBV. */
    ID3D12Device_CreateConstantBufferView(device, &cbv_desc,
            ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(heap));

    for (index = 0; index < 1200; index += 100)
    {
        vkd3d_test_push_context("index %u", index);

        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

        ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
        ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
        ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &heap);
        ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0,
                ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(heap));
        ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 1, 1, &index, 0);
        ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
        ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

        transition_sub_resource_state(command_list, context.render_target, 0,
                D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
        check_sub_resource_uint(context.render_target, 0, queue, command_list, 0x00000000, 0);

        reset_command_list(command_list, context.allocator);
        transition_sub_resource_state(command_list, context.render_target, 0,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

        vkd3d_test_pop_context();
    }

    ID3D12DescriptorHeap_Release(heap);
    destroy_test_context(&context);
}

static void test_null_srv(void)
{
    D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc;
    ID3D12GraphicsCommandList *command_list;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12DescriptorHeap *heap;
    ID3D12CommandQueue *queue;
    struct uvec4 location;
    ID3D12Device *device;
    unsigned int i, j;

    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    static const DWORD ps_sample_code[] =
    {
#if 0
        Texture2D t;
        SamplerState s;

        float4 main(float4 position : SV_Position) : SV_Target
        {
            return t.Sample(s, float2(position.x / 32.0f, position.y / 32.0f));
        }
#endif
        0x43425844, 0xe096fa11, 0xeb01c081, 0x961588d4, 0x27c031af, 0x00000001, 0x00000140, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000030f, 0x505f5653, 0x7469736f, 0x006e6f69,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x000000a4, 0x00000050,
        0x00000029, 0x0100086a, 0x0300005a, 0x00106000, 0x00000000, 0x04001858, 0x00107000, 0x00000000,
        0x00005555, 0x04002064, 0x00101032, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x02000068, 0x00000001, 0x0a000038, 0x00100032, 0x00000000, 0x00101046, 0x00000000, 0x00004002,
        0x3d000000, 0x3d000000, 0x00000000, 0x00000000, 0x8b000045, 0x800000c2, 0x00155543, 0x001020f2,
        0x00000000, 0x00100046, 0x00000000, 0x00107e46, 0x00000000, 0x00106000, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_sample = {ps_sample_code, sizeof(ps_sample_code)};
    static const DWORD ps_ld_code[] =
    {
#if 0
        Texture2D t;

        uint4 location;

        float4 main(float4 position : SV_Position) : SV_Target
        {
            return t.Load(location.xyz);
        }
#endif
        0x43425844, 0xfa13670e, 0x291af510, 0xc253cc12, 0x9474950b, 0x00000001, 0x00000100, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x505f5653, 0x7469736f, 0x006e6f69,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000064, 0x00000050,
        0x00000019, 0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x04001858, 0x00107000,
        0x00000000, 0x00005555, 0x03000065, 0x001020f2, 0x00000000, 0x8a00002d, 0x800000c2, 0x00155543,
        0x001020f2, 0x00000000, 0x00208a46, 0x00000000, 0x00000000, 0x00107e46, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_ld = {ps_ld_code, sizeof(ps_ld_code)};
    static const DWORD ps_buffer_code[] =
    {
#if 0
        ByteAddressBuffer t;

        uint location;

        float4 main(float4 position : SV_Position) : SV_Target
        {
            return t.Load(location);
        }
#endif
        0x43425844, 0x70170f6b, 0x16097169, 0x714f155c, 0x1e3d860f, 0x00000001, 0x00000118, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x505f5653, 0x7469736f, 0x006e6f69,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x0000007c, 0x00000050,
        0x0000001f, 0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x030000a1, 0x00107000,
        0x00000000, 0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000001, 0x8a0000a5, 0x800002c2,
        0x00199983, 0x00100012, 0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x00107006, 0x00000000,
        0x05000056, 0x001020f2, 0x00000000, 0x00100006, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_buffer = {ps_buffer_code, sizeof(ps_buffer_code)};
    static const DXGI_FORMAT formats[] =
    {
        DXGI_FORMAT_R32_FLOAT,
        DXGI_FORMAT_R32_UINT,
        DXGI_FORMAT_R8G8B8A8_UNORM,
    };
    /* component mapping is ignored for NULL SRVs */
    static const unsigned int component_mappings[] =
    {
        D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING,
        D3D12_ENCODE_SHADER_4_COMPONENT_MAPPING(
                D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_1,
                D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_1,
                D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_1,
                D3D12_SHADER_COMPONENT_MAPPING_FORCE_VALUE_1),
    };

    memset(&desc, 0, sizeof(desc));
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    command_list = context.list;
    queue = context.queue;
    device = context.device;

    context.root_signature = create_texture_root_signature(context.device,
            D3D12_SHADER_VISIBILITY_PIXEL, 4, 0);

    context.pipeline_state = create_pipeline_state(context.device,
            context.root_signature, context.render_target_desc.Format, NULL, &ps_sample, NULL);

    heap = create_gpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 16);

    for (i = 0; i < ARRAY_SIZE(formats); ++i)
    {
        for (j = 0; j < ARRAY_SIZE(component_mappings); ++j)
        {
            vkd3d_test_push_context("format %#x, component mapping %#x", formats[i], component_mappings[j]);

            memset(&srv_desc, 0, sizeof(srv_desc));
            srv_desc.Format = formats[i];
            srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
            srv_desc.Shader4ComponentMapping = component_mappings[j];
            srv_desc.Texture2D.MipLevels = 1;
            ID3D12Device_CreateShaderResourceView(device, NULL, &srv_desc,
                    ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(heap));

            ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

            ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
            ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
            ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
            ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &heap);
            ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0,
                    ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(heap));
            ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
            ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
            ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
            ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

            transition_sub_resource_state(command_list, context.render_target, 0,
                    D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
            check_sub_resource_uint(context.render_target, 0, queue, command_list, 0x00000000, 0);

            reset_command_list(command_list, context.allocator);
            transition_sub_resource_state(command_list, context.render_target, 0,
                    D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

            vkd3d_test_pop_context();
        }
    }

    ID3D12PipelineState_Release(context.pipeline_state);
    context.pipeline_state = create_pipeline_state(context.device,
            context.root_signature, context.render_target_desc.Format, NULL, &ps_ld, NULL);

    memset(&srv_desc, 0, sizeof(srv_desc));
    srv_desc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
    srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
    srv_desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
    srv_desc.Texture2D.MipLevels = 1;
    ID3D12Device_CreateShaderResourceView(device, NULL, &srv_desc,
            ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(heap));

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &heap);
    ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0,
            ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(heap));
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    location.x = 10;
    location.y = 20;
    location.z = 0;
    location.w = 0;
    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 1, 4, &location, 0);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_sub_resource_state(command_list, context.render_target, 0,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0x00000000, 0);

    /* buffer */
    ID3D12PipelineState_Release(context.pipeline_state);
    context.pipeline_state = create_pipeline_state(context.device,
            context.root_signature, context.render_target_desc.Format, NULL, &ps_buffer, NULL);
    reset_command_list(command_list, context.allocator);
    transition_sub_resource_state(command_list, context.render_target, 0,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    memset(&srv_desc, 0, sizeof(srv_desc));
    srv_desc.Format = DXGI_FORMAT_R32_TYPELESS;
    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 = 1024;
    srv_desc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_RAW;
    ID3D12Device_CreateShaderResourceView(device, NULL, &srv_desc,
            ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(heap));

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &heap);
    ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0,
            ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(heap));
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    location.x = 0;
    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 1, 4, &location, 0);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_sub_resource_state(command_list, context.render_target, 0,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0x00000000, 0);

    ID3D12DescriptorHeap_Release(heap);
    destroy_test_context(&context);
}

static void test_null_uav(void)
{
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    D3D12_DESCRIPTOR_RANGE descriptor_ranges[1];
    D3D12_ROOT_PARAMETER root_parameters[2];
    const D3D12_SHADER_BYTECODE *current_ps;
    ID3D12GraphicsCommandList *command_list;
    D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle;
    ID3D12DescriptorHeap *uav_heap;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Device *device;
    unsigned int i;
    HRESULT hr;

    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    static const DWORD ps_ld_texture_code[] =
    {
#if 0
        RWTexture2D<float> u;

        float4 main(float4 position : SV_Position) : SV_Target
        {
            float2 s;
            u.GetDimensions(s.x, s.y);
            return u[s * float2(position.x / 640.0f, position.y / 480.0f)];
        }
#endif
        0x43425844, 0x85c096ab, 0x210d7572, 0xdb1951af, 0x4dadced7, 0x00000001, 0x00000194, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000030f, 0x505f5653, 0x7469736f, 0x006e6f69,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x000000f8, 0x00000050,
        0x0000003e, 0x0100086a, 0x0400189c, 0x0011e000, 0x00000001, 0x00005555, 0x04002064, 0x00101032,
        0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000001, 0x8900003d,
        0x800000c2, 0x00155543, 0x00100032, 0x00000000, 0x00004001, 0x00000000, 0x0011ee46, 0x00000001,
        0x07000038, 0x001000f2, 0x00000000, 0x00100546, 0x00000000, 0x00101546, 0x00000000, 0x0a000038,
        0x001000f2, 0x00000000, 0x00100e46, 0x00000000, 0x00004002, 0x3acccccd, 0x3b088889, 0x3b088889,
        0x3b088889, 0x0500001c, 0x001000f2, 0x00000000, 0x00100e46, 0x00000000, 0x890000a3, 0x800000c2,
        0x00155543, 0x00100012, 0x00000000, 0x00100e46, 0x00000000, 0x0011ee46, 0x00000001, 0x05000036,
        0x001020f2, 0x00000000, 0x00100006, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_ld_texture = {ps_ld_texture_code, sizeof(ps_ld_texture_code)};
    static const DWORD ps_ld_buffer_code[] =
    {
#if 0
        RWByteAddressBuffer u;

        uint location;

        float4 main(float4 position : SV_Position) : SV_Target
        {
            return u.Load(4 * location);
        }
#endif
        0x43425844, 0xde636789, 0x7bc99233, 0x8b0609b6, 0x4b9a958e, 0x00000001, 0x00000134, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x505f5653, 0x7469736f, 0x006e6f69,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000098, 0x00000050,
        0x00000026, 0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x0300009d, 0x0011e000,
        0x00000001, 0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000001, 0x08000029, 0x00100012,
        0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x00004001, 0x00000002, 0x890000a5, 0x800002c2,
        0x00199983, 0x00100012, 0x00000000, 0x0010000a, 0x00000000, 0x0011e006, 0x00000001, 0x05000056,
        0x001020f2, 0x00000000, 0x00100006, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_ld_buffer = {ps_ld_buffer_code, sizeof(ps_ld_buffer_code)};
    static const struct test
    {
        const D3D12_SHADER_BYTECODE *ps;
        D3D12_UNORDERED_ACCESS_VIEW_DESC uav_desc;
        uint32_t location;
    }
    tests[] =
    {
        {&ps_ld_texture, {DXGI_FORMAT_R32_UINT, D3D12_UAV_DIMENSION_TEXTURE2D}, 0},
        {&ps_ld_buffer,
                {DXGI_FORMAT_R32_TYPELESS, D3D12_UAV_DIMENSION_BUFFER, .Buffer = {0, 1024, .Flags = D3D12_BUFFER_UAV_FLAG_RAW}},
                0},
        {&ps_ld_buffer,
                {DXGI_FORMAT_R32_TYPELESS, D3D12_UAV_DIMENSION_BUFFER, .Buffer = {0, 1024, .Flags = D3D12_BUFFER_UAV_FLAG_RAW}},
                1024},
    };

    memset(&desc, 0, sizeof(desc));
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    descriptor_ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
    descriptor_ranges[0].NumDescriptors = 1;
    descriptor_ranges[0].BaseShaderRegister = 1;
    descriptor_ranges[0].RegisterSpace = 0;
    descriptor_ranges[0].OffsetInDescriptorsFromTableStart = 0;
    root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameters[0].DescriptorTable.NumDescriptorRanges = 1;
    root_parameters[0].DescriptorTable.pDescriptorRanges = descriptor_ranges;
    root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
    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 = 1;
    root_parameters[1].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(device, &root_signature_desc, &context.root_signature);
    ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr);

    uav_heap = create_gpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1);

    current_ps = NULL;
    for (i = 0; i < ARRAY_SIZE(tests); ++i)
    {
        const struct test *test = &tests[i];

        vkd3d_test_push_context("Test %u", i);

        if (current_ps != test->ps)
        {
            if (context.pipeline_state)
                ID3D12PipelineState_Release(context.pipeline_state);
            current_ps = tests[i].ps;
            context.pipeline_state = create_pipeline_state(context.device,
                    context.root_signature, context.render_target_desc.Format, NULL, current_ps, NULL);
        }

        cpu_handle = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(uav_heap);
        ID3D12Device_CreateUnorderedAccessView(device, NULL, NULL, &test->uav_desc, cpu_handle);

        ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
        ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
        ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &uav_heap);
        ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0,
                ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(uav_heap));
        ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstant(command_list, 1, test->location, 0);
        ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
        ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

        transition_sub_resource_state(command_list, context.render_target, 0,
                D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
        check_sub_resource_uint(context.render_target, 0, queue, command_list, 0x00000000, 0);

        reset_command_list(command_list, context.allocator);
        transition_sub_resource_state(command_list, context.render_target, 0,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

        vkd3d_test_pop_context();
    }

    ID3D12DescriptorHeap_Release(uav_heap);
    destroy_test_context(&context);
}

static void test_null_vbv(void)
{
    ID3D12GraphicsCommandList *command_list;
    D3D12_INPUT_LAYOUT_DESC input_layout;
    D3D12_VERTEX_BUFFER_VIEW vbv[2];
    struct test_context_desc desc;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Resource *vb;

    static const D3D12_INPUT_ELEMENT_DESC layout_desc[] =
    {
        {"SV_POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT,
                D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
        {"COLOR",       0, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, D3D12_APPEND_ALIGNED_ELEMENT,
                D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
    };
    static const DWORD vs_code[] =
    {
#if 0
        struct vs_data
        {
            float4 pos : SV_POSITION;
            float4 color : COLOR;
        };

        void main(in struct vs_data vs_input, out struct vs_data vs_output)
        {
            vs_output.pos = vs_input.pos;
            vs_output.color = vs_input.color;
        }
#endif
        0x43425844, 0xd5b32785, 0x35332906, 0x4d05e031, 0xf66a58af, 0x00000001, 0x00000144, 0x00000003,
        0x0000002c, 0x00000080, 0x000000d4, 0x4e475349, 0x0000004c, 0x00000002, 0x00000008, 0x00000038,
        0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000f0f, 0x00000044, 0x00000000, 0x00000000,
        0x00000003, 0x00000001, 0x00000f0f, 0x505f5653, 0x5449534f, 0x004e4f49, 0x4f4c4f43, 0xabab0052,
        0x4e47534f, 0x0000004c, 0x00000002, 0x00000008, 0x00000038, 0x00000000, 0x00000001, 0x00000003,
        0x00000000, 0x0000000f, 0x00000044, 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x0000000f,
        0x505f5653, 0x5449534f, 0x004e4f49, 0x4f4c4f43, 0xabab0052, 0x52444853, 0x00000068, 0x00010040,
        0x0000001a, 0x0300005f, 0x001010f2, 0x00000000, 0x0300005f, 0x001010f2, 0x00000001, 0x04000067,
        0x001020f2, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000001, 0x05000036, 0x001020f2,
        0x00000000, 0x00101e46, 0x00000000, 0x05000036, 0x001020f2, 0x00000001, 0x00101e46, 0x00000001,
        0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE vs = {vs_code, sizeof(vs_code)};
    static const DWORD ps_code[] =
    {
#if 0
        struct ps_data
        {
            float4 pos : SV_POSITION;
            float4 color : COLOR;
        };

        float4 main(struct ps_data ps_input) : SV_Target
        {
            return ps_input.color;
        }
#endif
        0x43425844, 0x89803e59, 0x3f798934, 0xf99181df, 0xf5556512, 0x00000001, 0x000000f4, 0x00000003,
        0x0000002c, 0x00000080, 0x000000b4, 0x4e475349, 0x0000004c, 0x00000002, 0x00000008, 0x00000038,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x00000044, 0x00000000, 0x00000000,
        0x00000003, 0x00000001, 0x00000f0f, 0x505f5653, 0x5449534f, 0x004e4f49, 0x4f4c4f43, 0xabab0052,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x52444853, 0x00000038, 0x00000040,
        0x0000000e, 0x03001062, 0x001010f2, 0x00000001, 0x03000065, 0x001020f2, 0x00000000, 0x05000036,
        0x001020f2, 0x00000000, 0x00101e46, 0x00000001, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};
    static const struct vec4 positions[] =
    {
        {-1.0f, -1.0f, 0.0f, 1.0f},
        {-1.0f,  1.0f, 0.0f, 1.0f},
        { 1.0f, -1.0f, 0.0f, 1.0f},
        { 1.0f,  1.0f, 0.0f, 1.0f},
    };
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};

    memset(&desc, 0, sizeof(desc));
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    command_list = context.list;
    queue = context.queue;

    if (is_nvidia_windows_device(context.device))
    {
        skip("Setting NULL VBVs is broken on NVIDIA devices, skipping.\n");
        destroy_test_context(&context);
        return;
    }

    context.root_signature = create_empty_root_signature(context.device,
            D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
    input_layout.pInputElementDescs = layout_desc;
    input_layout.NumElements = ARRAY_SIZE(layout_desc);
    context.pipeline_state = create_pipeline_state(context.device,
            context.root_signature, context.render_target_desc.Format, &vs, &ps, &input_layout);

    vb = create_upload_buffer(context.device, sizeof(positions), positions);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);

    vbv[0].BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vb);
    vbv[0].StrideInBytes = sizeof(*positions);
    vbv[0].SizeInBytes = sizeof(positions);
    vbv[1] = vbv[0];
    ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, ARRAY_SIZE(vbv), vbv);
    vbv[1].BufferLocation = 0;
    vbv[1].StrideInBytes = 0;
    vbv[1].SizeInBytes = 0;
    ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, ARRAY_SIZE(vbv), vbv);

    ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 4, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0x00000000, 0);
    reset_command_list(command_list, context.allocator);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);

    vbv[1] = vbv[0];
    ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, ARRAY_SIZE(vbv), vbv);
    /* A call with a NULL "views" pointer is interpreted as a null buffer. */
    ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 1, 1, NULL);

    ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 4, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0x00000000, 0);
    reset_command_list(command_list, context.allocator);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);

    ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, ARRAY_SIZE(vbv), vbv);
    /* A NULL "views" pointer can apply to multiple view slots. */
    ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, 2, NULL);

    ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 4, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xffffffff, 0);

    ID3D12Resource_Release(vb);
    destroy_test_context(&context);
}

#define check_copyable_footprints(a, b, c, d, e, f, g, h) \
        check_copyable_footprints_(__FILE__, __LINE__, a, b, c, d, e, f, g, h)
static void check_copyable_footprints_(const char *file, unsigned int line, const D3D12_RESOURCE_DESC *desc,
        unsigned int sub_resource_idx, unsigned int sub_resource_count, uint64_t base_offset,
        const D3D12_PLACED_SUBRESOURCE_FOOTPRINT *layouts, const UINT *row_counts,
        const uint64_t *row_sizes, uint64_t *total_size)
{
    unsigned int miplevel, width, height, depth, row_count, row_size, row_pitch;
    unsigned int i, sub_resources_per_plane, plane_count, plane_idx;
    DXGI_FORMAT expected_format = desc->Format;
    uint64_t offset, size, total;

    sub_resources_per_plane = desc->MipLevels
            * (desc->Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE3D ? desc->DepthOrArraySize : 1);
    plane_count = format_plane_count(desc->Format);

    offset = total = 0;
    for (i = 0; i < sub_resource_count; ++i)
    {
        plane_idx = (sub_resource_idx + i) / sub_resources_per_plane;
        expected_format = format_get_subresource_plane_format(desc->Format, plane_idx, plane_count);

        miplevel = (sub_resource_idx + i) % desc->MipLevels;
        width = align(max(1, desc->Width >> miplevel), format_block_width(expected_format));
        height = align(max(1, desc->Height >> miplevel), format_block_height(expected_format));
        depth = desc->Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D ? desc->DepthOrArraySize : 1;
        depth = max(1, depth >> miplevel);
        row_count = height / format_block_height(expected_format);
        row_size = (width / format_block_width(expected_format)) * format_size(expected_format);
        /* D3D12 requires double the alignment for dual planes. */
        row_pitch = align(row_size, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT * plane_count);

        if (layouts)
        {
            const D3D12_PLACED_SUBRESOURCE_FOOTPRINT *l = &layouts[i];
            const D3D12_SUBRESOURCE_FOOTPRINT *f = &l->Footprint;

            ok_(file, line)(l->Offset == base_offset + offset,
                    "Got offset %"PRIu64", expected %"PRIu64".\n", l->Offset, base_offset + offset);
            ok_(file, line)(f->Format == expected_format, "Got format %#x, expected %#x.\n",
                    f->Format, expected_format);
            ok_(file, line)(f->Width == width, "Got width %u, expected %u.\n", f->Width, width);
            ok_(file, line)(f->Height == height, "Got height %u, expected %u.\n", f->Height, height);
            ok_(file, line)(f->Depth == depth, "Got depth %u, expected %u.\n", f->Depth, depth);
            ok_(file, line)(f->RowPitch == row_pitch, "Got row pitch %u, expected %u.\n", f->RowPitch, row_pitch);
        }

        if (row_counts)
            ok_(file, line)(row_counts[i] == row_count, "Got row count %u, expected %u.\n", row_counts[i], row_count);

        if (row_sizes)
            ok_(file, line)(row_sizes[i] == row_size, "Got row size %"PRIu64", expected %u.\n",
                    row_sizes[i], row_size);

        size = max(0, row_count - 1) * row_pitch + row_size;
        size = max(0, depth - 1) * align(size, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT * plane_count) + size;

        total = offset + size;
        offset = align(total, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT);
    }

    if (total_size)
        ok_(file, line)(*total_size == total, "Got total size %"PRIu64", expected %"PRIu64".\n", *total_size, total);
}

static void test_get_copyable_footprints(void)
{
    D3D12_PLACED_SUBRESOURCE_FOOTPRINT layouts[10];
    D3D12_RESOURCE_DESC1 resource_desc1;
    uint64_t row_sizes[10], total_size;
    D3D12_RESOURCE_DESC resource_desc;
    unsigned int sub_resource_count;
    bool unaligned_block_textures;
    ID3D12Device8 *device8;
    unsigned int i, j, k;
    ID3D12Device *device;
    UINT row_counts[10];
    ULONG refcount;

    static const struct
    {
        D3D12_RESOURCE_DIMENSION dimension;
        unsigned int width;
        unsigned int height;
        unsigned int depth_or_array_size;
        unsigned int miplevel_count;
        bool test_with_compressed;
    }
    resources[] =
    {
        {D3D12_RESOURCE_DIMENSION_BUFFER, 4, 1, 1, 1, false},
        {D3D12_RESOURCE_DIMENSION_TEXTURE1D, 4, 1, 1, 1, false},
        {D3D12_RESOURCE_DIMENSION_TEXTURE1D, 4, 1, 1, 2, false},
        {D3D12_RESOURCE_DIMENSION_TEXTURE1D, 3, 1, 1, 1, false},
        {D3D12_RESOURCE_DIMENSION_TEXTURE1D, 4, 1, 2, 1, false},
        {D3D12_RESOURCE_DIMENSION_TEXTURE2D, 4, 4, 1, 1, true},
        {D3D12_RESOURCE_DIMENSION_TEXTURE2D, 4, 4, 2, 1, true},
        {D3D12_RESOURCE_DIMENSION_TEXTURE2D, 4, 4, 1, 2, true},
        {D3D12_RESOURCE_DIMENSION_TEXTURE2D, 3, 1, 1, 2, false},
        {D3D12_RESOURCE_DIMENSION_TEXTURE2D, 3, 2, 1, 2, false},
        {D3D12_RESOURCE_DIMENSION_TEXTURE2D, 3, 1, 1, 1, false},
        {D3D12_RESOURCE_DIMENSION_TEXTURE2D, 3, 2, 1, 1, false},
        {D3D12_RESOURCE_DIMENSION_TEXTURE3D, 4, 4, 1, 1, true},
        {D3D12_RESOURCE_DIMENSION_TEXTURE3D, 4, 4, 2, 1, true},
        {D3D12_RESOURCE_DIMENSION_TEXTURE3D, 4, 4, 2, 2, true},
        {D3D12_RESOURCE_DIMENSION_TEXTURE3D, 8, 8, 8, 4, true},
        {D3D12_RESOURCE_DIMENSION_TEXTURE3D, 3, 2, 2, 2, false},
    };
    static const struct
    {
        DXGI_FORMAT format;
        bool is_compressed;
        bool is_depth;
    }
    formats[] =
    {
        {DXGI_FORMAT_R32G32B32A32_FLOAT, false},
        {DXGI_FORMAT_R32G32B32A32_UINT, false},
        {DXGI_FORMAT_R32_UINT, false},
        {DXGI_FORMAT_R8G8B8A8_UNORM, false},
        {DXGI_FORMAT_BC1_UNORM, true},
        {DXGI_FORMAT_BC2_UNORM, true},
        {DXGI_FORMAT_BC3_UNORM, true},
        {DXGI_FORMAT_BC4_UNORM, true},
        {DXGI_FORMAT_BC5_UNORM, true},
        {DXGI_FORMAT_BC6H_UF16, true},
        {DXGI_FORMAT_BC6H_SF16, true},
        {DXGI_FORMAT_BC7_UNORM, true},
        {DXGI_FORMAT_D32_FLOAT, false, true},
        {DXGI_FORMAT_D24_UNORM_S8_UINT, false, true},
        {DXGI_FORMAT_D32_FLOAT_S8X24_UINT, false, true},
    };
    static const uint64_t base_offsets[] =
    {
        0, 1, 2, 30, 255, 512, 513, 600, 4096, 4194304, ~(uint64_t)0,
    };
    static const struct
    {
        D3D12_RESOURCE_DESC resource_desc;
        unsigned int sub_resource_idx;
        unsigned int sub_resource_count;
    }
    invalid_descs[] =
    {
        {
            {D3D12_RESOURCE_DIMENSION_BUFFER, 0, 3, 2, 1, 1, DXGI_FORMAT_R32_UINT,
                {1, 0}, D3D12_TEXTURE_LAYOUT_UNKNOWN, D3D12_RESOURCE_FLAG_NONE}, 0, 1,
        },
        {
            {D3D12_RESOURCE_DIMENSION_TEXTURE1D, 0, 4, 2, 1, 1, DXGI_FORMAT_R32_UINT,
                {1, 0}, D3D12_TEXTURE_LAYOUT_UNKNOWN, D3D12_RESOURCE_FLAG_NONE}, 0, 1,
        },
        {
            {D3D12_RESOURCE_DIMENSION_TEXTURE2D, 0, 4, 4, 1, 1, DXGI_FORMAT_R32_UINT,
                {1, 0}, D3D12_TEXTURE_LAYOUT_UNKNOWN, D3D12_RESOURCE_FLAG_NONE}, 0, 2,
        },
        {
            {D3D12_RESOURCE_DIMENSION_TEXTURE2D, 3, 4, 4, 1, 1, DXGI_FORMAT_R32_UINT,
                {1, 0}, D3D12_TEXTURE_LAYOUT_UNKNOWN, D3D12_RESOURCE_FLAG_NONE}, 0, 1,
        },
        {
            {D3D12_RESOURCE_DIMENSION_TEXTURE2D, 0, 3, 1, 1, 2, DXGI_FORMAT_BC1_UNORM,
                {1, 0}, D3D12_TEXTURE_LAYOUT_UNKNOWN, D3D12_RESOURCE_FLAG_NONE}, 0, 2,
        },
        {
            {D3D12_RESOURCE_DIMENSION_TEXTURE2D, 0, 3, 1, 1, 1, DXGI_FORMAT_BC1_UNORM,
                {1, 0}, D3D12_TEXTURE_LAYOUT_UNKNOWN, D3D12_RESOURCE_FLAG_NONE}, 0, 1,
        },
        {
            {D3D12_RESOURCE_DIMENSION_TEXTURE2D, 0, 3, 1, 1, 2, DXGI_FORMAT_BC7_UNORM,
                {1, 0}, D3D12_TEXTURE_LAYOUT_UNKNOWN, D3D12_RESOURCE_FLAG_NONE}, 0, 2,
        },
        {
            {D3D12_RESOURCE_DIMENSION_TEXTURE2D, 0, 3, 1, 1, 1, DXGI_FORMAT_BC7_UNORM,
                {1, 0}, D3D12_TEXTURE_LAYOUT_UNKNOWN, D3D12_RESOURCE_FLAG_NONE}, 0, 1,
        },
        {
            {D3D12_RESOURCE_DIMENSION_TEXTURE3D, 0, 2, 2, 2, 2, DXGI_FORMAT_BC1_UNORM,
                {1, 0}, D3D12_TEXTURE_LAYOUT_UNKNOWN, D3D12_RESOURCE_FLAG_NONE}, 0, 1,
        },
        {
            {D3D12_RESOURCE_DIMENSION_TEXTURE2D, 0, 4, 4, 1, 1, DXGI_FORMAT_D32_FLOAT,
                {1, 0}, D3D12_TEXTURE_LAYOUT_UNKNOWN, D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL}, 0, 2,
        },
        {
            {D3D12_RESOURCE_DIMENSION_TEXTURE2D, 0, 4, 4, 1, 1, DXGI_FORMAT_D24_UNORM_S8_UINT,
                {1, 0}, D3D12_TEXTURE_LAYOUT_UNKNOWN, D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL}, 0, 3,
        },
    };

    if (!(device = create_device()))
    {
        skip("Failed to create device.\n");
        return;
    }

    for (i = 0; i < ARRAY_SIZE(resources); ++i)
    {
        const bool is_buffer = resources[i].dimension == D3D12_RESOURCE_DIMENSION_BUFFER;

        resource_desc.Dimension = resources[i].dimension;
        resource_desc.Alignment = 0;
        resource_desc.Width = resources[i].width;
        resource_desc.Height = resources[i].height;
        resource_desc.DepthOrArraySize = resources[i].depth_or_array_size;
        resource_desc.MipLevels = resources[i].miplevel_count;

        for (j = 0; j < ARRAY_SIZE(formats); ++j)
        {
            if (formats[j].is_compressed && !resources[i].test_with_compressed)
                continue;
            /* Depth formats are not supported for 3D textures in any current feature level including 12.1. */
            if (formats[j].is_depth && resources[i].dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D)
                continue;
            if (is_buffer && j > 0)
                continue;

            if (is_buffer)
                resource_desc.Format = DXGI_FORMAT_UNKNOWN;
            else
                resource_desc.Format = formats[j].format;

            resource_desc.SampleDesc.Count = 1;
            resource_desc.SampleDesc.Quality = 0;
            resource_desc.Layout = is_buffer ? D3D12_TEXTURE_LAYOUT_ROW_MAJOR : D3D12_TEXTURE_LAYOUT_UNKNOWN;
            resource_desc.Flags = D3D12_RESOURCE_FLAG_NONE;

            sub_resource_count = resource_desc.MipLevels;
            if (resources[i].dimension != D3D12_RESOURCE_DIMENSION_TEXTURE3D)
                sub_resource_count *= resource_desc.DepthOrArraySize;
            sub_resource_count *= format_plane_count(resource_desc.Format);
            assert(sub_resource_count <= ARRAY_SIZE(layouts));

            for (k = 0; k < ARRAY_SIZE(base_offsets); ++k)
            {
                vkd3d_test_push_context("resource %u, format %#x, offset %#"PRIx64,
                        i, resource_desc.Format, base_offsets[k]);

                memset(layouts, 0, sizeof(layouts));
                memset(row_counts, 0, sizeof(row_counts));
                memset(row_sizes, 0, sizeof(row_sizes));
                total_size = 0;
                ID3D12Device_GetCopyableFootprints(device, &resource_desc, 0, sub_resource_count, base_offsets[k],
                        layouts, row_counts, row_sizes, &total_size);
                check_copyable_footprints(&resource_desc, 0, sub_resource_count, base_offsets[k],
                        layouts, row_counts, row_sizes, &total_size);

                memset(layouts, 0, sizeof(layouts));
                ID3D12Device_GetCopyableFootprints(device, &resource_desc, 0, sub_resource_count, base_offsets[k],
                        layouts, NULL, NULL, NULL);
                check_copyable_footprints(&resource_desc, 0, sub_resource_count, base_offsets[k],
                        layouts, NULL, NULL, NULL);
                memset(row_counts, 0, sizeof(row_counts));
                ID3D12Device_GetCopyableFootprints(device, &resource_desc, 0, sub_resource_count, base_offsets[k],
                        NULL, row_counts, NULL, NULL);
                check_copyable_footprints(&resource_desc, 0, sub_resource_count, base_offsets[k],
                        NULL, row_counts, NULL, NULL);
                memset(row_sizes, 0, sizeof(row_sizes));
                ID3D12Device_GetCopyableFootprints(device, &resource_desc, 0, sub_resource_count, base_offsets[k],
                        NULL, NULL, row_sizes, NULL);
                check_copyable_footprints(&resource_desc, 0, sub_resource_count, base_offsets[k],
                        NULL, NULL, row_sizes, NULL);
                total_size = 0;
                ID3D12Device_GetCopyableFootprints(device, &resource_desc, 0, sub_resource_count, base_offsets[k],
                        NULL, NULL, NULL, &total_size);
                check_copyable_footprints(&resource_desc, 0, sub_resource_count, base_offsets[k],
                        NULL, NULL, NULL, &total_size);

                for (unsigned int l = 0; l < sub_resource_count; ++l)
                {
                    vkd3d_test_push_context("sub-resource %u", l);

                    memset(layouts, 0, sizeof(layouts));
                    memset(row_counts, 0, sizeof(row_counts));
                    memset(row_sizes, 0, sizeof(row_sizes));
                    total_size = 0;
                    ID3D12Device_GetCopyableFootprints(device, &resource_desc, l, 1, base_offsets[k],
                            layouts, row_counts, row_sizes, &total_size);
                    check_copyable_footprints(&resource_desc, l, 1, base_offsets[k],
                            layouts, row_counts, row_sizes, &total_size);

                    vkd3d_test_pop_context();
                }

                vkd3d_test_pop_context();
            }
        }
    }

    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_R8G8B8A8_UNORM;
    resource_desc.SampleDesc.Count = 4;
    resource_desc.SampleDesc.Quality = 0;
    resource_desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
    memset(layouts, 0, sizeof(layouts));
    memset(row_counts, 0, sizeof(row_counts));
    memset(row_sizes, 0, sizeof(row_sizes));
    total_size = 0;
    ID3D12Device_GetCopyableFootprints(device, &resource_desc, 0, 1, 0,
            layouts, row_counts, row_sizes, &total_size);
    check_copyable_footprints(&resource_desc, 0, 1, 0,
            layouts, row_counts, row_sizes, &total_size);

    if (SUCCEEDED(ID3D12Device_QueryInterface(device, &IID_ID3D12Device8, (void **)&device8)))
    {
        resource_desc1.Dimension = resource_desc.Dimension;
        resource_desc1.Alignment = resource_desc.Alignment;
        resource_desc1.Width = resource_desc.Width;
        resource_desc1.Height = resource_desc.Height;
        resource_desc1.DepthOrArraySize = resource_desc.DepthOrArraySize;
        resource_desc1.MipLevels = resource_desc.MipLevels;
        resource_desc1.Format = resource_desc.Format;
        resource_desc1.SampleDesc.Count = resource_desc.SampleDesc.Count;
        resource_desc1.SampleDesc.Quality = resource_desc.SampleDesc.Quality;
        resource_desc1.Layout = resource_desc.Layout;
        resource_desc1.Flags = resource_desc.Flags;
        memset(&resource_desc1.SamplerFeedbackMipRegion, 0, sizeof(resource_desc1.SamplerFeedbackMipRegion));
        memset(layouts, 0, sizeof(layouts));
        memset(row_counts, 0, sizeof(row_counts));
        memset(row_sizes, 0, sizeof(row_sizes));
        total_size = 0;
        ID3D12Device8_GetCopyableFootprints1(device8, &resource_desc1, 0, 1, 0,
                layouts, row_counts, row_sizes, &total_size);
        check_copyable_footprints(&resource_desc, 0, 1, 0,
                layouts, row_counts, row_sizes, &total_size);

        ID3D12Device8_Release(device8);
    }

    unaligned_block_textures = are_unaligned_block_textures_supported(device);

    for (i = 0; i < ARRAY_SIZE(invalid_descs); ++i)
    {
        bool expect_success;

        resource_desc = invalid_descs[i].resource_desc;
        /* If supported, succeeds for block compressed formats and returns aligned width and height. */
        expect_success = unaligned_block_textures && format_block_width(resource_desc.Format) > 1;

        memset(layouts, 0, sizeof(layouts));
        memset(row_counts, 0, sizeof(row_counts));
        memset(row_sizes, 0, sizeof(row_sizes));
        total_size = 0;
        ID3D12Device_GetCopyableFootprints(device, &resource_desc,
                invalid_descs[i].sub_resource_idx, invalid_descs[i].sub_resource_count, 0,
                layouts, row_counts, row_sizes, &total_size);

        for (j = 0; j < invalid_descs[i].sub_resource_count; ++j)
        {
            const D3D12_PLACED_SUBRESOURCE_FOOTPRINT *l = &layouts[j];

            vkd3d_test_push_context("resource %u, subresource %u", i, j);

            if (expect_success)
            {
                resource_desc.Width = align(resource_desc.Width, 4);
                resource_desc.Height = align(resource_desc.Height, 4);
                check_copyable_footprints(&resource_desc, 0, invalid_descs[i].sub_resource_count, 0,
                        layouts, row_counts, row_sizes, &total_size);
            }
            else
            {
                ok(l->Offset == ~(uint64_t)0, "Got offset %"PRIu64".\n", l->Offset);
                ok(l->Footprint.Format == ~(DXGI_FORMAT)0, "Got format %#x.\n", l->Footprint.Format);
                ok(l->Footprint.Width == ~0u, "Got width %u.\n", l->Footprint.Width);
                ok(l->Footprint.Height == ~0u, "Got height %u.\n", l->Footprint.Height);
                ok(l->Footprint.Depth == ~0u, "Got depth %u.\n", l->Footprint.Depth);
                ok(l->Footprint.RowPitch == ~0u, "Got row pitch %u.\n", l->Footprint.RowPitch);

                ok(row_counts[j] == ~0u, "Got row count %u.\n", row_counts[j]);
                ok(row_sizes[j] == ~(uint64_t)0, "Got row size %"PRIu64".\n", row_sizes[j]);
            }

            vkd3d_test_pop_context();
        }

        if (!expect_success)
            ok(total_size == ~(uint64_t)0, "Got total size %"PRIu64".\n", total_size);
    }

    refcount = ID3D12Device_Release(device);
    ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount);
}

static void test_depth_clip(void)
{
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
    ID3D12GraphicsCommandList *command_list;
    D3D12_INPUT_LAYOUT_DESC input_layout;
    struct depth_stencil_resource ds;
    struct test_context_desc desc;
    D3D12_VERTEX_BUFFER_VIEW vbv;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Resource *vb;
    unsigned int i;
    float depth;
    HRESULT hr;

    static const DWORD vs_code[] =
    {
#if 0
        float4 main(float4 p : POSITION) : SV_Position
        {
            return p;
        }
#endif
        0x43425844, 0x92767590, 0x06a6dba7, 0x0ae078b2, 0x7b5eb8f6, 0x00000001, 0x000000d8, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000f0f, 0x49534f50, 0x4e4f4954, 0xababab00,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000001, 0x00000003,
        0x00000000, 0x0000000f, 0x505f5653, 0x7469736f, 0x006e6f69, 0x52444853, 0x0000003c, 0x00010040,
        0x0000000f, 0x0300005f, 0x001010f2, 0x00000000, 0x04000067, 0x001020f2, 0x00000000, 0x00000001,
        0x05000036, 0x001020f2, 0x00000000, 0x00101e46, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE vs = {vs_code, sizeof(vs_code)};
    static const DWORD ps_depth_code[] =
    {
#if 0
        float depth;

        float4 main(float4 p : SV_Position, out float out_depth : SV_Depth) : SV_Target
        {
            out_depth = depth;
            return float4(0, 1, 0, 1);
        }
#endif
        0x43425844, 0x6744db20, 0x3e266cd1, 0xc50630b3, 0xd7455b94, 0x00000001, 0x00000120, 0x00000003,
        0x0000002c, 0x00000060, 0x000000b4, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x505f5653, 0x7469736f, 0x006e6f69,
        0x4e47534f, 0x0000004c, 0x00000002, 0x00000008, 0x00000038, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x00000042, 0x00000000, 0x00000000, 0x00000003, 0xffffffff, 0x00000e01,
        0x545f5653, 0x65677261, 0x56530074, 0x7065445f, 0xab006874, 0x52444853, 0x00000064, 0x00000040,
        0x00000019, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x02000065, 0x0000c001, 0x08000036, 0x001020f2, 0x00000000, 0x00004002, 0x00000000, 0x3f800000,
        0x00000000, 0x3f800000, 0x05000036, 0x0000c001, 0x0020800a, 0x00000000, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_depth = {ps_depth_code, sizeof(ps_depth_code)};
    static const D3D12_INPUT_ELEMENT_DESC layout_desc[] =
    {
        {"position", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
    };
    static const struct vec4 vertices[] =
    {
        {-1.0f, -1.0f,  0.0f, 1.0f},
        {-1.0f,  1.0f,  0.0f, 1.0f},
        { 1.0f, -1.0f,  0.0f, 1.0f},
        { 1.0f,  1.0f,  0.0f, 1.0f},

        {-1.0f, -1.0f,  0.5f, 1.0f},
        {-1.0f,  1.0f,  0.5f, 1.0f},
        { 1.0f, -1.0f,  0.5f, 1.0f},
        { 1.0f,  1.0f,  0.5f, 1.0f},

        {-1.0f, -1.0f, -0.5f, 1.0f},
        {-1.0f,  1.0f, -0.5f, 1.0f},
        { 1.0f, -1.0f, -0.5f, 1.0f},
        { 1.0f,  1.0f, -0.5f, 1.0f},

        {-1.0f, -1.0f,  1.0f, 1.0f},
        {-1.0f,  1.0f,  1.0f, 1.0f},
        { 1.0f, -1.0f,  1.0f, 1.0f},
        { 1.0f,  1.0f,  1.0f, 1.0f},

        {-1.0f, -1.0f,  1.5f, 1.0f},
        {-1.0f,  1.0f,  1.5f, 1.0f},
        { 1.0f, -1.0f,  1.5f, 1.0f},
        { 1.0f,  1.0f,  1.5f, 1.0f},
    };
    struct result
    {
        uint32_t expected_color;
        float expected_depth;
    };
    static const struct
    {
        struct result depth_clip;
        struct result no_depth_clip;
    }
    tests[] =
    {
        {{0xff00ff00, 0.0f  }, {0xff00ff00, 0.0f}},
        {{0xff00ff00, 0.5f  }, {0xff00ff00, 0.5f}},
        {{0xffffffff, 0.125f}, {0xff00ff00, 0.0f}},
        {{0xff00ff00, 1.0f  }, {0xff00ff00, 1.0f}},
        {{0xffffffff, 0.125f}, {0xff00ff00, 1.0f}},
    };

    memset(&desc, 0, sizeof(desc));
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    command_list = context.list;
    queue = context.queue;

    context.root_signature = create_32bit_constants_root_signature_(__FILE__, __LINE__, context.device,
            0, 4, D3D12_SHADER_VISIBILITY_PIXEL, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);

    init_depth_stencil(&ds, context.device, 32, 32, 1, 1, DXGI_FORMAT_D32_FLOAT, 0, NULL);

    vb = create_upload_buffer(context.device, sizeof(vertices), vertices);
    vbv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vb);
    vbv.StrideInBytes = sizeof(*vertices);
    vbv.SizeInBytes = sizeof(vertices);

    input_layout.pInputElementDescs = layout_desc;
    input_layout.NumElements = ARRAY_SIZE(layout_desc);
    init_pipeline_state_desc(&pso_desc, context.root_signature,
            context.render_target_desc.Format, &vs, NULL, &input_layout);
    pso_desc.RasterizerState.DepthClipEnable = true;
    pso_desc.DepthStencilState.DepthEnable = true;
    pso_desc.DepthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
    pso_desc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS;
    pso_desc.DSVFormat = DXGI_FORMAT_D32_FLOAT;
    hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
    ok(hr == S_OK, "Failed to create pipeline, hr %#x.\n", hr);

    for (i = 0; i < ARRAY_SIZE(tests); ++i)
    {
        const struct result *result = &tests[i].depth_clip;

        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
        ID3D12GraphicsCommandList_ClearDepthStencilView(command_list,
                ds.dsv_handle, D3D12_CLEAR_FLAG_DEPTH, 0.125f, 0, 0, NULL);

        ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, &ds.dsv_handle);
        ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
        ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
        ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, 1, &vbv);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
        ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 1, 4 * i, 0);

        transition_resource_state(command_list, ds.texture,
                D3D12_RESOURCE_STATE_DEPTH_WRITE, D3D12_RESOURCE_STATE_COPY_SOURCE);
        check_sub_resource_float(ds.texture, 0, queue, command_list, result->expected_depth, 2);
        reset_command_list(command_list, context.allocator);

        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
        check_sub_resource_uint(context.render_target, 0, queue, command_list, result->expected_color, 0);

        reset_command_list(command_list, context.allocator);
        transition_resource_state(command_list, ds.texture,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_DEPTH_WRITE);
        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
    }

    ID3D12PipelineState_Release(context.pipeline_state);
    pso_desc.RasterizerState.DepthClipEnable = false;
    hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
    ok(hr == S_OK, "Failed to create pipeline, hr %#x.\n", hr);

    for (i = 0; i < ARRAY_SIZE(tests); ++i)
    {
        const struct result *result = &tests[i].no_depth_clip;

        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
        ID3D12GraphicsCommandList_ClearDepthStencilView(command_list,
                ds.dsv_handle, D3D12_CLEAR_FLAG_DEPTH, 0.125f, 0, 0, NULL);

        ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, &ds.dsv_handle);
        ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
        ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
        ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, 1, &vbv);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
        ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 1, 4 * i, 0);

        transition_resource_state(command_list, ds.texture,
                D3D12_RESOURCE_STATE_DEPTH_WRITE, D3D12_RESOURCE_STATE_COPY_SOURCE);
        check_sub_resource_float(ds.texture, 0, queue, command_list, result->expected_depth, 2);
        reset_command_list(command_list, context.allocator);

        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
        check_sub_resource_uint(context.render_target, 0, queue, command_list, result->expected_color, 0);

        reset_command_list(command_list, context.allocator);
        transition_resource_state(command_list, ds.texture,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_DEPTH_WRITE);
        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
    }

    ID3D12PipelineState_Release(context.pipeline_state);
    pso_desc.PS = ps_depth;
    pso_desc.RasterizerState.DepthClipEnable = true;
    hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
    ok(hr == S_OK, "Failed to create pipeline, hr %#x.\n", hr);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
    ID3D12GraphicsCommandList_ClearDepthStencilView(command_list,
            ds.dsv_handle, D3D12_CLEAR_FLAG_DEPTH, 0.125f, 0, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, &ds.dsv_handle);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
    ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, 1, &vbv);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    depth = 2.0f;
    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 0, 1, &depth, 0);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 1, 0, 0);

    transition_resource_state(command_list, ds.texture,
            D3D12_RESOURCE_STATE_DEPTH_WRITE, D3D12_RESOURCE_STATE_COPY_SOURCE);
    bug_if(!is_depth_clip_enable_supported(context.device))
    check_sub_resource_float(ds.texture, 0, queue, command_list, 1.0f, 2);
    reset_command_list(command_list, context.allocator);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);

    ID3D12Resource_Release(vb);
    destroy_depth_stencil(&ds);
    destroy_test_context(&context);
}

#define check_depth_stencil_sampling(a, b, c, d, e, f, g, h) \
        check_depth_stencil_sampling_(__FILE__, __LINE__, a, b, c, d, e, f, g, h)
static void check_depth_stencil_sampling_(const char *file, unsigned int line,
        struct test_context *context, ID3D12PipelineState *pso, ID3D12Resource *cb, ID3D12Resource *texture,
        D3D12_CPU_DESCRIPTOR_HANDLE dsv_handle, ID3D12DescriptorHeap *srv_heap, float expected_value, bool is_bug)
{
    static const float black[] = {0.0f, 0.0f, 0.0f, 0.0f};
    ID3D12GraphicsCommandList *command_list;
    ID3D12CommandQueue *queue;
    HRESULT hr;

    command_list = context->list;
    queue = context->queue;

    transition_sub_resource_state(command_list, texture, 0,
            D3D12_RESOURCE_STATE_DEPTH_WRITE, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context->rtv, black, 0, NULL);
    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context->rtv, false, NULL);

    ID3D12GraphicsCommandList_SetPipelineState(command_list, pso);

    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context->root_signature);
    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &srv_heap);
    ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0,
            ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(srv_heap));
    ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 1,
            ID3D12Resource_GetGPUVirtualAddress(cb));
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context->viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context->scissor_rect);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_sub_resource_state(command_list, context->render_target, 0,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    bug_if(is_bug)
    check_sub_resource_float_(file, line, context->render_target, 0, queue, command_list, expected_value, 2);

    reset_command_list(command_list, context->allocator);
    transition_sub_resource_state(command_list, context->render_target, 0,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
    transition_sub_resource_state(command_list, texture, 0,
            D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_DEPTH_WRITE);
    hr = ID3D12GraphicsCommandList_Close(command_list);
    ok_(file, line)(SUCCEEDED(hr), "Failed to close command list, hr %#x.\n", hr);
    exec_command_list(queue, command_list);
    wait_queue_idle(context->device, queue);
}

static void test_depth_stencil_sampling(void)
{
    ID3D12PipelineState *pso_compare, *pso_depth, *pso_stencil, *pso_depth_stencil;
    D3D12_CPU_DESCRIPTOR_HANDLE dsv_handle, srv_cpu_handle;
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    D3D12_STATIC_SAMPLER_DESC sampler_desc[2];
    D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc;
    D3D12_DESCRIPTOR_RANGE descriptor_range;
    D3D12_ROOT_PARAMETER root_parameters[2];
    ID3D12GraphicsCommandList *command_list;
    struct depth_stencil_resource ds;
    ID3D12DescriptorHeap *srv_heap;
    struct test_context_desc desc;
    ID3D12Resource *cb, *texture;
    unsigned int descriptor_size;
    struct test_context context;
    struct vec4 ps_constant;
    ID3D12Device *device;
    unsigned int i;
    HRESULT hr;

    static const DWORD ps_compare_code[] =
    {
#if 0
        Texture2D t;
        SamplerComparisonState s : register(s1);

        float ref;

        float4 main(float4 position : SV_Position) : SV_Target
        {
            return t.SampleCmp(s, float2(position.x / 640.0f, position.y / 480.0f), ref);
        }
#endif
        0x43425844, 0xbea899fb, 0xcbeaa744, 0xbad6daa0, 0xd4363d30, 0x00000001, 0x00000164, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000030f, 0x505f5653, 0x7469736f, 0x006e6f69,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x52444853, 0x000000c8, 0x00000040,
        0x00000032, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x0300085a, 0x00106000, 0x00000001,
        0x04001858, 0x00107000, 0x00000000, 0x00005555, 0x04002064, 0x00101032, 0x00000000, 0x00000001,
        0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000001, 0x0a000038, 0x00100032, 0x00000000,
        0x00101046, 0x00000000, 0x00004002, 0x3acccccd, 0x3b088889, 0x00000000, 0x00000000, 0x0c000046,
        0x00100012, 0x00000000, 0x00100046, 0x00000000, 0x00107006, 0x00000000, 0x00106000, 0x00000001,
        0x0020800a, 0x00000000, 0x00000000, 0x05000036, 0x001020f2, 0x00000000, 0x00100006, 0x00000000,
        0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_compare = {ps_compare_code, sizeof(ps_compare_code)};
    static const DWORD ps_sample_code[] =
    {
#if 0
        Texture2D t;
        SamplerState s;

        float4 main(float4 position : SV_Position) : SV_Target
        {
            return t.Sample(s, float2(position.x / 640.0f, position.y / 480.0f));
        }
#endif
        0x43425844, 0x7472c092, 0x5548f00e, 0xf4e007f1, 0x5970429c, 0x00000001, 0x00000134, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000030f, 0x505f5653, 0x7469736f, 0x006e6f69,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x52444853, 0x00000098, 0x00000040,
        0x00000026, 0x0300005a, 0x00106000, 0x00000000, 0x04001858, 0x00107000, 0x00000000, 0x00005555,
        0x04002064, 0x00101032, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000, 0x02000068,
        0x00000001, 0x0a000038, 0x00100032, 0x00000000, 0x00101046, 0x00000000, 0x00004002, 0x3acccccd,
        0x3b088889, 0x00000000, 0x00000000, 0x09000045, 0x001020f2, 0x00000000, 0x00100046, 0x00000000,
        0x00107e46, 0x00000000, 0x00106000, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_sample = {ps_sample_code, sizeof(ps_sample_code)};
    static const DWORD ps_stencil_code[] =
    {
#if 0
        Texture2D<uint4> t : register(t1);

        float4 main(float4 position : SV_Position) : SV_Target
        {
            float2 s;
            t.GetDimensions(s.x, s.y);
            return t.Load(int3(float3(s.x * position.x / 640.0f, s.y * position.y / 480.0f, 0))).y;
        }
#endif
        0x43425844, 0x78574912, 0x1b7763f5, 0x0124de83, 0x39954d6c, 0x00000001, 0x000001a0, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000030f, 0x505f5653, 0x7469736f, 0x006e6f69,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x52444853, 0x00000104, 0x00000040,
        0x00000041, 0x04001858, 0x00107000, 0x00000001, 0x00004444, 0x04002064, 0x00101032, 0x00000000,
        0x00000001, 0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000001, 0x0700003d, 0x001000f2,
        0x00000000, 0x00004001, 0x00000000, 0x00107e46, 0x00000001, 0x07000038, 0x00100032, 0x00000000,
        0x00100046, 0x00000000, 0x00101046, 0x00000000, 0x0a000038, 0x00100032, 0x00000000, 0x00100046,
        0x00000000, 0x00004002, 0x3acccccd, 0x3b088889, 0x00000000, 0x00000000, 0x0500001b, 0x00100032,
        0x00000000, 0x00100046, 0x00000000, 0x08000036, 0x001000c2, 0x00000000, 0x00004002, 0x00000000,
        0x00000000, 0x00000000, 0x00000000, 0x0700002d, 0x001000f2, 0x00000000, 0x00100e46, 0x00000000,
        0x00107e46, 0x00000001, 0x05000056, 0x001020f2, 0x00000000, 0x00100556, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_stencil = {ps_stencil_code, sizeof(ps_stencil_code)};
    static const DWORD ps_depth_stencil_code[] =
    {
#if 0
        SamplerState samp;
        Texture2D depth_tex;
        Texture2D<uint4> stencil_tex;

        float main(float4 position: SV_Position) : SV_Target
        {
            float2 s, p;
            float depth, stencil;
            depth_tex.GetDimensions(s.x, s.y);
            p = float2(s.x * position.x / 640.0f, s.y * position.y / 480.0f);
            depth = depth_tex.Sample(samp, p).r;
            stencil = stencil_tex.Load(int3(float3(p.x, p.y, 0))).y;
            return depth + stencil;
        }
#endif
        0x43425844, 0x348f8377, 0x977d1ee0, 0x8cca4f35, 0xff5c5afc, 0x00000001, 0x000001fc, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000030f, 0x505f5653, 0x7469736f, 0x006e6f69,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x00000e01, 0x545f5653, 0x65677261, 0xabab0074, 0x52444853, 0x00000160, 0x00000040,
        0x00000058, 0x0300005a, 0x00106000, 0x00000000, 0x04001858, 0x00107000, 0x00000000, 0x00005555,
        0x04001858, 0x00107000, 0x00000001, 0x00004444, 0x04002064, 0x00101032, 0x00000000, 0x00000001,
        0x03000065, 0x00102012, 0x00000000, 0x02000068, 0x00000002, 0x0700003d, 0x001000f2, 0x00000000,
        0x00004001, 0x00000000, 0x00107e46, 0x00000000, 0x07000038, 0x00100032, 0x00000000, 0x00100046,
        0x00000000, 0x00101046, 0x00000000, 0x0a000038, 0x00100032, 0x00000000, 0x00100046, 0x00000000,
        0x00004002, 0x3acccccd, 0x3b088889, 0x00000000, 0x00000000, 0x0500001b, 0x00100032, 0x00000001,
        0x00100046, 0x00000000, 0x09000045, 0x001000f2, 0x00000000, 0x00100046, 0x00000000, 0x00107e46,
        0x00000000, 0x00106000, 0x00000000, 0x08000036, 0x001000c2, 0x00000001, 0x00004002, 0x00000000,
        0x00000000, 0x00000000, 0x00000000, 0x0700002d, 0x001000f2, 0x00000001, 0x00100e46, 0x00000001,
        0x00107e46, 0x00000001, 0x05000056, 0x00100022, 0x00000000, 0x0010001a, 0x00000001, 0x07000000,
        0x00102012, 0x00000000, 0x0010001a, 0x00000000, 0x0010000a, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_depth_stencil = {ps_depth_stencil_code, sizeof(ps_depth_stencil_code)};
    static const struct test
    {
        DXGI_FORMAT typeless_format;
        DXGI_FORMAT dsv_format;
        DXGI_FORMAT depth_view_format;
        DXGI_FORMAT stencil_view_format;
        bool is_mvk_bug;
        bool is_qualcomm_todo;
    }
    tests[] =
    {
        {DXGI_FORMAT_R32G8X24_TYPELESS, DXGI_FORMAT_D32_FLOAT_S8X24_UINT,
                DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS, DXGI_FORMAT_X32_TYPELESS_G8X24_UINT, false, true},
        {DXGI_FORMAT_R32_TYPELESS, DXGI_FORMAT_D32_FLOAT,
                DXGI_FORMAT_R32_FLOAT},
        {DXGI_FORMAT_R24G8_TYPELESS, DXGI_FORMAT_D24_UNORM_S8_UINT,
                DXGI_FORMAT_R24_UNORM_X8_TYPELESS, DXGI_FORMAT_X24_TYPELESS_G8_UINT},
        /* For 16 bit depth MoltenVK doesn't handle properly the case
         * when the comparison is tight (i.e., 0.5 <= 0.5). */
        {DXGI_FORMAT_R16_TYPELESS, DXGI_FORMAT_D16_UNORM,
                DXGI_FORMAT_R16_UNORM, DXGI_FORMAT_UNKNOWN, true},
    };

    memset(&desc, 0, sizeof(desc));
    desc.rt_width = 640;
    desc.rt_height = 480;
    desc.rt_format = DXGI_FORMAT_R32_FLOAT;
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    device = context.device;
    command_list = context.list;

    sampler_desc[0].Filter = D3D12_FILTER_MIN_MAG_MIP_POINT;
    sampler_desc[0].AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
    sampler_desc[0].AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
    sampler_desc[0].AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
    sampler_desc[0].MipLODBias = 0.0f;
    sampler_desc[0].MaxAnisotropy = 0;
    sampler_desc[0].ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER;
    sampler_desc[0].BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE;
    sampler_desc[0].MinLOD = 0.0f;
    sampler_desc[0].MaxLOD = 0.0f;
    sampler_desc[0].ShaderRegister = 0;
    sampler_desc[0].RegisterSpace = 0;
    sampler_desc[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;

    sampler_desc[1] = sampler_desc[0];
    sampler_desc[1].Filter = D3D12_FILTER_COMPARISON_MIN_MAG_MIP_POINT;
    sampler_desc[1].ComparisonFunc = D3D12_COMPARISON_FUNC_GREATER;
    sampler_desc[1].ShaderRegister = 1;

    descriptor_range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
    descriptor_range.NumDescriptors = 2;
    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_PIXEL;

    root_parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
    root_parameters[1].Descriptor.ShaderRegister = 0;
    root_parameters[1].Descriptor.RegisterSpace = 0;
    root_parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;

    memset(&root_signature_desc, 0, sizeof(root_signature_desc));
    root_signature_desc.NumParameters = 2;
    root_signature_desc.pParameters = root_parameters;
    root_signature_desc.NumStaticSamplers = 2;
    root_signature_desc.pStaticSamplers = sampler_desc;
    hr = create_root_signature(device, &root_signature_desc, &context.root_signature);
    ok(SUCCEEDED(hr), "Failed to create root signature, hr %#x.\n", hr);

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

    pso_compare = create_pipeline_state(device,
            context.root_signature, context.render_target_desc.Format, NULL, &ps_compare, NULL);
    pso_depth = create_pipeline_state(device,
            context.root_signature, context.render_target_desc.Format, NULL, &ps_sample, NULL);
    pso_stencil = create_pipeline_state(device,
            context.root_signature, context.render_target_desc.Format, NULL, &ps_stencil, NULL);
    pso_depth_stencil = create_pipeline_state(device,
            context.root_signature, context.render_target_desc.Format, NULL, &ps_depth_stencil, NULL);

    srv_heap = create_gpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 2);
    descriptor_size = ID3D12Device_GetDescriptorHandleIncrementSize(device,
            D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);

    memset(&ps_constant, 0, sizeof(ps_constant));
    cb = create_upload_buffer(device, sizeof(ps_constant), &ps_constant);

    hr = ID3D12GraphicsCommandList_Close(command_list);
    ok(SUCCEEDED(hr), "Failed to close command list, hr %#x.\n", hr);

    for (i = 0; i < ARRAY_SIZE(tests); ++i)
    {
        bool supported = is_ds_format_supported(device, tests[i].dsv_format);

        vkd3d_test_push_context("Test %u", i);

        todo_if(tests[i].is_qualcomm_todo && is_qualcomm_device(device))
        ok(supported, "Depth/stencil format %#x is not supported.\n", tests[i].dsv_format);

        if (!supported)
        {
            vkd3d_test_pop_context();
            continue;
        }

        reset_command_list(command_list, context.allocator);

        init_depth_stencil(&ds, device, context.render_target_desc.Width,
                context.render_target_desc.Height, 1, 1, tests[i].typeless_format,
                tests[i].dsv_format, NULL);
        texture = ds.texture;
        dsv_handle = ds.dsv_handle;

        srv_cpu_handle = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(srv_heap);

        memset(&srv_desc, 0, sizeof(srv_desc));
        srv_desc.Format = tests[i].depth_view_format;
        srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
        srv_desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
        srv_desc.Texture2D.MipLevels = 1;
        ID3D12Device_CreateShaderResourceView(device, texture, &srv_desc, srv_cpu_handle);
        srv_cpu_handle.ptr += descriptor_size;
        ID3D12Device_CreateShaderResourceView(device, NULL, &srv_desc, srv_cpu_handle);

        ps_constant.x = 0.5f;
        update_buffer_data(cb, 0, sizeof(ps_constant), &ps_constant);

        /* pso_compare */
        ID3D12GraphicsCommandList_ClearDepthStencilView(command_list, dsv_handle,
                D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, NULL);
        check_depth_stencil_sampling(&context, pso_compare, cb, texture, dsv_handle, srv_heap, 0.0f, false);

        reset_command_list(command_list, context.allocator);
        ID3D12GraphicsCommandList_ClearDepthStencilView(command_list, dsv_handle,
                D3D12_CLEAR_FLAG_DEPTH, 0.0f, 0, 0, NULL);
        check_depth_stencil_sampling(&context, pso_compare, cb, texture, dsv_handle, srv_heap, 1.0f, false);

        reset_command_list(command_list, context.allocator);
        ID3D12GraphicsCommandList_ClearDepthStencilView(command_list, dsv_handle,
                D3D12_CLEAR_FLAG_DEPTH, 0.5f, 0, 0, NULL);
        check_depth_stencil_sampling(&context, pso_compare, cb, texture, dsv_handle, srv_heap, 0.0f,
                tests[i].is_mvk_bug && is_mvk_device(device));

        reset_command_list(command_list, context.allocator);
        ID3D12GraphicsCommandList_ClearDepthStencilView(command_list, dsv_handle,
                D3D12_CLEAR_FLAG_DEPTH, 0.6f, 0, 0, NULL);
        check_depth_stencil_sampling(&context, pso_compare, cb, texture, dsv_handle, srv_heap, 0.0f, false);

        ps_constant.x = 0.7f;
        update_buffer_data(cb, 0, sizeof(ps_constant), &ps_constant);

        reset_command_list(command_list, context.allocator);
        check_depth_stencil_sampling(&context, pso_compare, cb, texture, dsv_handle, srv_heap, 1.0f, false);

        /* pso_depth */
        reset_command_list(command_list, context.allocator);
        ID3D12GraphicsCommandList_ClearDepthStencilView(command_list, dsv_handle,
                D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, NULL);
        check_depth_stencil_sampling(&context, pso_depth, cb, texture, dsv_handle, srv_heap, 1.0f, false);

        reset_command_list(command_list, context.allocator);
        ID3D12GraphicsCommandList_ClearDepthStencilView(command_list, dsv_handle,
                D3D12_CLEAR_FLAG_DEPTH, 0.2f, 0, 0, NULL);
        check_depth_stencil_sampling(&context, pso_depth, cb, texture, dsv_handle, srv_heap, 0.2f, false);

        if (!tests[i].stencil_view_format)
        {
            destroy_depth_stencil(&ds);
            vkd3d_test_pop_context();
            continue;
        }
        if (is_amd_windows_device(device))
        {
            skip("Reads from depth/stencil shader resource views return stale values on some AMD drivers.\n");
            destroy_depth_stencil(&ds);
            vkd3d_test_pop_context();
            continue;
        }

        srv_desc.Format = tests[i].stencil_view_format;
        srv_desc.Texture2D.PlaneSlice = 1;
        ID3D12Device_CreateShaderResourceView(device, texture, &srv_desc, srv_cpu_handle);

        /* pso_stencil */
        reset_command_list(command_list, context.allocator);
        ID3D12GraphicsCommandList_ClearDepthStencilView(command_list, dsv_handle,
                D3D12_CLEAR_FLAG_STENCIL, 0.0f, 0, 0, NULL);
        check_depth_stencil_sampling(&context, pso_stencil, cb, texture, dsv_handle, srv_heap, 0.0f, false);

        reset_command_list(command_list, context.allocator);
        ID3D12GraphicsCommandList_ClearDepthStencilView(command_list, dsv_handle,
                D3D12_CLEAR_FLAG_STENCIL, 0.0f, 100, 0, NULL);
        check_depth_stencil_sampling(&context, pso_stencil, cb, texture, dsv_handle, srv_heap, 100.0f, false);

        reset_command_list(command_list, context.allocator);
        ID3D12GraphicsCommandList_ClearDepthStencilView(command_list, dsv_handle,
                D3D12_CLEAR_FLAG_STENCIL, 0.0f, 255, 0, NULL);
        check_depth_stencil_sampling(&context, pso_stencil, cb, texture, dsv_handle, srv_heap, 255.0f, false);

        /* pso_depth_stencil */
        reset_command_list(command_list, context.allocator);
        ID3D12GraphicsCommandList_ClearDepthStencilView(command_list, dsv_handle,
                D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, 0.3f, 3, 0, NULL);
        check_depth_stencil_sampling(&context, pso_depth_stencil, cb, texture, dsv_handle, srv_heap, 3.3f, false);

        reset_command_list(command_list, context.allocator);
        ID3D12GraphicsCommandList_ClearDepthStencilView(command_list, dsv_handle,
                D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, 1.0f, 3, 0, NULL);
        check_depth_stencil_sampling(&context, pso_depth_stencil, cb, texture, dsv_handle, srv_heap, 4.0f, false);

        reset_command_list(command_list, context.allocator);
        ID3D12GraphicsCommandList_ClearDepthStencilView(command_list, dsv_handle,
                D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, 0.0f, 0, 0, NULL);
        check_depth_stencil_sampling(&context, pso_depth_stencil, cb, texture, dsv_handle, srv_heap, 0.0f, false);

        destroy_depth_stencil(&ds);

        vkd3d_test_pop_context();
    }

    ID3D12Resource_Release(cb);
    ID3D12DescriptorHeap_Release(srv_heap);
    ID3D12PipelineState_Release(pso_compare);
    ID3D12PipelineState_Release(pso_depth);
    ID3D12PipelineState_Release(pso_stencil);
    ID3D12PipelineState_Release(pso_depth_stencil);
    destroy_test_context(&context);
}

static void test_depth_load(void)
{
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    D3D12_DESCRIPTOR_RANGE descriptor_ranges[2];
    D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc;
    D3D12_ROOT_PARAMETER root_parameters[1];
    ID3D12GraphicsCommandList *command_list;
    ID3D12PipelineState *pipeline_state;
    struct depth_stencil_resource ds;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12DescriptorHeap *heap;
    ID3D12CommandQueue *queue;
    ID3D12Resource *texture;
    ID3D12Device *device;
    unsigned int i;
    HRESULT hr;

    static const DWORD cs_code[] =
    {
#if 0
        Texture2D<float> t;
        RWTexture2D<float> u;

        [numthreads(1, 1, 1)]
        void main(uint2 id : SV_GroupID)
        {
            u[id] = t[id];
        }
#endif
        0x43425844, 0x6ddce3d0, 0x24b47ad3, 0x7f6772d2, 0x6a644890, 0x00000001, 0x00000110, 0x00000003,
        0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x000000bc, 0x00050050, 0x0000002f, 0x0100086a,
        0x04001858, 0x00107000, 0x00000000, 0x00005555, 0x0400189c, 0x0011e000, 0x00000000, 0x00005555,
        0x0200005f, 0x00021032, 0x02000068, 0x00000001, 0x0400009b, 0x00000001, 0x00000001, 0x00000001,
        0x04000036, 0x00100032, 0x00000000, 0x00021046, 0x08000036, 0x001000c2, 0x00000000, 0x00004002,
        0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x8900002d, 0x800000c2, 0x00155543, 0x00100012,
        0x00000000, 0x00100e46, 0x00000000, 0x00107e46, 0x00000000, 0x060000a4, 0x0011e0f2, 0x00000000,
        0x00021546, 0x00100006, 0x00000000, 0x0100003e,
    };
    static const DWORD ps_code[] =
    {
#if 0
        Texture2D<float> t;

        float main(float4 position : SV_Position) : SV_Target
        {
            return t[int2(position.x, position.y)];
        }
#endif
        0x43425844, 0x0beace24, 0x5e10b05b, 0x742de364, 0xb2b65d2b, 0x00000001, 0x00000140, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000030f, 0x505f5653, 0x7469736f, 0x006e6f69,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x00000e01, 0x545f5653, 0x65677261, 0xabab0074, 0x52444853, 0x000000a4, 0x00000040,
        0x00000029, 0x04001858, 0x00107000, 0x00000000, 0x00005555, 0x04002064, 0x00101032, 0x00000000,
        0x00000001, 0x03000065, 0x00102012, 0x00000000, 0x02000068, 0x00000001, 0x0500001b, 0x00100032,
        0x00000000, 0x00101046, 0x00000000, 0x08000036, 0x001000c2, 0x00000000, 0x00004002, 0x00000000,
        0x00000000, 0x00000000, 0x00000000, 0x0700002d, 0x001000f2, 0x00000000, 0x00100e46, 0x00000000,
        0x00107e46, 0x00000000, 0x05000036, 0x00102012, 0x00000000, 0x0010000a, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    static const float tests[] = {0.00f, 0.25f, 0.75f, 1.00f};

    memset(&desc, 0, sizeof(desc));
    desc.rt_format = DXGI_FORMAT_R32_FLOAT;
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    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 = 0;
    descriptor_ranges[1].RegisterSpace = 0;
    descriptor_ranges[1].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
    root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameters[0].DescriptorTable.NumDescriptorRanges = 2;
    root_parameters[0].DescriptorTable.pDescriptorRanges = descriptor_ranges;
    root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    root_signature_desc.NumParameters = 1;
    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(device, &root_signature_desc, &context.root_signature);
    ok(SUCCEEDED(hr), "Failed to create root signature, hr %#x.\n", hr);

    pipeline_state = create_compute_pipeline_state(device, context.root_signature,
            shader_bytecode(cs_code, sizeof(cs_code)));
    context.pipeline_state = create_pipeline_state(context.device,
            context.root_signature, context.render_target_desc.Format, NULL, &ps, NULL);

    heap = create_gpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 2);

    init_depth_stencil(&ds, device, context.render_target_desc.Width,
            context.render_target_desc.Height, 1, 1, DXGI_FORMAT_R32_TYPELESS,
            DXGI_FORMAT_D32_FLOAT, NULL);
    memset(&srv_desc, 0, sizeof(srv_desc));
    srv_desc.Format = DXGI_FORMAT_R32_FLOAT;
    srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
    srv_desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
    srv_desc.Texture2D.MipLevels = 1;
    ID3D12Device_CreateShaderResourceView(device, ds.texture, &srv_desc,
            get_cpu_descriptor_handle(&context, heap, 0));

    texture = create_default_texture(device, 32, 32, DXGI_FORMAT_R16_UNORM,
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
    ID3D12Device_CreateUnorderedAccessView(device, texture, NULL, NULL,
            get_cpu_descriptor_handle(&context, heap, 1));

    for (i = 0; i < ARRAY_SIZE(tests); ++i)
    {
        vkd3d_test_push_context("Test %u", i);

        ID3D12GraphicsCommandList_ClearDepthStencilView(command_list, ds.dsv_handle,
                D3D12_CLEAR_FLAG_DEPTH, tests[i], 0, 0, NULL);
        transition_sub_resource_state(command_list, ds.texture, 0,
                D3D12_RESOURCE_STATE_DEPTH_WRITE, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);

        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
        ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
        ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);

        ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &heap);

        ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
        ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0,
                ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(heap));
        ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

        ID3D12GraphicsCommandList_SetPipelineState(command_list, pipeline_state);
        ID3D12GraphicsCommandList_SetComputeRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(command_list, 0,
                ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(heap));
        ID3D12GraphicsCommandList_Dispatch(command_list, 32, 32, 1);

        transition_sub_resource_state(command_list, context.render_target, 0,
                D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
        check_sub_resource_float(context.render_target, 0, queue, command_list, tests[i], 2);

        reset_command_list(command_list, context.allocator);
        transition_sub_resource_state(command_list, texture, 0,
                D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
        check_sub_resource_uint16(texture, 0, queue, command_list, tests[i] * UINT16_MAX, 2);

        reset_command_list(command_list, context.allocator);
        transition_sub_resource_state(command_list, context.render_target, 0,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
        transition_sub_resource_state(command_list, texture, 0,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
        transition_sub_resource_state(command_list, ds.texture, 0,
                D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_DEPTH_WRITE);

        vkd3d_test_pop_context();
    }

    destroy_depth_stencil(&ds);
    ID3D12Resource_Release(texture);
    ID3D12DescriptorHeap_Release(heap);
    ID3D12PipelineState_Release(pipeline_state);
    destroy_test_context(&context);
}

static void test_depth_read_only_view(void)
{
    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
    ID3D12GraphicsCommandList *command_list;
    D3D12_DEPTH_STENCIL_VIEW_DESC dsv_desc;
    D3D12_CPU_DESCRIPTOR_HANDLE dsv_handle;
    struct depth_stencil_resource ds;
    struct test_context_desc desc;
    D3D12_CLEAR_VALUE clear_value;
    struct test_context context;
    ID3D12DescriptorHeap *heap;
    ID3D12CommandQueue *queue;
    ID3D12Device *device;
    HRESULT hr;

    static const DWORD ps_code[] =
    {
#if 0
        float4 color;

        float4 main(float4 position : SV_POSITION) : SV_Target
        {
            return color;
        }
#endif
        0x43425844, 0xd18ead43, 0x8b8264c1, 0x9c0a062d, 0xfc843226, 0x00000001, 0x000000e0, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x505f5653, 0x5449534f, 0x004e4f49,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000044, 0x00000050,
        0x00000011, 0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2,
        0x00000000, 0x06000036, 0x001020f2, 0x00000000, 0x00208e46, 0x00000000, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    static const float green[] = {0.0f, 1.0f, 0.0f, 1.0f};
    static const float red[] = {1.0f, 0.0f, 0.0f, 1.0f};

    memset(&desc, 0, sizeof(desc));
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    context.root_signature = create_32bit_constants_root_signature(device,
            0, 4, D3D12_SHADER_VISIBILITY_PIXEL);

    init_pipeline_state_desc(&pso_desc, context.root_signature,
            context.render_target_desc.Format, NULL, &ps, NULL);
    pso_desc.DSVFormat = DXGI_FORMAT_D32_FLOAT;
    pso_desc.DepthStencilState.DepthEnable = true;
    pso_desc.DepthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ZERO;
    pso_desc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_GREATER;
    hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
    ok(SUCCEEDED(hr), "Failed to create graphics pipeline state, hr %#x.\n", hr);

    heap = create_cpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_DSV, 1);

    clear_value.Format = DXGI_FORMAT_D32_FLOAT;
    clear_value.DepthStencil.Depth = 0.5f;
    clear_value.DepthStencil.Stencil = 0;
    init_depth_stencil(&ds, device, context.render_target_desc.Width,
            context.render_target_desc.Height, 1, 1, DXGI_FORMAT_R32_TYPELESS,
            DXGI_FORMAT_D32_FLOAT, &clear_value);
    memset(&dsv_desc, 0, sizeof(dsv_desc));
    dsv_desc.Format = DXGI_FORMAT_D32_FLOAT;
    dsv_desc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
    dsv_desc.Flags = D3D12_DSV_FLAG_READ_ONLY_DEPTH;
    dsv_handle = get_cpu_descriptor_handle(&context, heap, 0);
    ID3D12Device_CreateDepthStencilView(device, ds.texture, &dsv_desc, dsv_handle);

    ID3D12GraphicsCommandList_ClearDepthStencilView(command_list, ds.dsv_handle,
            D3D12_CLEAR_FLAG_DEPTH, 0.5f, 0, 0, NULL);
    transition_sub_resource_state(command_list, ds.texture, 0,
            D3D12_RESOURCE_STATE_DEPTH_WRITE, D3D12_RESOURCE_STATE_DEPTH_READ);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, &dsv_handle);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);

    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

    context.viewport.MinDepth = 0.6f;
    context.viewport.MaxDepth = 0.6f;
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 0, 4, green, 0);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    context.viewport.MinDepth = 0.4f;
    context.viewport.MaxDepth = 0.4f;
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 0, 4, red, 0);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_sub_resource_state(command_list, context.render_target, 0,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);

    reset_command_list(command_list, context.allocator);
    transition_sub_resource_state(command_list, ds.texture, 0,
            D3D12_RESOURCE_STATE_DEPTH_READ, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_float(ds.texture, 0, queue, command_list, 0.5f, 2);

    destroy_depth_stencil(&ds);
    ID3D12DescriptorHeap_Release(heap);
    destroy_test_context(&context);
}

static void test_stencil_load(void)
{
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    D3D12_DESCRIPTOR_RANGE descriptor_ranges[2];
    D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc;
    D3D12_ROOT_PARAMETER root_parameters[1];
    ID3D12GraphicsCommandList *command_list;
    ID3D12PipelineState *pipeline_state;
    struct depth_stencil_resource ds;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12DescriptorHeap *heap;
    ID3D12CommandQueue *queue;
    struct uvec4 uvec4 = {0};
    ID3D12Resource *texture;
    ID3D12Device *device;
    unsigned int i;
    HRESULT hr;

    static const DWORD cs_code[] =
    {
#if 0
        Texture2D<uint4> t;
        RWTexture2D<uint4> u;

        [numthreads(1, 1, 1)]
        void main(uint2 id : SV_GroupID)
        {
            u[id] = t[id];
        }
#endif
        0x43425844, 0x0b41fa64, 0xd64df766, 0xc4c98283, 0xb810dc2b, 0x00000001, 0x00000110, 0x00000003,
        0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x000000bc, 0x00050050, 0x0000002f, 0x0100086a,
        0x04001858, 0x00107000, 0x00000000, 0x00004444, 0x0400189c, 0x0011e000, 0x00000000, 0x00004444,
        0x0200005f, 0x00021032, 0x02000068, 0x00000001, 0x0400009b, 0x00000001, 0x00000001, 0x00000001,
        0x04000036, 0x00100032, 0x00000000, 0x00021046, 0x08000036, 0x001000c2, 0x00000000, 0x00004002,
        0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x8900002d, 0x800000c2, 0x00111103, 0x001000f2,
        0x00000000, 0x00100e46, 0x00000000, 0x00107e46, 0x00000000, 0x060000a4, 0x0011e0f2, 0x00000000,
        0x00021546, 0x00100e46, 0x00000000, 0x0100003e,
    };
    static const DWORD ps_code[] =
    {
#if 0
        Texture2D<uint4> t;

        uint4 main(float4 position : SV_Position) : SV_Target
        {
            return t[int2(position.x, position.y)];
        }
#endif
        0x43425844, 0x9ad18dbc, 0x98de0e54, 0xe3c15d5b, 0xac8b580a, 0x00000001, 0x00000138, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000030f, 0x505f5653, 0x7469736f, 0x006e6f69,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x0000009c, 0x00000050,
        0x00000027, 0x0100086a, 0x04001858, 0x00107000, 0x00000000, 0x00004444, 0x04002064, 0x00101032,
        0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000001, 0x0500001b,
        0x00100032, 0x00000000, 0x00101046, 0x00000000, 0x08000036, 0x001000c2, 0x00000000, 0x00004002,
        0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x8900002d, 0x800000c2, 0x00111103, 0x001020f2,
        0x00000000, 0x00100e46, 0x00000000, 0x00107e46, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    static unsigned int tests[] = {0, 50, 75, 100, 150, 200, 255};

    memset(&desc, 0, sizeof(desc));
    desc.rt_format = DXGI_FORMAT_R32G32B32A32_UINT;
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    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 = 0;
    descriptor_ranges[1].RegisterSpace = 0;
    descriptor_ranges[1].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
    root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameters[0].DescriptorTable.NumDescriptorRanges = 2;
    root_parameters[0].DescriptorTable.pDescriptorRanges = descriptor_ranges;
    root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    root_signature_desc.NumParameters = 1;
    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(device, &root_signature_desc, &context.root_signature);
    ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr);

    pipeline_state = create_compute_pipeline_state(device, context.root_signature,
            shader_bytecode(cs_code, sizeof(cs_code)));
    context.pipeline_state = create_pipeline_state(context.device,
            context.root_signature, context.render_target_desc.Format, NULL, &ps, NULL);

    heap = create_gpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 2);

    init_depth_stencil(&ds, device, context.render_target_desc.Width,
            context.render_target_desc.Height, 1, 1, DXGI_FORMAT_R24G8_TYPELESS, DXGI_FORMAT_D24_UNORM_S8_UINT, NULL);
    memset(&srv_desc, 0, sizeof(srv_desc));
    srv_desc.Format = DXGI_FORMAT_X24_TYPELESS_G8_UINT;
    srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
    srv_desc.Shader4ComponentMapping = D3D12_ENCODE_SHADER_4_COMPONENT_MAPPING(
            D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_1,
            D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_1,
            D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_1,
            D3D12_SHADER_COMPONENT_MAPPING_FROM_MEMORY_COMPONENT_1);
    srv_desc.Texture2D.MipLevels = 1;
    srv_desc.Texture2D.PlaneSlice = 1;
    ID3D12Device_CreateShaderResourceView(device, ds.texture, &srv_desc,
            get_cpu_descriptor_handle(&context, heap, 0));

    texture = create_default_texture(device, 32, 32, DXGI_FORMAT_R32G32B32A32_UINT,
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
    ID3D12Device_CreateUnorderedAccessView(device, texture, NULL, NULL,
            get_cpu_descriptor_handle(&context, heap, 1));

    for (i = 0; i < ARRAY_SIZE(tests); ++i)
    {
        vkd3d_test_push_context("Test %u", i);

        uvec4.x = uvec4.y = uvec4.z = uvec4.w = tests[i];

        ID3D12GraphicsCommandList_ClearDepthStencilView(command_list, ds.dsv_handle,
                D3D12_CLEAR_FLAG_STENCIL, 0.0f, tests[i], 0, NULL);
        transition_sub_resource_state(command_list, ds.texture, 0,
                D3D12_RESOURCE_STATE_DEPTH_WRITE, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);

        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
        ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
        ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);

        ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &heap);

        ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
        ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0,
                ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(heap));
        ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

        ID3D12GraphicsCommandList_SetPipelineState(command_list, pipeline_state);
        ID3D12GraphicsCommandList_SetComputeRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(command_list, 0,
                ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(heap));
        ID3D12GraphicsCommandList_Dispatch(command_list, 32, 32, 1);

        transition_sub_resource_state(command_list, context.render_target, 0,
                D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
        bug_if(is_mvk_device(context.device))
        check_sub_resource_uvec4(context.render_target, 0, queue, command_list, &uvec4);

        reset_command_list(command_list, context.allocator);
        transition_sub_resource_state(command_list, texture, 0,
                D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
        bug_if(is_mvk_device(context.device))
        check_sub_resource_uvec4(texture, 0, queue, command_list, &uvec4);

        reset_command_list(command_list, context.allocator);
        transition_sub_resource_state(command_list, context.render_target, 0,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
        transition_sub_resource_state(command_list, texture, 0,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
        transition_sub_resource_state(command_list, ds.texture, 0,
                D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_DEPTH_WRITE);

        vkd3d_test_pop_context();
    }

    destroy_depth_stencil(&ds);
    ID3D12Resource_Release(texture);
    ID3D12DescriptorHeap_Release(heap);
    ID3D12PipelineState_Release(pipeline_state);
    destroy_test_context(&context);
}

static void test_stencil_export(void)
{
    D3D12_SHADER_RESOURCE_VIEW_DESC stencil_srv_desc;
    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
    ID3D12GraphicsCommandList *command_list;
    D3D12_DESCRIPTOR_HEAP_DESC heap_desc;
    struct depth_stencil_resource ds;
    ID3D12PipelineState *pso_sample;
    ID3D12RootSignature *rs_sample;
    ID3D12DescriptorHeap *srv_heap;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12CommandQueue *queue;
    HRESULT hr;

    static const DWORD ps_code[] =
    {
#if 0
        uint stencil_ref;

        uint main() : SV_StencilRef
        {
            return stencil_ref;
        }
#endif
        0x43425844, 0x3980cb16, 0xbbe87d38, 0xb93f7c61, 0x200c41ed, 0x00000001, 0x000000cc, 0x00000004,
        0x00000030, 0x00000040, 0x00000078, 0x000000bc, 0x4e475349, 0x00000008, 0x00000000, 0x00000008,
        0x4e47534f, 0x00000030, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001,
        0xffffffff, 0x00000e01, 0x535f5653, 0x636e6574, 0x65526c69, 0xabab0066, 0x58454853, 0x0000003c,
        0x00000050, 0x0000000f, 0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x02000065,
        0x00029001, 0x05000036, 0x00029001, 0x0020800a, 0x00000000, 0x00000000, 0x0100003e, 0x30494653,
        0x00000008, 0x00000200, 0x00000000,
    };
    const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};
    static const DWORD ps_sample_code[] =
    {
#if 0
        Texture2D<uint4> tex : register(t0);

        uint4 main(float4 pos : SV_Position) : SV_TARGET
        {
            return tex[uint2(pos.xy)].g;
        }
#endif
        0x43425844, 0xdd1e98d4, 0x53c81701, 0x576f6b2e, 0x040b18bb, 0x00000001, 0x0000014c, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000030f, 0x505f5653, 0x7469736f, 0x006e6f69,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001,
        0x00000000, 0x0000000f, 0x545f5653, 0x45475241, 0xabab0054, 0x58454853, 0x000000b0, 0x00000050,
        0x0000002c, 0x0100086a, 0x04001858, 0x00107000, 0x00000000, 0x00004444, 0x04002064, 0x00101032,
        0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000001, 0x0500001c,
        0x00100032, 0x00000000, 0x00101046, 0x00000000, 0x08000036, 0x001000c2, 0x00000000, 0x00004002,
        0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x8900002d, 0x800000c2, 0x00111103, 0x00100012,
        0x00000000, 0x00100e46, 0x00000000, 0x00107e16, 0x00000000, 0x05000036, 0x001020f2, 0x00000000,
        0x00100006, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_sample = {ps_sample_code, sizeof(ps_sample_code)};
    static const float black[] = {0.0f, 0.0f, 0.0f, 0.0f};

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

    if (!is_stencil_ref_export_supported(context.device))
    {
        skip("The device does not support stencil ref export.\n");
        destroy_test_context(&context);
        return;
    }

    command_list = context.list;
    queue = context.queue;

    init_depth_stencil(&ds, context.device, 640, 480, 1, 1,
            DXGI_FORMAT_R32G8X24_TYPELESS, DXGI_FORMAT_D32_FLOAT_S8X24_UINT, NULL);
    set_viewport(&context.viewport, 0.0f, 0.0f, 640.0f, 480.0f, 0.0f, 1.0f);
    set_rect(&context.scissor_rect, 0, 0, 640, 480);

    context.root_signature = create_32bit_constants_root_signature(context.device,
            0, 1, D3D12_SHADER_VISIBILITY_PIXEL);

    init_pipeline_state_desc(&pso_desc, context.root_signature, 0, NULL, &ps, NULL);

    pso_desc.NumRenderTargets = 0;
    pso_desc.DSVFormat = DXGI_FORMAT_D32_FLOAT_S8X24_UINT;
    pso_desc.DepthStencilState.StencilEnable = true;
    pso_desc.DepthStencilState.StencilReadMask = 0xff;
    pso_desc.DepthStencilState.StencilWriteMask = 0xff;
    pso_desc.DepthStencilState.FrontFace.StencilFailOp = D3D12_STENCIL_OP_REPLACE;
    pso_desc.DepthStencilState.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_REPLACE;
    pso_desc.DepthStencilState.FrontFace.StencilPassOp = D3D12_STENCIL_OP_REPLACE;
    pso_desc.DepthStencilState.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS;
    pso_desc.DepthStencilState.BackFace = pso_desc.DepthStencilState.FrontFace;
    hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);

    rs_sample = create_texture_root_signature(context.device, D3D12_SHADER_VISIBILITY_PIXEL, 0, 0);
    init_pipeline_state_desc(&pso_desc, rs_sample, DXGI_FORMAT_R8_UINT, NULL, &ps_sample, NULL);
    hr = ID3D12Device_CreateGraphicsPipelineState(context.device,
            &pso_desc, &IID_ID3D12PipelineState, (void **)&pso_sample);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);

    heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
    heap_desc.NumDescriptors = 1;
    heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
    heap_desc.NodeMask = 0;

    hr = ID3D12Device_CreateDescriptorHeap(context.device, &heap_desc,
            &IID_ID3D12DescriptorHeap, (void **)&srv_heap);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);

    ID3D12GraphicsCommandList_ClearDepthStencilView(command_list, ds.dsv_handle,
            D3D12_CLEAR_FLAG_STENCIL, 0.0f, 0x80, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 0, NULL, false, &ds.dsv_handle);
    ID3D12GraphicsCommandList_OMSetStencilRef(command_list, 0x40);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);

    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstant(command_list, 0, 0xff, 0);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, ds.texture,
            D3D12_RESOURCE_STATE_DEPTH_WRITE, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);

    stencil_srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
    stencil_srv_desc.Format = DXGI_FORMAT_X32_TYPELESS_G8X24_UINT;
    stencil_srv_desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
    stencil_srv_desc.Texture2D.MostDetailedMip = 0;
    stencil_srv_desc.Texture2D.MipLevels = 1;
    stencil_srv_desc.Texture2D.PlaneSlice = 1;
    stencil_srv_desc.Texture2D.ResourceMinLODClamp = 0.0f;

    ID3D12Device_CreateShaderResourceView(context.device, ds.texture, &stencil_srv_desc,
            ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(srv_heap));

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, black, 0, NULL);
    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &srv_heap);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, rs_sample);
    ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0,
            ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(srv_heap));
    ID3D12GraphicsCommandList_SetPipelineState(command_list, pso_sample);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);

    check_sub_resource_uint8(context.render_target, 0, queue, command_list, 0xff, 0);

    ID3D12DescriptorHeap_Release(srv_heap);
    ID3D12PipelineState_Release(pso_sample);
    ID3D12RootSignature_Release(rs_sample);
    destroy_depth_stencil(&ds);
    destroy_test_context(&context);
}

static void test_typed_buffer_uav(void)
{
    D3D12_CPU_DESCRIPTOR_HANDLE cpu_descriptor_handle;
    D3D12_GPU_DESCRIPTOR_HANDLE gpu_descriptor_handle;
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    D3D12_DESCRIPTOR_RANGE descriptor_ranges[1];
    D3D12_UNORDERED_ACCESS_VIEW_DESC uav_desc;
    ID3D12GraphicsCommandList *command_list;
    D3D12_ROOT_PARAMETER root_parameters[1];
    ID3D12DescriptorHeap *descriptor_heap;
    ID3D12RootSignature *root_signature;
    ID3D12PipelineState *pipeline_state;
    struct d3d12_resource_readback rb;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Resource *resource;
    ID3D12Device *device;
    HRESULT hr;

    static const DWORD cs_code[] =
    {
#if 0
        RWBuffer<float> buffer;

        [numthreads(32, 1, 1)]
        void main(uint3 group_id : SV_groupID, uint group_index : SV_GroupIndex)
        {
            uint global_index = 32 * group_id.x + group_index;
            buffer[global_index] = 0.5f;
        }
#endif
        0x43425844, 0xcc416762, 0xde23c7b7, 0x4012ae1f, 0xaed30ba4, 0x00000001, 0x000000e0, 0x00000003,
        0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x0000008c, 0x00050050, 0x00000023, 0x0100086a,
        0x0400089c, 0x0011e000, 0x00000000, 0x00005555, 0x0200005f, 0x00024000, 0x0200005f, 0x00021012,
        0x02000068, 0x00000001, 0x0400009b, 0x00000020, 0x00000001, 0x00000001, 0x07000023, 0x00100012,
        0x00000000, 0x0002100a, 0x00004001, 0x00000020, 0x0002400a, 0x0a0000a4, 0x0011e0f2, 0x00000000,
        0x00100006, 0x00000000, 0x00004002, 0x3f000000, 0x3f000000, 0x3f000000, 0x3f000000, 0x0100003e,
    };
    static const DWORD cs_vec4_code[] =
    {
#if 0
        RWBuffer<float4> buffer;

        [numthreads(8, 1, 1)]
        void main(uint3 group_id : SV_groupID, uint group_index : SV_GroupIndex)
        {
            uint global_index = 8 * group_id.x + group_index;
            buffer[global_index] = float4(0.5f, 0.625f, 0.75f, 1.0f);
        }
#endif
        0x43425844, 0x37e9fa91, 0x11ca38f4, 0x9d4a70b7, 0x4fd05c45, 0x00000001, 0x000000e0, 0x00000003,
        0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x0000008c, 0x00050050, 0x00000023, 0x0100086a,
        0x0400089c, 0x0011e000, 0x00000000, 0x00005555, 0x0200005f, 0x00024000, 0x0200005f, 0x00021012,
        0x02000068, 0x00000001, 0x0400009b, 0x00000008, 0x00000001, 0x00000001, 0x07000023, 0x00100012,
        0x00000000, 0x0002100a, 0x00004001, 0x00000008, 0x0002400a, 0x0a0000a4, 0x0011e0f2, 0x00000000,
        0x00100006, 0x00000000, 0x00004002, 0x3f000000, 0x3f200000, 0x3f400000, 0x3f800000, 0x0100003e,
    };
    static const DWORD cs_vec4_load_code[] =
    {
        /* Compiled with /Od */
#if 0
        RWBuffer<float4> buffer;

        [numthreads(4, 1, 1)]
        void main(uint3 group_id : SV_groupID, uint group_index : SV_GroupIndex)
        {
            uint global_index = 8 * group_id.x + group_index * 2u;
            buffer[global_index] = float4(0.625f, 0.5f, 1.0f, 0.75f);
            buffer[global_index + 1] = buffer[global_index];
        }
#endif
        0x43425844, 0xe4c1718b, 0xd2cd85b0, 0x06a8d73e, 0x2815a795, 0x00000001, 0x000001b0, 0x00000004,
        0x00000030, 0x00000040, 0x00000050, 0x000001a0, 0x4e475349, 0x00000008, 0x00000000, 0x00000008,
        0x4e47534f, 0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x00000148, 0x00050050, 0x00000052,
        0x0100886a, 0x0400089c, 0x0011e000, 0x00000000, 0x00005555, 0x0200005f, 0x00024000, 0x0200005f,
        0x00021012, 0x02000068, 0x00000002, 0x0400009b, 0x00000004, 0x00000001, 0x00000001, 0x05000036,
        0x00100012, 0x00000000, 0x00004001, 0x00000008, 0x07000026, 0x0000d000, 0x00100012, 0x00000000,
        0x0010000a, 0x00000000, 0x0002100a, 0x07000026, 0x0000d000, 0x00100022, 0x00000000, 0x0002400a,
        0x00004001, 0x00000002, 0x0700001e, 0x00100012, 0x00000000, 0x0010001a, 0x00000000, 0x0010000a,
        0x00000000, 0x0a0000a4, 0x0011e0f2, 0x00000000, 0x00100006, 0x00000000, 0x00004002, 0x3f200000,
        0x3f000000, 0x3f800000, 0x3f400000, 0x05000036, 0x00100022, 0x00000000, 0x00004001, 0x00000001,
        0x0700001e, 0x00100022, 0x00000000, 0x0010001a, 0x00000000, 0x0010000a, 0x00000000, 0x890000a3,
        0x80000042, 0x00155543, 0x001000f2, 0x00000001, 0x00100006, 0x00000000, 0x0011ee46, 0x00000000,
        0x070000a4, 0x0011e0f2, 0x00000000, 0x00100556, 0x00000000, 0x00100e46, 0x00000001, 0x0100003e,
        0x30494653, 0x00000008, 0x00000800, 0x00000000,
    };
    static const struct vec4 expected = {0.5f, 0.625f, 0.75f, 1.0f};
    static const struct vec4 expected_ld = {0.625f, 0.5f, 1.0f, 0.75f};

    if (!init_compute_test_context(&context))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    descriptor_ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
    descriptor_ranges[0].NumDescriptors = 1;
    descriptor_ranges[0].BaseShaderRegister = 0;
    descriptor_ranges[0].RegisterSpace = 0;
    descriptor_ranges[0].OffsetInDescriptorsFromTableStart = 0;
    root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameters[0].DescriptorTable.NumDescriptorRanges = 1;
    root_parameters[0].DescriptorTable.pDescriptorRanges = descriptor_ranges;
    root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    root_signature_desc.NumParameters = 1;
    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(device, &root_signature_desc, &root_signature);
    ok(SUCCEEDED(hr), "Failed to create root signature, hr %#x.\n", hr);

    pipeline_state = create_compute_pipeline_state(device, root_signature,
            shader_bytecode(cs_code, sizeof(cs_code)));

    descriptor_heap = create_gpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1);

    cpu_descriptor_handle = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(descriptor_heap);
    gpu_descriptor_handle = ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(descriptor_heap);

    resource = create_default_buffer(device, 64 * sizeof(float),
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

    uav_desc.Format = DXGI_FORMAT_R32_FLOAT;
    uav_desc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
    uav_desc.Buffer.FirstElement = 0;
    uav_desc.Buffer.NumElements = 64;
    uav_desc.Buffer.StructureByteStride = 0;
    uav_desc.Buffer.CounterOffsetInBytes = 0;
    uav_desc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_NONE;
    ID3D12Device_CreateUnorderedAccessView(device, resource, NULL, &uav_desc, cpu_descriptor_handle);

    ID3D12GraphicsCommandList_SetPipelineState(command_list, pipeline_state);
    ID3D12GraphicsCommandList_SetComputeRootSignature(command_list, root_signature);
    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &descriptor_heap);
    ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(command_list, 0, gpu_descriptor_handle);
    ID3D12GraphicsCommandList_Dispatch(command_list, 2, 1, 1);

    transition_sub_resource_state(command_list, resource, 0,
            D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);

    get_buffer_readback_with_command_list(resource, uav_desc.Format, &rb, queue, command_list);
    check_readback_data_float(&rb.rb, NULL, 0.5f, 0);
    release_resource_readback(&rb);

    reset_command_list(command_list, context.allocator);
    transition_sub_resource_state(command_list, resource, 0,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
    ID3D12PipelineState_Release(pipeline_state);
    pipeline_state = create_compute_pipeline_state(device, root_signature,
            shader_bytecode(cs_vec4_code, sizeof(cs_vec4_code)));

    uav_desc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
    uav_desc.Buffer.NumElements = 16;
    ID3D12Device_CreateUnorderedAccessView(device, resource, NULL, &uav_desc, cpu_descriptor_handle);

    ID3D12GraphicsCommandList_SetPipelineState(command_list, pipeline_state);
    ID3D12GraphicsCommandList_SetComputeRootSignature(command_list, root_signature);
    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &descriptor_heap);
    ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(command_list, 0, gpu_descriptor_handle);
    ID3D12GraphicsCommandList_Dispatch(command_list, 2, 1, 1);

    transition_sub_resource_state(command_list, resource, 0,
            D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);

    get_buffer_readback_with_command_list(resource, uav_desc.Format, &rb, queue, command_list);
    check_readback_data_vec4(&rb.rb, NULL, &expected, 0);
    release_resource_readback(&rb);

    reset_command_list(command_list, context.allocator);
    transition_sub_resource_state(command_list, resource, 0,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
    ID3D12PipelineState_Release(pipeline_state);
    pipeline_state = create_compute_pipeline_state(device, root_signature,
            shader_bytecode(cs_vec4_load_code, sizeof(cs_vec4_load_code)));

    ID3D12GraphicsCommandList_SetPipelineState(command_list, pipeline_state);
    ID3D12GraphicsCommandList_SetComputeRootSignature(command_list, root_signature);
    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &descriptor_heap);
    ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(command_list, 0, gpu_descriptor_handle);
    ID3D12GraphicsCommandList_Dispatch(command_list, 2, 1, 1);

    transition_sub_resource_state(command_list, resource, 0,
            D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);

    get_buffer_readback_with_command_list(resource, uav_desc.Format, &rb, queue, command_list);
    todo_if(!are_typed_uav_load_additional_formats_supported(device))
    check_readback_data_vec4(&rb.rb, NULL, &expected_ld, 0);
    release_resource_readback(&rb);

    ID3D12Resource_Release(resource);
    ID3D12RootSignature_Release(root_signature);
    ID3D12PipelineState_Release(pipeline_state);
    ID3D12DescriptorHeap_Release(descriptor_heap);
    destroy_test_context(&context);
}

static void test_typed_uav_store(void)
{
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    D3D12_DESCRIPTOR_RANGE descriptor_ranges[1];
    ID3D12GraphicsCommandList *command_list;
    D3D12_ROOT_PARAMETER root_parameters[2];
    ID3D12DescriptorHeap *descriptor_heap;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Resource *resource;
    ID3D12Device *device;
    unsigned int i;
    HRESULT hr;

    static const DWORD cs_float_code[] =
    {
#if 0
        RWTexture2D<float> u;

        float f;

        [numthreads(1, 1, 1)]
        void main(uint2 id : SV_GroupID)
        {
            u[id] = f;
        }
#endif
        0x43425844, 0xc3add41b, 0x67df51b1, 0x2b887930, 0xcb1ee991, 0x00000001, 0x000000b8, 0x00000003,
        0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x00000064, 0x00050050, 0x00000019, 0x0100086a,
        0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x0400189c, 0x0011e000, 0x00000000, 0x00005555,
        0x0200005f, 0x00021032, 0x0400009b, 0x00000001, 0x00000001, 0x00000001, 0x070000a4, 0x0011e0f2,
        0x00000000, 0x00021546, 0x00208006, 0x00000000, 0x00000000, 0x0100003e,
    };
    static const struct
    {
        DXGI_FORMAT format;
        float constant;
        union
        {
            float f;
            uint16_t u16;
        } result;
    }
    tests[] =
    {
        {DXGI_FORMAT_R16_FLOAT, 1.0f, {.u16 = 0x3c00}},
        {DXGI_FORMAT_R16_FLOAT, 0.5f, {.u16 = 0x3800}},

        {DXGI_FORMAT_R16_UNORM, 0.5f, {.u16 = 32768}},

        {DXGI_FORMAT_R32_FLOAT, 0.0f, {.f = 0.0f}},
        {DXGI_FORMAT_R32_FLOAT, 0.5f, {.f = 0.5f}},
        {DXGI_FORMAT_R32_FLOAT, 1.0f, {.f = 1.0f}},
    };

    if (!init_compute_test_context(&context))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    descriptor_ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
    descriptor_ranges[0].NumDescriptors = 1;
    descriptor_ranges[0].BaseShaderRegister = 0;
    descriptor_ranges[0].RegisterSpace = 0;
    descriptor_ranges[0].OffsetInDescriptorsFromTableStart = 0;
    root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameters[0].DescriptorTable.NumDescriptorRanges = 1;
    root_parameters[0].DescriptorTable.pDescriptorRanges = descriptor_ranges;
    root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    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 = 1;
    root_parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    root_signature_desc.NumParameters = 2;
    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(device, &root_signature_desc, &context.root_signature);
    ok(SUCCEEDED(hr), "Failed to create root signature, hr %#x.\n", hr);

    context.pipeline_state = create_compute_pipeline_state(device, context.root_signature,
            shader_bytecode(cs_float_code, sizeof(cs_float_code)));

    descriptor_heap = create_gpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1);

    for (i = 0; i < ARRAY_SIZE(tests); ++i)
    {
        vkd3d_test_push_context("Test %u", i);

        resource = create_default_texture(device, 32, 32, tests[i].format,
                D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
        ID3D12Device_CreateUnorderedAccessView(device, resource, NULL, NULL,
                ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(descriptor_heap));

        ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &descriptor_heap);
        ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
        ID3D12GraphicsCommandList_SetComputeRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(command_list, 0,
                ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(descriptor_heap));
        ID3D12GraphicsCommandList_SetComputeRoot32BitConstants(command_list, 1,
                1, &tests[i].constant, 0);
        ID3D12GraphicsCommandList_Dispatch(command_list, 32, 32, 1);

        transition_sub_resource_state(command_list, resource, 0,
                D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
        switch (tests[i].format)
        {
            default:
                trace("Unhandled format %#x.\n", tests[i].format);
                /* fall-through */
            case DXGI_FORMAT_R32_FLOAT:
                check_sub_resource_float(resource, 0, queue, command_list, tests[i].result.f, 2);
                break;
            case DXGI_FORMAT_R16_FLOAT:
            case DXGI_FORMAT_R16_UNORM:
                check_sub_resource_uint16(resource, 0, queue, command_list, tests[i].result.u16, 2);
                break;
        }

        ID3D12Resource_Release(resource);

        reset_command_list(command_list, context.allocator);

        vkd3d_test_pop_context();
    }

    ID3D12DescriptorHeap_Release(descriptor_heap);
    destroy_test_context(&context);
}

static void test_compute_shader_registers(void)
{
    struct data
    {
        unsigned int group_id[3];
        unsigned int group_index;
        unsigned int dispatch_id[3];
        unsigned int thread_id[3];
    };

    D3D12_CPU_DESCRIPTOR_HANDLE cpu_descriptor_handle;
    D3D12_GPU_DESCRIPTOR_HANDLE gpu_descriptor_handle;
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    D3D12_DESCRIPTOR_RANGE descriptor_ranges[1];
    D3D12_UNORDERED_ACCESS_VIEW_DESC uav_desc;
    ID3D12GraphicsCommandList *command_list;
    D3D12_ROOT_PARAMETER root_parameters[2];
    unsigned int i, x, y, group_x, group_y;
    ID3D12DescriptorHeap *descriptor_heap;
    struct d3d12_resource_readback rb;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Resource *resource;
    const struct data *data;
    struct uvec4 dimensions;
    ID3D12Device *device;
    HRESULT hr;

    static const DWORD cs_code[] =
    {
#if 0
        struct data
        {
            uint3 group_id;
            uint group_index;
            uint3 dispatch_id;
            uint3 group_thread_id;
        };

        RWStructuredBuffer<data> u;

        uint2 dim;

        [numthreads(3, 2, 1)]
        void main(uint3 group_id : SV_GroupID,
                uint group_index : SV_GroupIndex,
                uint3 dispatch_id : SV_DispatchThreadID,
                uint3 group_thread_id : SV_GroupThreadID)
        {
            uint i = dispatch_id.x + dispatch_id.y * 3 * dim.x;
            u[i].group_id = group_id;
            u[i].group_index = group_index;
            u[i].dispatch_id = dispatch_id;
            u[i].group_thread_id = group_thread_id;
        }
#endif
        0x43425844, 0xf0bce218, 0xfc1e8267, 0xe6d57544, 0x342df592, 0x00000001, 0x000001a4, 0x00000003,
        0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x00000150, 0x00050050, 0x00000054, 0x0100086a,
        0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x0400009e, 0x0011e000, 0x00000000, 0x00000028,
        0x0200005f, 0x00024000, 0x0200005f, 0x00021072, 0x0200005f, 0x00022072, 0x0200005f, 0x00020072,
        0x02000068, 0x00000002, 0x0400009b, 0x00000003, 0x00000002, 0x00000001, 0x04000036, 0x00100072,
        0x00000000, 0x00021246, 0x04000036, 0x00100082, 0x00000000, 0x0002400a, 0x08000026, 0x0000d000,
        0x00100012, 0x00000001, 0x0002001a, 0x0020800a, 0x00000000, 0x00000000, 0x08000023, 0x00100012,
        0x00000001, 0x0010000a, 0x00000001, 0x00004001, 0x00000003, 0x0002000a, 0x090000a8, 0x0011e0f2,
        0x00000000, 0x0010000a, 0x00000001, 0x00004001, 0x00000000, 0x00100e46, 0x00000000, 0x04000036,
        0x00100072, 0x00000000, 0x00020246, 0x04000036, 0x00100082, 0x00000000, 0x0002200a, 0x090000a8,
        0x0011e0f2, 0x00000000, 0x0010000a, 0x00000001, 0x00004001, 0x00000010, 0x00100e46, 0x00000000,
        0x080000a8, 0x0011e032, 0x00000000, 0x0010000a, 0x00000001, 0x00004001, 0x00000020, 0x00022596,
        0x0100003e,
    };

    if (!init_compute_test_context(&context))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    descriptor_ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
    descriptor_ranges[0].NumDescriptors = 1;
    descriptor_ranges[0].BaseShaderRegister = 0;
    descriptor_ranges[0].RegisterSpace = 0;
    descriptor_ranges[0].OffsetInDescriptorsFromTableStart = 0;
    root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameters[0].DescriptorTable.NumDescriptorRanges = 1;
    root_parameters[0].DescriptorTable.pDescriptorRanges = descriptor_ranges;
    root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    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;
    root_signature_desc.NumParameters = 2;
    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(device, &root_signature_desc, &context.root_signature);
    ok(SUCCEEDED(hr), "Failed to create root signature, hr %#x.\n", hr);

    context.pipeline_state = create_compute_pipeline_state(device, context.root_signature,
            shader_bytecode(cs_code, sizeof(cs_code)));

    resource = create_default_buffer(device, 10240,
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

    descriptor_heap = create_gpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1);
    cpu_descriptor_handle = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(descriptor_heap);
    gpu_descriptor_handle = ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(descriptor_heap);

    memset(&uav_desc, 0, sizeof(uav_desc));
    uav_desc.Format = DXGI_FORMAT_UNKNOWN;
    uav_desc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
    uav_desc.Buffer.FirstElement = 0;
    uav_desc.Buffer.NumElements = 256;
    uav_desc.Buffer.StructureByteStride = 40;
    ID3D12Device_CreateUnorderedAccessView(device, resource, NULL, &uav_desc, cpu_descriptor_handle);

    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_SetComputeRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &descriptor_heap);
    ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(command_list, 0, gpu_descriptor_handle);
    dimensions.x = 2;
    dimensions.y = 3;
    dimensions.z = 1;
    dimensions.w = 0;
    ID3D12GraphicsCommandList_SetComputeRoot32BitConstants(command_list, 1, 4, &dimensions, 0);
    ID3D12GraphicsCommandList_Dispatch(command_list, dimensions.x, dimensions.y, dimensions.z);

    transition_resource_state(command_list, resource,
            D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);

    get_buffer_readback_with_command_list(resource, uav_desc.Format, &rb, queue, command_list);
    i = 0;
    data = rb.rb.data;
    for (y = 0; y < dimensions.y; ++y)
    {
        for (group_y = 0; group_y < 2; ++group_y)
        {
            for (x = 0; x < dimensions.x; ++x)
            {
                for (group_x = 0; group_x < 3; ++group_x)
                {
                    const unsigned int dispatch_id[2] = {x * 3 + group_x, y * 2 + group_y};
                    const unsigned int group_index = group_y * 3 + group_x;
                    const struct data *d = &data[i];

                    ok(d->group_id[0] == x && d->group_id[1] == y && !d->group_id[2],
                            "Got group id (%u, %u, %u), expected (%u, %u, %u) at %u (%u, %u, %u, %u).\n",
                            d->group_id[0], d->group_id[1], d->group_id[2], x, y, 0,
                            i, x, y, group_x, group_y);
                    ok(d->group_index == group_index,
                            "Got group index %u, expected %u at %u (%u, %u, %u, %u).\n",
                            d->group_index, group_index, i, x, y, group_x, group_y);
                    ok(d->dispatch_id[0] == dispatch_id[0] && d->dispatch_id[1] == dispatch_id[1]
                            && !d->dispatch_id[2],
                            "Got dispatch id (%u, %u, %u), expected (%u, %u, %u) "
                            "at %u (%u, %u, %u, %u).\n",
                            d->dispatch_id[0], d->dispatch_id[1], d->dispatch_id[2],
                            dispatch_id[0], dispatch_id[1], 0,
                            i, x, y, group_x, group_y);
                    ok(d->thread_id[0] == group_x && d->thread_id[1] == group_y && !d->thread_id[2],
                            "Got group thread id (%u, %u, %u), expected (%u, %u, %u) "
                            "at %u (%u, %u, %u, %u).\n",
                            d->thread_id[0], d->thread_id[1], d->thread_id[2], group_x, group_y, 0,
                            i, x, y, group_x, group_y);
                    ++i;
                }
            }
        }
    }
    release_resource_readback(&rb);

    ID3D12DescriptorHeap_Release(descriptor_heap);
    ID3D12Resource_Release(resource);
    destroy_test_context(&context);
}

static void test_tgsm(void)
{
    D3D12_GPU_DESCRIPTOR_HANDLE gpu_descriptor_handle;
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    D3D12_DESCRIPTOR_RANGE descriptor_ranges[1];
    D3D12_UNORDERED_ACCESS_VIEW_DESC uav_desc;
    ID3D12DescriptorHeap *cpu_descriptor_heap;
    ID3D12GraphicsCommandList *command_list;
    D3D12_ROOT_PARAMETER root_parameters[1];
    ID3D12DescriptorHeap *descriptor_heap;
    struct d3d12_resource_readback rb;
    ID3D12Resource *buffer, *buffer2;
    unsigned int data, expected;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Device *device;
    float float_data;
    unsigned int i;
    HRESULT hr;

    static const DWORD raw_tgsm_code[] =
    {
#if 0
        RWByteAddressBuffer u;
        groupshared uint m;

        [numthreads(32, 1, 1)]
        void main(uint local_idx : SV_GroupIndex, uint group_id : SV_GroupID)
        {
            if (!local_idx)
                m = group_id.x;
            GroupMemoryBarrierWithGroupSync();
            InterlockedAdd(m, group_id.x);
            GroupMemoryBarrierWithGroupSync();
            if (!local_idx)
                u.Store(4 * group_id.x, m);
        }
#endif
        0x43425844, 0x467df6d9, 0x5f56edda, 0x5c96b787, 0x60c91fb8, 0x00000001, 0x00000148, 0x00000003,
        0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x000000f4, 0x00050050, 0x0000003d, 0x0100086a,
        0x0300009d, 0x0011e000, 0x00000000, 0x0200005f, 0x00024000, 0x0200005f, 0x00021012, 0x02000068,
        0x00000001, 0x0400009f, 0x0011f000, 0x00000000, 0x00000004, 0x0400009b, 0x00000020, 0x00000001,
        0x00000001, 0x0200001f, 0x0002400a, 0x060000a6, 0x0011f012, 0x00000000, 0x00004001, 0x00000000,
        0x0002100a, 0x01000015, 0x010018be, 0x060000ad, 0x0011f000, 0x00000000, 0x00004001, 0x00000000,
        0x0002100a, 0x010018be, 0x0200001f, 0x0002400a, 0x06000029, 0x00100012, 0x00000000, 0x0002100a,
        0x00004001, 0x00000002, 0x070000a5, 0x00100022, 0x00000000, 0x00004001, 0x00000000, 0x0011f006,
        0x00000000, 0x070000a6, 0x0011e012, 0x00000000, 0x0010000a, 0x00000000, 0x0010001a, 0x00000000,
        0x01000015, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE cs_raw_tgsm = {raw_tgsm_code, sizeof(raw_tgsm_code)};
    static const DWORD structured_tgsm_code[] =
    {
#if 0
        #define GROUP_SIZE 32

        RWByteAddressBuffer u;
        RWByteAddressBuffer u2;
        groupshared uint m[GROUP_SIZE];

        [numthreads(GROUP_SIZE, 1, 1)]
        void main(uint local_idx : SV_GroupIndex, uint group_id : SV_GroupID)
        {
            uint sum, original, i;

            if (!local_idx)
            {
                for (i = 0; i < GROUP_SIZE; ++i)
                    m[i] = 2 * group_id.x;
            }
            GroupMemoryBarrierWithGroupSync();
            InterlockedAdd(m[local_idx], 1);
            GroupMemoryBarrierWithGroupSync();
            for (i = 0, sum = 0; i < GROUP_SIZE; sum += m[i++]);
            u.InterlockedExchange(4 * group_id.x, sum, original);
            u2.Store(4 * group_id.x, original);
        }
#endif
        0x43425844, 0x9d906c94, 0x81f5ad92, 0x11e860b2, 0x3623c824, 0x00000001, 0x000002c0, 0x00000003,
        0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x0000026c, 0x00050050, 0x0000009b, 0x0100086a,
        0x0300009d, 0x0011e000, 0x00000000, 0x0300009d, 0x0011e000, 0x00000001, 0x0200005f, 0x00024000,
        0x0200005f, 0x00021012, 0x02000068, 0x00000002, 0x050000a0, 0x0011f000, 0x00000000, 0x00000004,
        0x00000020, 0x0400009b, 0x00000020, 0x00000001, 0x00000001, 0x0200001f, 0x0002400a, 0x06000029,
        0x00100012, 0x00000000, 0x0002100a, 0x00004001, 0x00000001, 0x05000036, 0x00100022, 0x00000000,
        0x00004001, 0x00000000, 0x01000030, 0x07000050, 0x00100042, 0x00000000, 0x0010001a, 0x00000000,
        0x00004001, 0x00000020, 0x03040003, 0x0010002a, 0x00000000, 0x090000a8, 0x0011f012, 0x00000000,
        0x0010001a, 0x00000000, 0x00004001, 0x00000000, 0x0010000a, 0x00000000, 0x0700001e, 0x00100022,
        0x00000000, 0x0010001a, 0x00000000, 0x00004001, 0x00000001, 0x01000016, 0x01000015, 0x010018be,
        0x04000036, 0x00100012, 0x00000000, 0x0002400a, 0x05000036, 0x00100022, 0x00000000, 0x00004001,
        0x00000000, 0x070000ad, 0x0011f000, 0x00000000, 0x00100046, 0x00000000, 0x00004001, 0x00000001,
        0x010018be, 0x08000036, 0x00100032, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000,
        0x00000000, 0x01000030, 0x07000050, 0x00100042, 0x00000000, 0x0010001a, 0x00000000, 0x00004001,
        0x00000020, 0x03040003, 0x0010002a, 0x00000000, 0x0700001e, 0x00100022, 0x00000001, 0x0010001a,
        0x00000000, 0x00004001, 0x00000001, 0x090000a7, 0x00100042, 0x00000000, 0x0010001a, 0x00000000,
        0x00004001, 0x00000000, 0x0011f006, 0x00000000, 0x0700001e, 0x00100012, 0x00000001, 0x0010000a,
        0x00000000, 0x0010002a, 0x00000000, 0x05000036, 0x00100032, 0x00000000, 0x00100046, 0x00000001,
        0x01000016, 0x06000029, 0x00100022, 0x00000000, 0x0002100a, 0x00004001, 0x00000002, 0x090000b8,
        0x00100012, 0x00000001, 0x0011e000, 0x00000000, 0x0010001a, 0x00000000, 0x0010000a, 0x00000000,
        0x070000a6, 0x0011e012, 0x00000001, 0x0010001a, 0x00000000, 0x0010000a, 0x00000001, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE cs_structured_tgsm = {structured_tgsm_code, sizeof(structured_tgsm_code)};
    static const DWORD structured_tgsm_float_code[] =
    {
#if 0
        #define GROUP_SIZE 32

        struct data
        {
            float f;
            uint u;
        };

        RWBuffer<float> u;
        RWBuffer<uint> u2;
        groupshared data m[GROUP_SIZE];

        [numthreads(GROUP_SIZE, 1, 1)]
        void main(uint local_idx : SV_GroupIndex, uint group_id : SV_GroupID,
                uint thread_id : SV_DispatchThreadID)
        {
            uint i;
            if (!local_idx)
            {
                for (i = 0; i < GROUP_SIZE; ++i)
                {
                    m[i].f = group_id.x;
                    m[i].u = group_id.x;
                }
            }
            GroupMemoryBarrierWithGroupSync();
            for (i = 0; i < local_idx; ++i)
            {
                m[local_idx].f += group_id.x;
                m[local_idx].u += group_id.x;
            }
            u[thread_id.x] = m[local_idx].f;
            u2[thread_id.x] = m[local_idx].u;
        }
#endif
        0x43425844, 0xaadf1a71, 0x16f60224, 0x89b6ce76, 0xb66fb96f, 0x00000001, 0x000002ac, 0x00000003,
        0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x00000258, 0x00050050, 0x00000096, 0x0100086a,
        0x0400089c, 0x0011e000, 0x00000000, 0x00005555, 0x0400089c, 0x0011e000, 0x00000001, 0x00004444,
        0x0200005f, 0x00024000, 0x0200005f, 0x00021012, 0x0200005f, 0x00020012, 0x02000068, 0x00000002,
        0x050000a0, 0x0011f000, 0x00000000, 0x00000008, 0x00000020, 0x0400009b, 0x00000020, 0x00000001,
        0x00000001, 0x0200001f, 0x0002400a, 0x04000056, 0x00100012, 0x00000000, 0x0002100a, 0x04000036,
        0x00100022, 0x00000000, 0x0002100a, 0x05000036, 0x00100042, 0x00000000, 0x00004001, 0x00000000,
        0x01000030, 0x07000050, 0x00100082, 0x00000000, 0x0010002a, 0x00000000, 0x00004001, 0x00000020,
        0x03040003, 0x0010003a, 0x00000000, 0x090000a8, 0x0011f032, 0x00000000, 0x0010002a, 0x00000000,
        0x00004001, 0x00000000, 0x00100046, 0x00000000, 0x0700001e, 0x00100042, 0x00000000, 0x0010002a,
        0x00000000, 0x00004001, 0x00000001, 0x01000016, 0x01000015, 0x010018be, 0x04000056, 0x00100012,
        0x00000000, 0x0002100a, 0x05000036, 0x00100022, 0x00000000, 0x00004001, 0x00000000, 0x01000030,
        0x06000050, 0x00100042, 0x00000000, 0x0010001a, 0x00000000, 0x0002400a, 0x03040003, 0x0010002a,
        0x00000000, 0x080000a7, 0x001000c2, 0x00000000, 0x0002400a, 0x00004001, 0x00000000, 0x0011f406,
        0x00000000, 0x07000000, 0x00100012, 0x00000001, 0x0010000a, 0x00000000, 0x0010002a, 0x00000000,
        0x0600001e, 0x00100022, 0x00000001, 0x0010003a, 0x00000000, 0x0002100a, 0x080000a8, 0x0011f032,
        0x00000000, 0x0002400a, 0x00004001, 0x00000000, 0x00100046, 0x00000001, 0x0700001e, 0x00100022,
        0x00000000, 0x0010001a, 0x00000000, 0x00004001, 0x00000001, 0x01000016, 0x080000a7, 0x00100032,
        0x00000000, 0x0002400a, 0x00004001, 0x00000000, 0x0011f046, 0x00000000, 0x060000a4, 0x0011e0f2,
        0x00000000, 0x00020006, 0x00100006, 0x00000000, 0x060000a4, 0x0011e0f2, 0x00000001, 0x00020006,
        0x00100556, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE cs_structured_tgsm_float
            = {structured_tgsm_float_code, sizeof(structured_tgsm_float_code)};
    static const unsigned int zero[4] = {0};

    if (!init_compute_test_context(&context))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    descriptor_ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
    descriptor_ranges[0].NumDescriptors = 2;
    descriptor_ranges[0].BaseShaderRegister = 0;
    descriptor_ranges[0].RegisterSpace = 0;
    descriptor_ranges[0].OffsetInDescriptorsFromTableStart = 0;
    root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameters[0].DescriptorTable.NumDescriptorRanges = 1;
    root_parameters[0].DescriptorTable.pDescriptorRanges = descriptor_ranges;
    root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    root_signature_desc.NumParameters = 1;
    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(device, &root_signature_desc, &context.root_signature);
    ok(SUCCEEDED(hr), "Failed to create root signature, hr %#x.\n", hr);

    buffer = create_default_buffer(device, 1024,
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

    descriptor_heap = create_gpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 2);
    cpu_descriptor_heap = create_cpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 2);

    gpu_descriptor_handle = ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(descriptor_heap);

    memset(&uav_desc, 0, sizeof(uav_desc));
    uav_desc.Format = DXGI_FORMAT_R32_TYPELESS;
    uav_desc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
    uav_desc.Buffer.FirstElement = 0;
    uav_desc.Buffer.NumElements = 256;
    uav_desc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_RAW;
    ID3D12Device_CreateUnorderedAccessView(device, buffer, NULL, &uav_desc,
            get_cpu_descriptor_handle(&context, descriptor_heap, 0));
    ID3D12Device_CreateUnorderedAccessView(device, NULL, NULL, &uav_desc,
            get_cpu_descriptor_handle(&context, descriptor_heap, 1));

    /* cs_raw_tgsm */
    context.pipeline_state = create_compute_pipeline_state(device, context.root_signature, cs_raw_tgsm);

    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_SetComputeRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &descriptor_heap);
    ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(command_list, 0, gpu_descriptor_handle);
    ID3D12GraphicsCommandList_Dispatch(command_list, 64, 1, 1);

    transition_resource_state(command_list, buffer,
            D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);

    get_buffer_readback_with_command_list(buffer, DXGI_FORMAT_R32_UINT, &rb, queue, command_list);
    for (i = 0; i < 64; ++i)
    {
        data = get_readback_uint(&rb.rb, i, 0, 0);
        expected = 33 * i;
        ok(data == expected, "Got %u, expected %u (index %u).\n", data, expected, i);
    }
    release_resource_readback(&rb);

    ID3D12PipelineState_Release(context.pipeline_state);

    /* cs_structured_tgsm */
    reset_command_list(command_list, context.allocator);

    bug_if(is_mvk_device(context.device))
    context.pipeline_state = create_compute_pipeline_state(device, context.root_signature, cs_structured_tgsm);

    buffer2 = create_default_buffer(device, 1024,
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

    ID3D12Device_CreateUnorderedAccessView(device, buffer2, NULL, &uav_desc,
            get_cpu_descriptor_handle(&context, descriptor_heap, 1));

    ID3D12Device_CreateUnorderedAccessView(device, buffer, NULL, &uav_desc,
            get_cpu_descriptor_handle(&context, cpu_descriptor_heap, 0));
    ID3D12Device_CreateUnorderedAccessView(device, buffer2, NULL, &uav_desc,
            get_cpu_descriptor_handle(&context, cpu_descriptor_heap, 1));

    ID3D12GraphicsCommandList_ClearUnorderedAccessViewUint(command_list,
            get_gpu_descriptor_handle(&context, descriptor_heap, 0),
            get_cpu_descriptor_handle(&context, cpu_descriptor_heap, 0),
            buffer, zero, 0, NULL);
    ID3D12GraphicsCommandList_ClearUnorderedAccessViewUint(command_list,
            get_gpu_descriptor_handle(&context, descriptor_heap, 1),
            get_cpu_descriptor_handle(&context, cpu_descriptor_heap, 1),
            buffer2, zero, 0, NULL);

    gpu_descriptor_handle = ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(descriptor_heap);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_SetComputeRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &descriptor_heap);
    ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(command_list, 0, gpu_descriptor_handle);
    ID3D12GraphicsCommandList_Dispatch(command_list, 32, 1, 1);

    transition_resource_state(command_list, buffer,
            D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
    transition_resource_state(command_list, buffer2,
            D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);

    get_buffer_readback_with_command_list(buffer, DXGI_FORMAT_R32_UINT, &rb, queue, command_list);
    for (i = 0; i < 32; ++i)
    {
        expected = 64 * i + 32;
        data = get_readback_uint(&rb.rb, i, 0, 0);
        ok(data == expected, "Got %u, expected %u (index %u).\n", data, expected, i);
    }
    release_resource_readback(&rb);
    reset_command_list(command_list, context.allocator);
    get_buffer_readback_with_command_list(buffer2, DXGI_FORMAT_R32_UINT, &rb, queue, command_list);
    for (i = 0; i < 32; ++i)
    {
        expected = 64 * i + 32;
        data = get_readback_uint(&rb.rb, i, 0, 0);
        ok(data == expected || !data, "Got %u, expected %u (index %u).\n", data, expected, i);
    }
    release_resource_readback(&rb);

    if (context.pipeline_state)
        ID3D12PipelineState_Release(context.pipeline_state);

    /* cs_structured_tgsm_float */
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, buffer,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
    transition_resource_state(command_list, buffer2,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

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

    uav_desc.Format = DXGI_FORMAT_R32_FLOAT;
    uav_desc.Buffer.Flags = 0;
    ID3D12Device_CreateUnorderedAccessView(device, buffer, NULL, &uav_desc,
            get_cpu_descriptor_handle(&context, descriptor_heap, 0));
    uav_desc.Format = DXGI_FORMAT_R32_UINT;
    ID3D12Device_CreateUnorderedAccessView(device, buffer2, NULL, &uav_desc,
            get_cpu_descriptor_handle(&context, descriptor_heap, 1));

    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_SetComputeRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &descriptor_heap);
    ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(command_list, 0, gpu_descriptor_handle);
    ID3D12GraphicsCommandList_Dispatch(command_list, 3, 1, 1);

    transition_resource_state(command_list, buffer,
            D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
    transition_resource_state(command_list, buffer2,
            D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);

    get_buffer_readback_with_command_list(buffer, DXGI_FORMAT_R32_UINT, &rb, queue, command_list);
    for (i = 0; i < 96; ++i)
    {
        expected = (i % 32 + 1) * (i / 32);
        float_data = get_readback_float(&rb.rb, i, 0);
        ok(float_data == expected, "Got %.8e, expected %u (index %u).\n", float_data, expected, i);
    }
    release_resource_readback(&rb);
    reset_command_list(command_list, context.allocator);
    get_buffer_readback_with_command_list(buffer2, DXGI_FORMAT_R32_UINT, &rb, queue, command_list);
    for (i = 0; i < 96; ++i)
    {
        expected = (i % 32 + 1) * (i / 32);
        data = get_readback_uint(&rb.rb, i, 0, 0);
        ok(data == expected, "Got %u, expected %u (index %u).\n", data, expected, i);
    }
    release_resource_readback(&rb);

    ID3D12Resource_Release(buffer);
    ID3D12Resource_Release(buffer2);
    ID3D12DescriptorHeap_Release(cpu_descriptor_heap);
    ID3D12DescriptorHeap_Release(descriptor_heap);
    destroy_test_context(&context);
}

static void test_uav_load(void)
{
    struct texture
    {
        unsigned int width;
        unsigned int height;
        unsigned int miplevel_count;
        unsigned int array_size;
        DXGI_FORMAT format;
        D3D12_SUBRESOURCE_DATA data[3];
    };

    D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle, rtv_float, rtv_uint, rtv_sint;
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    D3D12_DESCRIPTOR_RANGE descriptor_ranges[1];
    ID3D12DescriptorHeap *rtv_heap, *uav_heap;
    D3D12_ROOT_PARAMETER root_parameters[2];
    const D3D12_SHADER_BYTECODE *current_ps;
    ID3D12GraphicsCommandList *command_list;
    D3D12_RENDER_TARGET_VIEW_DESC rtv_desc;
    const struct texture *current_texture;
    D3D12_HEAP_PROPERTIES heap_properties;
    ID3D12Resource *texture, *rt_texture;
    struct d3d12_resource_readback rb;
    D3D12_RESOURCE_DESC resource_desc;
    D3D12_CLEAR_VALUE clear_value;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12CommandQueue *queue;
    unsigned int rtv_size;
    ID3D12Device *device;
    unsigned int i, x, y;
    HRESULT hr;

    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    static const DWORD ps_ld_2d_float_code[] =
    {
#if 0
        RWTexture2D<float> u;

        float main(float4 position : SV_Position) : SV_Target
        {
            float2 s;
            u.GetDimensions(s.x, s.y);
            return u[s * float2(position.x / 640.0f, position.y / 480.0f)];
        }
#endif
        0x43425844, 0xd5996e04, 0x6bede909, 0x0a7ad18e, 0x5eb277fb, 0x00000001, 0x00000194, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000030f, 0x505f5653, 0x7469736f, 0x006e6f69,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x00000e01, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x000000f8, 0x00000050,
        0x0000003e, 0x0100086a, 0x0400189c, 0x0011e000, 0x00000001, 0x00005555, 0x04002064, 0x00101032,
        0x00000000, 0x00000001, 0x03000065, 0x00102012, 0x00000000, 0x02000068, 0x00000001, 0x8900003d,
        0x800000c2, 0x00155543, 0x00100032, 0x00000000, 0x00004001, 0x00000000, 0x0011ee46, 0x00000001,
        0x07000038, 0x001000f2, 0x00000000, 0x00100546, 0x00000000, 0x00101546, 0x00000000, 0x0a000038,
        0x001000f2, 0x00000000, 0x00100e46, 0x00000000, 0x00004002, 0x3acccccd, 0x3b088889, 0x3b088889,
        0x3b088889, 0x0500001c, 0x001000f2, 0x00000000, 0x00100e46, 0x00000000, 0x890000a3, 0x800000c2,
        0x00155543, 0x00100012, 0x00000000, 0x00100e46, 0x00000000, 0x0011ee46, 0x00000001, 0x05000036,
        0x00102012, 0x00000000, 0x0010000a, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_ld_2d_float = {ps_ld_2d_float_code, sizeof(ps_ld_2d_float_code)};
    static const DWORD ps_ld_2d_uint_code[] =
    {
#if 0
        RWTexture2D<uint> u;

        uint main(float4 position : SV_Position) : SV_Target
        {
            float2 s;
            u.GetDimensions(s.x, s.y);
            return u[s * float2(position.x / 640.0f, position.y / 480.0f)];
        }
#endif
        0x43425844, 0x2cc0af18, 0xb28eca73, 0x9651215b, 0xebe3f361, 0x00000001, 0x00000194, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000030f, 0x505f5653, 0x7469736f, 0x006e6f69,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001,
        0x00000000, 0x00000e01, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x000000f8, 0x00000050,
        0x0000003e, 0x0100086a, 0x0400189c, 0x0011e000, 0x00000001, 0x00004444, 0x04002064, 0x00101032,
        0x00000000, 0x00000001, 0x03000065, 0x00102012, 0x00000000, 0x02000068, 0x00000001, 0x8900003d,
        0x800000c2, 0x00111103, 0x00100032, 0x00000000, 0x00004001, 0x00000000, 0x0011ee46, 0x00000001,
        0x07000038, 0x001000f2, 0x00000000, 0x00100546, 0x00000000, 0x00101546, 0x00000000, 0x0a000038,
        0x001000f2, 0x00000000, 0x00100e46, 0x00000000, 0x00004002, 0x3acccccd, 0x3b088889, 0x3b088889,
        0x3b088889, 0x0500001c, 0x001000f2, 0x00000000, 0x00100e46, 0x00000000, 0x890000a3, 0x800000c2,
        0x00111103, 0x00100012, 0x00000000, 0x00100e46, 0x00000000, 0x0011ee46, 0x00000001, 0x05000036,
        0x00102012, 0x00000000, 0x0010000a, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_ld_2d_uint = {ps_ld_2d_uint_code, sizeof(ps_ld_2d_uint_code)};
    static const DWORD ps_ld_2d_int_code[] =
    {
#if 0
        RWTexture2D<int> u;

        int main(float4 position : SV_Position) : SV_Target
        {
            float2 s;
            u.GetDimensions(s.x, s.y);
            return u[s * float2(position.x / 640.0f, position.y / 480.0f)];
        }
#endif
        0x43425844, 0x7deee248, 0xe7c48698, 0x9454db00, 0x921810e7, 0x00000001, 0x00000194, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000030f, 0x505f5653, 0x7469736f, 0x006e6f69,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000002,
        0x00000000, 0x00000e01, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x000000f8, 0x00000050,
        0x0000003e, 0x0100086a, 0x0400189c, 0x0011e000, 0x00000001, 0x00003333, 0x04002064, 0x00101032,
        0x00000000, 0x00000001, 0x03000065, 0x00102012, 0x00000000, 0x02000068, 0x00000001, 0x8900003d,
        0x800000c2, 0x000cccc3, 0x00100032, 0x00000000, 0x00004001, 0x00000000, 0x0011ee46, 0x00000001,
        0x07000038, 0x001000f2, 0x00000000, 0x00100546, 0x00000000, 0x00101546, 0x00000000, 0x0a000038,
        0x001000f2, 0x00000000, 0x00100e46, 0x00000000, 0x00004002, 0x3acccccd, 0x3b088889, 0x3b088889,
        0x3b088889, 0x0500001c, 0x001000f2, 0x00000000, 0x00100e46, 0x00000000, 0x890000a3, 0x800000c2,
        0x000cccc3, 0x00100012, 0x00000000, 0x00100e46, 0x00000000, 0x0011ee46, 0x00000001, 0x05000036,
        0x00102012, 0x00000000, 0x0010000a, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_ld_2d_int = {ps_ld_2d_int_code, sizeof(ps_ld_2d_int_code)};
    static const DWORD ps_ld_2d_uint_arr_code[] =
    {
#if 0
        RWTexture2DArray<uint> u;

        uint layer;

        uint main(float4 position : SV_Position) : SV_Target
        {
            float3 s;
            u.GetDimensions(s.x, s.y, s.z);
            s.z = layer;
            return u[s * float3(position.x / 640.0f, position.y / 480.0f, 1.0f)];
        }
#endif
        0x43425844, 0xa7630358, 0xd7e7228f, 0xa9f1be03, 0x838554f1, 0x00000001, 0x000001bc, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000030f, 0x505f5653, 0x7469736f, 0x006e6f69,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001,
        0x00000000, 0x00000e01, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000120, 0x00000050,
        0x00000048, 0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x0400409c, 0x0011e000,
        0x00000001, 0x00004444, 0x04002064, 0x00101032, 0x00000000, 0x00000001, 0x03000065, 0x00102012,
        0x00000000, 0x02000068, 0x00000001, 0x8900003d, 0x80000202, 0x00111103, 0x00100032, 0x00000000,
        0x00004001, 0x00000000, 0x0011ee46, 0x00000001, 0x07000038, 0x00100032, 0x00000000, 0x00100046,
        0x00000000, 0x00101046, 0x00000000, 0x06000056, 0x001000c2, 0x00000000, 0x00208006, 0x00000000,
        0x00000000, 0x0a000038, 0x001000f2, 0x00000000, 0x00100e46, 0x00000000, 0x00004002, 0x3acccccd,
        0x3b088889, 0x3f800000, 0x3f800000, 0x0500001c, 0x001000f2, 0x00000000, 0x00100e46, 0x00000000,
        0x890000a3, 0x80000202, 0x00111103, 0x00100012, 0x00000000, 0x00100e46, 0x00000000, 0x0011ee46,
        0x00000001, 0x05000036, 0x00102012, 0x00000000, 0x0010000a, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_ld_2d_uint_arr = {ps_ld_2d_uint_arr_code, sizeof(ps_ld_2d_uint_arr_code)};
    static const float float_data[] =
    {
         0.50f,  0.25f,  1.00f,  0.00f,
        -1.00f, -2.00f, -3.00f, -4.00f,
        -0.50f, -0.25f, -1.00f, -0.00f,
         1.00f,  2.00f,  3.00f,  4.00f,
    };
    static const unsigned int uint_data[] =
    {
        0x00, 0x10, 0x20, 0x30,
        0x40, 0x50, 0x60, 0x70,
        0x80, 0x90, 0xa0, 0xb0,
        0xc0, 0xd0, 0xe0, 0xf0,
    };
    static const unsigned int uint_data2[] =
    {
        0xffff, 0xffff, 0xffff, 0xffff,
        0xffff, 0xc000, 0xc000, 0xffff,
        0xffff, 0xc000, 0xc000, 0xffff,
        0xffff, 0xffff, 0xffff, 0xffff,
    };
    static const unsigned int uint_data3[] =
    {
        0xaa, 0xaa, 0xcc, 0xcc,
        0xaa, 0xaa, 0xdd, 0xdd,
        0xbb, 0xbb, 0xee, 0xee,
        0xbb, 0xbb, 0xff, 0xff,
    };
    static const int int_data[] =
    {
          -1, 0x10, 0x20, 0x30,
        0x40, 0x50, 0x60, -777,
        -666, 0x90, -555, 0xb0,
        0xc0, 0xd0, 0xe0, -101,
    };
    static const struct texture float_2d = {4, 4, 1, 1, DXGI_FORMAT_R32_FLOAT,
            {{float_data, 4 * sizeof(*float_data), 0}}};
    static const struct texture uint_2d = {4, 4, 1, 1, DXGI_FORMAT_R32_UINT,
            {{uint_data, 4 * sizeof(*uint_data), 0}}};
    static const struct texture uint2d_arr = {4, 4, 1, 3, DXGI_FORMAT_R32_UINT,
            {{uint_data, 4 * sizeof(*uint_data), 0},
            {uint_data2, 4 * sizeof(*uint_data2), 0},
            {uint_data3, 4 * sizeof(*uint_data3), 0}}};
    static const struct texture int_2d = {4, 4, 1, 1, DXGI_FORMAT_R32_SINT,
            {{int_data, 4 * sizeof(*int_data), 0}}};

    static const struct test
    {
        const D3D12_SHADER_BYTECODE *ps;
        const struct texture *texture;
        D3D12_UNORDERED_ACCESS_VIEW_DESC uav_desc;
        unsigned int constant;
        const DWORD *expected_colors;
    }
    tests[] =
    {
#define TEX_2D       D3D12_UAV_DIMENSION_TEXTURE2D
#define TEX_2D_ARRAY D3D12_UAV_DIMENSION_TEXTURE2DARRAY
#define R32_FLOAT    DXGI_FORMAT_R32_FLOAT
#define R32_UINT     DXGI_FORMAT_R32_UINT
#define R32_SINT     DXGI_FORMAT_R32_SINT
        {&ps_ld_2d_float,    &float_2d,   {R32_FLOAT, TEX_2D, .Texture2D = {0}}, 0, (const DWORD *)float_data},
        {&ps_ld_2d_uint,     &uint_2d,    {R32_UINT,  TEX_2D, .Texture2D = {0}}, 0, (const DWORD *)uint_data},
        {&ps_ld_2d_int,      &int_2d,     {R32_SINT,  TEX_2D, .Texture2D = {0}}, 0, (const DWORD *)int_data},

        {&ps_ld_2d_uint_arr, &uint2d_arr, {R32_UINT, TEX_2D_ARRAY, .Texture2DArray = {0, 0, ~0u}}, 0,
                (const DWORD *)uint_data},
        {&ps_ld_2d_uint_arr, &uint2d_arr, {R32_UINT, TEX_2D_ARRAY, .Texture2DArray = {0, 0, ~0u}}, 1,
                (const DWORD *)uint_data2},
        {&ps_ld_2d_uint_arr, &uint2d_arr, {R32_UINT, TEX_2D_ARRAY, .Texture2DArray = {0, 0, ~0u}}, 2,
                (const DWORD *)uint_data3},
        {&ps_ld_2d_uint_arr, &uint2d_arr, {R32_UINT, TEX_2D_ARRAY, .Texture2DArray = {0, 1, ~0u}}, 0,
                (const DWORD *)uint_data2},
        {&ps_ld_2d_uint_arr, &uint2d_arr, {R32_UINT, TEX_2D_ARRAY, .Texture2DArray = {0, 1, ~0u}}, 1,
                (const DWORD *)uint_data3},
        {&ps_ld_2d_uint_arr, &uint2d_arr, {R32_UINT, TEX_2D_ARRAY, .Texture2DArray = {0, 2, ~0u}}, 0,
                (const DWORD *)uint_data3},
#undef TEX_2D
#undef TEX_2D_ARRAY
#undef R32_FLOAT
#undef R32_UINT
#undef R32_SINT
    };

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

    descriptor_ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
    descriptor_ranges[0].NumDescriptors = 1;
    descriptor_ranges[0].BaseShaderRegister = 1;
    descriptor_ranges[0].RegisterSpace = 0;
    descriptor_ranges[0].OffsetInDescriptorsFromTableStart = 0;
    root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameters[0].DescriptorTable.NumDescriptorRanges = 1;
    root_parameters[0].DescriptorTable.pDescriptorRanges = descriptor_ranges;
    root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
    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 = 1;
    root_parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
    root_signature_desc.NumParameters = 2;
    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(device, &root_signature_desc, &context.root_signature);
    ok(SUCCEEDED(hr), "Failed to create root signature, hr %#x.\n", hr);

    set_viewport(&context.viewport, 0.0f, 0.0f, 640.0f, 480.0f, 0.0f, 1.0f);
    set_rect(&context.scissor_rect, 0, 0, 640, 480);

    memset(&heap_properties, 0, sizeof(heap_properties));
    heap_properties.Type = D3D12_HEAP_TYPE_DEFAULT;
    resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
    resource_desc.Alignment = 0;
    resource_desc.Width = 640;
    resource_desc.Height = 480;
    resource_desc.DepthOrArraySize = 1;
    resource_desc.MipLevels = 1;
    resource_desc.Format = DXGI_FORMAT_R32_TYPELESS;
    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;
    clear_value.Format = DXGI_FORMAT_R32_FLOAT;
    clear_value.Color[0] = 1.0f;
    clear_value.Color[1] = 1.0f;
    clear_value.Color[2] = 1.0f;
    clear_value.Color[3] = 1.0f;
    hr = ID3D12Device_CreateCommittedResource(device,
            &heap_properties, D3D12_HEAP_FLAG_NONE, &resource_desc,
            D3D12_RESOURCE_STATE_RENDER_TARGET, &clear_value,
            &IID_ID3D12Resource, (void **)&rt_texture);
    ok(SUCCEEDED(hr), "Failed to create texture, hr %#x.\n", hr);

    rtv_heap = create_cpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_RTV, 3);
    cpu_handle = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(rtv_heap);
    rtv_size = ID3D12Device_GetDescriptorHandleIncrementSize(device,
            D3D12_DESCRIPTOR_HEAP_TYPE_RTV);

    memset(&rtv_desc, 0, sizeof(rtv_desc));
    rtv_desc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D;
    rtv_desc.Format = DXGI_FORMAT_R32_FLOAT;
    ID3D12Device_CreateRenderTargetView(device, rt_texture, &rtv_desc, cpu_handle);
    rtv_float = cpu_handle;
    cpu_handle.ptr += rtv_size;

    rtv_desc.Format = DXGI_FORMAT_R32_UINT;
    ID3D12Device_CreateRenderTargetView(device, rt_texture, &rtv_desc, cpu_handle);
    rtv_uint = cpu_handle;
    cpu_handle.ptr += rtv_size;

    rtv_desc.Format = DXGI_FORMAT_R32_SINT;
    ID3D12Device_CreateRenderTargetView(device, rt_texture, &rtv_desc, cpu_handle);
    rtv_sint = cpu_handle;

    uav_heap = create_gpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1);

    resource_desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;

    texture = NULL;
    current_ps = NULL;
    current_texture = NULL;
    for (i = 0; i < ARRAY_SIZE(tests); ++i)
    {
        const struct test *test = &tests[i];

        if (current_ps != test->ps)
        {
            if (context.pipeline_state)
                ID3D12PipelineState_Release(context.pipeline_state);
            current_ps = tests[i].ps;
            context.pipeline_state = create_pipeline_state(context.device,
                    context.root_signature, test->uav_desc.Format, NULL, current_ps, NULL);
        }

        if (current_texture != test->texture)
        {
            if (texture)
                ID3D12Resource_Release(texture);

            current_texture = test->texture;

            resource_desc.Width = current_texture->width;
            resource_desc.Height = current_texture->height;
            resource_desc.MipLevels = current_texture->miplevel_count;
            resource_desc.DepthOrArraySize = current_texture->array_size;
            resource_desc.Format = current_texture->format;
            hr = ID3D12Device_CreateCommittedResource(device,
                    &heap_properties, D3D12_HEAP_FLAG_NONE, &resource_desc,
                    D3D12_RESOURCE_STATE_COPY_DEST, NULL,
                    &IID_ID3D12Resource, (void **)&texture);
            ok(SUCCEEDED(hr), "Test %u: Failed to create texture, hr %#x.\n", i, hr);

            upload_texture_data(texture, current_texture->data,
                    resource_desc.MipLevels * resource_desc.DepthOrArraySize, queue, command_list);
            reset_command_list(command_list, context.allocator);

            transition_resource_state(command_list, texture,
                    D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
        }

        cpu_handle = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(uav_heap);
        ID3D12Device_CreateUnorderedAccessView(device, texture, NULL, &test->uav_desc, cpu_handle);

        switch (test->uav_desc.Format)
        {
            default:
                trace("Unhandled format %#x.\n", test->uav_desc.Format);
                /* fall-through */
            case DXGI_FORMAT_R32_FLOAT:
                cpu_handle = rtv_float;
                break;
            case DXGI_FORMAT_R32_UINT:
                cpu_handle = rtv_uint;
                break;
            case DXGI_FORMAT_R32_SINT:
                cpu_handle = rtv_sint;
                break;
        }

        ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
        ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
        ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &uav_heap);
        ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0,
                ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(uav_heap));
        ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstant(command_list, 1, test->constant, 0);
        ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
        ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &cpu_handle, false, NULL);
        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, cpu_handle, white, 0, NULL);
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

        transition_sub_resource_state(command_list, rt_texture, 0,
                D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);

        get_resource_readback_with_command_list(rt_texture, 0, &rb, queue, command_list);
        for (y = 0; y < 4; ++y)
        {
            for (x = 0; x < 4; ++x)
            {
                unsigned int expected = test->expected_colors[y * 4 + x];
                unsigned int color = get_readback_uint(&rb.rb, 80 + x * 160, 60 + y * 120, 0);
                ok(compare_color(color, expected, 0),
                        "Test %u: Got 0x%08x, expected 0x%08x at (%u, %u).\n",
                        i, color, expected, x, y);
            }
        }
        release_resource_readback(&rb);

        reset_command_list(command_list, context.allocator);
        transition_sub_resource_state(command_list, rt_texture, 0,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
    }
    ID3D12Resource_Release(texture);

    ID3D12DescriptorHeap_Release(rtv_heap);
    ID3D12DescriptorHeap_Release(uav_heap);
    ID3D12Resource_Release(rt_texture);
    destroy_test_context(&context);
}

static void test_cs_uav_store(void)
{
    D3D12_CPU_DESCRIPTOR_HANDLE cpu_descriptor_handle;
    D3D12_GPU_DESCRIPTOR_HANDLE gpu_descriptor_handle;
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    const D3D12_SHADER_BYTECODE *current_shader;
    D3D12_DESCRIPTOR_RANGE descriptor_ranges[1];
    D3D12_UNORDERED_ACCESS_VIEW_DESC uav_desc;
    ID3D12DescriptorHeap *cpu_descriptor_heap;
    ID3D12GraphicsCommandList *command_list;
    D3D12_ROOT_PARAMETER root_parameters[2];
    ID3D12DescriptorHeap *descriptor_heap;
    ID3D12RootSignature *root_signature;
    ID3D12PipelineState *pipeline_state;
    struct d3d12_resource_readback rb;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Resource *resource;
    ID3D12Device *device;
    ID3D12Resource *cb;
    struct vec4 input;
    unsigned int i;
    HRESULT hr;
    RECT rect;

    static const DWORD cs_1_thread_code[] =
    {
#if 0
        RWTexture2D<float> u;

        float value;

        [numthreads(1, 1, 1)]
        void main()
        {
            uint x, y, width, height;
            u.GetDimensions(width, height);
            for (y = 0; y < height; ++y)
            {
                for (x = 0; x < width; ++x)
                    u[uint2(x, y)] = value;
            }
        }
#endif
        0x43425844, 0x6503503a, 0x4cd524e6, 0x2473915d, 0x93cf1201, 0x00000001, 0x000001c8, 0x00000003,
        0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x00000174, 0x00050050, 0x0000005d, 0x0100086a,
        0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x0400189c, 0x0011e000, 0x00000000, 0x00005555,
        0x02000068, 0x00000003, 0x0400009b, 0x00000001, 0x00000001, 0x00000001, 0x8900103d, 0x800000c2,
        0x00155543, 0x00100032, 0x00000000, 0x00004001, 0x00000000, 0x0011ee46, 0x00000000, 0x05000036,
        0x00100042, 0x00000000, 0x00004001, 0x00000000, 0x01000030, 0x07000050, 0x00100082, 0x00000000,
        0x0010002a, 0x00000000, 0x0010001a, 0x00000000, 0x03040003, 0x0010003a, 0x00000000, 0x05000036,
        0x001000e2, 0x00000001, 0x00100aa6, 0x00000000, 0x05000036, 0x00100082, 0x00000000, 0x00004001,
        0x00000000, 0x01000030, 0x07000050, 0x00100012, 0x00000002, 0x0010003a, 0x00000000, 0x0010000a,
        0x00000000, 0x03040003, 0x0010000a, 0x00000002, 0x05000036, 0x00100012, 0x00000001, 0x0010003a,
        0x00000000, 0x080000a4, 0x0011e0f2, 0x00000000, 0x00100e46, 0x00000001, 0x00208006, 0x00000000,
        0x00000000, 0x0700001e, 0x00100082, 0x00000000, 0x0010003a, 0x00000000, 0x00004001, 0x00000001,
        0x01000016, 0x0700001e, 0x00100042, 0x00000000, 0x0010002a, 0x00000000, 0x00004001, 0x00000001,
        0x01000016, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE cs_1_thread = {cs_1_thread_code, sizeof(cs_1_thread_code)};
    static const DWORD cs_1_group_code[] =
    {
#if 0
        RWTexture2D<float> u;

        float value;

        [numthreads(16, 16, 1)]
        void main(uint3 threadID : SV_GroupThreadID)
        {
            uint2 count, size ;
            u.GetDimensions(size.x, size.y);
            count = size / (uint2)16;
            for (uint y = 0; y < count.y; ++y)
                for (uint x = 0; x < count.x; ++x)
                    u[count * threadID.xy + uint2(x, y)] = value;
        }
#endif
        0x43425844, 0x9fb86044, 0x352c196d, 0x92e14094, 0x46bb95a7, 0x00000001, 0x00000218, 0x00000003,
        0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x000001c4, 0x00050050, 0x00000071, 0x0100086a,
        0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x0400189c, 0x0011e000, 0x00000000, 0x00005555,
        0x0200005f, 0x00022032, 0x02000068, 0x00000004, 0x0400009b, 0x00000010, 0x00000010, 0x00000001,
        0x8900103d, 0x800000c2, 0x00155543, 0x00100032, 0x00000000, 0x00004001, 0x00000000, 0x0011ee46,
        0x00000000, 0x0a000055, 0x001000f2, 0x00000000, 0x00100546, 0x00000000, 0x00004002, 0x00000004,
        0x00000004, 0x00000004, 0x00000004, 0x05000036, 0x00100012, 0x00000001, 0x00004001, 0x00000000,
        0x01000030, 0x07000050, 0x00100022, 0x00000001, 0x0010000a, 0x00000001, 0x0010003a, 0x00000000,
        0x03040003, 0x0010001a, 0x00000001, 0x05000036, 0x001000e2, 0x00000002, 0x00100006, 0x00000001,
        0x05000036, 0x00100022, 0x00000001, 0x00004001, 0x00000000, 0x01000030, 0x07000050, 0x00100042,
        0x00000001, 0x0010001a, 0x00000001, 0x0010000a, 0x00000000, 0x03040003, 0x0010002a, 0x00000001,
        0x05000036, 0x00100012, 0x00000002, 0x0010001a, 0x00000001, 0x08000023, 0x001000f2, 0x00000003,
        0x00100e46, 0x00000000, 0x00022546, 0x00100e46, 0x00000002, 0x080000a4, 0x0011e0f2, 0x00000000,
        0x00100e46, 0x00000003, 0x00208006, 0x00000000, 0x00000000, 0x0700001e, 0x00100022, 0x00000001,
        0x0010001a, 0x00000001, 0x00004001, 0x00000001, 0x01000016, 0x0700001e, 0x00100012, 0x00000001,
        0x0010000a, 0x00000001, 0x00004001, 0x00000001, 0x01000016, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE cs_1_group = {cs_1_group_code, sizeof(cs_1_group_code)};
    static const DWORD cs_1_store_code[] =
    {
#if 0
        RWTexture2D<float> u;

        float value;

        [numthreads(1, 1, 1)]
        void main(uint3 groupID : SV_GroupID)
        {
            u[groupID.xy] = value;
        }
#endif
        0x43425844, 0xc3add41b, 0x67df51b1, 0x2b887930, 0xcb1ee991, 0x00000001, 0x000000b8, 0x00000003,
        0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x00000064, 0x00050050, 0x00000019, 0x0100086a,
        0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x0400189c, 0x0011e000, 0x00000000, 0x00005555,
        0x0200005f, 0x00021032, 0x0400009b, 0x00000001, 0x00000001, 0x00000001, 0x070000a4, 0x0011e0f2,
        0x00000000, 0x00021546, 0x00208006, 0x00000000, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE cs_1_store = {cs_1_store_code, sizeof(cs_1_store_code)};
    static const DWORD cs_dispatch_id_code[] =
    {
#if 0
        RWTexture2D<float> u;

        float value;

        [numthreads(4, 4, 1)]
        void main(uint3 id : SV_DispatchThreadID)
        {
            u[id.xy] = value;
        }
#endif
        0x43425844, 0x60166991, 0x4b595266, 0x7fb67d79, 0x485c4f0d, 0x00000001, 0x000000b8, 0x00000003,
        0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x00000064, 0x00050050, 0x00000019, 0x0100086a,
        0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x0400189c, 0x0011e000, 0x00000000, 0x00005555,
        0x0200005f, 0x00020032, 0x0400009b, 0x00000004, 0x00000004, 0x00000001, 0x070000a4, 0x0011e0f2,
        0x00000000, 0x00020546, 0x00208006, 0x00000000, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE cs_dispatch_id = {cs_dispatch_id_code, sizeof(cs_dispatch_id_code)};
    static const DWORD cs_group_index_code[] =
    {
#if 0
        RWTexture2D<float> u;

        float value;

        [numthreads(32, 1, 1)]
        void main(uint index : SV_GroupIndex)
        {
            uint2 size;
            u.GetDimensions(size.x, size.y);
            uint count = size.x * size.y / 32;
            index *= count;
            for (uint i = 0; i < count; ++i, ++index)
                u[uint2(index % size.x, index / size.x)] = value;
        }
#endif
        0x43425844, 0xb685a70f, 0x94c2f263, 0x4f1d8eaa, 0xeab65731, 0x00000001, 0x000001f8, 0x00000003,
        0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x000001a4, 0x00050050, 0x00000069, 0x0100086a,
        0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x0400189c, 0x0011e000, 0x00000000, 0x00005555,
        0x0200005f, 0x00024000, 0x02000068, 0x00000004, 0x0400009b, 0x00000020, 0x00000001, 0x00000001,
        0x8900103d, 0x800000c2, 0x00155543, 0x00100032, 0x00000000, 0x00004001, 0x00000000, 0x0011ee46,
        0x00000000, 0x08000026, 0x0000d000, 0x00100022, 0x00000000, 0x0010000a, 0x00000000, 0x0010001a,
        0x00000000, 0x07000055, 0x00100022, 0x00000000, 0x0010001a, 0x00000000, 0x00004001, 0x00000005,
        0x07000026, 0x0000d000, 0x00100042, 0x00000000, 0x0002400a, 0x0010001a, 0x00000000, 0x05000036,
        0x00100012, 0x00000001, 0x0010002a, 0x00000000, 0x05000036, 0x00100022, 0x00000001, 0x00004001,
        0x00000000, 0x01000030, 0x07000050, 0x00100082, 0x00000000, 0x0010001a, 0x00000001, 0x0010001a,
        0x00000000, 0x03040003, 0x0010003a, 0x00000000, 0x0900004e, 0x00100012, 0x00000002, 0x00100012,
        0x00000003, 0x0010000a, 0x00000001, 0x0010000a, 0x00000000, 0x05000036, 0x001000e2, 0x00000003,
        0x00100006, 0x00000002, 0x080000a4, 0x0011e0f2, 0x00000000, 0x00100e46, 0x00000003, 0x00208006,
        0x00000000, 0x00000000, 0x0a00001e, 0x00100032, 0x00000001, 0x00100046, 0x00000001, 0x00004002,
        0x00000001, 0x00000001, 0x00000000, 0x00000000, 0x01000016, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE cs_group_index = {cs_group_index_code, sizeof(cs_group_index_code)};
    static const float zero[4] = {0};
    static const struct
    {
        const D3D12_SHADER_BYTECODE *shader;
        float value;
    }
    tests[] =
    {
        {&cs_1_thread,    1.0f},
        {&cs_1_thread,    0.5f},
        {&cs_1_group,     2.0f},
        {&cs_1_group,     4.0f},
        {&cs_group_index, 0.3f},
        {&cs_group_index, 0.1f},
    };

    if (!init_compute_test_context(&context))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    cb = create_upload_buffer(context.device, sizeof(input), NULL);

    resource = create_default_texture(device, 64, 64, DXGI_FORMAT_R32_FLOAT,
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);

    descriptor_ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
    descriptor_ranges[0].NumDescriptors = 1;
    descriptor_ranges[0].BaseShaderRegister = 0;
    descriptor_ranges[0].RegisterSpace = 0;
    descriptor_ranges[0].OffsetInDescriptorsFromTableStart = 0;
    root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameters[0].DescriptorTable.NumDescriptorRanges = 1;
    root_parameters[0].DescriptorTable.pDescriptorRanges = descriptor_ranges;
    root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    root_parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
    root_parameters[1].Descriptor.ShaderRegister = 0;
    root_parameters[1].Descriptor.RegisterSpace = 0;
    root_parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    root_signature_desc.NumParameters = 2;
    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(device, &root_signature_desc, &root_signature);
    ok(SUCCEEDED(hr), "Failed to create root signature, hr %#x.\n", hr);

    descriptor_heap = create_gpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1);
    cpu_descriptor_heap = create_cpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1);
    cpu_descriptor_handle = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(descriptor_heap);
    gpu_descriptor_handle = ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(descriptor_heap);

    uav_desc.Format = DXGI_FORMAT_R32_FLOAT;
    uav_desc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D;
    uav_desc.Texture2D.MipSlice = 0;
    uav_desc.Texture2D.PlaneSlice = 0;
    ID3D12Device_CreateUnorderedAccessView(device, resource, NULL, &uav_desc, cpu_descriptor_handle);
    cpu_descriptor_handle = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(cpu_descriptor_heap);
    ID3D12Device_CreateUnorderedAccessView(device, resource, NULL, &uav_desc, cpu_descriptor_handle);

    hr = ID3D12GraphicsCommandList_Close(command_list);
    ok(SUCCEEDED(hr), "Failed to close command list, hr %#x.\n", hr);

    current_shader = NULL;
    pipeline_state = NULL;
    for (i = 0; i < ARRAY_SIZE(tests); ++i)
    {
        vkd3d_test_push_context("Test %u", i);

        if (current_shader != tests[i].shader)
        {
            if (pipeline_state)
                ID3D12PipelineState_Release(pipeline_state);

            current_shader = tests[i].shader;
            pipeline_state = create_compute_pipeline_state(device, root_signature, *current_shader);
        }

        memset(&input, 0, sizeof(input));
        input.x = tests[i].value;
        update_buffer_data(cb, 0, sizeof(input), &input.x);

        reset_command_list(command_list, context.allocator);
        transition_sub_resource_state(command_list, resource, 0,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

        ID3D12GraphicsCommandList_ClearUnorderedAccessViewFloat(command_list,
                gpu_descriptor_handle, cpu_descriptor_handle, resource, zero, 0, NULL);
        uav_barrier(command_list, resource);

        ID3D12GraphicsCommandList_SetPipelineState(command_list, pipeline_state);
        ID3D12GraphicsCommandList_SetComputeRootSignature(command_list, root_signature);
        ID3D12GraphicsCommandList_SetComputeRootConstantBufferView(command_list, 1,
                ID3D12Resource_GetGPUVirtualAddress(cb));
        ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &descriptor_heap);
        ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(command_list, 0, gpu_descriptor_handle);
        ID3D12GraphicsCommandList_Dispatch(command_list, 1, 1, 1);

        transition_sub_resource_state(command_list, resource, 0,
                D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
        check_sub_resource_float(resource, 0, queue, command_list, tests[i].value, 2);

        vkd3d_test_pop_context();
    }

    ID3D12PipelineState_Release(pipeline_state);
    pipeline_state = create_compute_pipeline_state(device, root_signature, cs_1_store);

    memset(&input, 0, sizeof(input));
    input.x = 1.0f;
    update_buffer_data(cb, 0, sizeof(input), &input.x);

    reset_command_list(command_list, context.allocator);
    transition_sub_resource_state(command_list, resource, 0,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

    ID3D12GraphicsCommandList_ClearUnorderedAccessViewFloat(command_list,
            gpu_descriptor_handle, cpu_descriptor_handle, resource, zero, 0, NULL);
    uav_barrier(command_list, resource);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, pipeline_state);
    ID3D12GraphicsCommandList_SetComputeRootSignature(command_list, root_signature);
    ID3D12GraphicsCommandList_SetComputeRootConstantBufferView(command_list, 1,
            ID3D12Resource_GetGPUVirtualAddress(cb));
    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &descriptor_heap);
    ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(command_list, 0, gpu_descriptor_handle);
    ID3D12GraphicsCommandList_Dispatch(command_list, 64, 64, 1);

    transition_sub_resource_state(command_list, resource, 0,
            D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_float(resource, 0, queue, command_list, 1.0f, 2);

    memset(&input, 0, sizeof(input));
    input.x = 0.5f;
    update_buffer_data(cb, 0, sizeof(input), &input.x);

    reset_command_list(command_list, context.allocator);
    transition_sub_resource_state(command_list, resource, 0,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

    ID3D12GraphicsCommandList_SetPipelineState(command_list, pipeline_state);
    ID3D12GraphicsCommandList_SetComputeRootSignature(command_list, root_signature);
    ID3D12GraphicsCommandList_SetComputeRootConstantBufferView(command_list, 1,
            ID3D12Resource_GetGPUVirtualAddress(cb));
    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &descriptor_heap);
    ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(command_list, 0, gpu_descriptor_handle);
    ID3D12GraphicsCommandList_Dispatch(command_list, 16, 32, 1);

    transition_sub_resource_state(command_list, resource, 0,
            D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_resource_readback_with_command_list(resource, 0, &rb, queue, command_list);
    set_rect(&rect, 0, 0, 16, 32);
    check_readback_data_float(&rb.rb, &rect, 0.5f, 2);
    set_rect(&rect, 0, 32, rb.rb.width, rb.rb.height);
    check_readback_data_float(&rb.rb, &rect, 1.0f, 2);
    set_rect(&rect, 16, 0, rb.rb.width, rb.rb.height);
    check_readback_data_float(&rb.rb, &rect, 1.0f, 2);
    release_resource_readback(&rb);

    ID3D12PipelineState_Release(pipeline_state);
    pipeline_state = create_compute_pipeline_state(device, root_signature, cs_dispatch_id);

    memset(&input, 0, sizeof(input));
    input.x = 0.6f;
    update_buffer_data(cb, 0, sizeof(input), &input.x);

    reset_command_list(command_list, context.allocator);
    transition_sub_resource_state(command_list, resource, 0,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

    ID3D12GraphicsCommandList_SetPipelineState(command_list, pipeline_state);
    ID3D12GraphicsCommandList_SetComputeRootSignature(command_list, root_signature);
    ID3D12GraphicsCommandList_SetComputeRootConstantBufferView(command_list, 1,
            ID3D12Resource_GetGPUVirtualAddress(cb));
    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &descriptor_heap);
    ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(command_list, 0, gpu_descriptor_handle);
    ID3D12GraphicsCommandList_Dispatch(command_list, 15, 15, 1);

    transition_sub_resource_state(command_list, resource, 0,
            D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_resource_readback_with_command_list(resource, 0, &rb, queue, command_list);
    set_rect(&rect, 0, 0, 60, 60);
    check_readback_data_float(&rb.rb, &rect, 0.6f, 2);
    set_rect(&rect, 0, 60, rb.rb.width, rb.rb.height);
    check_readback_data_float(&rb.rb, &rect, 1.0f, 2);
    set_rect(&rect, 60, 0, rb.rb.width, rb.rb.height);
    check_readback_data_float(&rb.rb, &rect, 1.0f, 2);
    release_resource_readback(&rb);

    memset(&input, 0, sizeof(input));
    input.x = 0.7f;
    update_buffer_data(cb, 0, sizeof(input), &input.x);

    reset_command_list(command_list, context.allocator);
    transition_sub_resource_state(command_list, resource, 0,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

    ID3D12GraphicsCommandList_SetPipelineState(command_list, pipeline_state);
    ID3D12GraphicsCommandList_SetComputeRootSignature(command_list, root_signature);
    ID3D12GraphicsCommandList_SetComputeRootConstantBufferView(command_list, 1,
            ID3D12Resource_GetGPUVirtualAddress(cb));
    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &descriptor_heap);
    ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(command_list, 0, gpu_descriptor_handle);
    ID3D12GraphicsCommandList_Dispatch(command_list, 16, 16, 1);

    transition_sub_resource_state(command_list, resource, 0,
            D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_float(resource, 0, queue, command_list, 0.7f, 2);

    ID3D12Resource_Release(cb);
    ID3D12Resource_Release(resource);
    ID3D12PipelineState_Release(pipeline_state);
    ID3D12RootSignature_Release(root_signature);
    ID3D12DescriptorHeap_Release(cpu_descriptor_heap);
    ID3D12DescriptorHeap_Release(descriptor_heap);
    destroy_test_context(&context);
}

static unsigned int read_uav_counter(const struct test_context *context,
        ID3D12Resource *counter_buffer, size_t offset)
{
    struct d3d12_resource_readback rb;
    uint32_t counter;

    transition_sub_resource_state(context->list, counter_buffer, 0,
            D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_buffer_readback_with_command_list(counter_buffer, DXGI_FORMAT_R32_UINT, &rb,
            context->queue, context->list);
    counter = get_readback_uint(&rb.rb, offset / sizeof(counter), 0, 0);
    release_resource_readback(&rb);
    reset_command_list(context->list, context->allocator);
    transition_sub_resource_state(context->list, counter_buffer, 0,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
    return counter;
}

static int compare_id(const void *a, const void *b)
{
    return vkd3d_u32_compare(*(uint32_t *)a, *(uint32_t *)b);
}

static void test_uav_counters(void)
{
    ID3D12Resource *buffer, *out_buffer, *counter_buffer;
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    D3D12_DESCRIPTOR_RANGE descriptor_ranges[1];
    D3D12_UNORDERED_ACCESS_VIEW_DESC uav_desc;
    ID3D12GraphicsCommandList *command_list;
    D3D12_ROOT_PARAMETER root_parameters[1];
    ID3D12DescriptorHeap *descriptor_heap;
    struct d3d12_resource_readback rb;
    struct test_context context;
    ID3D12CommandQueue *queue;
    uint32_t data, id[128];
    ID3D12Device *device;
    uint32_t counter;
    unsigned int i;
    HRESULT hr;

    static const DWORD cs_producer_code[] =
    {
#if 0
        RWStructuredBuffer<uint> u;

        [numthreads(4, 1, 1)]
        void main(uint3 dispatch_id : SV_DispatchThreadID)
        {
            uint counter = u.IncrementCounter();
            u[counter] = dispatch_id.x;
        }
#endif
        0x43425844, 0x013163a8, 0xe7d371b8, 0x4f71e39a, 0xd479e584, 0x00000001, 0x000000c8, 0x00000003,
        0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x00000074, 0x00050050, 0x0000001d, 0x0100086a,
        0x0480009e, 0x0011e000, 0x00000000, 0x00000004, 0x0200005f, 0x00020012, 0x02000068, 0x00000001,
        0x0400009b, 0x00000004, 0x00000001, 0x00000001, 0x050000b2, 0x00100012, 0x00000000, 0x0011e000,
        0x00000000, 0x080000a8, 0x0011e012, 0x00000000, 0x0010000a, 0x00000000, 0x00004001, 0x00000000,
        0x0002000a, 0x0100003e,
    };
    static const DWORD cs_consumer_code[] =
    {
#if 0
        RWStructuredBuffer<uint> u;
        RWStructuredBuffer<uint> u2;

        [numthreads(4, 1, 1)]
        void main()
        {
            uint counter = u.DecrementCounter();
            u2[counter] = u[counter];
        }
#endif
        0x43425844, 0x957ef3dd, 0x9f317559, 0x09c8f12d, 0xdbfd98c8, 0x00000001, 0x00000100, 0x00000003,
        0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x000000ac, 0x00050050, 0x0000002b, 0x0100086a,
        0x0480009e, 0x0011e000, 0x00000000, 0x00000004, 0x0400009e, 0x0011e000, 0x00000001, 0x00000004,
        0x02000068, 0x00000001, 0x0400009b, 0x00000004, 0x00000001, 0x00000001, 0x050000b3, 0x00100012,
        0x00000000, 0x0011e000, 0x00000000, 0x8b0000a7, 0x80002302, 0x00199983, 0x00100022, 0x00000000,
        0x0010000a, 0x00000000, 0x00004001, 0x00000000, 0x0011e006, 0x00000000, 0x090000a8, 0x0011e012,
        0x00000001, 0x0010000a, 0x00000000, 0x00004001, 0x00000000, 0x0010001a, 0x00000000, 0x0100003e,
    };

    static const unsigned int counter_offsets[] = {0, D3D12_UAV_COUNTER_PLACEMENT_ALIGNMENT,
            D3D12_UAV_COUNTER_PLACEMENT_ALIGNMENT * 2};

    if (!init_compute_test_context(&context))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    descriptor_ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
    descriptor_ranges[0].NumDescriptors = 2;
    descriptor_ranges[0].BaseShaderRegister = 0;
    descriptor_ranges[0].RegisterSpace = 0;
    descriptor_ranges[0].OffsetInDescriptorsFromTableStart = 0;
    root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameters[0].DescriptorTable.NumDescriptorRanges = 1;
    root_parameters[0].DescriptorTable.pDescriptorRanges = descriptor_ranges;
    root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    root_signature_desc.NumParameters = 1;
    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(device, &root_signature_desc, &context.root_signature);
    ok(SUCCEEDED(hr), "Failed to create root signature, hr %#x.\n", hr);

    descriptor_heap = create_gpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 3);

    buffer = create_default_buffer(device, 1024,
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
    out_buffer = create_default_buffer(device, 1024,
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
    counter_buffer = create_default_buffer(device, D3D12_UAV_COUNTER_PLACEMENT_ALIGNMENT * 2 + sizeof(uint32_t),
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_DEST);

    for (i = 0; i < ARRAY_SIZE(counter_offsets); ++i)
    {
        unsigned int j;

        vkd3d_test_push_context("Offset %u", counter_offsets[i]);

        context.pipeline_state = create_compute_pipeline_state(device, context.root_signature,
                shader_bytecode(cs_producer_code, sizeof(cs_producer_code)));

        memset(&uav_desc, 0, sizeof(uav_desc));
        uav_desc.Format = DXGI_FORMAT_UNKNOWN;
        uav_desc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
        uav_desc.Buffer.NumElements = 256;
        uav_desc.Buffer.StructureByteStride = sizeof(uint32_t);
        uav_desc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_NONE;
        ID3D12Device_CreateUnorderedAccessView(device, out_buffer, NULL, &uav_desc,
                get_cpu_descriptor_handle(&context, descriptor_heap, 1));
        uav_desc.Buffer.CounterOffsetInBytes = counter_offsets[i];
        ID3D12Device_CreateUnorderedAccessView(device, buffer, counter_buffer, &uav_desc,
                get_cpu_descriptor_handle(&context, descriptor_heap, 0));

        counter = 0;
        upload_buffer_data(counter_buffer, counter_offsets[i], sizeof(counter), &counter, queue, command_list);
        reset_command_list(command_list, context.allocator);
        transition_sub_resource_state(command_list, counter_buffer, 0,
                D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

        /* produce */
        ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
        ID3D12GraphicsCommandList_SetComputeRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &descriptor_heap);
        ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(command_list, 0,
                ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(descriptor_heap));
        ID3D12GraphicsCommandList_Dispatch(command_list, 16, 1, 1);

        counter = read_uav_counter(&context, counter_buffer, uav_desc.Buffer.CounterOffsetInBytes);
        ok(counter == 64, "Got unexpected value %u.\n", counter);
        transition_sub_resource_state(command_list, buffer, 0,
                D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
        get_buffer_readback_with_command_list(buffer, DXGI_FORMAT_R32_UINT, &rb, queue, command_list);
        memcpy(id, rb.rb.data, 64 * sizeof(*id));
        release_resource_readback(&rb);
        qsort(id, 64, sizeof(*id), compare_id);
        for (j = 0; j < 64; ++j)
        {
            if (id[j] != j)
                break;
        }
        ok(j == 64, "Got unexpected id %u at %u.\n", id[j], j);

        reset_command_list(command_list, context.allocator);
        transition_sub_resource_state(command_list, buffer, 0,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

        ID3D12PipelineState_Release(context.pipeline_state);
        context.pipeline_state = create_compute_pipeline_state(device, context.root_signature,
                shader_bytecode(cs_consumer_code, sizeof(cs_consumer_code)));

        /* consume */
        ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
        ID3D12GraphicsCommandList_SetComputeRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &descriptor_heap);
        ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(command_list, 0,
                ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(descriptor_heap));
        ID3D12GraphicsCommandList_Dispatch(command_list, 16, 1, 1);

        transition_sub_resource_state(command_list, out_buffer, 0,
                D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
        counter = read_uav_counter(&context, counter_buffer, uav_desc.Buffer.CounterOffsetInBytes);
        ok(!counter, "Got unexpected value %u.\n", counter);
        get_buffer_readback_with_command_list(out_buffer, DXGI_FORMAT_R32_UINT, &rb, queue, command_list);
        memcpy(id, rb.rb.data, 64 * sizeof(*id));
        release_resource_readback(&rb);
        qsort(id, 64, sizeof(*id), compare_id);
        for (j = 0; j < 64; ++j)
        {
            if (id[j] != j)
                break;
        }
        ok(j == 64, "Got unexpected id %u at %u.\n", id[j], j);

        reset_command_list(command_list, context.allocator);
        transition_sub_resource_state(command_list, counter_buffer, 0,
                D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_DEST);
        transition_sub_resource_state(command_list, buffer, 0,
                D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_DEST);
        transition_sub_resource_state(command_list, out_buffer, 0,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

        /* produce on CPU */
        counter = 8;
        for (j = 0; j < counter; ++j)
            id[j] = 0xdeadbeef;
        upload_buffer_data(buffer, 0, counter * sizeof(*id), id, queue, command_list);
        reset_command_list(command_list, context.allocator);
        upload_buffer_data(counter_buffer, counter_offsets[i], sizeof(counter), &counter, queue, command_list);
        reset_command_list(command_list, context.allocator);

        transition_sub_resource_state(command_list, counter_buffer, 0,
                D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
        transition_sub_resource_state(command_list, buffer, 0,
                D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

        /* consume */
        ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
        ID3D12GraphicsCommandList_SetComputeRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &descriptor_heap);
        ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(command_list, 0,
                ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(descriptor_heap));
        ID3D12GraphicsCommandList_Dispatch(command_list, 1, 1, 1);
        ID3D12GraphicsCommandList_Dispatch(command_list, 1, 1, 1);

        transition_sub_resource_state(command_list, out_buffer, 0,
                D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
        counter = read_uav_counter(&context, counter_buffer, uav_desc.Buffer.CounterOffsetInBytes);
        ok(!counter, "Got unexpected value %u.\n", counter);

        get_buffer_readback_with_command_list(out_buffer, DXGI_FORMAT_R32_UINT, &rb, queue, command_list);
        for (j = 0; j < 8; ++j)
        {
            data = get_readback_uint(&rb.rb, j, 0, 0);
            ok(data == 0xdeadbeef, "Got data %u at %u.\n", data, j);
        }
        release_resource_readback(&rb);

        reset_command_list(command_list, context.allocator);
        transition_sub_resource_state(command_list, out_buffer, 0,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
        transition_sub_resource_state(command_list, counter_buffer, 0,
                D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_DEST);
        ID3D12PipelineState_Release(context.pipeline_state);
        context.pipeline_state = NULL;

        vkd3d_test_pop_context();
    }

    ID3D12Resource_Release(buffer);
    ID3D12Resource_Release(out_buffer);
    ID3D12Resource_Release(counter_buffer);
    ID3D12DescriptorHeap_Release(descriptor_heap);
    destroy_test_context(&context);
}

static void test_decrement_uav_counter(void)
{
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    D3D12_DESCRIPTOR_RANGE descriptor_ranges[1];
    D3D12_UNORDERED_ACCESS_VIEW_DESC uav_desc;
    ID3D12Resource *buffer, *counter_buffer;
    ID3D12GraphicsCommandList *command_list;
    D3D12_ROOT_PARAMETER root_parameters[1];
    ID3D12DescriptorHeap *descriptor_heap;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Device *device;
    uint32_t counter;
    unsigned int i;
    HRESULT hr;

    static const DWORD cs_code[] =
    {
#if 0
        RWStructuredBuffer<uint> u;

        [numthreads(1, 1, 1)]
        void main()
        {
            InterlockedMin(u[0], u.DecrementCounter());
        }
#endif
        0x43425844, 0xceb0e9d3, 0x64ea7417, 0xbd37d26f, 0x589c63c2, 0x00000001, 0x000000c8, 0x00000003,
        0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x00000074, 0x00050050, 0x0000001d, 0x0100086a,
        0x0480009e, 0x0011e000, 0x00000000, 0x00000004, 0x02000068, 0x00000001, 0x0400009b, 0x00000001,
        0x00000001, 0x00000001, 0x050000b3, 0x00100012, 0x00000000, 0x0011e000, 0x00000000, 0x0a0000b1,
        0x0011e000, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0010000a,
        0x00000000, 0x0100003e,
    };
    static const struct
    {
        uint32_t initial_value;
        unsigned int decrement_count;
        uint32_t expected_value;
        uint32_t expected_min_value;
    }
    tests[] =
    {
        {0x00000000,  1, 0xffffffff, 0xffffffff},
        {0x00000001,  1, 0x00000000, 0x00000000},
        {0xffffffff,  1, 0xfffffffe, 0xfffffffe},
        {0x00000010, 16, 0x00000000, 0x00000000},
        {0x00000010, 17, 0xffffffff, 0x00000000},
    };

    if (!init_compute_test_context(&context))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    descriptor_ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
    descriptor_ranges[0].NumDescriptors = 1;
    descriptor_ranges[0].BaseShaderRegister = 0;
    descriptor_ranges[0].RegisterSpace = 0;
    descriptor_ranges[0].OffsetInDescriptorsFromTableStart = 0;
    root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameters[0].DescriptorTable.NumDescriptorRanges = 1;
    root_parameters[0].DescriptorTable.pDescriptorRanges = descriptor_ranges;
    root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    root_signature_desc.NumParameters = 1;
    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(device, &root_signature_desc, &context.root_signature);
    ok(SUCCEEDED(hr), "Failed to create root signature, hr %#x.\n", hr);

    context.pipeline_state = create_compute_pipeline_state(device, context.root_signature,
            shader_bytecode(cs_code, sizeof(cs_code)));

    descriptor_heap = create_gpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1);

    buffer = create_default_buffer(device, 1024,
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
    counter_buffer = create_default_buffer(device, 1024,
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

    memset(&uav_desc, 0, sizeof(uav_desc));
    uav_desc.Format = DXGI_FORMAT_UNKNOWN;
    uav_desc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
    uav_desc.Buffer.NumElements = 256;
    uav_desc.Buffer.StructureByteStride = sizeof(uint32_t);
    uav_desc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_NONE;
    ID3D12Device_CreateUnorderedAccessView(device, buffer, counter_buffer, &uav_desc,
            get_cpu_descriptor_handle(&context, descriptor_heap, 0));

    for (i = 0; i < ARRAY_SIZE(tests); ++i)
    {
        vkd3d_test_push_context("Test %u", i);

        transition_sub_resource_state(command_list, buffer, 0,
                D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_DEST);
        counter = 0xffffffff;
        upload_buffer_data(buffer, 0, sizeof(counter), &counter, queue, command_list);
        reset_command_list(command_list, context.allocator);
        transition_sub_resource_state(command_list, buffer, 0,
                D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

        transition_sub_resource_state(command_list, counter_buffer, 0,
                D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_DEST);
        counter = tests[i].initial_value;
        upload_buffer_data(counter_buffer, 0, sizeof(counter), &counter, queue, command_list);
        reset_command_list(command_list, context.allocator);
        transition_sub_resource_state(command_list, counter_buffer, 0,
                D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

        ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
        ID3D12GraphicsCommandList_SetComputeRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &descriptor_heap);
        ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(command_list, 0,
                ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(descriptor_heap));
        ID3D12GraphicsCommandList_Dispatch(command_list, tests[i].decrement_count, 1, 1);

        counter = read_uav_counter(&context, counter_buffer, 0);
        bug_if(is_macos_lt(15, 0, 0))
        ok(counter == tests[i].expected_value, "Got %u, expected %u.\n",
                counter, tests[i].expected_value);

        counter = read_uav_counter(&context, buffer, 0);
        ok(counter == tests[i].expected_min_value, "Got %u, expected %u.\n",
                counter, tests[i].expected_min_value);

        vkd3d_test_pop_context();
    }

    ID3D12Resource_Release(buffer);
    ID3D12Resource_Release(counter_buffer);
    ID3D12DescriptorHeap_Release(descriptor_heap);
    destroy_test_context(&context);
}

static void test_graphics_uav_counters(void)
{
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    D3D12_DESCRIPTOR_RANGE descriptor_ranges[1];
    D3D12_UNORDERED_ACCESS_VIEW_DESC uav_desc;
    ID3D12DescriptorHeap *cpu_heap, *gpu_heap;
    ID3D12GraphicsCommandList *command_list;
    ID3D12Resource *buffer, *counter_buffer;
    D3D12_ROOT_PARAMETER root_parameters[1];
    struct d3d12_resource_readback rb;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Device *device;
    uint32_t counter;
    D3D12_BOX box;
    HRESULT hr;

    static const uint32_t clear_value[4] = {0};
    static const DWORD ps_code[] =
    {
#if 0
        RWStructuredBuffer<uint> u;

        void main()
        {
            uint i = u.IncrementCounter();
            ++u[i];
        }
#endif
        0x43425844, 0x951850a3, 0xb2e7dcee, 0x6195cf5f, 0x3e1e6220, 0x00000001, 0x000000fc, 0x00000003,
        0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x000000a8, 0x00000050, 0x0000002a, 0x0100086a,
        0x0480009e, 0x0011e000, 0x00000000, 0x00000004, 0x02000068, 0x00000001, 0x050000b2, 0x00100012,
        0x00000000, 0x0011e000, 0x00000000, 0x8b0000a7, 0x80002302, 0x00199983, 0x00100022, 0x00000000,
        0x0010000a, 0x00000000, 0x00004001, 0x00000000, 0x0011e006, 0x00000000, 0x0700001e, 0x00100022,
        0x00000000, 0x0010001a, 0x00000000, 0x00004001, 0x00000001, 0x090000a8, 0x0011e012, 0x00000000,
        0x0010000a, 0x00000000, 0x00004001, 0x00000000, 0x0010001a, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};

    memset(&desc, 0, sizeof(desc));
    desc.rt_width = 64;
    desc.rt_height = 1;
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    descriptor_ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
    descriptor_ranges[0].NumDescriptors = 1;
    descriptor_ranges[0].BaseShaderRegister = 0;
    descriptor_ranges[0].RegisterSpace = 0;
    descriptor_ranges[0].OffsetInDescriptorsFromTableStart = 0;
    root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameters[0].DescriptorTable.NumDescriptorRanges = 1;
    root_parameters[0].DescriptorTable.pDescriptorRanges = descriptor_ranges;
    root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
    root_signature_desc.NumParameters = 1;
    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(device, &root_signature_desc, &context.root_signature);
    ok(SUCCEEDED(hr), "Failed to create root signature, hr %#x.\n", hr);

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

    buffer = create_default_buffer(device, 64 * sizeof(uint32_t),
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
    counter_buffer = create_default_buffer(device, D3D12_UAV_COUNTER_PLACEMENT_ALIGNMENT + sizeof(uint32_t),
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_DEST);

    context.pipeline_state = create_pipeline_state(context.device, context.root_signature, 0, NULL, &ps, NULL);

    memset(&uav_desc, 0, sizeof(uav_desc));
    uav_desc.Format = DXGI_FORMAT_R32_UINT;
    uav_desc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
    uav_desc.Buffer.NumElements = 64;
    uav_desc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_NONE;
    ID3D12Device_CreateUnorderedAccessView(device, buffer, NULL, &uav_desc,
            get_cpu_descriptor_handle(&context, cpu_heap, 0));
    ID3D12Device_CreateUnorderedAccessView(device, buffer, NULL, &uav_desc,
            get_cpu_descriptor_handle(&context, gpu_heap, 0));

    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &gpu_heap);

    ID3D12GraphicsCommandList_ClearUnorderedAccessViewUint(command_list,
            get_gpu_descriptor_handle(&context, gpu_heap, 0),
            get_cpu_descriptor_handle(&context, cpu_heap, 0),
            buffer, clear_value, 0, NULL);
    uav_barrier(command_list, buffer);

    uav_desc.Format = DXGI_FORMAT_UNKNOWN;
    uav_desc.Buffer.StructureByteStride = sizeof(uint32_t);
    uav_desc.Buffer.CounterOffsetInBytes = D3D12_UAV_COUNTER_PLACEMENT_ALIGNMENT;
    ID3D12Device_CreateUnorderedAccessView(device, buffer, counter_buffer, &uav_desc,
            get_cpu_descriptor_handle(&context, gpu_heap, 0));

    counter = 0;
    upload_buffer_data(counter_buffer, uav_desc.Buffer.CounterOffsetInBytes,
            sizeof(counter), &counter, queue, command_list);
    reset_command_list(command_list, context.allocator);
    transition_sub_resource_state(command_list, counter_buffer, 0,
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &gpu_heap);
    ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0,
            ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(gpu_heap));
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    counter = read_uav_counter(&context, counter_buffer, uav_desc.Buffer.CounterOffsetInBytes);
    ok(counter == 64, "Got %u, expected 64.\n", counter);

    transition_sub_resource_state(command_list, buffer, 0,
            D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_buffer_readback_with_command_list(buffer, DXGI_FORMAT_R32_UINT, &rb, queue, command_list);
    set_box(&box, 0, 0, 0, uav_desc.Buffer.NumElements, 1, 1);
    check_readback_data_uint(&rb.rb, &box, 1, 0);
    release_resource_readback(&rb);

    ID3D12Resource_Release(buffer);
    ID3D12Resource_Release(counter_buffer);
    ID3D12DescriptorHeap_Release(cpu_heap);
    ID3D12DescriptorHeap_Release(gpu_heap);
    destroy_test_context(&context);
}

static void test_atomic_instructions(void)
{
    ID3D12Resource *ps_buffer, *cs_buffer, *cs_buffer2;
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    ID3D12GraphicsCommandList *command_list;
    D3D12_ROOT_PARAMETER root_parameters[3];
    ID3D12PipelineState *pipeline_state;
    struct d3d12_resource_readback rb;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Device *device;
    unsigned int i, j;
    HRESULT hr;

    static const DWORD ps_atomics_code[] =
    {
#if 0
        RWByteAddressBuffer u;

        uint4 v;
        int4 i;

        void main()
        {
            u.InterlockedAnd(0 * 4, v.x);
            u.InterlockedCompareStore(1 * 4, v.y, v.x);
            u.InterlockedAdd(2 * 4, v.x);
            u.InterlockedOr(3 * 4, v.x);
            u.InterlockedMax(4 * 4, i.x);
            u.InterlockedMin(5 * 4, i.x);
            u.InterlockedMax(6 * 4, v.x);
            u.InterlockedMin(7 * 4, v.x);
            u.InterlockedXor(8 * 4, v.x);
        }
#endif
        0x43425844, 0x24c6a30c, 0x2ce4437d, 0xdee8a0df, 0xd18cb4bc, 0x00000001, 0x000001ac, 0x00000003,
        0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x00000158, 0x00000050, 0x00000056, 0x0100086a,
        0x04000059, 0x00208e46, 0x00000000, 0x00000002, 0x0300009d, 0x0011e000, 0x00000000, 0x080000a9,
        0x0011e000, 0x00000000, 0x00004001, 0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x0b0000ac,
        0x0011e000, 0x00000000, 0x00004001, 0x00000004, 0x0020801a, 0x00000000, 0x00000000, 0x0020800a,
        0x00000000, 0x00000000, 0x080000ad, 0x0011e000, 0x00000000, 0x00004001, 0x00000008, 0x0020800a,
        0x00000000, 0x00000000, 0x080000aa, 0x0011e000, 0x00000000, 0x00004001, 0x0000000c, 0x0020800a,
        0x00000000, 0x00000000, 0x080000ae, 0x0011e000, 0x00000000, 0x00004001, 0x00000010, 0x0020800a,
        0x00000000, 0x00000001, 0x080000af, 0x0011e000, 0x00000000, 0x00004001, 0x00000014, 0x0020800a,
        0x00000000, 0x00000001, 0x080000b0, 0x0011e000, 0x00000000, 0x00004001, 0x00000018, 0x0020800a,
        0x00000000, 0x00000000, 0x080000b1, 0x0011e000, 0x00000000, 0x00004001, 0x0000001c, 0x0020800a,
        0x00000000, 0x00000000, 0x080000ab, 0x0011e000, 0x00000000, 0x00004001, 0x00000020, 0x0020800a,
        0x00000000, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_atomics = {ps_atomics_code, sizeof(ps_atomics_code)};
    static const DWORD cs_atomics_code[] =
    {
#if 0
        RWByteAddressBuffer u;
        RWByteAddressBuffer u2;

        uint4 v;
        int4 i;

        [numthreads(1, 1, 1)]
        void main()
        {
            uint r;
            u.InterlockedAnd(0 * 4, v.x, r);
            u2.Store(0 * 4, r);
            u.InterlockedCompareExchange(1 * 4, v.y, v.x, r);
            u2.Store(1 * 4, r);
            u.InterlockedAdd(2 * 4, v.x, r);
            u2.Store(2 * 4, r);
            u.InterlockedOr(3 * 4, v.x, r);
            u2.Store(3 * 4, r);
            u.InterlockedMax(4 * 4, i.x, r);
            u2.Store(4 * 4, r);
            u.InterlockedMin(5 * 4, i.x, r);
            u2.Store(5 * 4, r);
            u.InterlockedMax(6 * 4, v.x, r);
            u2.Store(6 * 4, r);
            u.InterlockedMin(7 * 4, v.x, r);
            u2.Store(7 * 4, r);
            u.InterlockedXor(8 * 4, v.x, r);
            u2.Store(8 * 4, r);
        }
#endif
        0x43425844, 0x859a96e3, 0x1a35e463, 0x1e89ce58, 0x5cfe430a, 0x00000001, 0x0000026c, 0x00000003,
        0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x00000218, 0x00050050, 0x00000086, 0x0100086a,
        0x04000059, 0x00208e46, 0x00000000, 0x00000002, 0x0300009d, 0x0011e000, 0x00000000, 0x0300009d,
        0x0011e000, 0x00000001, 0x02000068, 0x00000001, 0x0400009b, 0x00000001, 0x00000001, 0x00000001,
        0x0a0000b5, 0x00100012, 0x00000000, 0x0011e000, 0x00000000, 0x00004001, 0x00000000, 0x0020800a,
        0x00000000, 0x00000000, 0x0d0000b9, 0x00100022, 0x00000000, 0x0011e000, 0x00000000, 0x00004001,
        0x00000004, 0x0020801a, 0x00000000, 0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x0a0000b4,
        0x00100042, 0x00000000, 0x0011e000, 0x00000000, 0x00004001, 0x00000008, 0x0020800a, 0x00000000,
        0x00000000, 0x0a0000b6, 0x00100082, 0x00000000, 0x0011e000, 0x00000000, 0x00004001, 0x0000000c,
        0x0020800a, 0x00000000, 0x00000000, 0x070000a6, 0x0011e0f2, 0x00000001, 0x00004001, 0x00000000,
        0x00100e46, 0x00000000, 0x0a0000ba, 0x00100012, 0x00000000, 0x0011e000, 0x00000000, 0x00004001,
        0x00000010, 0x0020800a, 0x00000000, 0x00000001, 0x0a0000bb, 0x00100022, 0x00000000, 0x0011e000,
        0x00000000, 0x00004001, 0x00000014, 0x0020800a, 0x00000000, 0x00000001, 0x0a0000bc, 0x00100042,
        0x00000000, 0x0011e000, 0x00000000, 0x00004001, 0x00000018, 0x0020800a, 0x00000000, 0x00000000,
        0x0a0000bd, 0x00100082, 0x00000000, 0x0011e000, 0x00000000, 0x00004001, 0x0000001c, 0x0020800a,
        0x00000000, 0x00000000, 0x070000a6, 0x0011e0f2, 0x00000001, 0x00004001, 0x00000010, 0x00100e46,
        0x00000000, 0x0a0000b7, 0x00100012, 0x00000000, 0x0011e000, 0x00000000, 0x00004001, 0x00000020,
        0x0020800a, 0x00000000, 0x00000000, 0x070000a6, 0x0011e012, 0x00000001, 0x00004001, 0x00000020,
        0x0010000a, 0x00000000, 0x0100003e,
    };
    static D3D12_SHADER_BYTECODE cs_atomics = {cs_atomics_code, sizeof(cs_atomics_code)};
    static const char * const instructions[] =
    {
        "atomic_and", "atomic_cmp_store", "atomic_iadd", "atomic_or",
        "atomic_imax", "atomic_imin", "atomic_umax", "atomic_umin", "atomic_xor",
    };
    static const char * const imm_instructions[] =
    {
        "imm_atomic_and", "imm_atomic_cmp_exch", "imm_atomic_iadd", "imm_atomic_or",
        "imm_atomic_imax", "imm_atomic_imin", "imm_atomic_umax", "imm_atomic_umin", "imm_atomic_xor",
    };
    static const struct test
    {
        struct uvec4 v;
        struct ivec4 i;
        unsigned int input[ARRAY_SIZE(instructions)];
        unsigned int expected_result[ARRAY_SIZE(instructions)];
    }
    tests[] =
    {
        {{ 1,   0 }, {-1}, {0xffff,   0, 1, 0, 0, 0, 0, 0, 0xff }, {     1,   1, 2,   1, 0, ~0u, 1,  0, 0xfe}},
        {{~0u, ~0u}, { 0}, {0xffff, 0xf, 1, 0, 0, 0, 0, 9,   ~0u}, {0xffff, 0xf, 0, ~0u, 0,  0, ~0u, 9,    0}},
    };

    memset(&desc, 0, sizeof(desc));
    desc.rt_width = 1;
    desc.rt_height = 1;
    desc.rt_format = DXGI_FORMAT_R32_FLOAT;
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV;
    root_parameters[0].Descriptor.ShaderRegister = 0;
    root_parameters[0].Descriptor.RegisterSpace = 0;
    root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    root_parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV;
    root_parameters[1].Descriptor.ShaderRegister = 1;
    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.ShaderRegister = 0;
    root_parameters[2].Constants.RegisterSpace = 0;
    root_parameters[2].Constants.Num32BitValues = 8;
    root_parameters[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    root_signature_desc.NumParameters = 3;
    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(device, &root_signature_desc, &context.root_signature);
    ok(SUCCEEDED(hr), "Failed to create root signature, hr %#x.\n", hr);

    ps_buffer = create_default_buffer(device, sizeof(tests->input),
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_DEST);
    cs_buffer = create_default_buffer(device, sizeof(tests->input),
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_DEST);
    cs_buffer2 = create_default_buffer(device, sizeof(tests->input),
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_DEST);

    context.pipeline_state = create_pipeline_state(context.device, context.root_signature, 0, NULL, &ps_atomics, NULL);

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

    for (i = 0; i < ARRAY_SIZE(tests); ++i)
    {
        const struct test *test = &tests[i];

        upload_buffer_data(ps_buffer, 0, sizeof(test->input), test->input, queue, command_list);
        reset_command_list(command_list, context.allocator);

        upload_buffer_data(cs_buffer, 0, sizeof(test->input), test->input, queue, command_list);
        reset_command_list(command_list, context.allocator);

        transition_sub_resource_state(command_list, ps_buffer, 0,
                D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
        transition_sub_resource_state(command_list, cs_buffer, 0,
                D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
        transition_sub_resource_state(command_list, cs_buffer2, 0,
                D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

        ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
        ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
        ID3D12GraphicsCommandList_SetGraphicsRootUnorderedAccessView(command_list,
                0, ID3D12Resource_GetGPUVirtualAddress(ps_buffer));
        /* Resource binding tier < 3 requires all UAVs bound even if unused. */
        ID3D12GraphicsCommandList_SetGraphicsRootUnorderedAccessView(command_list,
                1, ID3D12Resource_GetGPUVirtualAddress(cs_buffer));
        ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 2, 4, &test->v, 0);
        ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 2, 4, &test->i, 4);

        ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

        ID3D12GraphicsCommandList_SetComputeRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetComputeRootUnorderedAccessView(command_list,
                0, ID3D12Resource_GetGPUVirtualAddress(cs_buffer));
        ID3D12GraphicsCommandList_SetComputeRootUnorderedAccessView(command_list,
                1, ID3D12Resource_GetGPUVirtualAddress(cs_buffer2));
        ID3D12GraphicsCommandList_SetComputeRoot32BitConstants(command_list, 2, 4, &test->v, 0);
        ID3D12GraphicsCommandList_SetComputeRoot32BitConstants(command_list, 2, 4, &test->i, 4);

        ID3D12GraphicsCommandList_SetPipelineState(command_list, pipeline_state);
        ID3D12GraphicsCommandList_Dispatch(command_list, 1, 1, 1);

        transition_sub_resource_state(command_list, ps_buffer, 0,
                D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
        get_buffer_readback_with_command_list(ps_buffer, DXGI_FORMAT_R32_UINT, &rb, queue, command_list);
        for (j = 0; j < ARRAY_SIZE(instructions); ++j)
        {
            unsigned int value = get_readback_uint(&rb.rb, j, 0, 0);
            unsigned int expected = test->expected_result[j];
            bool is_bug;

            is_bug = is_macos_lt(15, 0, 0) || (test->i.x < 0
                    && (!strcmp(instructions[j], "atomic_imax") || !strcmp(instructions[j], "atomic_imin")));

            /* Fixed at least on radv with mesa >= 21.3.7. */
            bug_if(is_bug)
            ok(value == expected, "Test %u: Got %#x (%d), expected %#x (%d) for '%s' "
                    "with inputs (%u, %u), (%d), %#x (%d).\n",
                    i, value, value, expected, expected, instructions[j],
                    test->v.x, test->v.y, test->i.x, test->input[j], test->input[j]);
        }
        release_resource_readback(&rb);
        reset_command_list(command_list, context.allocator);

        transition_sub_resource_state(command_list, cs_buffer, 0,
                D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
        get_buffer_readback_with_command_list(cs_buffer, DXGI_FORMAT_R32_UINT, &rb, queue, command_list);
        for (j = 0; j < ARRAY_SIZE(instructions); ++j)
        {
            bool bug_instruction = !strcmp(imm_instructions[j], "imm_atomic_imax")
                    || !strcmp(imm_instructions[j], "imm_atomic_imin");
            unsigned int value = get_readback_uint(&rb.rb, j, 0, 0);
            unsigned int expected = test->expected_result[j];

            /* Fixed at least on radv with mesa >= 21.3.7. */
            bug_if(test->i.x < 0 && bug_instruction)
            ok(value == expected, "Test %u: Got %#x (%d), expected %#x (%d) for '%s' "
                    "with inputs (%u, %u), (%d), %#x (%d).\n",
                    i, value, value, expected, expected, imm_instructions[j],
                    test->v.x, test->v.y, test->i.x, test->input[j], test->input[j]);
        }
        release_resource_readback(&rb);
        reset_command_list(command_list, context.allocator);

        transition_sub_resource_state(command_list, cs_buffer2, 0,
                D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
        get_buffer_readback_with_command_list(cs_buffer2, DXGI_FORMAT_R32_UINT, &rb, queue, command_list);
        for (j = 0; j < ARRAY_SIZE(instructions); ++j)
        {
            unsigned int out_value = get_readback_uint(&rb.rb, j, 0, 0);
            ok(out_value == test->input[j], "Got original value %u, expected %u for '%s'.\n",
                    out_value, test->input[j], imm_instructions[j]);
        }
        release_resource_readback(&rb);
        reset_command_list(command_list, context.allocator);

        transition_sub_resource_state(command_list, ps_buffer, 0,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_COPY_DEST);
        transition_sub_resource_state(command_list, cs_buffer, 0,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_COPY_DEST);
        transition_sub_resource_state(command_list, cs_buffer2, 0,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_COPY_DEST);
    }

    ID3D12Resource_Release(ps_buffer);
    ID3D12Resource_Release(cs_buffer);
    ID3D12Resource_Release(cs_buffer2);
    ID3D12PipelineState_Release(pipeline_state);
    destroy_test_context(&context);
}

static void test_buffer_srv(void)
{
    struct buffer
    {
        unsigned int byte_count;
        unsigned int data_offset;
        const void *data;
        unsigned int structure_byte_stride;
    };

    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    const D3D12_SHADER_BYTECODE *current_shader;
    D3D12_DESCRIPTOR_RANGE descriptor_ranges[1];
    D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc;
    ID3D12GraphicsCommandList *command_list;
    D3D12_ROOT_PARAMETER root_parameters[2];
    D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle;
    D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle;
    ID3D12DescriptorHeap *descriptor_heap;
    const struct buffer *current_buffer;
    unsigned int color, expected_color;
    struct d3d12_resource_readback rb;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Resource *buffer;
    ID3D12Device *device;
    unsigned int i, x, y;
    HRESULT hr;

    static const DWORD ps_float4_code[] =
    {
#if 0
        Buffer<float4> b;

        float2 size;

        float4 main(float4 position : SV_POSITION) : SV_Target
        {
            float2 p;
            int2 coords;
            p.x = position.x / 640.0f;
            p.y = position.y / 480.0f;
            coords = int2(p.x * size.x, p.y * size.y);
            return b.Load(coords.y * size.x + coords.x);
        }
#endif
        0x43425844, 0xf10ea650, 0x311f5c38, 0x3a888b7f, 0x58230334, 0x00000001, 0x000001a0, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000030f, 0x505f5653, 0x5449534f, 0x004e4f49,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x52444853, 0x00000104, 0x00000040,
        0x00000041, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x04000858, 0x00107000, 0x00000000,
        0x00005555, 0x04002064, 0x00101032, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x02000068, 0x00000001, 0x08000038, 0x00100032, 0x00000000, 0x00101516, 0x00000000, 0x00208516,
        0x00000000, 0x00000000, 0x0a000038, 0x00100032, 0x00000000, 0x00100046, 0x00000000, 0x00004002,
        0x3b088889, 0x3acccccd, 0x00000000, 0x00000000, 0x05000043, 0x00100032, 0x00000000, 0x00100046,
        0x00000000, 0x0a000032, 0x00100012, 0x00000000, 0x0010000a, 0x00000000, 0x0020800a, 0x00000000,
        0x00000000, 0x0010001a, 0x00000000, 0x0500001b, 0x00100012, 0x00000000, 0x0010000a, 0x00000000,
        0x0700002d, 0x001020f2, 0x00000000, 0x00100006, 0x00000000, 0x00107e46, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_float4 = {ps_float4_code, sizeof(ps_float4_code)};
    static const DWORD ps_structured_code[] =
    {
#if 0
        StructuredBuffer<float4> b;

        float2 size;

        float4 main(float4 position : SV_POSITION) : SV_Target
        {
            float2 p;
            int2 coords;
            p.x = position.x / 640.0f;
            p.y = position.y / 480.0f;
            coords = int2(p.x * size.x, p.y * size.y);
            return b[coords.y * size.x + coords.x];
        }
#endif
        0x43425844, 0x246caabb, 0xf1e7d6b9, 0xcbe720dc, 0xcdc23036, 0x00000001, 0x000001c0, 0x00000004,
        0x00000030, 0x00000064, 0x00000098, 0x000001b0, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008,
        0x00000020, 0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000030f, 0x505f5653, 0x5449534f,
        0x004e4f49, 0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000,
        0x00000003, 0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000110,
        0x00000040, 0x00000044, 0x0100486a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x040000a2,
        0x00107000, 0x00000000, 0x00000010, 0x04002064, 0x00101032, 0x00000000, 0x00000001, 0x03000065,
        0x001020f2, 0x00000000, 0x02000068, 0x00000001, 0x08000038, 0x00100032, 0x00000000, 0x00101516,
        0x00000000, 0x00208516, 0x00000000, 0x00000000, 0x0a000038, 0x00100032, 0x00000000, 0x00100046,
        0x00000000, 0x00004002, 0x3b088889, 0x3acccccd, 0x00000000, 0x00000000, 0x05000043, 0x00100032,
        0x00000000, 0x00100046, 0x00000000, 0x0a000032, 0x00100012, 0x00000000, 0x0010000a, 0x00000000,
        0x0020800a, 0x00000000, 0x00000000, 0x0010001a, 0x00000000, 0x0500001c, 0x00100012, 0x00000000,
        0x0010000a, 0x00000000, 0x090000a7, 0x001020f2, 0x00000000, 0x0010000a, 0x00000000, 0x00004001,
        0x00000000, 0x00107e46, 0x00000000, 0x0100003e, 0x30494653, 0x00000008, 0x00000002, 0x00000000,
    };
    static const D3D12_SHADER_BYTECODE ps_structured = {ps_structured_code, sizeof(ps_structured_code)};
    static const unsigned int rgba16[] =
    {
        0xff0000ff, 0xff00ffff, 0xff00ff00, 0xffffff00,
        0xffff0000, 0xffff00ff, 0xff000000, 0xff7f7f7f,
        0xffffffff, 0xffffffff, 0xffffffff, 0xff000000,
        0xffffffff, 0xff000000, 0xff000000, 0xff000000,
    };
    static const unsigned int rgba4[] =
    {
        0xffffffff, 0xff0000ff,
        0xff000000, 0xff00ff00,
    };
    static const BYTE r4[] =
    {
        0xde, 0xad,
        0xba, 0xbe,
    };
    static const struct vec4 rgba_float[] =
    {
        {1.0f, 1.0f, 1.0f, 1.0f}, {1.0f, 0.0f, 0.0f, 1.0f},
        {0.0f, 0.0f, 0.0f, 1.0f}, {0.0f, 1.0f, 0.0f, 1.0f},
    };
    static const struct buffer rgba16_buffer = {sizeof(rgba16), 0, &rgba16};
    static const struct buffer rgba16_offset_buffer = {256 + sizeof(rgba16), 256, &rgba16};
    static const struct buffer rgba4_buffer  = {sizeof(rgba4), 0, &rgba4};
    static const struct buffer r4_buffer = {sizeof(r4), 0, &r4};
    static const struct buffer r4_offset_buffer = {256 + sizeof(r4), 256, &r4};
    static const struct buffer float_buffer = {sizeof(rgba_float), 0, &rgba_float, sizeof(*rgba_float)};
    static const struct buffer float_offset_buffer = {256 + sizeof(rgba_float), 256,
            &rgba_float, sizeof(*rgba_float)};
    static const unsigned int rgba16_colors2x2[] =
    {
        0xff0000ff, 0xff0000ff, 0xff00ffff, 0xff00ffff,
        0xff0000ff, 0xff0000ff, 0xff00ffff, 0xff00ffff,
        0xff00ff00, 0xff00ff00, 0xffffff00, 0xffffff00,
        0xff00ff00, 0xff00ff00, 0xffffff00, 0xffffff00,
    };
    static const unsigned int rgba16_colors1x1[] =
    {
        0xff0000ff, 0xff0000ff, 0xff0000ff, 0xff0000ff,
        0xff0000ff, 0xff0000ff, 0xff0000ff, 0xff0000ff,
        0xff0000ff, 0xff0000ff, 0xff0000ff, 0xff0000ff,
        0xff0000ff, 0xff0000ff, 0xff0000ff, 0xff0000ff,
    };
    static const unsigned int rgba4_colors[] =
    {
        0xffffffff, 0xffffffff, 0xff0000ff, 0xff0000ff,
        0xffffffff, 0xffffffff, 0xff0000ff, 0xff0000ff,
        0xff000000, 0xff000000, 0xff00ff00, 0xff00ff00,
        0xff000000, 0xff000000, 0xff00ff00, 0xff00ff00,
    };
    static const unsigned int r4_colors[] =
    {
        0xff0000de, 0xff0000de, 0xff0000ad, 0xff0000ad,
        0xff0000de, 0xff0000de, 0xff0000ad, 0xff0000ad,
        0xff0000ba, 0xff0000ba, 0xff0000be, 0xff0000be,
        0xff0000ba, 0xff0000ba, 0xff0000be, 0xff0000be,
    };
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};

    static const struct test
    {
        const D3D12_SHADER_BYTECODE *shader;
        const struct buffer *buffer;
        DXGI_FORMAT srv_format;
        unsigned int srv_first_element;
        unsigned int srv_element_count;
        struct vec2 size;
        const unsigned int *expected_colors;
    }
    tests[] =
    {
        {&ps_float4,     &rgba16_buffer,        DXGI_FORMAT_R8G8B8A8_UNORM,   0, 16, {4.0f, 4.0f}, rgba16},
        {&ps_float4,     &rgba16_offset_buffer, DXGI_FORMAT_R8G8B8A8_UNORM,  64, 16, {4.0f, 4.0f}, rgba16},
        {&ps_float4,     &rgba16_buffer,        DXGI_FORMAT_R8G8B8A8_UNORM,   0,  4, {2.0f, 2.0f}, rgba16_colors2x2},
        {&ps_float4,     &rgba16_buffer,        DXGI_FORMAT_R8G8B8A8_UNORM,   0,  1, {1.0f, 1.0f}, rgba16_colors1x1},
        {&ps_float4,     &rgba4_buffer,         DXGI_FORMAT_R8G8B8A8_UNORM,   0,  4, {2.0f, 2.0f}, rgba4_colors},
        {&ps_float4,     &r4_buffer,            DXGI_FORMAT_R8_UNORM,         0,  4, {2.0f, 2.0f}, r4_colors},
        {&ps_float4,     &r4_offset_buffer,     DXGI_FORMAT_R8_UNORM,       256,  4, {2.0f, 2.0f}, r4_colors},
        {&ps_structured, &float_buffer,         DXGI_FORMAT_UNKNOWN,          0,  4, {2.0f, 2.0f}, rgba4_colors},
        {&ps_structured, &float_offset_buffer,  DXGI_FORMAT_UNKNOWN,         16,  4, {2.0f, 2.0f}, rgba4_colors},
    };

    memset(&desc, 0, sizeof(desc));
    desc.rt_width = 640;
    desc.rt_height = 480;
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    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;
    root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameters[0].DescriptorTable.NumDescriptorRanges = 1;
    root_parameters[0].DescriptorTable.pDescriptorRanges = descriptor_ranges;
    root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
    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 = 2;
    root_parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
    root_signature_desc.NumParameters = 2;
    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(device, &root_signature_desc, &context.root_signature);
    ok(SUCCEEDED(hr), "Failed to create root signature, hr %#x.\n", hr);

    descriptor_heap = create_gpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1);
    cpu_handle = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(descriptor_heap);
    gpu_handle = ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(descriptor_heap);

    buffer = NULL;
    current_shader = NULL;
    current_buffer = NULL;
    for (i = 0; i < ARRAY_SIZE(tests); ++i)
    {
        const struct test *test = &tests[i];

        vkd3d_test_push_context("Test %u", i);

        if (current_shader != test->shader)
        {
            if (context.pipeline_state)
                ID3D12PipelineState_Release(context.pipeline_state);
            current_shader = tests[i].shader;
            context.pipeline_state = create_pipeline_state(context.device,
                    context.root_signature, context.render_target_desc.Format,
                    NULL, current_shader, NULL);
        }

        if (current_buffer != test->buffer)
        {
            if (buffer)
                ID3D12Resource_Release(buffer);

            current_buffer = test->buffer;

            buffer = create_default_buffer(device, current_buffer->byte_count,
                    D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST);

            upload_buffer_data(buffer, current_buffer->data_offset,
                    current_buffer->byte_count - current_buffer->data_offset,
                    current_buffer->data, queue, command_list);
            reset_command_list(command_list, context.allocator);

            transition_sub_resource_state(command_list, buffer, 0,
                    D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
        }

        memset(&srv_desc, 0, sizeof(srv_desc));
        srv_desc.Format = test->srv_format;
        srv_desc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER;
        srv_desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
        srv_desc.Buffer.FirstElement = test->srv_first_element;
        srv_desc.Buffer.NumElements = test->srv_element_count;
        srv_desc.Buffer.StructureByteStride = current_buffer->structure_byte_stride;
        ID3D12Device_CreateShaderResourceView(device, buffer, &srv_desc, cpu_handle);

        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

        ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
        ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
        ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
        ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
        ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &descriptor_heap);
        ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0, gpu_handle);
        ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 1, 2, &test->size.x, 0);
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

        transition_sub_resource_state(command_list, context.render_target, 0,
                D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);

        get_resource_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
        for (y = 0; y < 4; ++y)
        {
            for (x = 0; x < 4; ++x)
            {
                color = get_readback_uint(&rb.rb, 80 + x * 160, 60 + y * 120, 0);
                expected_color = test->expected_colors[y * 4 + x];
                ok(compare_color(color, expected_color, 1),
                        "Got 0x%08x, expected 0x%08x at (%u, %u).\n", color, expected_color, x, y);
            }
        }
        release_resource_readback(&rb);

        reset_command_list(command_list, context.allocator);
        transition_sub_resource_state(command_list, context.render_target, 0,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

        vkd3d_test_pop_context();
    }

    ID3D12DescriptorHeap_Release(descriptor_heap);
    ID3D12Resource_Release(buffer);
    destroy_test_context(&context);
}

static void test_create_query_heap(void)
{
    ID3D12Device *device;
    D3D12_QUERY_HEAP_DESC heap_desc;
    ID3D12QueryHeap *query_heap;
    ULONG refcount;
    HRESULT hr;
    int i;

    static const D3D12_QUERY_HEAP_TYPE types[] =
    {
        D3D12_QUERY_HEAP_TYPE_OCCLUSION,
        D3D12_QUERY_HEAP_TYPE_TIMESTAMP,
        D3D12_QUERY_HEAP_TYPE_PIPELINE_STATISTICS,
    };

    if (!(device = create_device()))
    {
        skip("Failed to create device.\n");
        return;
    }

    for (i = 0; i < ARRAY_SIZE(types); ++i)
    {
        heap_desc.Type = types[i];
        heap_desc.Count = 1;
        heap_desc.NodeMask = 0;

        hr = ID3D12Device_CreateQueryHeap(device, &heap_desc, &IID_ID3D12QueryHeap, (void **)&query_heap);
        bug_if(types[i] == D3D12_QUERY_HEAP_TYPE_PIPELINE_STATISTICS && is_mvk_device(device))
        ok(hr == S_OK, "Failed to create query heap, type %u, hr %#x.\n", types[i], hr);

        if (hr == S_OK)
            ID3D12QueryHeap_Release(query_heap);
    }

    heap_desc.Type = D3D12_QUERY_HEAP_TYPE_SO_STATISTICS;
    heap_desc.Count = 1;
    heap_desc.NodeMask = 0;

    hr = ID3D12Device_CreateQueryHeap(device, &heap_desc, &IID_ID3D12QueryHeap, (void **)&query_heap);
    if (hr != E_NOTIMPL)
    {
        ok(hr == S_OK, "Failed to create query heap, type %u, hr %#x.\n", heap_desc.Type, hr);
        ID3D12QueryHeap_Release(query_heap);
    }
    else
    {
        skip("Stream output is not supported.\n");
    }

    refcount = ID3D12Device_Release(device);
    ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount);
}

static void test_query_timestamp(void)
{
    uint64_t timestamps[4], timestamp_frequency, timestamp_diff, time_diff;
    ID3D12GraphicsCommandList *command_list;
    struct d3d12_resource_readback rb;
    D3D12_QUERY_HEAP_DESC heap_desc;
    struct test_context_desc desc;
    ID3D12QueryHeap *query_heap;
    struct test_context context;
    time_t time_start, time_end;
    ID3D12CommandQueue *queue;
    ID3D12Resource *resource;
    ID3D12Device *device;
    unsigned int i;
    HRESULT hr;

    time_start = time(NULL);

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

    hr = ID3D12CommandQueue_GetTimestampFrequency(queue, &timestamp_frequency);
    ok(SUCCEEDED(hr), "Failed to get timestamp frequency, hr %#x.\n", hr);

    heap_desc.Type = D3D12_QUERY_HEAP_TYPE_TIMESTAMP;
    heap_desc.Count = ARRAY_SIZE(timestamps);
    heap_desc.NodeMask = 0;
    hr = ID3D12Device_CreateQueryHeap(device, &heap_desc, &IID_ID3D12QueryHeap, (void **)&query_heap);
    ok(SUCCEEDED(hr), "Failed to create query heap, type %u, hr %#x.\n", heap_desc.Type, hr);

    resource = create_readback_buffer(device, sizeof(timestamps));

    for (i = 0; i < ARRAY_SIZE(timestamps); ++i)
        ID3D12GraphicsCommandList_EndQuery(command_list, query_heap, D3D12_QUERY_TYPE_TIMESTAMP, i);

    ID3D12GraphicsCommandList_ResolveQueryData(command_list, query_heap,
            D3D12_QUERY_TYPE_TIMESTAMP, 0, 1, resource, 0);
    ID3D12GraphicsCommandList_ResolveQueryData(command_list, query_heap,
            D3D12_QUERY_TYPE_TIMESTAMP, 1, 3, resource, sizeof(uint64_t));

    get_buffer_readback_with_command_list(resource, DXGI_FORMAT_UNKNOWN, &rb, queue, command_list);

    time_end = time(NULL) + 1;

    for (i = 0; i < ARRAY_SIZE(timestamps); ++i)
        timestamps[i] = get_readback_uint64(&rb.rb, i, 0);

    for (i = 0; i < ARRAY_SIZE(timestamps) - 1; ++i)
    {
        bug_if(is_mvk_device(device))
        ok(timestamps[i] <= timestamps[i + 1], "Expected timestamps to monotonically increase, "
                "but got %"PRIu64" > %"PRIu64".\n", timestamps[i], timestamps[i + 1]);
    }

    time_diff = (uint64_t)difftime(time_end, time_start) * timestamp_frequency;
    timestamp_diff = timestamps[ARRAY_SIZE(timestamps) - 1] - timestamps[0];

    bug_if(is_mvk_device(device))
    ok(timestamp_diff <= time_diff, "Expected timestamp difference to be bounded by CPU time difference, "
            "but got %"PRIu64" > %"PRIu64".\n", timestamp_diff, time_diff);

    release_resource_readback(&rb);
    ID3D12QueryHeap_Release(query_heap);
    ID3D12Resource_Release(resource);
    destroy_test_context(&context);
}

static void test_query_pipeline_statistics(void)
{
    D3D12_QUERY_DATA_PIPELINE_STATISTICS *pipeline_statistics;
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    ID3D12GraphicsCommandList *command_list;
    struct d3d12_resource_readback rb;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Device *device;
    D3D12_QUERY_HEAP_DESC heap_desc;
    ID3D12QueryHeap *query_heap;
    ID3D12PipelineState *pso;
    ID3D12Resource *resource;
    unsigned int pixel_count, i;
    HRESULT hr;

    static const uint32_t ps_code[] =
    {
#if 0
        float4 main(float4 pos : sv_position) : sv_target
        {
            return pos;
        }
#endif
        0x43425844, 0xac408178, 0x2ca4213f, 0x4f2551e1, 0x1626b422, 0x00000001, 0x000000d8, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x00000f0f, 0x705f7673, 0x7469736f, 0x006e6f69,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x745f7673, 0x65677261, 0xabab0074, 0x52444853, 0x0000003c, 0x00000040,
        0x0000000f, 0x04002064, 0x001010f2, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x05000036, 0x001020f2, 0x00000000, 0x00101e46, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};

    if (!init_test_context(&context, NULL))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    pso = create_pipeline_state(context.device,
            context.root_signature, context.render_target_desc.Format, NULL, &ps, NULL);

    heap_desc.Type = D3D12_QUERY_HEAP_TYPE_PIPELINE_STATISTICS;
    heap_desc.Count = 2;
    heap_desc.NodeMask = 0;
    hr = ID3D12Device_CreateQueryHeap(device, &heap_desc, &IID_ID3D12QueryHeap, (void **)&query_heap);
    bug_if(is_mvk_device(device))
    ok(SUCCEEDED(hr), "Failed to create query heap, type %u, hr %#x.\n", heap_desc.Type, hr);
    if (FAILED(hr))
    {
        ID3D12PipelineState_Release(pso);
        destroy_test_context(&context);
        return;
    }

    resource = create_readback_buffer(device, 2 * sizeof(struct D3D12_QUERY_DATA_PIPELINE_STATISTICS));

    /* First query: do nothing. */
    ID3D12GraphicsCommandList_BeginQuery(command_list, query_heap, D3D12_QUERY_TYPE_PIPELINE_STATISTICS, 0);
    ID3D12GraphicsCommandList_EndQuery(command_list, query_heap, D3D12_QUERY_TYPE_PIPELINE_STATISTICS, 0);
    ID3D12GraphicsCommandList_ResolveQueryData(command_list, query_heap, D3D12_QUERY_TYPE_PIPELINE_STATISTICS, 0, 1,
            resource, 0);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

    /* Second query: draw something simple. */
    ID3D12GraphicsCommandList_BeginQuery(command_list, query_heap, D3D12_QUERY_TYPE_PIPELINE_STATISTICS, 1);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, pso);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    ID3D12GraphicsCommandList_EndQuery(command_list, query_heap, D3D12_QUERY_TYPE_PIPELINE_STATISTICS, 1);
    ID3D12GraphicsCommandList_ResolveQueryData(command_list, query_heap, D3D12_QUERY_TYPE_PIPELINE_STATISTICS, 1, 1,
            resource, sizeof(struct D3D12_QUERY_DATA_PIPELINE_STATISTICS));

    get_buffer_readback_with_command_list(resource, DXGI_FORMAT_UNKNOWN, &rb, queue, command_list);

    for (i = 0; i < sizeof(struct D3D12_QUERY_DATA_PIPELINE_STATISTICS) / sizeof(uint64_t); ++i)
    {
        uint64_t value = get_readback_uint64(&rb.rb, i, 0);
        ok(!value, "Element %d: Got %"PRIu64", expected 0.\n", i, value);
    }

    pipeline_statistics = get_readback_data(&rb.rb, 1, 0, 0, sizeof(*pipeline_statistics));

    /* We read 3 vertices that formed one primitive. */
    ok(pipeline_statistics->IAVertices == 3, "IAVertices: Got %"PRIu64", expected 3.\n",
            pipeline_statistics->IAVertices);
    ok(pipeline_statistics->IAPrimitives == 1, "IAPrimitives: Got %"PRIu64", expected 1.\n",
            pipeline_statistics->IAPrimitives);
    ok(pipeline_statistics->VSInvocations == 3, "VSInvocations: Got %"PRIu64", expected 3.\n",
            pipeline_statistics->VSInvocations);

    /* No geometry shader output primitives.
     * Depending on the graphics card, the geometry shader might still have been invoked, so
     * GSInvocations might be whatever. */
    ok(pipeline_statistics->GSPrimitives == 0, "GSPrimitives: Got %"PRIu64", expected 0.\n",
            pipeline_statistics->GSPrimitives);

    /* One primitive sent to the rasterizer, but it might have been broken up into smaller pieces then. */
    ok(pipeline_statistics->CInvocations == 1, "CInvocations: Got %"PRIu64", expected 1.\n",
            pipeline_statistics->CInvocations);
    ok(pipeline_statistics->CPrimitives > 0, "CPrimitives: Got %"PRIu64", expected > 0.\n",
            pipeline_statistics->CPrimitives);

    /* Exact number of pixel shader invocations depends on the graphics card. */
    pixel_count = context.render_target_desc.Width * context.render_target_desc.Height;
    ok(pipeline_statistics->PSInvocations >= pixel_count, "PSInvocations: Got %"PRIu64", expected >= %u.\n",
            pipeline_statistics->PSInvocations, pixel_count);

    /* We used no tessellation or compute shaders at all. */
    ok(pipeline_statistics->HSInvocations == 0, "HSInvocations: Got %"PRIu64", expected 0.\n",
            pipeline_statistics->HSInvocations);
    ok(pipeline_statistics->DSInvocations == 0, "DSInvocations: Got %"PRIu64", expected 0.\n",
            pipeline_statistics->DSInvocations);
    ok(pipeline_statistics->CSInvocations == 0, "CSInvocations: Got %"PRIu64", expected 0.\n",
            pipeline_statistics->CSInvocations);

    release_resource_readback(&rb);
    ID3D12PipelineState_Release(pso);
    ID3D12QueryHeap_Release(query_heap);
    ID3D12Resource_Release(resource);
    destroy_test_context(&context);
}

static void test_query_occlusion(void)
{
    struct test_context_desc desc;
    ID3D12GraphicsCommandList *command_list;
    struct d3d12_resource_readback rb;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Device *device;
    struct depth_stencil_resource ds;
    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
    D3D12_QUERY_HEAP_DESC heap_desc;
    ID3D12QueryHeap *query_heap;
    ID3D12Resource *resource;
    unsigned int i;
    HRESULT hr;

    static const DWORD ps_code[] =
    {
#if 0
        float depth;

        float main() : SV_Depth
        {
            return depth;
        }
#endif
        0x43425844, 0x91af6cd0, 0x7e884502, 0xcede4f54, 0x6f2c9326, 0x00000001, 0x000000b0, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0xffffffff,
        0x00000e01, 0x445f5653, 0x68747065, 0xababab00, 0x52444853, 0x00000038, 0x00000040, 0x0000000e,
        0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x02000065, 0x0000c001, 0x05000036, 0x0000c001,
        0x0020800a, 0x00000000, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};
    static const struct
    {
        D3D12_QUERY_TYPE type;
        bool draw;
        float clear_depth;
        float depth;
    }
    tests[] =
    {
        {D3D12_QUERY_TYPE_OCCLUSION,        false, 1.0f, 0.5f},
        {D3D12_QUERY_TYPE_OCCLUSION,        true,  1.0f, 0.5f},
        {D3D12_QUERY_TYPE_BINARY_OCCLUSION, false, 1.0f, 0.5f},
        {D3D12_QUERY_TYPE_BINARY_OCCLUSION, true,  1.0f, 0.5f},
        {D3D12_QUERY_TYPE_OCCLUSION,        false, 0.0f, 0.5f},
        {D3D12_QUERY_TYPE_OCCLUSION,        true,  0.0f, 0.5f},
        {D3D12_QUERY_TYPE_BINARY_OCCLUSION, false, 0.0f, 0.5f},
        {D3D12_QUERY_TYPE_BINARY_OCCLUSION, true,  0.0f, 0.5f},
    };

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

    init_depth_stencil(&ds, context.device, 640, 480, 1, 1, DXGI_FORMAT_D32_FLOAT, 0, NULL);
    set_viewport(&context.viewport, 0.0f, 0.0f, 640.0f, 480.0f, 0.0f, 1.0f);
    set_rect(&context.scissor_rect, 0, 0, 640, 480);

    context.root_signature = create_32bit_constants_root_signature(context.device,
            0, 1, D3D12_SHADER_VISIBILITY_PIXEL);
    init_pipeline_state_desc(&pso_desc, context.root_signature, 0, NULL, &ps, NULL);
    pso_desc.NumRenderTargets = 0;
    pso_desc.DSVFormat = DXGI_FORMAT_D32_FLOAT;
    pso_desc.DepthStencilState.DepthEnable = true;
    pso_desc.DepthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
    pso_desc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_LESS;
    hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
    ok(SUCCEEDED(hr), "Failed to create graphics pipeline state, hr %#x.\n", hr);

    heap_desc.Type = D3D12_QUERY_HEAP_TYPE_OCCLUSION;
    heap_desc.Count = ARRAY_SIZE(tests);
    heap_desc.NodeMask = 0;
    hr = ID3D12Device_CreateQueryHeap(device, &heap_desc, &IID_ID3D12QueryHeap, (void **)&query_heap);
    ok(SUCCEEDED(hr), "Failed to create query heap, type %u, hr %#x.\n", heap_desc.Type, hr);

    resource = create_readback_buffer(device, ARRAY_SIZE(tests) * sizeof(uint64_t));

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 0, NULL, false, &ds.dsv_handle);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);

    for (i = 0; i < ARRAY_SIZE(tests); ++i)
    {
        vkd3d_test_push_context("Test %u", i);

        ID3D12GraphicsCommandList_ClearDepthStencilView(command_list, ds.dsv_handle,
                D3D12_CLEAR_FLAG_DEPTH, tests[i].clear_depth, 0, 0, NULL);

        ID3D12GraphicsCommandList_BeginQuery(command_list, query_heap, tests[i].type, i);

        if (tests[i].draw)
        {
            ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 0, 1, &tests[i].depth, 0);
            ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);
        }

        ID3D12GraphicsCommandList_EndQuery(command_list, query_heap, tests[i].type, i);
        ID3D12GraphicsCommandList_ResolveQueryData(command_list, query_heap, tests[i].type, i, 1,
                resource, i * sizeof(uint64_t));

        vkd3d_test_pop_context();
    }

    get_buffer_readback_with_command_list(resource, DXGI_FORMAT_UNKNOWN, &rb, queue, command_list);
    for (i = 0; i < ARRAY_SIZE(tests); ++i)
    {
        const bool samples_passed = tests[i].draw && tests[i].clear_depth > tests[i].depth;
        const uint64_t result = get_readback_uint64(&rb.rb, i, 0);
        uint64_t expected_result;

        if (tests[i].type == D3D12_QUERY_TYPE_BINARY_OCCLUSION)
            expected_result = samples_passed ? 1 : 0;
        else
            expected_result = samples_passed ? 640 * 480 : 0;

        ok(result == expected_result || (expected_result && result >= expected_result),
                "Test %u: Got unexpected result %"PRIu64".\n", i, result);
    }
    release_resource_readback(&rb);

    ID3D12QueryHeap_Release(query_heap);
    ID3D12Resource_Release(resource);
    destroy_depth_stencil(&ds);
    destroy_test_context(&context);
}

static void test_resolve_non_issued_query_data(void)
{
    static const uint64_t initial_data[] = {0xdeadbeef, 0xdeadbeef, 0xdeadbabe, 0xdeadbeef};
    ID3D12Resource *readback_buffer, *upload_buffer;
    ID3D12GraphicsCommandList *command_list;
    struct d3d12_resource_readback rb;
    D3D12_QUERY_HEAP_DESC heap_desc;
    struct test_context_desc desc;
    ID3D12QueryHeap *query_heap;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Device *device;
    uint64_t *timestamps;
    HRESULT hr;

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

    heap_desc.Type = D3D12_QUERY_HEAP_TYPE_TIMESTAMP;
    heap_desc.Count = ARRAY_SIZE(initial_data);
    heap_desc.NodeMask = 0;
    hr = ID3D12Device_CreateQueryHeap(device, &heap_desc, &IID_ID3D12QueryHeap, (void **)&query_heap);
    ok(SUCCEEDED(hr), "Failed to create query heap, hr %#x.\n", hr);

    readback_buffer = create_readback_buffer(device, sizeof(initial_data));
    upload_buffer = create_upload_buffer(context.device, sizeof(initial_data), initial_data);

    ID3D12GraphicsCommandList_EndQuery(command_list, query_heap, D3D12_QUERY_TYPE_TIMESTAMP, 0);
    ID3D12GraphicsCommandList_CopyResource(command_list, readback_buffer, upload_buffer);
    ID3D12GraphicsCommandList_EndQuery(command_list, query_heap, D3D12_QUERY_TYPE_TIMESTAMP, 3);
    ID3D12GraphicsCommandList_ResolveQueryData(command_list, query_heap,
            D3D12_QUERY_TYPE_TIMESTAMP, 0, 4, readback_buffer, 0);

    get_buffer_readback_with_command_list(readback_buffer, DXGI_FORMAT_UNKNOWN, &rb, queue, command_list);
    timestamps = get_readback_data(&rb.rb, 0, 0, 0, sizeof(*timestamps));
    bug_if(is_mvk_device(device))
    ok(timestamps[0] != initial_data[0] && timestamps[0] > 0,
            "Got unexpected timestamp %#"PRIx64".\n", timestamps[0]);
    ok(!timestamps[1], "Got unexpected timestamp %#"PRIx64".\n", timestamps[1]);
    ok(!timestamps[2], "Got unexpected timestamp %#"PRIx64".\n", timestamps[2]);
    bug_if(is_mvk_device(device))
    ok(timestamps[3] != initial_data[3] && timestamps[3] > 0,
            "Got unexpected timestamp %#"PRIx64".\n", timestamps[3]);
    release_resource_readback(&rb);

    ID3D12QueryHeap_Release(query_heap);
    ID3D12Resource_Release(readback_buffer);
    ID3D12Resource_Release(upload_buffer);
    destroy_test_context(&context);
}

static void test_resolve_query_data_in_different_command_list(void)
{
    ID3D12GraphicsCommandList *command_list;
    struct d3d12_resource_readback rb;
    D3D12_QUERY_HEAP_DESC heap_desc;
    ID3D12Resource *readback_buffer;
    ID3D12QueryHeap *query_heap;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Device *device;
    unsigned int i;
    HRESULT hr;

    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    const unsigned int readback_buffer_capacity = 4;

    if (!init_test_context(&context, NULL))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    heap_desc.Type = D3D12_QUERY_HEAP_TYPE_OCCLUSION;
    heap_desc.Count = 1;
    heap_desc.NodeMask = 0;
    hr = ID3D12Device_CreateQueryHeap(device, &heap_desc, &IID_ID3D12QueryHeap, (void **)&query_heap);
    ok(SUCCEEDED(hr), "Failed to create query heap, hr %#x.\n", hr);

    readback_buffer = create_readback_buffer(device, readback_buffer_capacity * sizeof(uint64_t));

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);

    ID3D12GraphicsCommandList_BeginQuery(command_list, query_heap, D3D12_QUERY_TYPE_OCCLUSION, 0);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);
    ID3D12GraphicsCommandList_EndQuery(command_list, query_heap, D3D12_QUERY_TYPE_OCCLUSION, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);

    reset_command_list(command_list, context.allocator);
    for (i = 0; i < readback_buffer_capacity / 2; ++i)
    {
        ID3D12GraphicsCommandList_ResolveQueryData(command_list,
                query_heap, D3D12_QUERY_TYPE_OCCLUSION, 0, 1, readback_buffer, i * sizeof(uint64_t));
    }
    hr = ID3D12GraphicsCommandList_Close(command_list);
    ok(SUCCEEDED(hr), "Failed to close command list, hr %#x.\n", hr);
    exec_command_list(queue, command_list);
    wait_queue_idle(context.device, queue);

    reset_command_list(command_list, context.allocator);
    for (; i < readback_buffer_capacity; ++i)
    {
        ID3D12GraphicsCommandList_ResolveQueryData(command_list,
                query_heap, D3D12_QUERY_TYPE_OCCLUSION, 0, 1, readback_buffer, i * sizeof(uint64_t));
    }

    get_buffer_readback_with_command_list(readback_buffer, DXGI_FORMAT_UNKNOWN, &rb, queue, command_list);
    for (i = 0; i < readback_buffer_capacity; ++i)
    {
        uint64_t expected_result = context.render_target_desc.Width * context.render_target_desc.Height;
        uint64_t result = get_readback_uint64(&rb.rb, i, 0);

        ok(result == expected_result, "Got unexpected result %"PRIu64" at %u.\n", result, i);
    }
    release_resource_readback(&rb);

    ID3D12QueryHeap_Release(query_heap);
    ID3D12Resource_Release(readback_buffer);
    destroy_test_context(&context);
}

static void test_resolve_query_data_in_reordered_command_list(void)
{
    ID3D12GraphicsCommandList *command_lists[2];
    ID3D12CommandAllocator *command_allocator;
    struct d3d12_resource_readback rb;
    D3D12_QUERY_HEAP_DESC heap_desc;
    ID3D12Resource *readback_buffer;
    ID3D12QueryHeap *query_heap;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Device *device;
    uint64_t result;
    HRESULT hr;

    if (!init_test_context(&context, NULL))
        return;
    device = context.device;
    command_lists[0] = context.list;
    queue = context.queue;

    hr = ID3D12Device_CreateCommandAllocator(device, D3D12_COMMAND_LIST_TYPE_DIRECT,
            &IID_ID3D12CommandAllocator, (void **)&command_allocator);
    ok(SUCCEEDED(hr), "Failed to create command allocator, hr %#x.\n", hr);
    hr = ID3D12Device_CreateCommandList(device, 0, D3D12_COMMAND_LIST_TYPE_DIRECT,
            command_allocator, NULL, &IID_ID3D12GraphicsCommandList, (void **)&command_lists[1]);
    ok(SUCCEEDED(hr), "Failed to create command list, hr %#x.\n", hr);

    heap_desc.Type = D3D12_QUERY_HEAP_TYPE_OCCLUSION;
    heap_desc.Count = 1;
    heap_desc.NodeMask = 0;
    hr = ID3D12Device_CreateQueryHeap(device, &heap_desc, &IID_ID3D12QueryHeap, (void **)&query_heap);
    ok(SUCCEEDED(hr), "Failed to create query heap, hr %#x.\n", hr);

    readback_buffer = create_readback_buffer(device, sizeof(uint64_t));

    /* Read query results in the second command list. */
    ID3D12GraphicsCommandList_ResolveQueryData(command_lists[1],
            query_heap, D3D12_QUERY_TYPE_OCCLUSION, 0, 1, readback_buffer, 0);
    hr = ID3D12GraphicsCommandList_Close(command_lists[1]);
    ok(SUCCEEDED(hr), "Failed to close command list, hr %#x.\n", hr);

    /* Produce query results in the first command list. */
    ID3D12GraphicsCommandList_OMSetRenderTargets(command_lists[0], 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_lists[0], context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_lists[0], context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_lists[0], D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_lists[0], 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_lists[0], 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_BeginQuery(command_lists[0], query_heap, D3D12_QUERY_TYPE_OCCLUSION, 0);
    ID3D12GraphicsCommandList_DrawInstanced(command_lists[0], 3, 1, 0, 0);
    ID3D12GraphicsCommandList_EndQuery(command_lists[0], query_heap, D3D12_QUERY_TYPE_OCCLUSION, 0);
    hr = ID3D12GraphicsCommandList_Close(command_lists[0]);
    ok(SUCCEEDED(hr), "Failed to close command list, hr %#x.\n", hr);

    ID3D12CommandQueue_ExecuteCommandLists(queue,
            ARRAY_SIZE(command_lists), (ID3D12CommandList **)command_lists);
    wait_queue_idle(device, queue);

    reset_command_list(command_lists[0], context.allocator);
    get_buffer_readback_with_command_list(readback_buffer, DXGI_FORMAT_UNKNOWN, &rb, queue, command_lists[0]);
    result = get_readback_uint64(&rb.rb, 0, 0);
    todo ok(result == context.render_target_desc.Width * context.render_target_desc.Height,
            "Got unexpected result %"PRIu64".\n", result);
    release_resource_readback(&rb);

    ID3D12GraphicsCommandList_Release(command_lists[1]);
    ID3D12CommandAllocator_Release(command_allocator);
    ID3D12QueryHeap_Release(query_heap);
    ID3D12Resource_Release(readback_buffer);
    destroy_test_context(&context);
}

static void test_execute_indirect(void)
{
    ID3D12Resource *argument_buffer, *count_buffer, *uav;
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    ID3D12CommandSignature *command_signature;
    ID3D12GraphicsCommandList *command_list;
    D3D12_INPUT_LAYOUT_DESC input_layout;
    D3D12_ROOT_PARAMETER root_parameter;
    ID3D12PipelineState *pipeline_state;
    ID3D12RootSignature *root_signature;
    struct d3d12_resource_readback rb;
    struct test_context_desc desc;
    D3D12_VERTEX_BUFFER_VIEW vbv;
    D3D12_INDEX_BUFFER_VIEW ibv;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Resource *vb, *ib;
    unsigned int i;
    ULONG refcount;
    D3D12_BOX box;
    HRESULT hr;

    static const struct
    {
        struct vec4 position;
        uint32_t color;
    }
    vertices[] =
    {
        {{-1.0f, -1.0f, 0.0f, 1.0f}, 0xffffff00},
        {{-1.0f,  1.0f, 0.0f, 1.0f}, 0xffffff00},
        {{ 1.0f, -1.0f, 0.0f, 1.0f}, 0xffffff00},
        {{ 1.0f,  1.0f, 0.0f, 1.0f}, 0xffffff00},

        {{-1.0f, -1.0f, 0.0f, 1.0f}, 0xff00ff00},
        {{-1.0f,  0.5f, 0.0f, 1.0f}, 0xff00ff00},
        {{ 0.5f, -1.0f, 0.0f, 1.0f}, 0xff00ff00},
        {{ 0.5f,  0.5f, 0.0f, 1.0f}, 0xff00ff00},

        {{-1.0f, -1.0f, 0.0f, 1.0f}, 0xff00ff00},
        {{-1.0f,  1.0f, 0.0f, 1.0f}, 0xff00ff00},
        {{ 1.0f, -1.0f, 0.0f, 1.0f}, 0xff00ff00},
        {{ 1.0f,  1.0f, 0.0f, 1.0f}, 0xff00ff00},
    };
    static const uint32_t indices[] = {0, 1, 2, 3, 2, 1};
    static const D3D12_INPUT_ELEMENT_DESC layout_desc[] =
    {
        {"SV_POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0,  0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
        {"COLOR",       0, DXGI_FORMAT_R8G8B8A8_UNORM,     0, 16, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
    };
    static const DWORD vs_code[] =
    {
#if 0
        struct vs_data
        {
            float4 pos : SV_POSITION;
            float4 color : COLOR;
        };

        void main(in struct vs_data vs_input, out struct vs_data vs_output)
        {
            vs_output.pos = vs_input.pos;
            vs_output.color = vs_input.color;
        }
#endif
        0x43425844, 0xd5b32785, 0x35332906, 0x4d05e031, 0xf66a58af, 0x00000001, 0x00000144, 0x00000003,
        0x0000002c, 0x00000080, 0x000000d4, 0x4e475349, 0x0000004c, 0x00000002, 0x00000008, 0x00000038,
        0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000f0f, 0x00000044, 0x00000000, 0x00000000,
        0x00000003, 0x00000001, 0x00000f0f, 0x505f5653, 0x5449534f, 0x004e4f49, 0x4f4c4f43, 0xabab0052,
        0x4e47534f, 0x0000004c, 0x00000002, 0x00000008, 0x00000038, 0x00000000, 0x00000001, 0x00000003,
        0x00000000, 0x0000000f, 0x00000044, 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x0000000f,
        0x505f5653, 0x5449534f, 0x004e4f49, 0x4f4c4f43, 0xabab0052, 0x52444853, 0x00000068, 0x00010040,
        0x0000001a, 0x0300005f, 0x001010f2, 0x00000000, 0x0300005f, 0x001010f2, 0x00000001, 0x04000067,
        0x001020f2, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000001, 0x05000036, 0x001020f2,
        0x00000000, 0x00101e46, 0x00000000, 0x05000036, 0x001020f2, 0x00000001, 0x00101e46, 0x00000001,
        0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE vs = {vs_code, sizeof(vs_code)};
    static const DWORD ps_code[] =
    {
#if 0
        struct ps_data
        {
            float4 pos : SV_POSITION;
            float4 color : COLOR;
        };

        float4 main(struct ps_data ps_input) : SV_Target
        {
            return ps_input.color;
        }
#endif
        0x43425844, 0x89803e59, 0x3f798934, 0xf99181df, 0xf5556512, 0x00000001, 0x000000f4, 0x00000003,
        0x0000002c, 0x00000080, 0x000000b4, 0x4e475349, 0x0000004c, 0x00000002, 0x00000008, 0x00000038,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x00000044, 0x00000000, 0x00000000,
        0x00000003, 0x00000001, 0x00000f0f, 0x505f5653, 0x5449534f, 0x004e4f49, 0x4f4c4f43, 0xabab0052,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x52444853, 0x00000038, 0x00000040,
        0x0000000e, 0x03001062, 0x001010f2, 0x00000001, 0x03000065, 0x001020f2, 0x00000000, 0x05000036,
        0x001020f2, 0x00000000, 0x00101e46, 0x00000001, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};
    static const DWORD cs_code[] =
    {
#if 0
        RWByteAddressBuffer o;

        [numthreads(1, 1, 1)]
        void main(uint3 group_id : SV_groupID)
        {
            uint idx = group_id.x + group_id.y * 2 + group_id.z * 6;
            o.Store(idx * 4, idx);
        }
#endif
        0x43425844, 0xfdd6a339, 0xf3b8096e, 0xb5977014, 0xcdb26cfd, 0x00000001, 0x00000118, 0x00000003,
        0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x000000c4, 0x00050050, 0x00000031, 0x0100086a,
        0x0300009d, 0x0011e000, 0x00000000, 0x0200005f, 0x00021072, 0x02000068, 0x00000001, 0x0400009b,
        0x00000001, 0x00000001, 0x00000001, 0x06000029, 0x00100012, 0x00000000, 0x0002101a, 0x00004001,
        0x00000001, 0x0600001e, 0x00100012, 0x00000000, 0x0010000a, 0x00000000, 0x0002100a, 0x08000023,
        0x00100012, 0x00000000, 0x0002102a, 0x00004001, 0x00000006, 0x0010000a, 0x00000000, 0x07000029,
        0x00100022, 0x00000000, 0x0010000a, 0x00000000, 0x00004001, 0x00000002, 0x070000a6, 0x0011e012,
        0x00000000, 0x0010001a, 0x00000000, 0x0010000a, 0x00000000, 0x0100003e,
    };
    static const struct argument_data
    {
        D3D12_DRAW_ARGUMENTS draws[4];
        D3D12_DISPATCH_ARGUMENTS dispatch;
        D3D12_DRAW_INDEXED_ARGUMENTS indexed_draws[2];
    }
    argument_data =
    {
        {{6, 1, 4, 0}, {6, 1, 8, 0}, {6, 1, 0, 0}},
        {2, 3, 4},
        {{6, 1, 0, 0, 0}, {6, 1, 0, 4, 0}},
    };
    static const uint32_t count_data[] = {2, 1};
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};

    if (test_options.use_warp_device)
    {
        skip("Broken on WARP.\n");
        return;
    }

    memset(&desc, 0, sizeof(desc));
    desc.root_signature_flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
    desc.no_pipeline = true;
    if (!init_test_context(&context, &desc))
        return;
    command_list = context.list;
    queue = context.queue;

    input_layout.pInputElementDescs = layout_desc;
    input_layout.NumElements = ARRAY_SIZE(layout_desc);
    context.pipeline_state = create_pipeline_state(context.device,
            context.root_signature, context.render_target_desc.Format, &vs, &ps, &input_layout);

    vb = create_upload_buffer(context.device, sizeof(vertices), vertices);
    vbv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vb);
    vbv.StrideInBytes = sizeof(*vertices);
    vbv.SizeInBytes = sizeof(vertices);

    ib = create_upload_buffer(context.device, sizeof(indices), indices);
    ibv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(ib);
    ibv.SizeInBytes = sizeof(indices);
    ibv.Format = DXGI_FORMAT_R32_UINT;

    argument_buffer = create_upload_buffer(context.device, sizeof(argument_data), &argument_data);
    count_buffer = create_upload_buffer(context.device, sizeof(count_data), count_data);

    command_signature = create_command_signature(context.device, D3D12_INDIRECT_ARGUMENT_TYPE_DRAW);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
    ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, 1, &vbv);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_ExecuteIndirect(command_list, command_signature, 2, argument_buffer, 0, NULL, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);

    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
    ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, 1, &vbv);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_ExecuteIndirect(command_list, command_signature, 4, argument_buffer, 0,
            count_buffer, 0);

    refcount = ID3D12CommandSignature_Release(command_signature);
    ok(!refcount, "ID3D12CommandSignature has %u references left.\n", (unsigned int)refcount);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    bug_if(is_mvk_device(context.device))
    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);

    reset_command_list(command_list, context.allocator);

    command_signature = create_command_signature(context.device, D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH);

    uav = create_default_buffer(context.device, 2 * 3 * 4 * sizeof(UINT),
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

    root_parameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV;
    root_parameter.Descriptor.ShaderRegister = 0;
    root_parameter.Descriptor.RegisterSpace = 0;
    root_parameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    root_signature_desc.NumParameters = 1;
    root_signature_desc.pParameters = &root_parameter;
    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);

    ID3D12GraphicsCommandList_SetComputeRootSignature(command_list, root_signature);
    pipeline_state = create_compute_pipeline_state(context.device, root_signature,
            shader_bytecode(cs_code, sizeof(cs_code)));
    ID3D12GraphicsCommandList_SetPipelineState(command_list, pipeline_state);
    ID3D12GraphicsCommandList_SetComputeRootUnorderedAccessView(command_list,
            0, ID3D12Resource_GetGPUVirtualAddress(uav));
    ID3D12GraphicsCommandList_ExecuteIndirect(command_list, command_signature, 1, argument_buffer,
            offsetof(struct argument_data, dispatch), NULL, 0);

    refcount = ID3D12CommandSignature_Release(command_signature);
    ok(!refcount, "ID3D12CommandSignature has %u references left.\n", (unsigned int)refcount);

    transition_sub_resource_state(command_list, uav, 0,
            D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_buffer_readback_with_command_list(uav, DXGI_FORMAT_R32_UINT, &rb, queue, command_list);
    for (i = 0; i < rb.rb.width; ++i)
    {
        unsigned int ret = get_readback_uint(&rb.rb, i, 0, 0);
        ok(ret == i, "Got unexpected result %#x at index %u.\n", ret, i);
    }
    release_resource_readback(&rb);

    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    command_signature = create_command_signature(context.device, D3D12_INDIRECT_ARGUMENT_TYPE_DRAW_INDEXED);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_IASetIndexBuffer(command_list, &ibv);
    ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, 1, &vbv);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_ExecuteIndirect(command_list, command_signature,
            ARRAY_SIZE(argument_data.indexed_draws), argument_buffer,
            offsetof(struct argument_data, indexed_draws), NULL, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_resource_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
    set_box(&box, 0, 0, 0, 32, 8, 1);
    check_readback_data_uint(&rb.rb, &box, 0xffffff00, 0);
    set_box(&box, 24, 8, 0, 32, 32, 1);
    check_readback_data_uint(&rb.rb, &box, 0xffffff00, 0);
    set_box(&box, 0, 8, 0, 24, 32, 1);
    check_readback_data_uint(&rb.rb, &box, 0xff00ff00, 0);
    release_resource_readback(&rb);

    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_IASetIndexBuffer(command_list, &ibv);
    ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, 1, &vbv);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_ExecuteIndirect(command_list, command_signature,
            ARRAY_SIZE(argument_data.indexed_draws), argument_buffer,
            offsetof(struct argument_data, indexed_draws), count_buffer, sizeof(uint32_t));

    refcount = ID3D12CommandSignature_Release(command_signature);
    ok(!refcount, "ID3D12CommandSignature has %u references left.\n", (unsigned int)refcount);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    bug_if(is_mvk_device(context.device))
    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xffffff00, 0);

    ID3D12PipelineState_Release(pipeline_state);
    ID3D12RootSignature_Release(root_signature);
    ID3D12Resource_Release(ib);
    ID3D12Resource_Release(uav);
    ID3D12Resource_Release(vb);
    ID3D12Resource_Release(argument_buffer);
    ID3D12Resource_Release(count_buffer);
    destroy_test_context(&context);
}

static void test_dispatch_zero_thread_groups(void)
{
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    ID3D12CommandSignature *command_signature;
    ID3D12GraphicsCommandList *command_list;
    D3D12_ROOT_PARAMETER root_parameters[2];
    ID3D12Resource *argument_buffer, *uav;
    struct d3d12_resource_readback rb;
    struct test_context context;
    ID3D12CommandQueue *queue;
    unsigned int ret, i;
    HRESULT hr;

    static const DWORD cs_code[] =
    {
#if 0
        RWByteAddressBuffer o;

        uint v;

        [numthreads(1, 1, 1)]
        void main()
        {
            o.Store(0, v);
        }
#endif
        0x43425844, 0x3ad946e3, 0x83e33b81, 0x83532aa4, 0x40831f89, 0x00000001, 0x000000b0, 0x00000003,
        0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x0000005c, 0x00050050, 0x00000017, 0x0100086a,
        0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x0300009d, 0x0011e000, 0x00000000, 0x0400009b,
        0x00000001, 0x00000001, 0x00000001, 0x080000a6, 0x0011e012, 0x00000000, 0x00004001, 0x00000000,
        0x0020800a, 0x00000000, 0x00000000, 0x0100003e,
    };
    static const D3D12_DISPATCH_ARGUMENTS argument_data[] =
    {
        {1, 1, 1},
        {0, 3, 4},
        {0, 0, 4},
        {0, 0, 0},
        {4, 0, 0},
        {4, 0, 3},
        {4, 2, 0},
        {0, 2, 0},
        {0, 0, 0},
    };

    if (!init_compute_test_context(&context))
        return;
    command_list = context.list;
    queue = context.queue;

    argument_buffer = create_upload_buffer(context.device, sizeof(argument_data), &argument_data);

    command_signature = create_command_signature(context.device, D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH);

    uav = create_default_buffer(context.device, 2 * 256, /* minTexelBufferOffsetAlignment */
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

    root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV;
    root_parameters[0].Descriptor.ShaderRegister = 0;
    root_parameters[0].Descriptor.RegisterSpace = 0;
    root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    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 = 1;
    root_parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    root_signature_desc.NumParameters = 2;
    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, &context.root_signature);
    ok(SUCCEEDED(hr), "Failed to create root signature, hr %#x.\n", hr);

    context.pipeline_state = create_compute_pipeline_state(context.device, context.root_signature,
            shader_bytecode(cs_code, sizeof(cs_code)));

    ID3D12GraphicsCommandList_SetComputeRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);

    ID3D12GraphicsCommandList_SetComputeRootUnorderedAccessView(command_list,
            0, ID3D12Resource_GetGPUVirtualAddress(uav));
    for (i = 0; i < ARRAY_SIZE(argument_data); ++i)
    {
        ID3D12GraphicsCommandList_SetComputeRoot32BitConstant(command_list,
                1, 10 + i, 0);
        ID3D12GraphicsCommandList_ExecuteIndirect(command_list, command_signature,
                1, argument_buffer, i * sizeof(*argument_data), NULL, 0);
    }

    ID3D12GraphicsCommandList_SetComputeRootUnorderedAccessView(command_list,
            0, ID3D12Resource_GetGPUVirtualAddress(uav) + 256);
    for (i = 0; i < ARRAY_SIZE(argument_data); ++i)
    {
        const D3D12_DISPATCH_ARGUMENTS *arg = &argument_data[i];
        ID3D12GraphicsCommandList_SetComputeRoot32BitConstant(command_list,
                1, 50 + i, 0);
        ID3D12GraphicsCommandList_Dispatch(command_list,
                arg->ThreadGroupCountX, arg->ThreadGroupCountY, arg->ThreadGroupCountZ);
    }

    transition_sub_resource_state(command_list, uav, 0,
            D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_buffer_readback_with_command_list(uav, DXGI_FORMAT_R32_UINT, &rb, queue, command_list);
    ret = get_readback_uint(&rb.rb, 0, 0, 0);
    ok(ret == 10, "Got unexpected result %#x.\n", ret);
    ret = get_readback_uint(&rb.rb, 64, 0, 0);
    ok(ret == 50, "Got unexpected result %#x.\n", ret);
    release_resource_readback(&rb);

    ID3D12Resource_Release(uav);
    ID3D12CommandSignature_Release(command_signature);
    ID3D12Resource_Release(argument_buffer);
    destroy_test_context(&context);
}

static void test_zero_vertex_stride(void)
{
    ID3D12PipelineState *instance_pipeline_state;
    ID3D12GraphicsCommandList *command_list;
    D3D12_INPUT_LAYOUT_DESC input_layout;
    D3D12_VERTEX_BUFFER_VIEW vbv[2];
    struct test_context_desc desc;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Resource *vb[2];

    static const D3D12_INPUT_ELEMENT_DESC layout_desc[] =
    {
        {"sv_position", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT,
                D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
        {"color",       0, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, D3D12_APPEND_ALIGNED_ELEMENT,
                D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
    };
    static const D3D12_INPUT_ELEMENT_DESC instance_layout_desc[] =
    {
        {"sv_position", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT,
                D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
        {"color",       0, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, D3D12_APPEND_ALIGNED_ELEMENT,
                D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA, 0},
    };
    static const DWORD vs_code[] =
    {
#if 0
        struct vs_data
        {
            float4 pos : SV_POSITION;
            float4 color : COLOR;
        };

        void main(in struct vs_data vs_input, out struct vs_data vs_output)
        {
            vs_output.pos = vs_input.pos;
            vs_output.color = vs_input.color;
        }
#endif
        0x43425844, 0xd5b32785, 0x35332906, 0x4d05e031, 0xf66a58af, 0x00000001, 0x00000144, 0x00000003,
        0x0000002c, 0x00000080, 0x000000d4, 0x4e475349, 0x0000004c, 0x00000002, 0x00000008, 0x00000038,
        0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000f0f, 0x00000044, 0x00000000, 0x00000000,
        0x00000003, 0x00000001, 0x00000f0f, 0x505f5653, 0x5449534f, 0x004e4f49, 0x4f4c4f43, 0xabab0052,
        0x4e47534f, 0x0000004c, 0x00000002, 0x00000008, 0x00000038, 0x00000000, 0x00000001, 0x00000003,
        0x00000000, 0x0000000f, 0x00000044, 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x0000000f,
        0x505f5653, 0x5449534f, 0x004e4f49, 0x4f4c4f43, 0xabab0052, 0x52444853, 0x00000068, 0x00010040,
        0x0000001a, 0x0300005f, 0x001010f2, 0x00000000, 0x0300005f, 0x001010f2, 0x00000001, 0x04000067,
        0x001020f2, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000001, 0x05000036, 0x001020f2,
        0x00000000, 0x00101e46, 0x00000000, 0x05000036, 0x001020f2, 0x00000001, 0x00101e46, 0x00000001,
        0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE vs = {vs_code, sizeof(vs_code)};
    static const DWORD ps_code[] =
    {
#if 0
        struct ps_data
        {
            float4 pos : SV_POSITION;
            float4 color : COLOR;
        };

        float4 main(struct ps_data ps_input) : SV_Target
        {
            return ps_input.color;
        }
#endif
        0x43425844, 0x89803e59, 0x3f798934, 0xf99181df, 0xf5556512, 0x00000001, 0x000000f4, 0x00000003,
        0x0000002c, 0x00000080, 0x000000b4, 0x4e475349, 0x0000004c, 0x00000002, 0x00000008, 0x00000038,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x00000044, 0x00000000, 0x00000000,
        0x00000003, 0x00000001, 0x00000f0f, 0x505f5653, 0x5449534f, 0x004e4f49, 0x4f4c4f43, 0xabab0052,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x52444853, 0x00000038, 0x00000040,
        0x0000000e, 0x03001062, 0x001010f2, 0x00000001, 0x03000065, 0x001020f2, 0x00000000, 0x05000036,
        0x001020f2, 0x00000000, 0x00101e46, 0x00000001, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};
    static const struct vec4 positions[] =
    {
        {-1.0f, -1.0f, 0.0f, 1.0f},
        {-1.0f,  1.0f, 0.0f, 1.0f},
        { 1.0f, -1.0f, 0.0f, 1.0f},
        { 1.0f,  1.0f, 0.0f, 1.0f},
    };
    static const struct vec4 colors[] =
    {
        {0.0f, 1.0f, 0.0f, 1.0f},
        {1.0f, 0.0f, 0.0f, 1.0f},
        {0.5f, 0.5f, 0.5f, 1.0f},
        {1.0f, 0.0f, 1.0f, 1.0f},
        {1.0f, 0.0f, 1.0f, 1.0f},
        {1.0f, 0.0f, 1.0f, 1.0f},
        {1.0f, 0.0f, 1.0f, 1.0f},
        {1.0f, 0.0f, 1.0f, 1.0f},
        {1.0f, 0.0f, 1.0f, 1.0f},
    };
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};

    memset(&desc, 0, sizeof(desc));
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    command_list = context.list;
    queue = context.queue;

    context.root_signature = create_empty_root_signature(context.device,
            D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
    input_layout.pInputElementDescs = layout_desc;
    input_layout.NumElements = ARRAY_SIZE(layout_desc);
    context.pipeline_state = create_pipeline_state(context.device,
            context.root_signature, context.render_target_desc.Format, &vs, &ps, &input_layout);

    input_layout.pInputElementDescs = instance_layout_desc;
    input_layout.NumElements = ARRAY_SIZE(instance_layout_desc);
    instance_pipeline_state = create_pipeline_state(context.device,
            context.root_signature, context.render_target_desc.Format, &vs, &ps, &input_layout);

    memset(vbv, 0, sizeof(vbv));
    vb[0] = create_upload_buffer(context.device, sizeof(positions), positions);
    vbv[0].BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vb[0]);
    vbv[0].StrideInBytes = sizeof(*positions);
    vbv[0].SizeInBytes = sizeof(positions);

    vb[1] = create_upload_buffer(context.device, sizeof(colors), colors);
    vbv[1].BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vb[1]) + 2 * sizeof(*colors);
    vbv[1].StrideInBytes = 0;
    vbv[1].SizeInBytes = sizeof(colors);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
    ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, ARRAY_SIZE(vbv), vbv);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 4, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff808080, 2);

    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    vbv[1].BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vb[1]);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, instance_pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
    ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, ARRAY_SIZE(vbv), vbv);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 4, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);

    ID3D12Resource_Release(vb[1]);
    ID3D12Resource_Release(vb[0]);
    ID3D12PipelineState_Release(instance_pipeline_state);
    destroy_test_context(&context);
}

static void test_instance_id(void)
{
    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
    ID3D12CommandSignature *command_signature;
    ID3D12GraphicsCommandList *command_list;
    D3D12_INPUT_LAYOUT_DESC input_layout;
    D3D12_CPU_DESCRIPTOR_HANDLE rtvs[2];
    struct d3d12_resource_readback rb;
    D3D12_VERTEX_BUFFER_VIEW vbv[3];
    ID3D12Resource *argument_buffer;
    ID3D12Resource *render_target;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Resource *vb[3];
    unsigned int i, j;
    HRESULT hr;

    D3D12_INPUT_ELEMENT_DESC layout_desc[] =
    {
        {"position", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT,
                D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
        {"color",    0, DXGI_FORMAT_R8_UNORM,           1, D3D12_APPEND_ALIGNED_ELEMENT,
                D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA, 1},
        {"v_offset", 0, DXGI_FORMAT_R32_FLOAT,          2, D3D12_APPEND_ALIGNED_ELEMENT,
                D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA, 1},
    };
    static const DWORD vs_code[] =
    {
#if 0
        struct vs_in
        {
            float4 position : Position;
            float color : Color;
            float v_offset : V_Offset;
            uint instance_id : SV_InstanceId;
        };

        struct vs_out
        {
            float4 position : SV_Position;
            float color : Color;
            uint instance_id : InstanceId;
        };

        void main(vs_in i, out vs_out o)
        {
            o.position = i.position;
            o.position.x += i.v_offset;
            o.color = i.color;
            o.instance_id = i.instance_id;
        }
#endif
        0x43425844, 0xcde3cfbf, 0xe2e3d090, 0xe2eb1038, 0x7e5ad1cf, 0x00000001, 0x00000204, 0x00000003,
        0x0000002c, 0x000000c4, 0x0000013c, 0x4e475349, 0x00000090, 0x00000004, 0x00000008, 0x00000068,
        0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000f0f, 0x00000071, 0x00000000, 0x00000000,
        0x00000003, 0x00000001, 0x00000101, 0x00000077, 0x00000000, 0x00000000, 0x00000003, 0x00000002,
        0x00000101, 0x00000080, 0x00000000, 0x00000008, 0x00000001, 0x00000003, 0x00000101, 0x69736f50,
        0x6e6f6974, 0x6c6f4300, 0x5600726f, 0x66664f5f, 0x00746573, 0x495f5653, 0x6174736e, 0x4965636e,
        0xabab0064, 0x4e47534f, 0x00000070, 0x00000003, 0x00000008, 0x00000050, 0x00000000, 0x00000001,
        0x00000003, 0x00000000, 0x0000000f, 0x0000005c, 0x00000000, 0x00000000, 0x00000003, 0x00000001,
        0x00000e01, 0x00000062, 0x00000000, 0x00000000, 0x00000001, 0x00000002, 0x00000e01, 0x505f5653,
        0x7469736f, 0x006e6f69, 0x6f6c6f43, 0x6e490072, 0x6e617473, 0x64496563, 0xababab00, 0x52444853,
        0x000000c0, 0x00010040, 0x00000030, 0x0300005f, 0x001010f2, 0x00000000, 0x0300005f, 0x00101012,
        0x00000001, 0x0300005f, 0x00101012, 0x00000002, 0x04000060, 0x00101012, 0x00000003, 0x00000008,
        0x04000067, 0x001020f2, 0x00000000, 0x00000001, 0x03000065, 0x00102012, 0x00000001, 0x03000065,
        0x00102012, 0x00000002, 0x07000000, 0x00102012, 0x00000000, 0x0010100a, 0x00000000, 0x0010100a,
        0x00000002, 0x05000036, 0x001020e2, 0x00000000, 0x00101e56, 0x00000000, 0x05000036, 0x00102012,
        0x00000001, 0x0010100a, 0x00000001, 0x05000036, 0x00102012, 0x00000002, 0x0010100a, 0x00000003,
        0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE vs = {vs_code, sizeof(vs_code)};
    static const DWORD ps_code[] =
    {
#if 0
        struct vs_out
        {
            float4 position : SV_Position;
            float color : Color;
            uint instance_id : InstanceId;
        };

        void main(vs_out i, out float4 o0 : SV_Target0, out uint4 o1 : SV_Target1)
        {
            o0 = float4(i.color, i.color, i.color, 1.0f);
            o1 = i.instance_id;
        }
#endif
        0x43425844, 0xda0ad0bb, 0x4743f5f5, 0xfbc6d0b1, 0x7c8e7df5, 0x00000001, 0x00000170, 0x00000003,
        0x0000002c, 0x000000a4, 0x000000f0, 0x4e475349, 0x00000070, 0x00000003, 0x00000008, 0x00000050,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x0000005c, 0x00000000, 0x00000000,
        0x00000003, 0x00000001, 0x00000101, 0x00000062, 0x00000000, 0x00000000, 0x00000001, 0x00000002,
        0x00000101, 0x505f5653, 0x7469736f, 0x006e6f69, 0x6f6c6f43, 0x6e490072, 0x6e617473, 0x64496563,
        0xababab00, 0x4e47534f, 0x00000044, 0x00000002, 0x00000008, 0x00000038, 0x00000000, 0x00000000,
        0x00000003, 0x00000000, 0x0000000f, 0x00000038, 0x00000001, 0x00000000, 0x00000001, 0x00000001,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x52444853, 0x00000078, 0x00000040, 0x0000001e,
        0x03001062, 0x00101012, 0x00000001, 0x03000862, 0x00101012, 0x00000002, 0x03000065, 0x001020f2,
        0x00000000, 0x03000065, 0x001020f2, 0x00000001, 0x05000036, 0x00102072, 0x00000000, 0x00101006,
        0x00000001, 0x05000036, 0x00102082, 0x00000000, 0x00004001, 0x3f800000, 0x05000036, 0x001020f2,
        0x00000001, 0x00101006, 0x00000002, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};
    static const struct vec4 stream0[] =
    {
        {-1.00f, 0.0f, 0.0f, 1.0f},
        {-1.00f, 1.0f, 0.0f, 1.0f},
        {-0.75f, 0.0f, 0.0f, 1.0f},
        {-0.75f, 1.0f, 0.0f, 1.0f},
        /* indirect draws data */
        {-1.00f, -1.0f, 0.0f, 1.0f},
        {-1.00f,  0.0f, 0.0f, 1.0f},
        {-0.75f, -1.0f, 0.0f, 1.0f},
        {-0.75f,  0.0f, 0.0f, 1.0f},
    };
    static const BYTE stream1[] =
    {
        0xf0,
        0x80,
        0x10,
        0x40,

        0xaa,
        0xbb,
        0xcc,
        0x90,
    };
    static const float stream2[] =
    {
        0.00f,
        0.25f,
        0.50f,
        0.75f,

        1.00f,
        1.25f,
        1.50f,
        1.75f,
    };
    static const D3D12_DRAW_ARGUMENTS argument_data[] =
    {
        {4, 4, 4, 0},
        {4, 4, 4, 4},
    };
    static const struct
    {
        unsigned int color_step_rate;
        unsigned int expected_colors[16];
    }
    tests[] =
    {
        {0, {0xfff0f0f0, 0xfff0f0f0, 0xfff0f0f0, 0xfff0f0f0, 0xffaaaaaa, 0xffaaaaaa, 0xffaaaaaa, 0xffaaaaaa,
             0xfff0f0f0, 0xfff0f0f0, 0xfff0f0f0, 0xfff0f0f0, 0xffaaaaaa, 0xffaaaaaa, 0xffaaaaaa, 0xffaaaaaa}},
        {1, {0xfff0f0f0, 0xff808080, 0xff101010, 0xff404040, 0xffaaaaaa, 0xffbbbbbb, 0xffcccccc, 0xff909090,
             0xfff0f0f0, 0xff808080, 0xff101010, 0xff404040, 0xffaaaaaa, 0xffbbbbbb, 0xffcccccc, 0xff909090}},
        {2, {0xfff0f0f0, 0xfff0f0f0, 0xff808080, 0xff808080, 0xffaaaaaa, 0xffaaaaaa, 0xffbbbbbb, 0xffbbbbbb,
             0xfff0f0f0, 0xfff0f0f0, 0xff808080, 0xff808080, 0xffaaaaaa, 0xffaaaaaa, 0xffbbbbbb, 0xffbbbbbb}},
    };
    static const struct
    {
        D3D12_BOX box;
        unsigned int instance_id;
    }
    expected_results[] =
    {
        {{ 0, 0, 0, 10, 10, 1}, 0},
        {{10, 0, 0, 20, 10, 1}, 1},
        {{20, 0, 0, 30, 10, 1}, 2},
        {{30, 0, 0, 40, 10, 1}, 3},
        {{40, 0, 0, 50, 10, 1}, 0},
        {{50, 0, 0, 60, 10, 1}, 1},
        {{60, 0, 0, 70, 10, 1}, 2},
        {{70, 0, 0, 80, 10, 1}, 3},
        /* indirect draws results */
        {{ 0, 10, 0, 10, 20, 1}, 0},
        {{10, 10, 0, 20, 20, 1}, 1},
        {{20, 10, 0, 30, 20, 1}, 2},
        {{30, 10, 0, 40, 20, 1}, 3},
        {{40, 10, 0, 50, 20, 1}, 0},
        {{50, 10, 0, 60, 20, 1}, 1},
        {{60, 10, 0, 70, 20, 1}, 2},
        {{70, 10, 0, 80, 20, 1}, 3},
    };
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};

    assert(ARRAY_SIZE(tests->expected_colors) == ARRAY_SIZE(expected_results));

    memset(&desc, 0, sizeof(desc));
    desc.rt_width = 80;
    desc.rt_height = 20;
    desc.rt_descriptor_count = 2;
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    command_list = context.list;
    queue = context.queue;

    context.root_signature = create_empty_root_signature(context.device,
            D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);

    rtvs[0] = context.rtv;
    rtvs[1] = get_cpu_rtv_handle(&context, context.rtv_heap, 1);

    desc.rt_format = DXGI_FORMAT_R32_UINT;
    create_render_target(&context, &desc, &render_target, &rtvs[1]);

    vb[0] = create_upload_buffer(context.device, sizeof(stream0), stream0);
    vbv[0].BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vb[0]);
    vbv[0].StrideInBytes = sizeof(*stream0);
    vbv[0].SizeInBytes = sizeof(stream0);

    vb[1] = create_upload_buffer(context.device, sizeof(stream1), stream1);
    vbv[1].BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vb[1]);
    vbv[1].StrideInBytes = sizeof(*stream1);
    vbv[1].SizeInBytes = sizeof(stream1);

    vb[2] = create_upload_buffer(context.device, sizeof(stream2), stream2);
    vbv[2].BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vb[2]);
    vbv[2].StrideInBytes = sizeof(*stream2);
    vbv[2].SizeInBytes = sizeof(stream2);

    command_signature = create_command_signature(context.device, D3D12_INDIRECT_ARGUMENT_TYPE_DRAW);

    argument_buffer = create_upload_buffer(context.device, sizeof(argument_data), &argument_data);

    for (i = 0; i < ARRAY_SIZE(tests); ++i)
    {
        vkd3d_test_push_context("Test %u", i);

        layout_desc[1].InstanceDataStepRate = tests[i].color_step_rate;
        input_layout.pInputElementDescs = layout_desc;
        input_layout.NumElements = ARRAY_SIZE(layout_desc);
        init_pipeline_state_desc(&pso_desc, context.root_signature, 0, &vs, &ps, &input_layout);
        pso_desc.NumRenderTargets = 2;
        pso_desc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
        pso_desc.RTVFormats[1] = DXGI_FORMAT_R32_UINT;
        hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
                &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
        ok(hr == S_OK, "Failed to create graphics pipeline state, hr %#x.\n", hr);

        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, rtvs[0], white, 0, NULL);
        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, rtvs[1], white, 0, NULL);

        ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 2, rtvs, false, NULL);
        ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
        ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
        ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, ARRAY_SIZE(vbv), vbv);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
        ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 4, 0, 0);
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 4, 0, 4);

        ID3D12GraphicsCommandList_ExecuteIndirect(command_list, command_signature,
                2, argument_buffer, 0, NULL, 0);

        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
        get_resource_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
        for (j = 0; j < ARRAY_SIZE(expected_results); ++j)
        {
            /* MoltenVK seems to have a bug with instanced draws when
             * the instance data step rate is zero: in some cases
             * StartVertexLocation seems to be ignored. */
            bug_if(is_mvk_device(context.device) && i == 0 && 8 <= j && j < 12)
            check_readback_data_uint(&rb.rb, &expected_results[j].box, tests[i].expected_colors[j], 1);
        }
        release_resource_readback(&rb);
        reset_command_list(command_list, context.allocator);
        transition_resource_state(command_list, render_target,
                D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
        get_resource_readback_with_command_list(render_target, 0, &rb, queue, command_list);
        for (j = 0; j < ARRAY_SIZE(expected_results); ++j)
            check_readback_data_uint(&rb.rb, &expected_results[j].box, expected_results[j].instance_id, 0);
        release_resource_readback(&rb);

        reset_command_list(command_list, context.allocator);
        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
        transition_resource_state(command_list, render_target,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

        ID3D12PipelineState_Release(context.pipeline_state);
        context.pipeline_state = NULL;

        vkd3d_test_pop_context();
    }

    ID3D12CommandSignature_Release(command_signature);
    ID3D12Resource_Release(argument_buffer);
    ID3D12Resource_Release(render_target);
    for (i = 0; i < ARRAY_SIZE(vb); ++i)
        ID3D12Resource_Release(vb[i]);
    destroy_test_context(&context);
}

static void test_vertex_id(void)
{
    static const DWORD vs_code[] =
    {
#if 0
        uint4 main(uint id : ID, uint instance_id : SV_InstanceID, uint vertex_id : SV_VertexID) : OUTPUT
        {
            return uint4(id, instance_id, vertex_id, 0);
        }
#endif
        0x43425844, 0x5625197b, 0x588ccf8f, 0x48694905, 0x961d19ca, 0x00000001, 0x00000170, 0x00000003,
        0x0000002c, 0x000000a4, 0x000000d4, 0x4e475349, 0x00000070, 0x00000003, 0x00000008, 0x00000050,
        0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x00000101, 0x00000053, 0x00000000, 0x00000008,
        0x00000001, 0x00000001, 0x00000101, 0x00000061, 0x00000000, 0x00000006, 0x00000001, 0x00000002,
        0x00000101, 0x53004449, 0x6e495f56, 0x6e617473, 0x44496563, 0x5f565300, 0x74726556, 0x44497865,
        0xababab00, 0x4e47534f, 0x00000028, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000,
        0x00000001, 0x00000000, 0x0000000f, 0x5054554f, 0xab005455, 0x52444853, 0x00000094, 0x00010040,
        0x00000025, 0x0300005f, 0x00101012, 0x00000000, 0x04000060, 0x00101012, 0x00000001, 0x00000008,
        0x04000060, 0x00101012, 0x00000002, 0x00000006, 0x03000065, 0x001020f2, 0x00000000, 0x05000036,
        0x00102012, 0x00000000, 0x0010100a, 0x00000000, 0x05000036, 0x00102022, 0x00000000, 0x0010100a,
        0x00000001, 0x05000036, 0x00102042, 0x00000000, 0x0010100a, 0x00000002, 0x05000036, 0x00102082,
        0x00000000, 0x00004001, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE vs = {vs_code, sizeof(vs_code)};
    D3D12_INPUT_ELEMENT_DESC layout_desc[] =
    {
        {"ID", 0, DXGI_FORMAT_R32_UINT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
    };
    static const D3D12_SO_DECLARATION_ENTRY so_declaration[] =
    {
        {0, "OUTPUT", 0, 0, 4, 0},
    };
    static const unsigned int strides[] = {16};
    static const unsigned int vertices[] =
    {
        0,
        1,
        2,

        3,
        4,
        5,

        6,
        7,
        8,

        5,
        6,
        7,

        0xa,
        0xb,
        0xc,
        0xd,

        0xe,
        0xf,
    };
    static const unsigned int indices[] =
    {
        6, 7, 8,

        0, 1, 2,

        0, 1, 2, 3,

        8, 9,
    };
    static const D3D12_DRAW_ARGUMENTS argument_data[] =
    {
        {4, 1, 12, 1},
        {2, 3, 16, 0},
    };
    static const D3D12_DRAW_INDEXED_ARGUMENTS indexed_argument_data[] =
    {
        {4, 1,  6, 12, 1},
        {2, 3, 10,  8, 0},
    };
    struct uvec4 expected_values[] =
    {
        {0, 0, 0},
        {1, 0, 1},
        {2, 0, 2},
        {0, 1, 0},
        {1, 1, 1},
        {2, 1, 2},

        {3, 0, 0},
        {4, 0, 1},
        {5, 0, 2},

        {6, 0, 6},
        {7, 0, 7},
        {8, 0, 8},
        {6, 1, 6},
        {7, 1, 7},
        {8, 1, 8},

        {5, 0, 0},
        {6, 0, 1},
        {7, 0, 2},

        {0xa, 0, 0},
        {0xb, 0, 1},
        {0xc, 0, 2},
        {0xd, 0, 3},

        {0xe, 0, 0},
        {0xf, 0, 1},
        {0xe, 1, 0},
        {0xf, 1, 1},
        {0xe, 2, 0},
        {0xf, 2, 1},

        {0xa, 0, 0},
        {0xb, 0, 1},
        {0xc, 0, 2},
        {0xd, 0, 3},

        {0xe, 0, 8},
        {0xf, 0, 9},
        {0xe, 1, 8},
        {0xf, 1, 9},
        {0xe, 2, 8},
        {0xf, 2, 9},
    };

    bool found_values[ARRAY_SIZE(expected_values)] = {0};
    bool used_values[ARRAY_SIZE(expected_values)] = {0};
    ID3D12Resource *args_buffer, *indexed_args_buffer;
    ID3D12CommandSignature *indexed_command_signature;
    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
    ID3D12Resource *counter_buffer, *so_buffer;
    ID3D12CommandSignature *command_signature;
    ID3D12GraphicsCommandList *command_list;
    D3D12_INPUT_LAYOUT_DESC input_layout;
    D3D12_STREAM_OUTPUT_BUFFER_VIEW sobv;
    struct d3d12_resource_readback rb;
    ID3D12Resource *upload_buffer;
    struct test_context_desc desc;
    D3D12_VERTEX_BUFFER_VIEW vbv;
    D3D12_INDEX_BUFFER_VIEW ibv;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Resource *vb, *ib;
    ID3D12Device *device;
    unsigned int count;
    unsigned int i, j;
    HRESULT hr;

    memset(&desc, 0, sizeof(desc));
    desc.root_signature_flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT
            | D3D12_ROOT_SIGNATURE_FLAG_ALLOW_STREAM_OUTPUT;
    desc.no_pipeline = true;
    if (!init_test_context(&context, &desc))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    input_layout.pInputElementDescs = layout_desc;
    input_layout.NumElements = ARRAY_SIZE(layout_desc);

    init_pipeline_state_desc(&pso_desc, context.root_signature, 0, &vs, NULL, &input_layout);
    memset(&pso_desc.PS, 0, sizeof(pso_desc.PS));
    pso_desc.StreamOutput.NumEntries = ARRAY_SIZE(so_declaration);
    pso_desc.StreamOutput.pSODeclaration = so_declaration;
    pso_desc.StreamOutput.pBufferStrides = strides;
    pso_desc.StreamOutput.NumStrides = ARRAY_SIZE(strides);
    pso_desc.StreamOutput.RasterizedStream = D3D12_SO_NO_RASTERIZED_STREAM;
    pso_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT;
    hr = ID3D12Device_CreateGraphicsPipelineState(device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
    if (hr == E_NOTIMPL)
    {
        skip("Stream output is not supported.\n");
        destroy_test_context(&context);
        return;
    }
    ok(hr == S_OK, "Failed to create graphics pipeline state, hr %#x.\n", hr);

    vb = create_upload_buffer(context.device, sizeof(vertices), vertices);
    vbv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vb);
    vbv.StrideInBytes = sizeof(*vertices);
    vbv.SizeInBytes = sizeof(vertices);

    ib = create_upload_buffer(context.device, sizeof(indices), indices);
    ibv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(ib);
    ibv.SizeInBytes = sizeof(indices);
    ibv.Format = DXGI_FORMAT_R32_UINT;

    args_buffer = create_upload_buffer(device, sizeof(argument_data), &argument_data);
    indexed_args_buffer = create_upload_buffer(device, sizeof(indexed_argument_data), &indexed_argument_data);

    command_signature = create_command_signature(device, D3D12_INDIRECT_ARGUMENT_TYPE_DRAW);
    indexed_command_signature = create_command_signature(device, D3D12_INDIRECT_ARGUMENT_TYPE_DRAW_INDEXED);

    count = 0;
    upload_buffer = create_upload_buffer(device, sizeof(count), &count);

    counter_buffer = create_default_buffer(device, 32,
            D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST);
    so_buffer = create_default_buffer(device, 1024,
            D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_STREAM_OUT);
    sobv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(so_buffer);
    sobv.SizeInBytes = 1024;
    sobv.BufferFilledSizeLocation = ID3D12Resource_GetGPUVirtualAddress(counter_buffer);

    ID3D12GraphicsCommandList_CopyBufferRegion(command_list, counter_buffer, 0,
            upload_buffer, 0, sizeof(count));

    transition_resource_state(command_list, counter_buffer,
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_STREAM_OUT);

    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_POINTLIST);
    ID3D12GraphicsCommandList_IASetIndexBuffer(command_list, &ibv);
    ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, 1, &vbv);

    ID3D12GraphicsCommandList_SOSetTargets(command_list, 0, 1, &sobv);

    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 2, 0, 0);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 3, 16);

    ID3D12GraphicsCommandList_DrawIndexedInstanced(command_list, 3, 2, 0, 0, 0);
    ID3D12GraphicsCommandList_DrawIndexedInstanced(command_list, 3, 1, 3, 9, 7);

    ID3D12GraphicsCommandList_ExecuteIndirect(command_list,
            command_signature, 1, args_buffer, 0, NULL, 0);
    ID3D12GraphicsCommandList_ExecuteIndirect(command_list,
            command_signature, 1, args_buffer, sizeof(*argument_data), NULL, 0);

    ID3D12GraphicsCommandList_ExecuteIndirect(command_list,
            indexed_command_signature, 1, indexed_args_buffer, 0, NULL, 0);
    ID3D12GraphicsCommandList_ExecuteIndirect(command_list,
            indexed_command_signature, 1, indexed_args_buffer, sizeof(*indexed_argument_data), NULL, 0);

    transition_resource_state(command_list, counter_buffer,
            D3D12_RESOURCE_STATE_STREAM_OUT, D3D12_RESOURCE_STATE_COPY_SOURCE);
    transition_resource_state(command_list, so_buffer,
            D3D12_RESOURCE_STATE_STREAM_OUT, D3D12_RESOURCE_STATE_COPY_SOURCE);

    get_buffer_readback_with_command_list(counter_buffer, DXGI_FORMAT_R32_UINT, &rb, queue, command_list);
    count = get_readback_uint(&rb.rb, 0, 0, 0);
    ok(count == ARRAY_SIZE(expected_values) * sizeof(struct vec4), "Got counter value %u, expected %uu.\n",
            count, (unsigned int)(ARRAY_SIZE(expected_values) * sizeof(struct vec4)));
    release_resource_readback(&rb);
    reset_command_list(command_list, context.allocator);
    count /= sizeof(struct vec4);

    count = min(count, ARRAY_SIZE(used_values));
    get_buffer_readback_with_command_list(so_buffer, DXGI_FORMAT_UNKNOWN, &rb, queue, command_list);
    for (i = 0; i < ARRAY_SIZE(expected_values); ++i)
    {
        for (j = 0; j < count; ++j)
        {
            if (!used_values[j] && compare_uvec4(get_readback_uvec4(&rb.rb, j, 0), &expected_values[i]))
            {
                found_values[i] = true;
                used_values[j] = true;
                break;
            }
        }
    }

    for (i = 0; i < count; ++i)
    {
        const struct uvec4 *v = get_readback_uvec4(&rb.rb, i, 0);
        ok(used_values[i], "Found unexpected value {0x%08x, 0x%08x, 0x%08x, 0x%08x}.\n", v->x, v->y, v->z, v->w);
    }
    release_resource_readback(&rb);

    for (i = 0; i < ARRAY_SIZE(expected_values); ++i)
    {
        ok(found_values[i], "Failed to find value {0x%08x, 0x%08x, 0x%08x, 0x%08x}.\n",
                expected_values[i].x, expected_values[i].y, expected_values[i].z, expected_values[i].w);
    }

    ID3D12CommandSignature_Release(command_signature);
    ID3D12CommandSignature_Release(indexed_command_signature);
    ID3D12Resource_Release(args_buffer);
    ID3D12Resource_Release(counter_buffer);
    ID3D12Resource_Release(ib);
    ID3D12Resource_Release(indexed_args_buffer);
    ID3D12Resource_Release(so_buffer);
    ID3D12Resource_Release(upload_buffer);
    ID3D12Resource_Release(vb);
    destroy_test_context(&context);
}

static void test_copy_texture(void)
{
    D3D12_TEXTURE_COPY_LOCATION src_location, dst_location;
    ID3D12Resource *src_texture, *dst_texture;
    ID3D12GraphicsCommandList *command_list;
    D3D12_SUBRESOURCE_DATA texture_data;
    struct d3d12_resource_readback rb;
    struct depth_stencil_resource ds;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12DescriptorHeap *heap;
    ID3D12CommandQueue *queue;
    ID3D12Device *device;
    unsigned int x, y, i;
    D3D12_BOX box;

    static const unsigned int clear_data[] =
    {
        0x00000000, 0x00000000, 0x00000000, 0x00000000,
        0x00000000, 0x00000000, 0x00000000, 0x00000000,
        0x00000000, 0x00000000, 0x00000000, 0x00000000,
        0x00000000, 0x00000000, 0x00000000, 0x00000000,
    };
    static const unsigned int bitmap_data[] =
    {
        0xff00ff00, 0xff00ff01, 0xff00ff02, 0xff00ff03,
        0xff00ff10, 0xff00ff12, 0xff00ff12, 0xff00ff13,
        0xff00ff20, 0xff00ff21, 0xff00ff22, 0xff00ff23,
        0xff00ff30, 0xff00ff31, 0xff00ff32, 0xff00ff33,
    };
    static const unsigned int result_data[] =
    {
        0x00000000, 0x00000000, 0x00000000, 0x00000000,
        0x00000000, 0xff00ff00, 0xff00ff01, 0x00000000,
        0x00000000, 0xff00ff10, 0xff00ff12, 0x00000000,
        0x00000000, 0x00000000, 0x00000000, 0x00000000,
    };
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    static const DWORD ps_code[] =
    {
#if 0
        Texture2D<float> t;

        float main(float4 position : SV_Position) : SV_Target
        {
            return t[int2(position.x, position.y)];
        }
#endif
        0x43425844, 0x0beace24, 0x5e10b05b, 0x742de364, 0xb2b65d2b, 0x00000001, 0x00000140, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000030f, 0x505f5653, 0x7469736f, 0x006e6f69,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x00000e01, 0x545f5653, 0x65677261, 0xabab0074, 0x52444853, 0x000000a4, 0x00000040,
        0x00000029, 0x04001858, 0x00107000, 0x00000000, 0x00005555, 0x04002064, 0x00101032, 0x00000000,
        0x00000001, 0x03000065, 0x00102012, 0x00000000, 0x02000068, 0x00000001, 0x0500001b, 0x00100032,
        0x00000000, 0x00101046, 0x00000000, 0x08000036, 0x001000c2, 0x00000000, 0x00004002, 0x00000000,
        0x00000000, 0x00000000, 0x00000000, 0x0700002d, 0x001000f2, 0x00000000, 0x00100e46, 0x00000000,
        0x00107e46, 0x00000000, 0x05000036, 0x00102012, 0x00000000, 0x0010000a, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};
    static const float depth_values[] = {0.0f, 0.5f, 0.7f, 1.0f};
    static const D3D12_RESOURCE_STATES resource_states[] =
    {
        D3D12_RESOURCE_STATE_COPY_SOURCE,
        D3D12_RESOURCE_STATE_GENERIC_READ,
    };

    memset(&desc, 0, sizeof(desc));
    desc.rt_format = DXGI_FORMAT_R32_FLOAT;
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    for (i = 0; i < ARRAY_SIZE(resource_states); ++i)
    {
        src_texture = create_default_texture(device, 4, 4, DXGI_FORMAT_R8G8B8A8_UNORM,
                0, D3D12_RESOURCE_STATE_COPY_DEST);
        texture_data.pData = bitmap_data;
        texture_data.RowPitch = 4 * sizeof(*bitmap_data);
        texture_data.SlicePitch = texture_data.RowPitch * 4;
        upload_texture_data(src_texture, &texture_data, 1, queue, command_list);
        reset_command_list(command_list, context.allocator);

        dst_texture = create_default_texture(device, 4, 4, DXGI_FORMAT_R8G8B8A8_UNORM,
                0, D3D12_RESOURCE_STATE_COPY_DEST);
        texture_data.pData = clear_data;
        texture_data.RowPitch = 4 * sizeof(*bitmap_data);
        texture_data.SlicePitch = texture_data.RowPitch * 4;
        upload_texture_data(dst_texture, &texture_data, 1, queue, command_list);
        reset_command_list(command_list, context.allocator);
        transition_resource_state(command_list, src_texture,
                D3D12_RESOURCE_STATE_COPY_DEST, resource_states[i]);

        src_location.pResource = src_texture;
        src_location.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
        src_location.SubresourceIndex = 0;
        dst_location.pResource = dst_texture;
        dst_location.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
        dst_location.SubresourceIndex = 0;
        set_box(&box, 0, 0, 0, 2, 2, 1);
        ID3D12GraphicsCommandList_CopyTextureRegion(command_list,
                &dst_location, 1, 1, 0, &src_location, &box);

        transition_resource_state(command_list, dst_texture,
                D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COPY_SOURCE);
        get_resource_readback_with_command_list(dst_texture, 0, &rb, queue, command_list);
        for (y = 0; y < 4; ++y)
        {
            for (x = 0; x < 4; ++x)
            {
                unsigned int color = get_readback_uint(&rb.rb, x, y, 0);
                unsigned int expected = result_data[y * 4 + x];

                ok(color == expected,
                        "Got unexpected color 0x%08x at (%u, %u), expected 0x%08x.\n",
                        color, x, y, expected);
            }
        }
        release_resource_readback(&rb);
        ID3D12Resource_Release(src_texture);
        ID3D12Resource_Release(dst_texture);
        reset_command_list(command_list, context.allocator);
    }

    context.root_signature = create_texture_root_signature(device,
            D3D12_SHADER_VISIBILITY_PIXEL, 0, 0);
    context.pipeline_state = create_pipeline_state(device,
            context.root_signature, context.render_target_desc.Format, NULL, &ps, NULL);

    heap = create_gpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1);

    for (i = 0; i < ARRAY_SIZE(depth_values); ++i)
    {
        init_depth_stencil(&ds, device, context.render_target_desc.Width,
                context.render_target_desc.Height, 1, 1, DXGI_FORMAT_D32_FLOAT,
                DXGI_FORMAT_D32_FLOAT, NULL);
        ID3D12GraphicsCommandList_ClearDepthStencilView(command_list, ds.dsv_handle,
                D3D12_CLEAR_FLAG_DEPTH, depth_values[i], 0, 0, NULL);
        transition_sub_resource_state(command_list, ds.texture, 0,
                D3D12_RESOURCE_STATE_DEPTH_WRITE, resource_states[i % ARRAY_SIZE(resource_states)]);

        dst_texture = create_default_texture(device, 32, 32, DXGI_FORMAT_R32_FLOAT,
                0, D3D12_RESOURCE_STATE_COPY_DEST);
        ID3D12Device_CreateShaderResourceView(device, dst_texture, NULL,
                ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(heap));

        src_location.pResource = ds.texture;
        src_location.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
        src_location.SubresourceIndex = 0;
        dst_location.pResource = dst_texture;
        dst_location.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
        dst_location.SubresourceIndex = 0;
        ID3D12GraphicsCommandList_CopyTextureRegion(command_list, &dst_location, 0, 0, 0,
                &src_location, NULL);
        transition_sub_resource_state(command_list, dst_texture, 0,
                D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);

        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
        ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
        ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);

        ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &heap);

        ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
        ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0,
                ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(heap));
        ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

        transition_sub_resource_state(command_list, context.render_target, 0,
                D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
        check_sub_resource_float(context.render_target, 0, queue, command_list, depth_values[i], 2);

        destroy_depth_stencil(&ds);
        ID3D12Resource_Release(dst_texture);

        reset_command_list(command_list, context.allocator);
        transition_sub_resource_state(command_list, context.render_target, 0,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
    }

    for (i = 0; i < ARRAY_SIZE(depth_values); ++i)
    {
        init_depth_stencil(&ds, device, context.render_target_desc.Width,
                context.render_target_desc.Height, 1, 1, DXGI_FORMAT_D32_FLOAT,
                DXGI_FORMAT_D32_FLOAT, NULL);
        ID3D12GraphicsCommandList_ClearDepthStencilView(command_list, ds.dsv_handle,
                D3D12_CLEAR_FLAG_DEPTH, depth_values[i], 0, 0, NULL);
        transition_sub_resource_state(command_list, ds.texture, 0,
                D3D12_RESOURCE_STATE_DEPTH_WRITE, resource_states[i % ARRAY_SIZE(resource_states)]);

        dst_texture = create_default_texture(device, 32, 32, DXGI_FORMAT_R32_FLOAT,
                0, D3D12_RESOURCE_STATE_COPY_DEST);
        ID3D12Device_CreateShaderResourceView(device, dst_texture, NULL,
                ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(heap));

        ID3D12GraphicsCommandList_CopyResource(command_list, dst_texture, ds.texture);
        transition_sub_resource_state(command_list, dst_texture, 0,
                D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);

        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
        ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
        ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);

        ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &heap);

        ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
        ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0,
                ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(heap));
        ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

        transition_sub_resource_state(command_list, context.render_target, 0,
                D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
        check_sub_resource_float(context.render_target, 0, queue, command_list, depth_values[i], 2);

        destroy_depth_stencil(&ds);
        ID3D12Resource_Release(dst_texture);

        reset_command_list(command_list, context.allocator);
        transition_sub_resource_state(command_list, context.render_target, 0,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
    }

    ID3D12DescriptorHeap_Release(heap);
    destroy_test_context(&context);
}

static void test_copy_texture_buffer(void)
{
    D3D12_TEXTURE_COPY_LOCATION src_location, dst_location;
    ID3D12GraphicsCommandList *command_list;
    D3D12_SUBRESOURCE_DATA texture_data;
    struct d3d12_resource_readback rb;
    ID3D12Resource *dst_buffers[4];
    struct test_context_desc desc;
    struct test_context context;
    ID3D12Resource *src_texture;
    unsigned int got, expected;
    ID3D12CommandQueue *queue;
    ID3D12Device *device;
    unsigned int x, y;
    unsigned int *ptr;
    unsigned int i;
    D3D12_BOX box;

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

    ptr = calloc(64 * 32, sizeof(*ptr));
    ok(ptr, "Failed to allocate memory.\n");

    for (i = 0; i < 64 * 32; ++i)
        ptr[i] = i;

    src_texture = create_default_texture(device,
            64, 32, DXGI_FORMAT_R32_UINT, 0, D3D12_RESOURCE_STATE_COPY_DEST);
    texture_data.pData = ptr;
    texture_data.RowPitch = 64 * sizeof(*ptr);
    texture_data.SlicePitch = texture_data.RowPitch * 32;
    upload_texture_data(src_texture, &texture_data, 1, queue, command_list);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, src_texture,
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COPY_SOURCE);

    free(ptr);

    for (i = 0; i < ARRAY_SIZE(dst_buffers); ++i)
    {
        dst_buffers[i] = create_default_buffer(device,
                64 * 32 * sizeof(*ptr), 0, D3D12_RESOURCE_STATE_COPY_DEST);
    }

    dst_location.pResource = dst_buffers[0];
    dst_location.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
    dst_location.PlacedFootprint.Offset = 0;
    dst_location.PlacedFootprint.Footprint.Format = DXGI_FORMAT_R32_UINT;
    dst_location.PlacedFootprint.Footprint.Width = 64;
    dst_location.PlacedFootprint.Footprint.Height = 32;
    dst_location.PlacedFootprint.Footprint.Depth = 1;
    dst_location.PlacedFootprint.Footprint.RowPitch = 64 * sizeof(*ptr);

    src_location.pResource = src_texture;
    src_location.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
    src_location.SubresourceIndex = 0;

    ID3D12GraphicsCommandList_CopyTextureRegion(command_list,
            &dst_location, 0, 0, 0, &src_location, NULL);

    dst_location.pResource = dst_buffers[1];
    for (y = 0; y < 32; ++y)
    {
        set_box(&box, 0, y, 0, 64, y + 1, 1);
        ID3D12GraphicsCommandList_CopyTextureRegion(command_list,
                &dst_location, 0, 31 - y, 0, &src_location, &box);
    }

    dst_location.pResource = dst_buffers[2];
    for (x = 0; x < 64; ++x)
    {
        set_box(&box, x, 0, 0, x + 1, 32, 1);
        ID3D12GraphicsCommandList_CopyTextureRegion(command_list,
                &dst_location, 63 - x, 0, 0, &src_location, &box);
    }

    dst_location.pResource = dst_buffers[3];
    set_box(&box, 0, 0, 0, 32, 32, 1);
    ID3D12GraphicsCommandList_CopyTextureRegion(command_list,
            &dst_location, 0, 0, 0, &src_location, &box);
    ID3D12GraphicsCommandList_CopyTextureRegion(command_list,
            &dst_location, 32, 0, 0, &src_location, &box);

    /* empty box */
    set_box(&box, 128, 0, 0, 32, 32, 1);
    ID3D12GraphicsCommandList_CopyTextureRegion(command_list,
            &dst_location, 0, 0, 0, &src_location, &box);

    for (i = 0; i < ARRAY_SIZE(dst_buffers); ++i)
    {
        transition_resource_state(command_list, dst_buffers[i],
                D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COPY_SOURCE);
    }

    got = expected = 0;
    get_buffer_readback_with_command_list(dst_buffers[0], DXGI_FORMAT_R32_UINT, &rb, queue, command_list);
    for (i = 0; i < 64 * 32; ++i)
    {
        got = get_readback_uint(&rb.rb, i, 0, 0);
        expected = i;

        if (got != expected)
            break;
    }
    release_resource_readback(&rb);
    ok(got == expected, "Got unexpected value 0x%08x at %u, expected 0x%08x.\n", got, i, expected);

    reset_command_list(command_list, context.allocator);
    got = expected = 0;
    get_buffer_readback_with_command_list(dst_buffers[1], DXGI_FORMAT_R32_UINT, &rb, queue, command_list);
    for (y = 0; y < 32; ++y)
    {
        for (x = 0; x < 64; ++x)
        {
            got = get_readback_uint(&rb.rb, 64 * y + x, 0, 0);
            expected = 64 * (31 - y) + x;

            if (got != expected)
                break;
        }
        if (got != expected)
            break;
    }
    release_resource_readback(&rb);
    ok(got == expected, "Got unexpected value 0x%08x at (%u, %u), expected 0x%08x.\n", got, x, y, expected);

    reset_command_list(command_list, context.allocator);
    got = expected = 0;
    get_buffer_readback_with_command_list(dst_buffers[2], DXGI_FORMAT_R32_UINT, &rb, queue, command_list);
    for (y = 0; y < 32; ++y)
    {
        for (x = 0; x < 64; ++x)
        {
            got = get_readback_uint(&rb.rb, 64 * y + x, 0, 0);
            expected = 64 * y + 63 - x;

            if (got != expected)
                break;
        }
        if (got != expected)
            break;
    }
    release_resource_readback(&rb);
    ok(got == expected, "Got unexpected value 0x%08x at (%u, %u), expected 0x%08x.\n", got, x, y, expected);

    reset_command_list(command_list, context.allocator);
    got = expected = 0;
    get_buffer_readback_with_command_list(dst_buffers[3], DXGI_FORMAT_R32_UINT, &rb, queue, command_list);
    for (y = 0; y < 32; ++y)
    {
        for (x = 0; x < 64; ++x)
        {
            got = get_readback_uint(&rb.rb, 64 * y + x, 0, 0);
            expected = 64 * y + x % 32;

            if (got != expected)
                break;
        }
        if (got != expected)
            break;
    }
    release_resource_readback(&rb);
    ok(got == expected, "Got unexpected value 0x%08x at (%u, %u), expected 0x%08x.\n", got, x, y, expected);

    ID3D12Resource_Release(src_texture);
    for (i = 0; i < ARRAY_SIZE(dst_buffers); ++i)
        ID3D12Resource_Release(dst_buffers[i]);
    destroy_test_context(&context);
}

static void test_copy_buffer_texture(void)
{
    D3D12_TEXTURE_COPY_LOCATION src_location, dst_location;
    ID3D12GraphicsCommandList *command_list;
    struct d3d12_resource_readback rb;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12Resource *zero_buffer;
    ID3D12Resource *dst_texture;
    ID3D12Resource *src_buffer;
    unsigned int got, expected;
    ID3D12CommandQueue *queue;
    unsigned int buffer_size;
    ID3D12Device *device;
    unsigned int x, y, z;
    unsigned int *ptr;
    unsigned int i;
    D3D12_BOX box;
    HRESULT hr;

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

    buffer_size = 128 * 100 * 64;

    zero_buffer = create_upload_buffer(device, buffer_size * sizeof(*ptr) + D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT, NULL);
    hr = ID3D12Resource_Map(zero_buffer, 0, NULL, (void **)&ptr);
    ok(hr == S_OK, "Failed to map buffer, hr %#x.\n", hr);
    memset(ptr, 0, buffer_size * sizeof(*ptr) + D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT);
    for (i = 0; i < D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT / sizeof(*ptr); ++i)
        ptr[i] = 0xdeadbeef;
    ID3D12Resource_Unmap(zero_buffer, 0, NULL);

    src_buffer = create_upload_buffer(device, buffer_size * sizeof(*ptr), NULL);
    hr = ID3D12Resource_Map(src_buffer, 0, NULL, (void **)&ptr);
    ok(hr == S_OK, "Failed to map buffer, hr %#x.\n", hr);
    for (z = 0; z < 64; ++z)
    {
        for (y = 0; y < 100; ++y)
        {
            for (x = 0; x < 128; ++x)
            {
                ptr[z * 128 * 100 + y * 128 + x] = (z + 1) << 16 | (y + 1) << 8 | (x + 1);
            }
        }
    }
    ID3D12Resource_Unmap(src_buffer, 0, NULL);

    dst_texture = create_default_texture3d(device, 128, 100, 64, 2,
            DXGI_FORMAT_R32_UINT, 0, D3D12_RESOURCE_STATE_COPY_DEST);

    dst_location.pResource = dst_texture;
    dst_location.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
    dst_location.SubresourceIndex = 0;

    src_location.pResource = zero_buffer;
    src_location.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
    src_location.PlacedFootprint.Offset = D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT;
    src_location.PlacedFootprint.Footprint.Format = DXGI_FORMAT_R32_UINT;
    src_location.PlacedFootprint.Footprint.Width = 128;
    src_location.PlacedFootprint.Footprint.Height = 100;
    src_location.PlacedFootprint.Footprint.Depth = 64;
    src_location.PlacedFootprint.Footprint.RowPitch = 128 * sizeof(*ptr);

    /* fill with 0 */
    ID3D12GraphicsCommandList_CopyTextureRegion(command_list,
            &dst_location, 0, 0, 0, &src_location, NULL);

    src_location.pResource = src_buffer;
    src_location.PlacedFootprint.Offset = 0;

    /* copy region 1 */
    set_box(&box, 64, 16, 8, 128, 100, 64);
    ID3D12GraphicsCommandList_CopyTextureRegion(command_list,
            &dst_location, 64, 16, 8, &src_location, &box);

    /* empty boxes */
    for (z = 0; z < 2; ++z)
    {
        for (y = 0; y < 4; ++y)
        {
            for (x = 0; x < 8; ++x)
            {
                set_box(&box, x, y, z, x, y, z);
                ID3D12GraphicsCommandList_CopyTextureRegion(command_list,
                        &dst_location, 0, 0, 0, &src_location, &box);
                ID3D12GraphicsCommandList_CopyTextureRegion(command_list,
                        &dst_location, x, y, z, &src_location, &box);
            }
        }
    }

    /* copy region 2 */
    set_box(&box, 0, 0, 0, 4, 4, 4);
    ID3D12GraphicsCommandList_CopyTextureRegion(command_list,
            &dst_location, 2, 2, 2, &src_location, &box);

    /* fill sub-resource 1 */
    dst_location.SubresourceIndex = 1;
    set_box(&box, 0, 0, 0, 64, 50, 32);
    ID3D12GraphicsCommandList_CopyTextureRegion(command_list,
            &dst_location, 0, 0, 0, &src_location, &box);

    transition_resource_state(command_list, dst_texture,
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COPY_SOURCE);

    got = expected = 0;
    get_resource_readback_with_command_list(dst_texture, 0, &rb, queue, command_list);
    for (z = 0; z < 64; ++z)
    {
        for (y = 0; y < 100; ++y)
        {
            for (x = 0; x < 128; ++x)
            {
                got = get_readback_uint(&rb.rb, x, y, z);

                if (2 <= x && x < 6 && 2 <= y && y < 6 && 2 <= z && z < 6)
                    expected = (z - 1) << 16 | (y - 1) << 8 | (x - 1); /* copy region 1 */
                else if (64 <= x && 16 <= y && 8 <= z)
                    expected = (z + 1) << 16 | (y + 1) << 8 | (x + 1); /* copy region 2 */
                else
                    expected = 0;

                if (got != expected)
                    break;
            }
            if (got != expected)
                break;
        }
        if (got != expected)
            break;
    }
    release_resource_readback(&rb);
    ok(got == expected,
            "Got unexpected value 0x%08x at (%u, %u, %u), expected 0x%08x.\n",
            got, x, y, z, expected);

    reset_command_list(command_list, context.allocator);
    got = expected = 0;
    get_resource_readback_with_command_list(dst_texture, 1, &rb, queue, command_list);
    for (z = 0; z < 32; ++z)
    {
        for (y = 0; y < 50; ++y)
        {
            for (x = 0; x < 64; ++x)
            {
                got = get_readback_uint(&rb.rb, x, y, z);
                expected = (z + 1) << 16 | (y + 1) << 8 | (x + 1);

                if (got != expected)
                    break;
            }
            if (got != expected)
                break;
        }
        if (got != expected)
            break;
    }
    release_resource_readback(&rb);
    ok(got == expected,
            "Got unexpected value 0x%08x at (%u, %u, %u), expected 0x%08x.\n",
            got, x, y, z, expected);

    ID3D12Resource_Release(dst_texture);
    ID3D12Resource_Release(src_buffer);
    ID3D12Resource_Release(zero_buffer);
    destroy_test_context(&context);
}

static void test_copy_block_compressed_texture(void)
{
    D3D12_TEXTURE_COPY_LOCATION src_location, dst_location;
    ID3D12Resource *dst_buffer, *src_buffer;
    ID3D12GraphicsCommandList *command_list;
    struct d3d12_resource_readback rb;
    struct test_context_desc desc;
    unsigned int x, y, block_id;
    struct test_context context;
    struct uvec4 got, expected;
    ID3D12CommandQueue *queue;
    ID3D12Resource *texture;
    ID3D12Device *device;
    unsigned int *ptr;
    D3D12_BOX box;
    HRESULT hr;

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

    dst_buffer = create_default_buffer(device, 4096, 0, D3D12_RESOURCE_STATE_COPY_DEST);
    src_buffer = create_upload_buffer(device, 4096, NULL);
    hr = ID3D12Resource_Map(src_buffer, 0, NULL, (void **)&ptr);
    ok(hr == S_OK, "Failed to map buffer, hr %#x.\n", hr);
    for (x = 0; x < 4096 / format_size(DXGI_FORMAT_BC2_UNORM); ++x)
    {
        block_id = x << 8;
        *ptr++ = block_id | 0;
        *ptr++ = block_id | 1;
        *ptr++ = block_id | 2;
        *ptr++ = block_id | 3;
    }
    ID3D12Resource_Unmap(src_buffer, 0, NULL);

    texture = create_default_texture2d(device, 8, 8, 1, 4, DXGI_FORMAT_BC2_UNORM,
            D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST);

    /* copy from buffer to texture */
    dst_location.pResource = texture;
    dst_location.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
    dst_location.SubresourceIndex = 0;

    src_location.pResource = src_buffer;
    src_location.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
    src_location.PlacedFootprint.Offset = 0;
    src_location.PlacedFootprint.Footprint.Format = DXGI_FORMAT_BC2_UNORM;
    src_location.PlacedFootprint.Footprint.Width = 32;
    src_location.PlacedFootprint.Footprint.Height = 32;
    src_location.PlacedFootprint.Footprint.Depth = 1;
    src_location.PlacedFootprint.Footprint.RowPitch
            = 32 / format_block_width(DXGI_FORMAT_BC2_UNORM) * format_size(DXGI_FORMAT_BC2_UNORM);
    src_location.PlacedFootprint.Footprint.RowPitch
            = align(src_location.PlacedFootprint.Footprint.RowPitch, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT);

    set_box(&box, 4, 4, 0, 8, 8, 1);
    ID3D12GraphicsCommandList_CopyTextureRegion(command_list,
            &dst_location, 0, 0, 0, &src_location, &box);
    set_box(&box, 28, 0, 0, 32, 4, 1);
    ID3D12GraphicsCommandList_CopyTextureRegion(command_list,
            &dst_location, 4, 0, 0, &src_location, &box);
    set_box(&box, 0, 24, 0, 4, 28, 1);
    ID3D12GraphicsCommandList_CopyTextureRegion(command_list,
            &dst_location, 0, 4, 0, &src_location, &box);
    set_box(&box, 16, 16, 0, 20, 20, 1);
    ID3D12GraphicsCommandList_CopyTextureRegion(command_list,
            &dst_location, 4, 4, 0, &src_location, &box);

    /* miplevels smaller than 4x4 */
    dst_location.SubresourceIndex = 2;
    set_box(&box, 4, 0, 0, 8, 4, 1);
    ID3D12GraphicsCommandList_CopyTextureRegion(command_list,
            &dst_location, 0, 0, 0, &src_location, &box);
    dst_location.SubresourceIndex = 3;
    set_box(&box, 8, 0, 0, 12, 4, 1);
    ID3D12GraphicsCommandList_CopyTextureRegion(command_list,
            &dst_location, 0, 0, 0, &src_location, &box);

    transition_resource_state(command_list, texture,
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COPY_SOURCE);

    /* copy from texture to buffer */
    dst_location.pResource = dst_buffer;
    dst_location.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
    dst_location.PlacedFootprint.Offset = 0;
    dst_location.PlacedFootprint.Footprint.Format = DXGI_FORMAT_BC2_UNORM;
    dst_location.PlacedFootprint.Footprint.Width = 8;
    dst_location.PlacedFootprint.Footprint.Height = 24;
    dst_location.PlacedFootprint.Footprint.Depth = 1;
    dst_location.PlacedFootprint.Footprint.RowPitch
            = 8 / format_block_width(DXGI_FORMAT_BC2_UNORM) * format_size(DXGI_FORMAT_BC2_UNORM);
    dst_location.PlacedFootprint.Footprint.RowPitch
            = align(dst_location.PlacedFootprint.Footprint.RowPitch, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT);

    src_location.pResource = texture;
    src_location.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
    src_location.SubresourceIndex = 0;

    ID3D12GraphicsCommandList_CopyTextureRegion(command_list,
            &dst_location, 0, 0, 0, &src_location, NULL);
    ID3D12GraphicsCommandList_CopyTextureRegion(command_list,
            &dst_location, 0, 8, 0, &src_location, NULL);
    set_box(&box, 0, 0, 0, 8, 8, 1);
    ID3D12GraphicsCommandList_CopyTextureRegion(command_list,
            &dst_location, 0, 16, 0, &src_location, &box);

    transition_resource_state(command_list, dst_buffer,
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COPY_SOURCE);

    get_resource_readback_with_command_list(texture, 0, &rb, queue, command_list);
    for (y = 0; y < 8 / format_block_height(DXGI_FORMAT_BC2_UNORM); ++y)
    {
        for (x = 0; x < 8 / format_block_width(DXGI_FORMAT_BC2_UNORM); ++x)
        {
            if (x == 0 && y == 0)
                block_id = 33;
            else if (x == 1 && y == 0)
                block_id = 7;
            else if (x == 0 && y == 1)
                block_id = 192;
            else
                block_id = 132;

            expected.x = block_id << 8 | 0;
            expected.y = block_id << 8 | 1;
            expected.z = block_id << 8 | 2;
            expected.w = block_id << 8 | 3;
            got = *get_readback_uvec4(&rb.rb, x, y);

            if (!compare_uvec4(&got, &expected))
                break;
        }
        if (!compare_uvec4(&got, &expected))
            break;
    }
    release_resource_readback(&rb);
    ok(compare_uvec4(&got, &expected),
            "Got {0x%08x, 0x%08x, 0x%08x, 0x%08x} at (%u, %u), expected {0x%08x, 0x%08x, 0x%08x, 0x%08x}.\n",
            got.x, got.y, got.z, got.w, x, y, expected.x, expected.y, expected.z, expected.w);

    reset_command_list(command_list, context.allocator);
    get_resource_readback_with_command_list(texture, 2, &rb, queue, command_list);
    block_id = 1;
    expected.x = block_id << 8 | 0;
    expected.y = block_id << 8 | 1;
    expected.z = block_id << 8 | 2;
    expected.w = block_id << 8 | 3;
    got = *get_readback_uvec4(&rb.rb, 0, 0);
    release_resource_readback(&rb);
    ok(compare_uvec4(&got, &expected),
            "Got {0x%08x, 0x%08x, 0x%08x, 0x%08x}, expected {0x%08x, 0x%08x, 0x%08x, 0x%08x}.\n",
            got.x, got.y, got.z, got.w, expected.x, expected.y, expected.z, expected.w);

    reset_command_list(command_list, context.allocator);
    get_resource_readback_with_command_list(texture, 3, &rb, queue, command_list);
    block_id = 2;
    expected.x = block_id << 8 | 0;
    expected.y = block_id << 8 | 1;
    expected.z = block_id << 8 | 2;
    expected.w = block_id << 8 | 3;
    got = *get_readback_uvec4(&rb.rb, 0, 0);
    release_resource_readback(&rb);
    ok(compare_uvec4(&got, &expected),
            "Got {0x%08x, 0x%08x, 0x%08x, 0x%08x}, expected {0x%08x, 0x%08x, 0x%08x, 0x%08x}.\n",
            got.x, got.y, got.z, got.w, expected.x, expected.y, expected.z, expected.w);

    reset_command_list(command_list, context.allocator);
    get_buffer_readback_with_command_list(dst_buffer, DXGI_FORMAT_R32_UINT, &rb, queue, command_list);
    for (y = 0; y < 24 / format_block_height(DXGI_FORMAT_BC2_UNORM); ++y)
    {
        unsigned int row_offset = dst_location.PlacedFootprint.Footprint.RowPitch / sizeof(got) * y;

        for (x = 0; x < 4 / format_block_width(DXGI_FORMAT_BC2_UNORM); ++x)
        {
            if (x == 0 && y % 2 == 0)
                block_id = 33;
            else if (x == 1 && y % 2 == 0)
                block_id = 7;
            else if (x == 0 && y % 2 == 1)
                block_id = 192;
            else
                block_id = 132;

            expected.x = block_id << 8 | 0;
            expected.y = block_id << 8 | 1;
            expected.z = block_id << 8 | 2;
            expected.w = block_id << 8 | 3;
            got = *get_readback_uvec4(&rb.rb, x + row_offset, 0);

            if (!compare_uvec4(&got, &expected))
                break;
        }
        if (!compare_uvec4(&got, &expected))
            break;
    }
    release_resource_readback(&rb);
    ok(compare_uvec4(&got, &expected),
            "Got {0x%08x, 0x%08x, 0x%08x, 0x%08x} at (%u, %u), expected {0x%08x, 0x%08x, 0x%08x, 0x%08x}.\n",
            got.x, got.y, got.z, got.w, x, y, expected.x, expected.y, expected.z, expected.w);

    ID3D12Resource_Release(texture);
    ID3D12Resource_Release(src_buffer);
    ID3D12Resource_Release(dst_buffer);
    destroy_test_context(&context);
}

static void test_separate_bindings(void)
{
    ID3D12Resource *cs_raw_buffer, *cs_raw_uav_buffer;
    ID3D12Resource *ps_raw_buffer, *ps_raw_uav_buffer;
    ID3D12Resource *cs_textures[2], *ps_textures[2];
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    D3D12_DESCRIPTOR_RANGE descriptor_ranges[2];
    ID3D12GraphicsCommandList *command_list;
    D3D12_ROOT_PARAMETER root_parameters[4];
    struct d3d12_resource_readback rb;
    ID3D12PipelineState *compute_pso;
    ID3D12Resource *cs_cb, *ps_cb;
    struct test_context_desc desc;
    D3D12_SUBRESOURCE_DATA data;
    struct test_context context;
    ID3D12DescriptorHeap *heap;
    ID3D12CommandQueue *queue;
    ID3D12Device *device;
    unsigned int i;
    HRESULT hr;

    static const DWORD cs_code[] =
    {
#if 0
        ByteAddressBuffer t0;

        RWByteAddressBuffer u1 : register(u1);

        cbuffer cb0
        {
            float4 cb0;
        };

        Texture2D t1;
        RWTexture2D<float> u2 : register(u2);

        [numthreads(1, 1, 1)]
        void main()
        {
            uint ret = 0xffffffff;

            if (t0.Load(0) != 2)
                ret = 0;
            if (any(cb0 != float4(4, 8, 16, 32)))
                ret = 0;
            if (any(t1.Load(0) != float4(4, 8, 16, 32)))
                ret = 0;
            if (u2[(int2)0] != 4)
                ret = 0;

            u1.Store(0, ret);
        }
#endif
        0x43425844, 0x5ef0e316, 0x8a886806, 0x06279aa8, 0x10936fa5, 0x00000001, 0x000002bc, 0x00000003,
        0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x00000268, 0x00050050, 0x0000009a, 0x0100086a,
        0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x030000a1, 0x00107000, 0x00000000, 0x04001858,
        0x00107000, 0x00000001, 0x00005555, 0x0300009d, 0x0011e000, 0x00000001, 0x0400189c, 0x0011e000,
        0x00000002, 0x00005555, 0x02000068, 0x00000002, 0x0400009b, 0x00000001, 0x00000001, 0x00000001,
        0x8c00002d, 0x800000c2, 0x00155543, 0x001000f2, 0x00000000, 0x00004002, 0x00000000, 0x00000000,
        0x00000000, 0x00000000, 0x00107e46, 0x00000001, 0x0a000039, 0x001000f2, 0x00000000, 0x00100e46,
        0x00000000, 0x00004002, 0x40800000, 0x41000000, 0x41800000, 0x42000000, 0x0700003c, 0x00100032,
        0x00000000, 0x00100ae6, 0x00000000, 0x00100046, 0x00000000, 0x0700003c, 0x00100012, 0x00000000,
        0x0010001a, 0x00000000, 0x0010000a, 0x00000000, 0x0b000039, 0x001000f2, 0x00000001, 0x00208e46,
        0x00000000, 0x00000000, 0x00004002, 0x40800000, 0x41000000, 0x41800000, 0x42000000, 0x0700003c,
        0x00100062, 0x00000000, 0x00100ba6, 0x00000001, 0x00100106, 0x00000001, 0x0700003c, 0x00100022,
        0x00000000, 0x0010002a, 0x00000000, 0x0010001a, 0x00000000, 0x0700003c, 0x00100012, 0x00000000,
        0x0010000a, 0x00000000, 0x0010001a, 0x00000000, 0x8c0000a3, 0x800000c2, 0x00155543, 0x00100022,
        0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0011ee16, 0x00000002,
        0x07000039, 0x00100022, 0x00000000, 0x0010001a, 0x00000000, 0x00004001, 0x40800000, 0x0700003c,
        0x00100012, 0x00000000, 0x0010001a, 0x00000000, 0x0010000a, 0x00000000, 0x890000a5, 0x800002c2,
        0x00199983, 0x00100022, 0x00000000, 0x00004001, 0x00000000, 0x00107006, 0x00000000, 0x07000020,
        0x00100022, 0x00000000, 0x0010001a, 0x00000000, 0x00004001, 0x00000002, 0x09000037, 0x00100012,
        0x00000000, 0x0010000a, 0x00000000, 0x00004001, 0x00000000, 0x0010001a, 0x00000000, 0x070000a6,
        0x0011e012, 0x00000001, 0x00004001, 0x00000000, 0x0010000a, 0x00000000, 0x0100003e,
    };
    static const DWORD ps_code[] =
    {
#if 0
        ByteAddressBuffer t0;

        RWByteAddressBuffer u1 : register(u1);

        cbuffer cb0
        {
            float4 cb0;
        };

        Texture2D t1;
        RWTexture2D<float> u2 : register(u2);

        float4 main() : SV_Target0
        {
            bool ret = true;

            if (t0.Load(0) != 1)
                ret = false;
            if (u1.Load(0) != 2)
                ret = false;
            if (any(cb0 != float4(1, 2, 3, 4)))
                ret = false;
            if (any(t1.Load(0) != float4(1, 2, 3, 4)))
                ret = false;
            if (u2[(int2)0] != 1)
                ret = false;

            return ret ? float4(0, 1, 0, 1) : float4(1, 0, 0, 1);
        }
#endif
        0x43425844, 0xb5db404c, 0xd1dd05ca, 0xf5c1284d, 0x58d71b13, 0x00000001, 0x00000358, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x000002e0, 0x00000050, 0x000000b8,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x030000a1, 0x00107000, 0x00000000,
        0x04001858, 0x00107000, 0x00000001, 0x00005555, 0x0300009d, 0x0011e000, 0x00000001, 0x0400189c,
        0x0011e000, 0x00000002, 0x00005555, 0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000002,
        0x0b000039, 0x001000f2, 0x00000000, 0x00208e46, 0x00000000, 0x00000000, 0x00004002, 0x3f800000,
        0x40000000, 0x40400000, 0x40800000, 0x0700003c, 0x00100032, 0x00000000, 0x00100ae6, 0x00000000,
        0x00100046, 0x00000000, 0x0700003c, 0x00100012, 0x00000000, 0x0010001a, 0x00000000, 0x0010000a,
        0x00000000, 0x890000a5, 0x800002c2, 0x00199983, 0x00100022, 0x00000000, 0x00004001, 0x00000000,
        0x0011e006, 0x00000001, 0x07000027, 0x00100022, 0x00000000, 0x0010001a, 0x00000000, 0x00004001,
        0x00000002, 0x0700003c, 0x00100012, 0x00000000, 0x0010000a, 0x00000000, 0x0010001a, 0x00000000,
        0x8c00002d, 0x800000c2, 0x00155543, 0x001000f2, 0x00000001, 0x00004002, 0x00000000, 0x00000000,
        0x00000000, 0x00000000, 0x00107e46, 0x00000001, 0x0a000039, 0x001000f2, 0x00000001, 0x00100e46,
        0x00000001, 0x00004002, 0x3f800000, 0x40000000, 0x40400000, 0x40800000, 0x0700003c, 0x00100062,
        0x00000000, 0x00100ba6, 0x00000001, 0x00100106, 0x00000001, 0x0700003c, 0x00100022, 0x00000000,
        0x0010002a, 0x00000000, 0x0010001a, 0x00000000, 0x0700003c, 0x00100012, 0x00000000, 0x0010001a,
        0x00000000, 0x0010000a, 0x00000000, 0x8c0000a3, 0x800000c2, 0x00155543, 0x00100022, 0x00000000,
        0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0011ee16, 0x00000002, 0x07000039,
        0x00100022, 0x00000000, 0x0010001a, 0x00000000, 0x00004001, 0x3f800000, 0x0700003c, 0x00100012,
        0x00000000, 0x0010001a, 0x00000000, 0x0010000a, 0x00000000, 0x890000a5, 0x800002c2, 0x00199983,
        0x00100022, 0x00000000, 0x00004001, 0x00000000, 0x00107006, 0x00000000, 0x07000020, 0x00100022,
        0x00000000, 0x0010001a, 0x00000000, 0x00004001, 0x00000001, 0x09000037, 0x00100012, 0x00000000,
        0x0010000a, 0x00000000, 0x00004001, 0x00000000, 0x0010001a, 0x00000000, 0x0f000037, 0x001020f2,
        0x00000000, 0x00100006, 0x00000000, 0x00004002, 0x00000000, 0x3f800000, 0x00000000, 0x3f800000,
        0x00004002, 0x3f800000, 0x00000000, 0x00000000, 0x3f800000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};
    static const struct vec4 cs_data = {4.0f, 8.0f, 16.0f, 32.0f};
    static const struct vec4 ps_data = {1.0f, 2.0f, 3.0f, 4.0f};
    static const float cs_texture_data = 4.0f;
    static const float ps_texture_data = 1.0f;
    static const uint32_t cs_raw_data = 2;
    static const uint32_t ps_raw_data = 1;
    static const uint32_t ps_raw_uav_data = 2;
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};

    memset(&desc, 0, sizeof(desc));
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
    root_parameters[0].Descriptor.ShaderRegister = 0;
    root_parameters[0].Descriptor.RegisterSpace = 0;
    root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    root_parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_SRV;
    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_UAV;
    root_parameters[2].Descriptor.ShaderRegister = 1;
    root_parameters[2].Descriptor.RegisterSpace = 0;
    root_parameters[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    descriptor_ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
    descriptor_ranges[0].NumDescriptors = 1;
    descriptor_ranges[0].BaseShaderRegister = 1;
    descriptor_ranges[0].RegisterSpace = 0;
    descriptor_ranges[0].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
    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 = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
    root_parameters[3].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameters[3].DescriptorTable.NumDescriptorRanges = 2;
    root_parameters[3].DescriptorTable.pDescriptorRanges = descriptor_ranges;
    root_parameters[3].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    memset(&root_signature_desc, 0, sizeof(root_signature_desc));
    root_signature_desc.NumParameters = 4;
    root_signature_desc.pParameters = root_parameters;
    hr = create_root_signature(device, &root_signature_desc, &context.root_signature);
    ok(SUCCEEDED(hr), "Failed to create root signature, hr %#x.\n", hr);

    compute_pso = create_compute_pipeline_state(device, context.root_signature,
            shader_bytecode(cs_code, sizeof(cs_code)));

    context.pipeline_state = create_pipeline_state(device,
            context.root_signature, context.render_target_desc.Format,
            NULL, &ps, NULL);

    heap = create_gpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 20);

    cs_cb = create_upload_buffer(device, sizeof(cs_data), &cs_data);
    ps_cb = create_upload_buffer(device, sizeof(ps_data), &ps_data);

    cs_raw_buffer = create_upload_buffer(device, sizeof(cs_raw_data), &cs_raw_data);
    ps_raw_buffer = create_upload_buffer(device, sizeof(ps_raw_data), &ps_raw_data);

    cs_raw_uav_buffer = create_default_buffer(device, sizeof(uint32_t),
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
    ps_raw_uav_buffer = create_default_buffer(device, sizeof(ps_raw_uav_data),
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_DEST);
    upload_buffer_data(ps_raw_uav_buffer, 0, sizeof(ps_raw_uav_data), &ps_raw_uav_data, queue, command_list);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, ps_raw_uav_buffer,
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

    cs_textures[0] = create_default_texture(device,
            1, 1, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D12_RESOURCE_STATE_COPY_DEST);
    data.pData = &cs_data;
    data.RowPitch = sizeof(cs_data);
    data.SlicePitch = data.RowPitch;
    upload_texture_data(cs_textures[0], &data, 1, queue, command_list);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, cs_textures[0],
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);
    cs_textures[1] = create_default_texture(device, 1, 1, DXGI_FORMAT_R32_FLOAT,
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_DEST);
    data.pData = &cs_texture_data;
    data.RowPitch = sizeof(cs_texture_data);
    data.SlicePitch = data.RowPitch;
    upload_texture_data(cs_textures[1], &data, 1, queue, command_list);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, cs_textures[1],
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

    ps_textures[0] = create_default_texture(device,
            1, 1, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D12_RESOURCE_STATE_COPY_DEST);
    data.pData = &ps_data;
    data.RowPitch = sizeof(ps_data);
    data.SlicePitch = data.RowPitch;
    upload_texture_data(ps_textures[0], &data, 1, queue, command_list);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, ps_textures[0],
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
    ps_textures[1] = create_default_texture(device, 1, 1, DXGI_FORMAT_R32_FLOAT,
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_DEST);
    data.pData = &ps_texture_data;
    data.RowPitch = sizeof(ps_texture_data);
    data.SlicePitch = data.RowPitch;
    upload_texture_data(ps_textures[1], &data, 1, queue, command_list);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, ps_textures[1],
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

    ID3D12Device_CreateShaderResourceView(device, cs_textures[0], NULL,
            get_cpu_descriptor_handle(&context, heap, 0));
    ID3D12Device_CreateUnorderedAccessView(device, cs_textures[1], NULL, NULL,
            get_cpu_descriptor_handle(&context, heap, 1));

    ID3D12Device_CreateShaderResourceView(device, ps_textures[0], NULL,
            get_cpu_descriptor_handle(&context, heap, 10));
    ID3D12Device_CreateUnorderedAccessView(device, ps_textures[1], NULL, NULL,
            get_cpu_descriptor_handle(&context, heap, 11));

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);

    ID3D12GraphicsCommandList_SetComputeRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &heap);

    ID3D12GraphicsCommandList_SetComputeRootConstantBufferView(command_list,
            0, ID3D12Resource_GetGPUVirtualAddress(cs_cb));
    ID3D12GraphicsCommandList_SetComputeRootShaderResourceView(command_list,
            1, ID3D12Resource_GetGPUVirtualAddress(cs_raw_buffer));
    ID3D12GraphicsCommandList_SetComputeRootUnorderedAccessView(command_list,
            2, ID3D12Resource_GetGPUVirtualAddress(cs_raw_uav_buffer));
    ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(command_list,
            3, get_gpu_descriptor_handle(&context, heap, 0));

    ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list,
            0, ID3D12Resource_GetGPUVirtualAddress(ps_cb));
    ID3D12GraphicsCommandList_SetGraphicsRootShaderResourceView(command_list,
            1, ID3D12Resource_GetGPUVirtualAddress(ps_raw_buffer));
    ID3D12GraphicsCommandList_SetGraphicsRootUnorderedAccessView(command_list,
            2, ID3D12Resource_GetGPUVirtualAddress(ps_raw_uav_buffer));
    ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list,
            3, get_gpu_descriptor_handle(&context, heap, 10));

    ID3D12GraphicsCommandList_SetPipelineState(command_list, compute_pso);
    ID3D12GraphicsCommandList_Dispatch(command_list, 1, 1, 1);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, cs_raw_uav_buffer,
            D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_buffer_readback_with_command_list(cs_raw_uav_buffer, DXGI_FORMAT_R32_UINT, &rb, queue, command_list);
    check_readback_data_uint(&rb.rb, NULL, 0xffffffff, 0);
    release_resource_readback(&rb);

    ID3D12Resource_Release(cs_cb);
    ID3D12Resource_Release(ps_cb);
    ID3D12Resource_Release(cs_raw_buffer);
    ID3D12Resource_Release(cs_raw_uav_buffer);
    ID3D12Resource_Release(ps_raw_buffer);
    ID3D12Resource_Release(ps_raw_uav_buffer);
    for (i = 0; i < ARRAY_SIZE(cs_textures); ++i)
        ID3D12Resource_Release(cs_textures[i]);
    for (i = 0; i < ARRAY_SIZE(ps_textures); ++i)
        ID3D12Resource_Release(ps_textures[i]);
    ID3D12DescriptorHeap_Release(heap);
    ID3D12PipelineState_Release(compute_pso);
    destroy_test_context(&context);
}

static void test_face_culling(void)
{
    ID3D12PipelineState *color_pso, *ccw_color_pso, *pso;
    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
    ID3D12GraphicsCommandList *command_list;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Device *device;
    unsigned int i;
    HRESULT hr;

    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    static const struct vec4 green = {0.0f, 1.0f, 0.0f, 1.0f};
    static const DWORD vs_ccw_code[] =
    {
#if 0
        void main(uint id : SV_VertexID, out float4 position : SV_Position)
        {
            float2 coords = float2((id << 1) & 2, id & 2);
            position = float4(coords * float2(2, 2) + float2(-1, -1), 0, 1);
        }
#endif
        0x43425844, 0xdcd52e92, 0x3f4a3922, 0xa376c4ed, 0x2bc626c0, 0x00000001, 0x0000018c, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000006, 0x00000001, 0x00000000, 0x00000101, 0x565f5653, 0x65747265, 0x00444978,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000001, 0x00000003,
        0x00000000, 0x0000000f, 0x505f5653, 0x7469736f, 0x006e6f69, 0x58454853, 0x000000f0, 0x00010050,
        0x0000003c, 0x0100086a, 0x04000060, 0x00101012, 0x00000000, 0x00000006, 0x04000067, 0x001020f2,
        0x00000000, 0x00000001, 0x02000068, 0x00000001, 0x0b00008c, 0x00100012, 0x00000000, 0x00004001,
        0x00000001, 0x00004001, 0x00000001, 0x0010100a, 0x00000000, 0x00004001, 0x00000000, 0x07000001,
        0x00100042, 0x00000000, 0x0010100a, 0x00000000, 0x00004001, 0x00000002, 0x05000056, 0x00100032,
        0x00000000, 0x00100086, 0x00000000, 0x0f000032, 0x00102032, 0x00000000, 0x00100046, 0x00000000,
        0x00004002, 0x40000000, 0x40000000, 0x00000000, 0x00000000, 0x00004002, 0xbf800000, 0xbf800000,
        0x00000000, 0x00000000, 0x08000036, 0x001020c2, 0x00000000, 0x00004002, 0x00000000, 0x00000000,
        0x00000000, 0x3f800000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE vs_ccw = {vs_ccw_code, sizeof(vs_ccw_code)};
    static const DWORD ps_color_code[] =
    {
#if 0
        float4 color;

        float4 main(float4 position : SV_POSITION) : SV_Target
        {
            return color;
        }
#endif
        0x43425844, 0xd18ead43, 0x8b8264c1, 0x9c0a062d, 0xfc843226, 0x00000001, 0x000000e0, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x505f5653, 0x5449534f, 0x004e4f49,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000044, 0x00000050,
        0x00000011, 0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2,
        0x00000000, 0x06000036, 0x001020f2, 0x00000000, 0x00208e46, 0x00000000, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_color = {ps_color_code, sizeof(ps_color_code)};
    static const DWORD ps_front_code[] =
    {
#if 0
        float4 main(uint front : SV_IsFrontFace) : SV_Target
        {
            return (front == ~0u) ? float4(0.0f, 1.0f, 0.0f, 1.0f) : float4(0.0f, 0.0f, 1.0f, 1.0f);
        }
#endif
        0x43425844, 0x92002fad, 0xc5c620b9, 0xe7a154fb, 0x78b54e63, 0x00000001, 0x00000128, 0x00000003,
        0x0000002c, 0x00000064, 0x00000098, 0x4e475349, 0x00000030, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000009, 0x00000001, 0x00000000, 0x00000101, 0x495f5653, 0x6f724673, 0x6146746e,
        0xab006563, 0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000,
        0x00000003, 0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x52444853, 0x00000088,
        0x00000040, 0x00000022, 0x04000863, 0x00101012, 0x00000000, 0x00000009, 0x03000065, 0x001020f2,
        0x00000000, 0x02000068, 0x00000001, 0x07000020, 0x00100012, 0x00000000, 0x0010100a, 0x00000000,
        0x00004001, 0xffffffff, 0x0f000037, 0x001020f2, 0x00000000, 0x00100006, 0x00000000, 0x00004002,
        0x00000000, 0x3f800000, 0x00000000, 0x3f800000, 0x00004002, 0x00000000, 0x00000000, 0x3f800000,
        0x3f800000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_front = {ps_front_code, sizeof(ps_front_code)};
    static const struct
    {
        D3D12_CULL_MODE cull_mode;
        bool front_ccw;
        bool expected_cw;
        bool expected_ccw;
    }
    tests[] =
    {
        {D3D12_CULL_MODE_NONE,  false, true,  true},
        {D3D12_CULL_MODE_NONE,  true,  true,  true},
        {D3D12_CULL_MODE_FRONT, false, false, true},
        {D3D12_CULL_MODE_FRONT, true,  true,  false},
        {D3D12_CULL_MODE_BACK,  false, true,  false},
        {D3D12_CULL_MODE_BACK,  true,  false, true},
    };
    static const bool front_tests[] = {false, true};

    memset(&desc, 0, sizeof(desc));
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    context.root_signature = create_32bit_constants_root_signature(device,
            0, 4, D3D12_SHADER_VISIBILITY_PIXEL);

    color_pso = create_pipeline_state(device, context.root_signature,
            context.render_target_desc.Format, NULL, &ps_color, NULL);
    ccw_color_pso = create_pipeline_state(device, context.root_signature,
            context.render_target_desc.Format, &vs_ccw, &ps_color, NULL);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, color_pso);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 0, 4, &green.x, 0);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, ccw_color_pso);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 0, 4, &green.x, 0);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xffffffff, 0);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    for (i = 0; i < ARRAY_SIZE(tests); ++i)
    {
        vkd3d_test_push_context("Test %u", i);

        init_pipeline_state_desc(&pso_desc, context.root_signature,
                context.render_target_desc.Format, NULL, &ps_color, NULL);
        pso_desc.RasterizerState.CullMode = tests[i].cull_mode;
        pso_desc.RasterizerState.FrontCounterClockwise = tests[i].front_ccw;
        hr = ID3D12Device_CreateGraphicsPipelineState(device, &pso_desc,
                &IID_ID3D12PipelineState, (void **)&pso);
        ok(SUCCEEDED(hr), "Failed to create graphics pipeline state, hr %#x.\n", hr);

        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
        ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
        ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
        ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetPipelineState(command_list, pso);
        ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
        ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 0, 4, &green.x, 0);
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
        check_sub_resource_uint(context.render_target, 0, queue, command_list,
                tests[i].expected_cw ? 0xff00ff00 : 0xffffffff, 0);
        reset_command_list(command_list, context.allocator);
        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

        ID3D12PipelineState_Release(pso);
        pso_desc.VS = vs_ccw;
        hr = ID3D12Device_CreateGraphicsPipelineState(device, &pso_desc,
                &IID_ID3D12PipelineState, (void **)&pso);
        ok(SUCCEEDED(hr), "Failed to create graphics pipeline state, hr %#x.\n", hr);

        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
        ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
        ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
        ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetPipelineState(command_list, pso);
        ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
        ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 0, 4, &green.x, 0);
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
        check_sub_resource_uint(context.render_target, 0, queue, command_list,
                tests[i].expected_ccw ? 0xff00ff00 : 0xffffffff, 0);
        reset_command_list(command_list, context.allocator);
        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

        ID3D12PipelineState_Release(pso);

        vkd3d_test_pop_context();
    }

    /* Test SV_IsFrontFace. */
    for (i = 0; i < ARRAY_SIZE(front_tests); ++i)
    {
        vkd3d_test_push_context("Test %u", i);

        init_pipeline_state_desc(&pso_desc, context.root_signature,
                context.render_target_desc.Format, NULL, &ps_front, NULL);
        pso_desc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE;
        pso_desc.RasterizerState.FrontCounterClockwise = front_tests[i];
        hr = ID3D12Device_CreateGraphicsPipelineState(device, &pso_desc,
                &IID_ID3D12PipelineState, (void **)&pso);
        ok(SUCCEEDED(hr), "Failed to create graphics pipeline state, hr %#x.\n", hr);

        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
        ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
        ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
        ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetPipelineState(command_list, pso);
        ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
        check_sub_resource_uint(context.render_target, 0, queue, command_list,
                front_tests[i] ? 0xffff0000 : 0xff00ff00, 0);
        reset_command_list(command_list, context.allocator);
        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

        ID3D12PipelineState_Release(pso);
        pso_desc.VS = vs_ccw;
        hr = ID3D12Device_CreateGraphicsPipelineState(device, &pso_desc,
                &IID_ID3D12PipelineState, (void **)&pso);
        ok(SUCCEEDED(hr), "Failed to create graphics pipeline state, hr %#x.\n", hr);

        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
        ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
        ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
        ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetPipelineState(command_list, pso);
        ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
        check_sub_resource_uint(context.render_target, 0, queue, command_list,
                front_tests[i] ? 0xff00ff00 : 0xffff0000, 0);
        reset_command_list(command_list, context.allocator);
        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

        ID3D12PipelineState_Release(pso);

        vkd3d_test_pop_context();
    }

    ID3D12PipelineState_Release(color_pso);
    ID3D12PipelineState_Release(ccw_color_pso);
    destroy_test_context(&context);
}

static void draw_thread_main(void *thread_data)
{
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    struct test_context *context = thread_data;
    ID3D12GraphicsCommandList *command_list;
    ID3D12CommandAllocator *allocator;
    D3D12_CPU_DESCRIPTOR_HANDLE rtv;
    ID3D12Resource *render_target;
    ID3D12DescriptorHeap *heap;
    ID3D12CommandQueue *queue;
    ID3D12Device *device;
    unsigned int i;
    HRESULT hr;

    queue = context->queue;
    device = context->device;
    heap = create_cpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_RTV, 1);
    rtv = get_cpu_descriptor_handle(context, heap, 0);
    create_render_target(context, NULL, &render_target, &rtv);

    hr = ID3D12Device_CreateCommandAllocator(device, D3D12_COMMAND_LIST_TYPE_DIRECT,
            &IID_ID3D12CommandAllocator, (void **)&allocator);
    ok(SUCCEEDED(hr), "Failed to create command allocator, hr %#x.\n", hr);
    hr = ID3D12Device_CreateCommandList(device, 0, D3D12_COMMAND_LIST_TYPE_DIRECT,
            allocator, NULL, &IID_ID3D12GraphicsCommandList, (void **)&command_list);
    ok(SUCCEEDED(hr), "Failed to create command list, hr %#x.\n", hr);

    for (i = 0; i < 100; ++i)
    {
        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, rtv, white, 0, NULL);
        ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &rtv, false, NULL);
        ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context->root_signature);
        ID3D12GraphicsCommandList_SetPipelineState(command_list, context->pipeline_state);
        ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context->viewport);
        ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context->scissor_rect);
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

        transition_resource_state(command_list, render_target,
                D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
        check_sub_resource_uint(render_target, 0, queue, command_list, 0xff00ff00, 0);
        reset_command_list(command_list, allocator);
        transition_resource_state(command_list, render_target,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
    }

    ID3D12DescriptorHeap_Release(heap);
    ID3D12Resource_Release(render_target);
    ID3D12CommandAllocator_Release(allocator);
    ID3D12GraphicsCommandList_Release(command_list);
}

static void test_multithread_command_queue_exec(void)
{
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    ID3D12GraphicsCommandList *command_list;
    struct test_context context;
    ID3D12CommandQueue *queue;
    HANDLE threads[10];
    unsigned int i;

    if (!init_test_context(&context, NULL))
        return;
    command_list = context.list;
    queue = context.queue;

    for (i = 0; i < ARRAY_SIZE(threads); ++i)
    {
        threads[i] = create_thread(draw_thread_main, &context);
        ok(threads[i], "Failed to create thread %u.\n", i);
    }

    for (i = 0; i < 100; ++i)
    {
        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
        ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
        ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
        ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
        ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
        check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);
        reset_command_list(command_list, context.allocator);
        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
    }

    for (i = 0; i < ARRAY_SIZE(threads); ++i)
        ok(join_thread(threads[i]), "Failed to join thread %u.\n", i);

    destroy_test_context(&context);
}

static void test_geometry_shader(void)
{
    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
    ID3D12GraphicsCommandList *command_list;
    D3D12_INPUT_LAYOUT_DESC input_layout;
    D3D12_CPU_DESCRIPTOR_HANDLE rtvs[2];
    ID3D12PipelineState *pso_5_0, *pso;
    struct d3d12_resource_readback rb;
    struct test_context_desc desc;
    D3D12_VERTEX_BUFFER_VIEW vbv;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Resource *texture;
    ID3D12Device *device;
    ID3D12Resource *vb;
    unsigned int color;
    unsigned int i;
    HRESULT hr;

    static const float red[] = {1.0f, 0.0f, 0.0f, 1.0f};
    static const struct
    {
        struct vec4 position;
        unsigned int color;
    }
    vertex[] =
    {
        {{0.0f, 0.0f, 1.0f, 1.0f}, 0xffffff00},
    };
    static const D3D12_INPUT_ELEMENT_DESC layout_desc[] =
    {
        {"SV_POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0,  0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
        {"COLOR",       0, DXGI_FORMAT_R8G8B8A8_UNORM,     0, 16, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
    };
#if 0
    struct vs_data
    {
        float4 pos : SV_POSITION;
        float4 color : COLOR;
    };

    void main(in struct vs_data vs_input, out struct vs_data vs_output)
    {
        vs_output.pos = vs_input.pos;
        vs_output.color = vs_input.color;
    }
#endif
    static const DWORD vs_code[] =
    {
        0x43425844, 0xd5b32785, 0x35332906, 0x4d05e031, 0xf66a58af, 0x00000001, 0x00000144, 0x00000003,
        0x0000002c, 0x00000080, 0x000000d4, 0x4e475349, 0x0000004c, 0x00000002, 0x00000008, 0x00000038,
        0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000f0f, 0x00000044, 0x00000000, 0x00000000,
        0x00000003, 0x00000001, 0x00000f0f, 0x505f5653, 0x5449534f, 0x004e4f49, 0x4f4c4f43, 0xabab0052,
        0x4e47534f, 0x0000004c, 0x00000002, 0x00000008, 0x00000038, 0x00000000, 0x00000001, 0x00000003,
        0x00000000, 0x0000000f, 0x00000044, 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x0000000f,
        0x505f5653, 0x5449534f, 0x004e4f49, 0x4f4c4f43, 0xabab0052, 0x52444853, 0x00000068, 0x00010040,
        0x0000001a, 0x0300005f, 0x001010f2, 0x00000000, 0x0300005f, 0x001010f2, 0x00000001, 0x04000067,
        0x001020f2, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000001, 0x05000036, 0x001020f2,
        0x00000000, 0x00101e46, 0x00000000, 0x05000036, 0x001020f2, 0x00000001, 0x00101e46, 0x00000001,
        0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE vs = {vs_code, sizeof(vs_code)};
#if 0
    struct gs_data
    {
        float4 pos : SV_POSITION;
        float4 color : COLOR;
    };

    [maxvertexcount(4)]
    void main(point struct gs_data vin[1], inout TriangleStream<gs_data> vout)
    {
        float offset = 0.2 * vin[0].pos.w;
        gs_data v;

        v.color = vin[0].color;

        v.pos = float4(vin[0].pos.x - offset, vin[0].pos.y - offset, vin[0].pos.z, 1.0);
        vout.Append(v);
        v.pos = float4(vin[0].pos.x - offset, vin[0].pos.y + offset, vin[0].pos.z, 1.0);
        vout.Append(v);
        v.pos = float4(vin[0].pos.x + offset, vin[0].pos.y - offset, vin[0].pos.z, 1.0);
        vout.Append(v);
        v.pos = float4(vin[0].pos.x + offset, vin[0].pos.y + offset, vin[0].pos.z, 1.0);
        vout.Append(v);
    }
#endif
    static const DWORD gs_code[] =
    {
        0x43425844, 0x70616045, 0x96756e1f, 0x1caeecb8, 0x3749528c, 0x00000001, 0x0000034c, 0x00000003,
        0x0000002c, 0x00000080, 0x000000d4, 0x4e475349, 0x0000004c, 0x00000002, 0x00000008, 0x00000038,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x00000f0f, 0x00000044, 0x00000000, 0x00000000,
        0x00000003, 0x00000001, 0x00000f0f, 0x505f5653, 0x5449534f, 0x004e4f49, 0x4f4c4f43, 0xabab0052,
        0x4e47534f, 0x0000004c, 0x00000002, 0x00000008, 0x00000038, 0x00000000, 0x00000001, 0x00000003,
        0x00000000, 0x0000000f, 0x00000044, 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x0000000f,
        0x505f5653, 0x5449534f, 0x004e4f49, 0x4f4c4f43, 0xabab0052, 0x52444853, 0x00000270, 0x00020040,
        0x0000009c, 0x05000061, 0x002010f2, 0x00000001, 0x00000000, 0x00000001, 0x0400005f, 0x002010f2,
        0x00000001, 0x00000001, 0x02000068, 0x00000001, 0x0100085d, 0x0100285c, 0x04000067, 0x001020f2,
        0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000001, 0x0200005e, 0x00000004, 0x0f000032,
        0x00100032, 0x00000000, 0x80201ff6, 0x00000041, 0x00000000, 0x00000000, 0x00004002, 0x3e4ccccd,
        0x3e4ccccd, 0x00000000, 0x00000000, 0x00201046, 0x00000000, 0x00000000, 0x05000036, 0x00102032,
        0x00000000, 0x00100046, 0x00000000, 0x06000036, 0x00102042, 0x00000000, 0x0020102a, 0x00000000,
        0x00000000, 0x05000036, 0x00102082, 0x00000000, 0x00004001, 0x3f800000, 0x06000036, 0x001020f2,
        0x00000001, 0x00201e46, 0x00000000, 0x00000001, 0x01000013, 0x05000036, 0x00102012, 0x00000000,
        0x0010000a, 0x00000000, 0x0e000032, 0x00100052, 0x00000000, 0x00201ff6, 0x00000000, 0x00000000,
        0x00004002, 0x3e4ccccd, 0x00000000, 0x3e4ccccd, 0x00000000, 0x00201106, 0x00000000, 0x00000000,
        0x05000036, 0x00102022, 0x00000000, 0x0010002a, 0x00000000, 0x06000036, 0x00102042, 0x00000000,
        0x0020102a, 0x00000000, 0x00000000, 0x05000036, 0x00102082, 0x00000000, 0x00004001, 0x3f800000,
        0x06000036, 0x001020f2, 0x00000001, 0x00201e46, 0x00000000, 0x00000001, 0x01000013, 0x05000036,
        0x00102012, 0x00000000, 0x0010000a, 0x00000000, 0x05000036, 0x00102022, 0x00000000, 0x0010001a,
        0x00000000, 0x06000036, 0x00102042, 0x00000000, 0x0020102a, 0x00000000, 0x00000000, 0x05000036,
        0x00102082, 0x00000000, 0x00004001, 0x3f800000, 0x06000036, 0x001020f2, 0x00000001, 0x00201e46,
        0x00000000, 0x00000001, 0x01000013, 0x05000036, 0x00102032, 0x00000000, 0x00100086, 0x00000000,
        0x06000036, 0x00102042, 0x00000000, 0x0020102a, 0x00000000, 0x00000000, 0x05000036, 0x00102082,
        0x00000000, 0x00004001, 0x3f800000, 0x06000036, 0x001020f2, 0x00000001, 0x00201e46, 0x00000000,
        0x00000001, 0x01000013, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE gs = {gs_code, sizeof(gs_code)};
    static const DWORD gs_5_0_code[] =
    {
        0x43425844, 0x57251c23, 0x4971d115, 0x8fee0b13, 0xba149ea1, 0x00000001, 0x00000384, 0x00000003,
        0x0000002c, 0x00000080, 0x000000dc, 0x4e475349, 0x0000004c, 0x00000002, 0x00000008, 0x00000038,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x00000f0f, 0x00000044, 0x00000000, 0x00000000,
        0x00000003, 0x00000001, 0x00000f0f, 0x505f5653, 0x5449534f, 0x004e4f49, 0x4f4c4f43, 0xabab0052,
        0x3547534f, 0x00000054, 0x00000002, 0x00000008, 0x00000000, 0x00000040, 0x00000000, 0x00000001,
        0x00000003, 0x00000000, 0x0000000f, 0x00000000, 0x0000004c, 0x00000000, 0x00000000, 0x00000003,
        0x00000001, 0x0000000f, 0x505f5653, 0x5449534f, 0x004e4f49, 0x4f4c4f43, 0xabab0052, 0x58454853,
        0x000002a0, 0x00020050, 0x000000a8, 0x0100086a, 0x05000061, 0x002010f2, 0x00000001, 0x00000000,
        0x00000001, 0x0400005f, 0x002010f2, 0x00000001, 0x00000001, 0x02000068, 0x00000001, 0x0100085d,
        0x0300008f, 0x00110000, 0x00000000, 0x0100285c, 0x04000067, 0x001020f2, 0x00000000, 0x00000001,
        0x03000065, 0x001020f2, 0x00000001, 0x0200005e, 0x00000004, 0x0f000032, 0x00100032, 0x00000000,
        0x80201ff6, 0x00000041, 0x00000000, 0x00000000, 0x00004002, 0x3e4ccccd, 0x3e4ccccd, 0x00000000,
        0x00000000, 0x00201046, 0x00000000, 0x00000000, 0x05000036, 0x00102032, 0x00000000, 0x00100046,
        0x00000000, 0x06000036, 0x00102042, 0x00000000, 0x0020102a, 0x00000000, 0x00000000, 0x05000036,
        0x00102082, 0x00000000, 0x00004001, 0x3f800000, 0x06000036, 0x001020f2, 0x00000001, 0x00201e46,
        0x00000000, 0x00000001, 0x03000075, 0x00110000, 0x00000000, 0x05000036, 0x00102012, 0x00000000,
        0x0010000a, 0x00000000, 0x0e000032, 0x00100052, 0x00000000, 0x00201ff6, 0x00000000, 0x00000000,
        0x00004002, 0x3e4ccccd, 0x00000000, 0x3e4ccccd, 0x00000000, 0x00201106, 0x00000000, 0x00000000,
        0x05000036, 0x00102022, 0x00000000, 0x0010002a, 0x00000000, 0x06000036, 0x00102042, 0x00000000,
        0x0020102a, 0x00000000, 0x00000000, 0x05000036, 0x00102082, 0x00000000, 0x00004001, 0x3f800000,
        0x06000036, 0x001020f2, 0x00000001, 0x00201e46, 0x00000000, 0x00000001, 0x03000075, 0x00110000,
        0x00000000, 0x05000036, 0x00102012, 0x00000000, 0x0010000a, 0x00000000, 0x05000036, 0x00102022,
        0x00000000, 0x0010001a, 0x00000000, 0x06000036, 0x00102042, 0x00000000, 0x0020102a, 0x00000000,
        0x00000000, 0x05000036, 0x00102082, 0x00000000, 0x00004001, 0x3f800000, 0x06000036, 0x001020f2,
        0x00000001, 0x00201e46, 0x00000000, 0x00000001, 0x03000075, 0x00110000, 0x00000000, 0x05000036,
        0x00102032, 0x00000000, 0x00100086, 0x00000000, 0x06000036, 0x00102042, 0x00000000, 0x0020102a,
        0x00000000, 0x00000000, 0x05000036, 0x00102082, 0x00000000, 0x00004001, 0x3f800000, 0x06000036,
        0x001020f2, 0x00000001, 0x00201e46, 0x00000000, 0x00000001, 0x03000075, 0x00110000, 0x00000000,
        0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE gs_5_0 = {gs_5_0_code, sizeof(gs_5_0_code)};
#if 0
    struct ps_data
    {
        float4 pos : SV_POSITION;
        float4 color : COLOR;
    };

    float4 main(struct ps_data ps_input) : SV_Target
    {
        return ps_input.color;
    }
#endif
    static const DWORD ps_code[] =
    {
        0x43425844, 0x89803e59, 0x3f798934, 0xf99181df, 0xf5556512, 0x00000001, 0x000000f4, 0x00000003,
        0x0000002c, 0x00000080, 0x000000b4, 0x4e475349, 0x0000004c, 0x00000002, 0x00000008, 0x00000038,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x00000044, 0x00000000, 0x00000000,
        0x00000003, 0x00000001, 0x00000f0f, 0x505f5653, 0x5449534f, 0x004e4f49, 0x4f4c4f43, 0xabab0052,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x52444853, 0x00000038, 0x00000040,
        0x0000000e, 0x03001062, 0x001010f2, 0x00000001, 0x03000065, 0x001020f2, 0x00000000, 0x05000036,
        0x001020f2, 0x00000000, 0x00101e46, 0x00000001, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};

    memset(&desc, 0, sizeof(desc));
    desc.rt_width = 640;
    desc.rt_height = 480;
    desc.rt_descriptor_count = 2;
    desc.root_signature_flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
    desc.no_pipeline = true;
    if (!init_test_context(&context, &desc))
        return;

    if (!is_geometry_shader_supported(context.device))
    {
        skip("The device does not support geometry shaders.\n");
        destroy_test_context(&context);
        return;
    }

    device = context.device;
    command_list = context.list;
    queue = context.queue;

    rtvs[0] = context.rtv;
    rtvs[1] = get_cpu_rtv_handle(&context, context.rtv_heap, 1);
    create_render_target(&context, &desc, &texture, &rtvs[1]);

    input_layout.pInputElementDescs = layout_desc;
    input_layout.NumElements = ARRAY_SIZE(layout_desc);

    init_pipeline_state_desc(&pso_desc, context.root_signature,
            context.render_target_desc.Format, &vs, &ps, &input_layout);
    pso_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT;
    pso_desc.GS = gs_5_0;
    hr = ID3D12Device_CreateGraphicsPipelineState(device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&pso_5_0);
    ok(hr == S_OK, "Failed to create graphics pipeline state, hr %#x.\n", hr);
    pso_desc.GS = gs;
    hr = ID3D12Device_CreateGraphicsPipelineState(device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&pso);
    ok(hr == S_OK, "Failed to create graphics pipeline state, hr %#x.\n", hr);

    vb = create_upload_buffer(context.device, sizeof(vertex), vertex);
    vbv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vb);
    vbv.StrideInBytes = sizeof(*vertex);
    vbv.SizeInBytes = sizeof(vertex);

    for (i = 0; i < ARRAY_SIZE(rtvs); ++i)
        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, rtvs[i], red, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &rtvs[0], false, NULL);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, pso_5_0);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_POINTLIST);
    ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, 1, &vbv);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 1, 1, 0, 0);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &rtvs[1], false, NULL);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, pso_5_0);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 1, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    transition_resource_state(command_list, texture,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);

    get_resource_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
    color = get_readback_uint(&rb.rb, 320, 190, 0);
    ok(compare_color(color, 0xff0000ff, 1), "Got unexpected color 0x%08x.\n", color);
    color = get_readback_uint(&rb.rb, 255, 240, 0);
    ok(compare_color(color, 0xff0000ff, 1), "Got unexpected color 0x%08x.\n", color);
    color = get_readback_uint(&rb.rb, 320, 240, 0);
    ok(compare_color(color, 0xffffff00, 1), "Got unexpected color 0x%08x.\n", color);
    color = get_readback_uint(&rb.rb, 385, 240, 0);
    ok(compare_color(color, 0xff0000ff, 1), "Got unexpected color 0x%08x.\n", color);
    color = get_readback_uint(&rb.rb, 320, 290, 0);
    ok(compare_color(color, 0xff0000ff, 1), "Got unexpected color 0x%08x.\n", color);
    release_resource_readback(&rb);

    reset_command_list(command_list, context.allocator);
    get_resource_readback_with_command_list(texture, 0, &rb, queue, command_list);
    color = get_readback_uint(&rb.rb, 320, 190, 0);
    ok(compare_color(color, 0xff0000ff, 1), "Got unexpected color 0x%08x.\n", color);
    color = get_readback_uint(&rb.rb, 255, 240, 0);
    ok(compare_color(color, 0xff0000ff, 1), "Got unexpected color 0x%08x.\n", color);
    color = get_readback_uint(&rb.rb, 320, 240, 0);
    ok(compare_color(color, 0xffffff00, 1), "Got unexpected color 0x%08x.\n", color);
    color = get_readback_uint(&rb.rb, 385, 240, 0);
    ok(compare_color(color, 0xff0000ff, 1), "Got unexpected color 0x%08x.\n", color);
    color = get_readback_uint(&rb.rb, 320, 290, 0);
    ok(compare_color(color, 0xff0000ff, 1), "Got unexpected color 0x%08x.\n", color);
    release_resource_readback(&rb);

    ID3D12Resource_Release(vb);
    ID3D12Resource_Release(texture);
    ID3D12PipelineState_Release(pso);
    ID3D12PipelineState_Release(pso_5_0);
    destroy_test_context(&context);
}

static void test_layered_rendering(void)
{
    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
    ID3D12GraphicsCommandList *command_list;
    D3D12_INPUT_LAYOUT_DESC input_layout;
    struct test_context_desc desc;
    D3D12_VERTEX_BUFFER_VIEW vbv;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Device *device;
    ID3D12Resource *vb;
    HRESULT hr;

    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    static const struct
    {
        uint32_t color;
        struct vec4 position;
        uint32_t layer;
    }
    vertices[] =
    {
        {0x00000000, {-1.0f, -1.0f, 0.0f, 1.0f}, 2},
        {0x00000000, {-1.0f,  1.0f, 0.0f, 1.0f}, 2},
        {0x00000000, { 1.0f, -1.0f, 0.0f, 1.0f}, 2},
        {0x00000000, { 1.0f,  1.0f, 0.0f, 1.0f}, 2},
        {0xff00ff00, {-1.0f, -1.0f, 0.0f, 1.0f}, 0},
        {0xff00ff00, {-1.0f,  1.0f, 0.0f, 1.0f}, 0},
        {0xff00ff00, { 1.0f, -1.0f, 0.0f, 1.0f}, 0},
        {0xff00ff00, { 1.0f,  1.0f, 0.0f, 1.0f}, 0},
        {0xffffff00, {-1.0f, -1.0f, 0.0f, 1.0f}, 3},
        {0xffffff00, {-1.0f,  1.0f, 0.0f, 1.0f}, 3},
        {0xffffff00, { 1.0f, -1.0f, 0.0f, 1.0f}, 3},
        {0xffffff00, { 1.0f,  1.0f, 0.0f, 1.0f}, 3},
    };
    static const D3D12_INPUT_ELEMENT_DESC layout_desc[] =
    {
        {"COLOR",       0, DXGI_FORMAT_R8G8B8A8_UNORM,     0,  0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
        {"SV_POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0,  4, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
        {"LAYER",       0, DXGI_FORMAT_R32_UINT,           0, 20, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
    };
    static const DWORD vs_code[] =
    {
#if 0
        struct vertex
        {
            float4 color : COLOR;
            float4 position : SV_Position;
            uint layer : LAYER;
        };

        struct vertex main(in vertex v)
        {
            return v;
        }
#endif
        0x43425844, 0x96d7f39a, 0x03d06cd5, 0x32c1fa04, 0xd509128f, 0x00000001, 0x000001ac, 0x00000003,
        0x0000002c, 0x0000009c, 0x0000010c, 0x4e475349, 0x00000068, 0x00000003, 0x00000008, 0x00000050,
        0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000f0f, 0x00000056, 0x00000000, 0x00000000,
        0x00000003, 0x00000001, 0x00000f0f, 0x00000062, 0x00000000, 0x00000000, 0x00000001, 0x00000002,
        0x00000101, 0x4f4c4f43, 0x56530052, 0x736f505f, 0x6f697469, 0x414c006e, 0x00524559, 0x4e47534f,
        0x00000068, 0x00000003, 0x00000008, 0x00000050, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x00000056, 0x00000000, 0x00000001, 0x00000003, 0x00000001, 0x0000000f, 0x00000062,
        0x00000000, 0x00000000, 0x00000001, 0x00000002, 0x00000e01, 0x4f4c4f43, 0x56530052, 0x736f505f,
        0x6f697469, 0x414c006e, 0x00524559, 0x58454853, 0x00000098, 0x00010050, 0x00000026, 0x0100086a,
        0x0300005f, 0x001010f2, 0x00000000, 0x0300005f, 0x001010f2, 0x00000001, 0x0300005f, 0x00101012,
        0x00000002, 0x03000065, 0x001020f2, 0x00000000, 0x04000067, 0x001020f2, 0x00000001, 0x00000001,
        0x03000065, 0x00102012, 0x00000002, 0x05000036, 0x001020f2, 0x00000000, 0x00101e46, 0x00000000,
        0x05000036, 0x001020f2, 0x00000001, 0x00101e46, 0x00000001, 0x05000036, 0x00102012, 0x00000002,
        0x0010100a, 0x00000002, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE vs = {vs_code, sizeof(vs_code)};
    static const DWORD gs_code[] =
    {
#if 0
        struct gs_in
        {
            float4 color : COLOR;
            float4 position : SV_Position;
            uint layer : LAYER;
        };

        struct gs_out
        {
            float4 color : COLOR;
            float4 position : SV_Position;
            uint layer : SV_RenderTargetArrayIndex;
        };

        [maxvertexcount(3)]
        void main(triangle gs_in vin[3], inout TriangleStream<gs_out> vout)
        {
            gs_out o;

            o.color = vin[0].color;
            o.position = vin[0].position;
            o.layer = vin[0].layer;
            vout.Append(o);

            o.color = vin[1].color;
            o.position = vin[1].position;
            o.layer = vin[1].layer;
            vout.Append(o);

            o.color = vin[2].color;
            o.position = vin[2].position;
            o.layer = vin[2].layer;
            vout.Append(o);
        }
#endif
        0x43425844, 0x29d7c0a0, 0xcf146fd1, 0x5cd36ca7, 0xab2b10ff, 0x00000001, 0x000002bc, 0x00000003,
        0x0000002c, 0x0000009c, 0x0000012c, 0x4e475349, 0x00000068, 0x00000003, 0x00000008, 0x00000050,
        0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000f0f, 0x00000056, 0x00000000, 0x00000001,
        0x00000003, 0x00000001, 0x00000f0f, 0x00000062, 0x00000000, 0x00000000, 0x00000001, 0x00000002,
        0x00000101, 0x4f4c4f43, 0x56530052, 0x736f505f, 0x6f697469, 0x414c006e, 0x00524559, 0x3547534f,
        0x00000088, 0x00000003, 0x00000008, 0x00000000, 0x0000005c, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x00000000, 0x00000062, 0x00000000, 0x00000001, 0x00000003, 0x00000001,
        0x0000000f, 0x00000000, 0x0000006e, 0x00000000, 0x00000004, 0x00000001, 0x00000002, 0x00000e01,
        0x4f4c4f43, 0x56530052, 0x736f505f, 0x6f697469, 0x5653006e, 0x6e65525f, 0x54726564, 0x65677261,
        0x72724174, 0x6e497961, 0x00786564, 0x58454853, 0x00000188, 0x00020050, 0x00000062, 0x0100086a,
        0x0400005f, 0x002010f2, 0x00000003, 0x00000000, 0x05000061, 0x002010f2, 0x00000003, 0x00000001,
        0x00000001, 0x0400005f, 0x00201012, 0x00000003, 0x00000002, 0x0100185d, 0x0300008f, 0x00110000,
        0x00000000, 0x0100285c, 0x03000065, 0x001020f2, 0x00000000, 0x04000067, 0x001020f2, 0x00000001,
        0x00000001, 0x04000067, 0x00102012, 0x00000002, 0x00000004, 0x0200005e, 0x00000003, 0x06000036,
        0x001020f2, 0x00000000, 0x00201e46, 0x00000000, 0x00000000, 0x06000036, 0x001020f2, 0x00000001,
        0x00201e46, 0x00000000, 0x00000001, 0x06000036, 0x00102012, 0x00000002, 0x0020100a, 0x00000000,
        0x00000002, 0x03000075, 0x00110000, 0x00000000, 0x06000036, 0x001020f2, 0x00000000, 0x00201e46,
        0x00000001, 0x00000000, 0x06000036, 0x001020f2, 0x00000001, 0x00201e46, 0x00000001, 0x00000001,
        0x06000036, 0x00102012, 0x00000002, 0x0020100a, 0x00000001, 0x00000002, 0x03000075, 0x00110000,
        0x00000000, 0x06000036, 0x001020f2, 0x00000000, 0x00201e46, 0x00000002, 0x00000000, 0x06000036,
        0x001020f2, 0x00000001, 0x00201e46, 0x00000002, 0x00000001, 0x06000036, 0x00102012, 0x00000002,
        0x0020100a, 0x00000002, 0x00000002, 0x03000075, 0x00110000, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE gs = {gs_code, sizeof(gs_code)};
    static const DWORD ps_code[] =
    {
#if 0
        float4 main(float4 color : COLOR) : SV_Target0
        {
            return color;
        }
#endif
        0x43425844, 0xdccf00bf, 0xcc96375e, 0xba21f157, 0xe47b8b1c, 0x00000001, 0x000000d4, 0x00000003,
        0x0000002c, 0x0000005c, 0x00000090, 0x4e475349, 0x00000028, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000f0f, 0x4f4c4f43, 0xabab0052, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x0000003c, 0x00000050, 0x0000000f,
        0x0100086a, 0x03001062, 0x001010f2, 0x00000000, 0x03000065, 0x001020f2, 0x00000000, 0x05000036,
        0x001020f2, 0x00000000, 0x00101e46, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};

    memset(&desc, 0, sizeof(desc));
    desc.rt_array_size = 4;
    desc.root_signature_flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
    desc.no_pipeline = true;
    if (!init_test_context(&context, &desc))
        return;

    if (!is_geometry_shader_supported(context.device))
    {
        skip("The device does not support geometry shaders.\n");
        destroy_test_context(&context);
        return;
    }

    device = context.device;
    command_list = context.list;
    queue = context.queue;

    input_layout.pInputElementDescs = layout_desc;
    input_layout.NumElements = ARRAY_SIZE(layout_desc);

    init_pipeline_state_desc(&pso_desc, context.root_signature,
            context.render_target_desc.Format, &vs, &ps, &input_layout);
    pso_desc.GS = gs;
    hr = ID3D12Device_CreateGraphicsPipelineState(device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
    ok(hr == S_OK, "Failed to create graphics pipeline state, hr %#x.\n", hr);

    vb = create_upload_buffer(context.device, sizeof(vertices), vertices);
    vbv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vb);
    vbv.StrideInBytes = sizeof(*vertices);
    vbv.SizeInBytes = sizeof(vertices);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
    ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, 1, &vbv);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 12, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);

    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);
    reset_command_list(command_list, context.allocator);
    check_sub_resource_uint(context.render_target, 1, queue, command_list, 0xffffffff, 0);
    reset_command_list(command_list, context.allocator);
    check_sub_resource_uint(context.render_target, 2, queue, command_list, 0x00000000, 0);
    reset_command_list(command_list, context.allocator);
    check_sub_resource_uint(context.render_target, 3, queue, command_list, 0xffffff00, 0);

    ID3D12Resource_Release(vb);
    destroy_test_context(&context);
}

static void test_ps_layer(void)
{
    ID3D12GraphicsCommandList *command_list;
    ID3D10Blob *vs_bytecode, *ps_bytecode;
    struct test_context_desc desc;
    D3D12_SHADER_BYTECODE vs, ps;
    struct test_context context;
    ID3D12CommandQueue *queue;
    unsigned int i;

    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    static const char vs_code[] =
            "float4 main(in uint vertex_id : SV_VertexID, in uint instance_id : SV_InstanceID,\n"
            "        out uint layer : SV_RenderTargetArrayIndex) : SV_Position\n"
            "{\n"
            "    layer = instance_id;\n"
            "    switch (vertex_id)\n"
            "    {\n"
            "        case 0:\n"
            "            return float4(-1, 1, 0, 1);\n"
            "        case 1:\n"
            "            return float4(3, 1, 0, 1);\n"
            "        case 2:\n"
            "            return float4(-1, -3, 0, 1);\n"
            "        default:\n"
            "            return float4(0, 0, 0, 0);\n"
            "    }\n"
            "}\n";
    static const char ps_code[] =
            "float4 main(float4 p : SV_Position, uint layer : SV_RenderTargetArrayIndex) : SV_Target0\n"
            "{\n"
            "    return layer / 255.0;\n"
            "}\n";
    static const unsigned int expected_results[] =
    {
        0x00000000,
        0x01010101,
        0x02020202,
        0x03030303,
        0x04040404,
        0x05050505,
    };

    vs_bytecode = compile_shader(vs_code, sizeof(vs_code) - 1, "vs_4_0");
    vs = shader_bytecode_from_blob(vs_bytecode);

    ps_bytecode = compile_shader(ps_code, sizeof(ps_code) - 1, "ps_4_0");
    ps = shader_bytecode_from_blob(ps_bytecode);

    memset(&desc, 0, sizeof(desc));
    desc.rt_array_size = 6;
    desc.vs = &vs;
    desc.ps = &ps;
    if (!init_test_context(&context, &desc))
    {
        ID3D10Blob_Release(vs_bytecode);
        ID3D10Blob_Release(ps_bytecode);
        return;
    }

    if (!is_vs_array_index_supported(context.device))
    {
        skip("The device does not support SV_RenderTargetArrayIndex in VS.\n");
        destroy_test_context(&context);
        return;
    }

    command_list = context.list;
    queue = context.queue;

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 6, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);

    for (i = 0; i < ARRAY_SIZE(expected_results); ++i)
    {
        check_sub_resource_uint(context.render_target, i, queue, command_list, expected_results[i], 0);
        reset_command_list(command_list, context.allocator);
    }

    destroy_test_context(&context);

    ID3D10Blob_Release(vs_bytecode);
    ID3D10Blob_Release(ps_bytecode);
}

static void test_nop_tessellation_shaders(void)
{
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
    ID3D12GraphicsCommandList *command_list;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12CommandQueue *queue;
    struct vec4 tess_factors;
    unsigned int i;
    HRESULT hr;

    static const DWORD hs_cb_code[] =
    {
#if 0
        float4 tess_factor;

        struct data
        {
            float4 position : SV_Position;
            float r : RED;
            float g : GREEN;
            float b : BLUE;
        };

        struct patch_constant_data
        {
            float edges[3] : SV_TessFactor;
            float inside : SV_InsideTessFactor;
        };

        void patch_constant(InputPatch<data, 3> input, out patch_constant_data output)
        {
            output.edges[0] = tess_factor.x;
            output.edges[1] = tess_factor.y;
            output.edges[2] = tess_factor.z;
            output.inside = tess_factor.w;
        }

        [domain("tri")]
        [outputcontrolpoints(3)]
        [partitioning("integer")]
        [outputtopology("triangle_cw")]
        [patchconstantfunc("patch_constant")]
        data hs_main(InputPatch<data, 3> input, uint i : SV_OutputControlPointID)
        {
            return input[i];
        }
#endif
        0x43425844, 0x5898b436, 0x481490e6, 0xaadf8d29, 0xd440168e, 0x00000001, 0x000002d8, 0x00000004,
        0x00000030, 0x000000bc, 0x00000148, 0x000001dc, 0x4e475349, 0x00000084, 0x00000004, 0x00000008,
        0x00000068, 0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x00000f0f, 0x00000074, 0x00000000,
        0x00000000, 0x00000003, 0x00000001, 0x00000101, 0x00000078, 0x00000000, 0x00000000, 0x00000003,
        0x00000001, 0x00000202, 0x0000007e, 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x00000404,
        0x505f5653, 0x7469736f, 0x006e6f69, 0x00444552, 0x45455247, 0x4c42004e, 0xab004555, 0x4e47534f,
        0x00000084, 0x00000004, 0x00000008, 0x00000068, 0x00000000, 0x00000001, 0x00000003, 0x00000000,
        0x0000000f, 0x00000074, 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x00000e01, 0x00000078,
        0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x00000d02, 0x0000007e, 0x00000000, 0x00000000,
        0x00000003, 0x00000001, 0x00000b04, 0x505f5653, 0x7469736f, 0x006e6f69, 0x00444552, 0x45455247,
        0x4c42004e, 0xab004555, 0x47534350, 0x0000008c, 0x00000004, 0x00000008, 0x00000068, 0x00000000,
        0x0000000d, 0x00000003, 0x00000000, 0x00000e01, 0x00000068, 0x00000001, 0x0000000d, 0x00000003,
        0x00000001, 0x00000e01, 0x00000068, 0x00000002, 0x0000000d, 0x00000003, 0x00000002, 0x00000e01,
        0x00000076, 0x00000000, 0x0000000e, 0x00000003, 0x00000003, 0x00000e01, 0x545f5653, 0x46737365,
        0x6f746361, 0x56530072, 0x736e495f, 0x54656469, 0x46737365, 0x6f746361, 0xabab0072, 0x58454853,
        0x000000f4, 0x00030050, 0x0000003d, 0x01000071, 0x01001893, 0x01001894, 0x01001095, 0x01000896,
        0x01001897, 0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x01000073, 0x04000067,
        0x00102012, 0x00000000, 0x00000011, 0x06000036, 0x00102012, 0x00000000, 0x0020800a, 0x00000000,
        0x00000000, 0x0100003e, 0x01000073, 0x04000067, 0x00102012, 0x00000001, 0x00000012, 0x06000036,
        0x00102012, 0x00000001, 0x0020801a, 0x00000000, 0x00000000, 0x0100003e, 0x01000073, 0x04000067,
        0x00102012, 0x00000002, 0x00000013, 0x06000036, 0x00102012, 0x00000002, 0x0020802a, 0x00000000,
        0x00000000, 0x0100003e, 0x01000073, 0x04000067, 0x00102012, 0x00000003, 0x00000014, 0x06000036,
        0x00102012, 0x00000003, 0x0020803a, 0x00000000, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE hs_cb = {hs_cb_code, sizeof(hs_cb_code)};
#if 0
    struct data
    {
        float4 position : SV_Position;
        float r : RED;
        float g : GREEN;
        float b : BLUE;
    };

    struct patch_constant_data
    {
        float edges[3] : SV_TessFactor;
        float inside : SV_InsideTessFactor;
    };

    void patch_constant(InputPatch<data, 3> input, out patch_constant_data output)
    {
        output.edges[0] = output.edges[1] = output.edges[2] = 1.0f;
        output.inside = 1.0f;
    }

    [domain("tri")]
    [outputcontrolpoints(3)]
    [partitioning("integer")]
    [outputtopology("triangle_cw")]
    [patchconstantfunc("patch_constant")]
    data hs_main(InputPatch<data, 3> input, uint i : SV_OutputControlPointID)
    {
        return input[i];
    }

    [domain("tri")]
    void ds_main(patch_constant_data input,
            float3 tess_coord : SV_DomainLocation,
            const OutputPatch<data, 3> patch,
            out data output)
    {
        output.position = tess_coord.x * patch[0].position
                + tess_coord.y * patch[1].position
                + tess_coord.z * patch[2].position;
        output.r = tess_coord.x * patch[0].r + tess_coord.y * patch[1].r + tess_coord.z * patch[2].r;
        output.g = tess_coord.x * patch[0].g + tess_coord.y * patch[1].g + tess_coord.z * patch[2].g;
        output.b = tess_coord.x * patch[0].b + tess_coord.y * patch[1].b + tess_coord.z * patch[2].b;
    }

    void vs_main(uint id : SV_VertexID, out data output)
    {
        float2 coords = float2((id << 1) & 2, id & 2);
        output.position = float4(coords * float2(2, -2) + float2(-1, 1), 0, 1);
        output.r = 0.0;
        output.g = 1.0;
        output.b = 0.0;
    }

    float4 ps_main(data input) : sv_target
    {
        return float4(input.r, input.g, input.b, 1.0);
    }
#endif
    static const DWORD hs_code[] =
    {
        0x43425844, 0xb0b68b70, 0x6076b55f, 0x1bd848a8, 0xebe19ac5, 0x00000001, 0x000002bc, 0x00000004,
        0x00000030, 0x000000bc, 0x00000148, 0x000001dc, 0x4e475349, 0x00000084, 0x00000004, 0x00000008,
        0x00000068, 0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x00000f0f, 0x00000074, 0x00000000,
        0x00000000, 0x00000003, 0x00000001, 0x00000101, 0x00000078, 0x00000000, 0x00000000, 0x00000003,
        0x00000001, 0x00000202, 0x0000007e, 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x00000404,
        0x505f5653, 0x7469736f, 0x006e6f69, 0x00444552, 0x45455247, 0x4c42004e, 0xab004555, 0x4e47534f,
        0x00000084, 0x00000004, 0x00000008, 0x00000068, 0x00000000, 0x00000001, 0x00000003, 0x00000000,
        0x0000000f, 0x00000074, 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x00000e01, 0x00000078,
        0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x00000d02, 0x0000007e, 0x00000000, 0x00000000,
        0x00000003, 0x00000001, 0x00000b04, 0x505f5653, 0x7469736f, 0x006e6f69, 0x00444552, 0x45455247,
        0x4c42004e, 0xab004555, 0x47534350, 0x0000008c, 0x00000004, 0x00000008, 0x00000068, 0x00000000,
        0x0000000d, 0x00000003, 0x00000000, 0x00000e01, 0x00000068, 0x00000001, 0x0000000d, 0x00000003,
        0x00000001, 0x00000e01, 0x00000068, 0x00000002, 0x0000000d, 0x00000003, 0x00000002, 0x00000e01,
        0x00000076, 0x00000000, 0x0000000e, 0x00000003, 0x00000003, 0x00000e01, 0x545f5653, 0x46737365,
        0x6f746361, 0x56530072, 0x736e495f, 0x54656469, 0x46737365, 0x6f746361, 0xabab0072, 0x58454853,
        0x000000d8, 0x00030050, 0x00000036, 0x01000071, 0x01001893, 0x01001894, 0x01001095, 0x01000896,
        0x01001897, 0x0100086a, 0x01000073, 0x02000099, 0x00000003, 0x0200005f, 0x00017000, 0x04000067,
        0x00102012, 0x00000000, 0x00000011, 0x04000067, 0x00102012, 0x00000001, 0x00000012, 0x04000067,
        0x00102012, 0x00000002, 0x00000013, 0x02000068, 0x00000001, 0x0400005b, 0x00102012, 0x00000000,
        0x00000003, 0x04000036, 0x00100012, 0x00000000, 0x0001700a, 0x06000036, 0x00902012, 0x0010000a,
        0x00000000, 0x00004001, 0x3f800000, 0x0100003e, 0x01000073, 0x04000067, 0x00102012, 0x00000003,
        0x00000014, 0x05000036, 0x00102012, 0x00000003, 0x00004001, 0x3f800000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE hs = {hs_code, sizeof(hs_code)};
    static const DWORD ds_code[] =
    {
        0x43425844, 0x91d80cd6, 0x17d82aea, 0x9291f244, 0xa3494181, 0x00000001, 0x00000348, 0x00000004,
        0x00000030, 0x000000bc, 0x00000150, 0x000001dc, 0x4e475349, 0x00000084, 0x00000004, 0x00000008,
        0x00000068, 0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x00000f0f, 0x00000074, 0x00000000,
        0x00000000, 0x00000003, 0x00000001, 0x00000101, 0x00000078, 0x00000000, 0x00000000, 0x00000003,
        0x00000001, 0x00000202, 0x0000007e, 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x00000404,
        0x505f5653, 0x7469736f, 0x006e6f69, 0x00444552, 0x45455247, 0x4c42004e, 0xab004555, 0x47534350,
        0x0000008c, 0x00000004, 0x00000008, 0x00000068, 0x00000000, 0x0000000d, 0x00000003, 0x00000000,
        0x00000001, 0x00000068, 0x00000001, 0x0000000d, 0x00000003, 0x00000001, 0x00000001, 0x00000068,
        0x00000002, 0x0000000d, 0x00000003, 0x00000002, 0x00000001, 0x00000076, 0x00000000, 0x0000000e,
        0x00000003, 0x00000003, 0x00000001, 0x545f5653, 0x46737365, 0x6f746361, 0x56530072, 0x736e495f,
        0x54656469, 0x46737365, 0x6f746361, 0xabab0072, 0x4e47534f, 0x00000084, 0x00000004, 0x00000008,
        0x00000068, 0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x00000074, 0x00000000,
        0x00000000, 0x00000003, 0x00000001, 0x00000e01, 0x00000078, 0x00000000, 0x00000000, 0x00000003,
        0x00000001, 0x00000d02, 0x0000007e, 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x00000b04,
        0x505f5653, 0x7469736f, 0x006e6f69, 0x00444552, 0x45455247, 0x4c42004e, 0xab004555, 0x58454853,
        0x00000164, 0x00040050, 0x00000059, 0x01001893, 0x01001095, 0x0100086a, 0x0200005f, 0x0001c072,
        0x0400005f, 0x002190f2, 0x00000003, 0x00000000, 0x0400005f, 0x00219012, 0x00000003, 0x00000001,
        0x0400005f, 0x00219022, 0x00000003, 0x00000001, 0x0400005f, 0x00219042, 0x00000003, 0x00000001,
        0x04000067, 0x001020f2, 0x00000000, 0x00000001, 0x03000065, 0x00102012, 0x00000001, 0x03000065,
        0x00102022, 0x00000001, 0x03000065, 0x00102042, 0x00000001, 0x02000068, 0x00000001, 0x07000038,
        0x001000f2, 0x00000000, 0x0001c556, 0x00219e46, 0x00000001, 0x00000000, 0x09000032, 0x001000f2,
        0x00000000, 0x0001c006, 0x00219e46, 0x00000000, 0x00000000, 0x00100e46, 0x00000000, 0x09000032,
        0x001020f2, 0x00000000, 0x0001caa6, 0x00219e46, 0x00000002, 0x00000000, 0x00100e46, 0x00000000,
        0x07000038, 0x00100072, 0x00000000, 0x0001c556, 0x00219246, 0x00000001, 0x00000001, 0x09000032,
        0x00100072, 0x00000000, 0x0001c006, 0x00219246, 0x00000000, 0x00000001, 0x00100246, 0x00000000,
        0x09000032, 0x00102072, 0x00000001, 0x0001caa6, 0x00219246, 0x00000002, 0x00000001, 0x00100246,
        0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ds = {ds_code, sizeof(ds_code)};
    static const DWORD vs_code[] =
    {
        0x43425844, 0x63ca51fa, 0x5cd6c45d, 0x98d50fe7, 0x71c687a4, 0x00000001, 0x00000228, 0x00000003,
        0x0000002c, 0x00000060, 0x000000ec, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000006, 0x00000001, 0x00000000, 0x00000101, 0x565f5653, 0x65747265, 0x00444978,
        0x4e47534f, 0x00000084, 0x00000004, 0x00000008, 0x00000068, 0x00000000, 0x00000001, 0x00000003,
        0x00000000, 0x0000000f, 0x00000074, 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x00000e01,
        0x00000078, 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x00000d02, 0x0000007e, 0x00000000,
        0x00000000, 0x00000003, 0x00000001, 0x00000b04, 0x505f5653, 0x7469736f, 0x006e6f69, 0x00444552,
        0x45455247, 0x4c42004e, 0xab004555, 0x58454853, 0x00000134, 0x00010050, 0x0000004d, 0x0100086a,
        0x04000060, 0x00101012, 0x00000000, 0x00000006, 0x04000067, 0x001020f2, 0x00000000, 0x00000001,
        0x03000065, 0x00102012, 0x00000001, 0x03000065, 0x00102022, 0x00000001, 0x03000065, 0x00102042,
        0x00000001, 0x02000068, 0x00000001, 0x0b00008c, 0x00100012, 0x00000000, 0x00004001, 0x00000001,
        0x00004001, 0x00000001, 0x0010100a, 0x00000000, 0x00004001, 0x00000000, 0x07000001, 0x00100042,
        0x00000000, 0x0010100a, 0x00000000, 0x00004001, 0x00000002, 0x05000056, 0x00100032, 0x00000000,
        0x00100086, 0x00000000, 0x0f000032, 0x00102032, 0x00000000, 0x00100046, 0x00000000, 0x00004002,
        0x40000000, 0xc0000000, 0x00000000, 0x00000000, 0x00004002, 0xbf800000, 0x3f800000, 0x00000000,
        0x00000000, 0x08000036, 0x001020c2, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000,
        0x3f800000, 0x08000036, 0x00102072, 0x00000001, 0x00004002, 0x00000000, 0x3f800000, 0x00000000,
        0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE vs = {vs_code, sizeof(vs_code)};
    static const DWORD ps_code[] =
    {
        0x43425844, 0xbf404347, 0x7cda7395, 0x472b84ef, 0x884c2b47, 0x00000001, 0x0000015c, 0x00000003,
        0x0000002c, 0x000000b8, 0x000000ec, 0x4e475349, 0x00000084, 0x00000004, 0x00000008, 0x00000068,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x00000074, 0x00000000, 0x00000000,
        0x00000003, 0x00000001, 0x00000101, 0x00000078, 0x00000000, 0x00000000, 0x00000003, 0x00000001,
        0x00000202, 0x0000007e, 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x00000404, 0x505f5653,
        0x7469736f, 0x006e6f69, 0x00444552, 0x45455247, 0x4c42004e, 0xab004555, 0x4e47534f, 0x0000002c,
        0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x0000000f,
        0x745f7673, 0x65677261, 0xabab0074, 0x58454853, 0x00000068, 0x00000050, 0x0000001a, 0x0100086a,
        0x03001062, 0x00101012, 0x00000001, 0x03001062, 0x00101022, 0x00000001, 0x03001062, 0x00101042,
        0x00000001, 0x03000065, 0x001020f2, 0x00000000, 0x05000036, 0x00102072, 0x00000000, 0x00101246,
        0x00000001, 0x05000036, 0x00102082, 0x00000000, 0x00004001, 0x3f800000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};
    static const DWORD hs_index_range_code[] =
    {
#if 0
        float4 tess_factor;

        struct data
        {
            float4 position : SV_Position;
            float r : RED;
            float g : GREEN;
            float b : BLUE;
        };

        struct patch_constant_data
        {
            float edges[3] : SV_TessFactor;
            float inside : SV_InsideTessFactor;
        };

        void patch_constant(InputPatch<data, 3> input, out patch_constant_data output)
        {
            output.edges[0] = tess_factor.x;
            output.edges[1] = 1.0f;
            output.edges[2] = 1.0f;
            output.inside = tess_factor.y;
        }

        [domain("tri")]
        [outputcontrolpoints(3)]
        [partitioning("integer")]
        [outputtopology("triangle_cw")]
        [patchconstantfunc("patch_constant")]
        data hs_main(InputPatch<data, 3> input, uint i : SV_OutputControlPointID)
        {
            return input[i];
        }
#endif
        0x43425844, 0x286206d2, 0x7da51a27, 0x9024d379, 0xce869231, 0x00000001, 0x000002f4, 0x00000004,
        0x00000030, 0x000000bc, 0x00000148, 0x000001dc, 0x4e475349, 0x00000084, 0x00000004, 0x00000008,
        0x00000068, 0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x00000f0f, 0x00000074, 0x00000000,
        0x00000000, 0x00000003, 0x00000001, 0x00000101, 0x00000078, 0x00000000, 0x00000000, 0x00000003,
        0x00000001, 0x00000202, 0x0000007e, 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x00000404,
        0x505f5653, 0x7469736f, 0x006e6f69, 0x00444552, 0x45455247, 0x4c42004e, 0xab004555, 0x4e47534f,
        0x00000084, 0x00000004, 0x00000008, 0x00000068, 0x00000000, 0x00000001, 0x00000003, 0x00000000,
        0x0000000f, 0x00000074, 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x00000e01, 0x00000078,
        0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x00000d02, 0x0000007e, 0x00000000, 0x00000000,
        0x00000003, 0x00000001, 0x00000b04, 0x505f5653, 0x7469736f, 0x006e6f69, 0x00444552, 0x45455247,
        0x4c42004e, 0xab004555, 0x47534350, 0x0000008c, 0x00000004, 0x00000008, 0x00000068, 0x00000000,
        0x0000000d, 0x00000003, 0x00000000, 0x00000e01, 0x00000068, 0x00000001, 0x0000000d, 0x00000003,
        0x00000001, 0x00000e01, 0x00000068, 0x00000002, 0x0000000d, 0x00000003, 0x00000002, 0x00000e01,
        0x00000076, 0x00000000, 0x0000000e, 0x00000003, 0x00000003, 0x00000e01, 0x545f5653, 0x46737365,
        0x6f746361, 0x56530072, 0x736e495f, 0x54656469, 0x46737365, 0x6f746361, 0xabab0072, 0x58454853,
        0x00000110, 0x00030050, 0x00000044, 0x01000071, 0x01001893, 0x01001894, 0x01001095, 0x01000896,
        0x01001897, 0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x01000073, 0x04000067,
        0x00102012, 0x00000000, 0x00000011, 0x06000036, 0x00102012, 0x00000000, 0x0020800a, 0x00000000,
        0x00000000, 0x0100003e, 0x01000073, 0x02000099, 0x00000002, 0x0200005f, 0x00017000, 0x04000067,
        0x00102012, 0x00000001, 0x00000012, 0x04000067, 0x00102012, 0x00000002, 0x00000013, 0x02000068,
        0x00000001, 0x0400005b, 0x00102012, 0x00000001, 0x00000002, 0x04000036, 0x00100012, 0x00000000,
        0x0001700a, 0x07000036, 0x00d02012, 0x00000001, 0x0010000a, 0x00000000, 0x00004001, 0x3f800000,
        0x0100003e, 0x01000073, 0x04000067, 0x00102012, 0x00000003, 0x00000014, 0x06000036, 0x00102012,
        0x00000003, 0x0020801a, 0x00000000, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE hs_index_range = {hs_index_range_code, sizeof(hs_index_range_code)};
    static const D3D12_SHADER_BYTECODE *hull_shaders[] = {&hs_cb, &hs, &hs_index_range};

    memset(&desc, 0, sizeof(desc));
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    command_list = context.list;
    queue = context.queue;

    context.root_signature = create_32bit_constants_root_signature(context.device,
            0, 4, D3D12_SHADER_VISIBILITY_HULL);

    init_pipeline_state_desc(&pso_desc, context.root_signature,
            context.render_target_desc.Format, &vs, &ps, NULL);
    pso_desc.HS = hs_cb;
    pso_desc.DS = ds;
    hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    pso_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_PATCH;
    hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
    ok(hr == S_OK, "Failed to create state, hr %#x.\n", hr);
    ID3D12PipelineState_Release(context.pipeline_state);

    for (i = 0; i < ARRAY_SIZE(hull_shaders); ++i)
    {
        vkd3d_test_push_context("Test %u", i);

        pso_desc.HS = *hull_shaders[i];
        hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
                &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
        ok(hr == S_OK, "Failed to create state, hr %#x.\n", hr);

        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

        ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
        ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
        ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
        ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
        tess_factors.x = tess_factors.y = tess_factors.z = tess_factors.w = 1.0f;
        ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 0, 4, &tess_factors.x, 0);
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);

        bug_if(is_mvk_device(context.device))
        check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);

        reset_command_list(command_list, context.allocator);
        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

        ID3D12PipelineState_Release(context.pipeline_state);
        context.pipeline_state = NULL;

        vkd3d_test_pop_context();
    }

    destroy_test_context(&context);
}

struct triangle
{
    struct vec4 v[3];
};

#define check_triangles(a, b, c) check_triangles_(__FILE__, __LINE__, a, b, c)
static void check_triangles_(const char *file, unsigned int line, struct resource_readback *rb,
        const struct triangle *triangles, unsigned int triangle_count)
{
    const struct triangle *current, *expected;
    unsigned int i, j, offset;
    bool all_match = true;

    for (i = 0; i < triangle_count; ++i)
    {
        current = get_readback_data(rb, i, 0, 0, sizeof(*current));
        expected = &triangles[i];

        offset = ~0u;
        for (j = 0; j < ARRAY_SIZE(expected->v); ++j)
        {
            if (compare_vec4(&current->v[0], &expected->v[j], 0))
            {
                offset = j;
                break;
            }
        }

        if (offset == ~0u)
        {
            all_match = false;
            break;
        }

        for (j = 0; j < ARRAY_SIZE(expected->v); ++j)
        {
            if (!compare_vec4(&current->v[j], &expected->v[(j + offset) % 3], 0))
            {
                all_match = false;
                break;
            }
        }
        if (!all_match)
            break;
    }

    ok_(file, line)(all_match, "Triangle %u vertices {%.8e, %.8e, %.8e, %.8e}, "
            "{%.8e, %.8e, %.8e, %.8e}, {%.8e, %.8e, %.8e, %.8e} "
            "do not match {%.8e, %.8e, %.8e, %.8e}, {%.8e, %.8e, %.8e, %.8e}, "
            "{%.8e, %.8e, %.8e, %.8e}.\n", i,
            current->v[0].x, current->v[0].y, current->v[0].z, current->v[0].w,
            current->v[1].x, current->v[1].y, current->v[1].z, current->v[1].w,
            current->v[2].x, current->v[2].y, current->v[2].z, current->v[2].w,
            expected->v[0].x, expected->v[0].y, expected->v[0].z, expected->v[0].w,
            expected->v[1].x, expected->v[1].y, expected->v[1].z, expected->v[1].w,
            expected->v[2].x, expected->v[2].y, expected->v[2].z, expected->v[2].w);
}

static void test_quad_tessellation(void)
{
    static const DWORD vs_code[] =
    {
#if 0
        void main(float4 in_position : POSITION, out float4 out_position : SV_POSITION)
        {
            out_position = in_position;
        }
#endif
        0x43425844, 0xa7a2f22d, 0x83ff2560, 0xe61638bd, 0x87e3ce90, 0x00000001, 0x000000d8, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000f0f, 0x49534f50, 0x4e4f4954, 0xababab00,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000001, 0x00000003,
        0x00000000, 0x0000000f, 0x505f5653, 0x5449534f, 0x004e4f49, 0x52444853, 0x0000003c, 0x00010040,
        0x0000000f, 0x0300005f, 0x001010f2, 0x00000000, 0x04000067, 0x001020f2, 0x00000000, 0x00000001,
        0x05000036, 0x001020f2, 0x00000000, 0x00101e46, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE vs = {vs_code, sizeof(vs_code)};
#if 0
    struct point_data
    {
        float4 position : SV_POSITION;
    };

    struct patch_constant_data
    {
        float edges[4] : SV_TessFactor;
        float inside[2] : SV_InsideTessFactor;
    };

    float4 tess_factors;
    float2 inside_tess_factors;

    patch_constant_data patch_constant(InputPatch<point_data, 4> input)
    {
        patch_constant_data output;

        output.edges[0] = tess_factors.x;
        output.edges[1] = tess_factors.y;
        output.edges[2] = tess_factors.z;
        output.edges[3] = tess_factors.w;
        output.inside[0] = inside_tess_factors.x;
        output.inside[1] = inside_tess_factors.y;

        return output;
    }

    [domain("quad")]
    [outputcontrolpoints(4)]
    [outputtopology("triangle_ccw")]
    [partitioning("integer")]
    [patchconstantfunc("patch_constant")]
    point_data hs_main(InputPatch<point_data, 4> input,
            uint i : SV_OutputControlPointID)
    {
        return input[i];
    }

    [domain("quad")]
    point_data ds_main(patch_constant_data input,
            float2 tess_coord : SV_DomainLocation,
            const OutputPatch<point_data, 4> patch)
    {
        point_data output;

        float4 a = lerp(patch[0].position, patch[1].position, tess_coord.x);
        float4 b = lerp(patch[2].position, patch[3].position, tess_coord.x);
        output.position = lerp(a, b, tess_coord.y);

        return output;
    }
#endif
    static const DWORD hs_quad_ccw_code[] =
    {
        0x43425844, 0xdf8df700, 0x58b08fb1, 0xbd23d2c3, 0xcf884094, 0x00000001, 0x000002b8, 0x00000004,
        0x00000030, 0x00000064, 0x00000098, 0x0000015c, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008,
        0x00000020, 0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x00000f0f, 0x505f5653, 0x5449534f,
        0x004e4f49, 0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000,
        0x00000003, 0x00000000, 0x0000000f, 0x505f5653, 0x5449534f, 0x004e4f49, 0x47534350, 0x000000bc,
        0x00000006, 0x00000008, 0x00000098, 0x00000000, 0x0000000b, 0x00000003, 0x00000000, 0x00000e01,
        0x00000098, 0x00000001, 0x0000000b, 0x00000003, 0x00000001, 0x00000e01, 0x00000098, 0x00000002,
        0x0000000b, 0x00000003, 0x00000002, 0x00000e01, 0x00000098, 0x00000003, 0x0000000b, 0x00000003,
        0x00000003, 0x00000e01, 0x000000a6, 0x00000000, 0x0000000c, 0x00000003, 0x00000004, 0x00000e01,
        0x000000a6, 0x00000001, 0x0000000c, 0x00000003, 0x00000005, 0x00000e01, 0x545f5653, 0x46737365,
        0x6f746361, 0x56530072, 0x736e495f, 0x54656469, 0x46737365, 0x6f746361, 0xabab0072, 0x58454853,
        0x00000154, 0x00030050, 0x00000055, 0x01000071, 0x01002093, 0x01002094, 0x01001895, 0x01000896,
        0x01002097, 0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000002, 0x01000073, 0x04000067,
        0x00102012, 0x00000000, 0x0000000b, 0x06000036, 0x00102012, 0x00000000, 0x0020800a, 0x00000000,
        0x00000000, 0x0100003e, 0x01000073, 0x04000067, 0x00102012, 0x00000001, 0x0000000c, 0x06000036,
        0x00102012, 0x00000001, 0x0020801a, 0x00000000, 0x00000000, 0x0100003e, 0x01000073, 0x04000067,
        0x00102012, 0x00000002, 0x0000000d, 0x06000036, 0x00102012, 0x00000002, 0x0020802a, 0x00000000,
        0x00000000, 0x0100003e, 0x01000073, 0x04000067, 0x00102012, 0x00000003, 0x0000000e, 0x06000036,
        0x00102012, 0x00000003, 0x0020803a, 0x00000000, 0x00000000, 0x0100003e, 0x01000073, 0x04000067,
        0x00102012, 0x00000004, 0x0000000f, 0x06000036, 0x00102012, 0x00000004, 0x0020800a, 0x00000000,
        0x00000001, 0x0100003e, 0x01000073, 0x04000067, 0x00102012, 0x00000005, 0x00000010, 0x06000036,
        0x00102012, 0x00000005, 0x0020801a, 0x00000000, 0x00000001, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE hs_quad_ccw = {hs_quad_ccw_code, sizeof(hs_quad_ccw_code)};
    static const DWORD ds_code[] =
    {
        0x43425844, 0xeb6b7631, 0x07f5469e, 0xed0cbf4a, 0x7158b3a6, 0x00000001, 0x00000284, 0x00000004,
        0x00000030, 0x00000064, 0x00000128, 0x0000015c, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008,
        0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000f0f, 0x505f5653, 0x5449534f,
        0x004e4f49, 0x47534350, 0x000000bc, 0x00000006, 0x00000008, 0x00000098, 0x00000000, 0x0000000b,
        0x00000003, 0x00000000, 0x00000001, 0x00000098, 0x00000001, 0x0000000b, 0x00000003, 0x00000001,
        0x00000001, 0x00000098, 0x00000002, 0x0000000b, 0x00000003, 0x00000002, 0x00000001, 0x00000098,
        0x00000003, 0x0000000b, 0x00000003, 0x00000003, 0x00000001, 0x000000a6, 0x00000000, 0x0000000c,
        0x00000003, 0x00000004, 0x00000001, 0x000000a6, 0x00000001, 0x0000000c, 0x00000003, 0x00000005,
        0x00000001, 0x545f5653, 0x46737365, 0x6f746361, 0x56530072, 0x736e495f, 0x54656469, 0x46737365,
        0x6f746361, 0xabab0072, 0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000,
        0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x505f5653, 0x5449534f, 0x004e4f49, 0x58454853,
        0x00000120, 0x00040050, 0x00000048, 0x01002093, 0x01001895, 0x0100086a, 0x0200005f, 0x0001c032,
        0x0400005f, 0x002190f2, 0x00000004, 0x00000000, 0x04000067, 0x001020f2, 0x00000000, 0x00000001,
        0x02000068, 0x00000002, 0x0a000000, 0x001000f2, 0x00000000, 0x80219e46, 0x00000041, 0x00000002,
        0x00000000, 0x00219e46, 0x00000003, 0x00000000, 0x09000032, 0x001000f2, 0x00000000, 0x0001c006,
        0x00100e46, 0x00000000, 0x00219e46, 0x00000002, 0x00000000, 0x0a000000, 0x001000f2, 0x00000001,
        0x80219e46, 0x00000041, 0x00000000, 0x00000000, 0x00219e46, 0x00000001, 0x00000000, 0x09000032,
        0x001000f2, 0x00000001, 0x0001c006, 0x00100e46, 0x00000001, 0x00219e46, 0x00000000, 0x00000000,
        0x08000000, 0x001000f2, 0x00000000, 0x00100e46, 0x00000000, 0x80100e46, 0x00000041, 0x00000001,
        0x08000032, 0x001020f2, 0x00000000, 0x0001c556, 0x00100e46, 0x00000000, 0x00100e46, 0x00000001,
        0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ds = {ds_code, sizeof(ds_code)};
#if 0
    ...
    [outputtopology("triangle_cw")]
    ...
#endif
    static const DWORD hs_quad_cw_code[] =
    {
        0x43425844, 0x1ab30cc8, 0x94174771, 0x61f4cdd0, 0xa287f62c, 0x00000001, 0x000002b8, 0x00000004,
        0x00000030, 0x00000064, 0x00000098, 0x0000015c, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008,
        0x00000020, 0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x00000f0f, 0x505f5653, 0x5449534f,
        0x004e4f49, 0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000,
        0x00000003, 0x00000000, 0x0000000f, 0x505f5653, 0x5449534f, 0x004e4f49, 0x47534350, 0x000000bc,
        0x00000006, 0x00000008, 0x00000098, 0x00000000, 0x0000000b, 0x00000003, 0x00000000, 0x00000e01,
        0x00000098, 0x00000001, 0x0000000b, 0x00000003, 0x00000001, 0x00000e01, 0x00000098, 0x00000002,
        0x0000000b, 0x00000003, 0x00000002, 0x00000e01, 0x00000098, 0x00000003, 0x0000000b, 0x00000003,
        0x00000003, 0x00000e01, 0x000000a6, 0x00000000, 0x0000000c, 0x00000003, 0x00000004, 0x00000e01,
        0x000000a6, 0x00000001, 0x0000000c, 0x00000003, 0x00000005, 0x00000e01, 0x545f5653, 0x46737365,
        0x6f746361, 0x56530072, 0x736e495f, 0x54656469, 0x46737365, 0x6f746361, 0xabab0072, 0x58454853,
        0x00000154, 0x00030050, 0x00000055, 0x01000071, 0x01002093, 0x01002094, 0x01001895, 0x01000896,
        0x01001897, 0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000002, 0x01000073, 0x04000067,
        0x00102012, 0x00000000, 0x0000000b, 0x06000036, 0x00102012, 0x00000000, 0x0020800a, 0x00000000,
        0x00000000, 0x0100003e, 0x01000073, 0x04000067, 0x00102012, 0x00000001, 0x0000000c, 0x06000036,
        0x00102012, 0x00000001, 0x0020801a, 0x00000000, 0x00000000, 0x0100003e, 0x01000073, 0x04000067,
        0x00102012, 0x00000002, 0x0000000d, 0x06000036, 0x00102012, 0x00000002, 0x0020802a, 0x00000000,
        0x00000000, 0x0100003e, 0x01000073, 0x04000067, 0x00102012, 0x00000003, 0x0000000e, 0x06000036,
        0x00102012, 0x00000003, 0x0020803a, 0x00000000, 0x00000000, 0x0100003e, 0x01000073, 0x04000067,
        0x00102012, 0x00000004, 0x0000000f, 0x06000036, 0x00102012, 0x00000004, 0x0020800a, 0x00000000,
        0x00000001, 0x0100003e, 0x01000073, 0x04000067, 0x00102012, 0x00000005, 0x00000010, 0x06000036,
        0x00102012, 0x00000005, 0x0020801a, 0x00000000, 0x00000001, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE hs_quad_cw = {hs_quad_cw_code, sizeof(hs_quad_cw_code)};
    static const struct vec4 quad[] =
    {
        {-1.0f, -1.0f, 0.0f, 1.0f},
        {-1.0f,  1.0f, 0.0f, 1.0f},
        { 1.0f, -1.0f, 0.0f, 1.0f},
        { 1.0f,  1.0f, 0.0f, 1.0f},
    };
    static const D3D12_INPUT_ELEMENT_DESC layout_desc[] =
    {
        {"POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
    };
    static const D3D12_SO_DECLARATION_ENTRY so_declaration[] =
    {
        {0, "SV_POSITION", 0, 0, 4, 0},
    };
    unsigned int strides[] = {16};
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    static const BYTE zero_data[2048];
    static const struct triangle expected_quad_ccw[] =
    {
        {{{-1.0f, -1.0f, 0.0f, 1.0f},
          { 1.0f, -1.0f, 0.0f, 1.0f},
          {-1.0f,  1.0f, 0.0f, 1.0f}}},
        {{{-1.0f,  1.0f, 0.0f, 1.0f},
          { 1.0f, -1.0f, 0.0f, 1.0f},
          { 1.0f,  1.0f, 0.0f, 1.0f}}},
        {{{ 0.0f,  0.0f, 0.0f, 0.0f},
          { 0.0f,  0.0f, 0.0f, 0.0f},
          { 0.0f,  0.0f, 0.0f, 0.0f}}},
    };
    static const struct triangle expected_quad_cw[] =
    {
        {{{-1.0f, -1.0f, 0.0f, 1.0f},
          {-1.0f,  1.0f, 0.0f, 1.0f},
          { 1.0f, -1.0f, 0.0f, 1.0f}}},
        {{{-1.0f,  1.0f, 0.0f, 1.0f},
          { 1.0f,  1.0f, 0.0f, 1.0f},
          { 1.0f, -1.0f, 0.0f, 1.0f}}},
        {{{ 0.0f,  0.0f, 0.0f, 0.0f},
          { 0.0f,  0.0f, 0.0f, 0.0f},
          { 0.0f,  0.0f, 0.0f, 0.0f}}},
    };
    struct
    {
        float tess_factors[4];
        float inside_tess_factors[2];
        uint32_t padding[2];
    } constant;

    ID3D12Resource *vb, *so_buffer, *upload_buffer, *readback_buffer;
    D3D12_QUERY_DATA_SO_STATISTICS *so_statistics;
    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
    ID3D12GraphicsCommandList *command_list;
    D3D12_QUERY_HEAP_DESC query_heap_desc;
    D3D12_STREAM_OUTPUT_BUFFER_VIEW sobv;
    D3D12_INPUT_LAYOUT_DESC input_layout;
    struct d3d12_resource_readback rb;
    struct test_context_desc desc;
    D3D12_VERTEX_BUFFER_VIEW vbv;
    ID3D12QueryHeap *query_heap;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Device *device;
    unsigned int i;
    HRESULT hr;

    memset(&desc, 0, sizeof(desc));
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    query_heap_desc.Type = D3D12_QUERY_HEAP_TYPE_SO_STATISTICS;
    query_heap_desc.Count = 2;
    query_heap_desc.NodeMask = 0;
    hr = ID3D12Device_CreateQueryHeap(device, &query_heap_desc, &IID_ID3D12QueryHeap, (void **)&query_heap);
    if (hr == E_NOTIMPL)
    {
        skip("Stream output is not supported.\n");
        destroy_test_context(&context);
        return;
    }
    ok(hr == S_OK, "Failed to create query heap, hr %#x.\n", hr);

    context.root_signature = create_32bit_constants_root_signature_(__FILE__, __LINE__,
            device, 0, 6, D3D12_SHADER_VISIBILITY_HULL,
            D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT
            | D3D12_ROOT_SIGNATURE_FLAG_ALLOW_STREAM_OUTPUT);

    input_layout.pInputElementDescs = layout_desc;
    input_layout.NumElements = ARRAY_SIZE(layout_desc);

    init_pipeline_state_desc(&pso_desc, context.root_signature,
            context.render_target_desc.Format, NULL, NULL, &input_layout);
    pso_desc.VS = vs;
    pso_desc.HS = hs_quad_cw;
    pso_desc.DS = ds;
    pso_desc.StreamOutput.NumEntries = ARRAY_SIZE(so_declaration);
    pso_desc.StreamOutput.pSODeclaration = so_declaration;
    pso_desc.StreamOutput.pBufferStrides = strides;
    pso_desc.StreamOutput.NumStrides = ARRAY_SIZE(strides);
    pso_desc.StreamOutput.RasterizedStream = 0;
    pso_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_PATCH;

    vb = create_upload_buffer(device, sizeof(quad), quad);

    vbv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vb);
    vbv.StrideInBytes = sizeof(*quad);
    vbv.SizeInBytes = sizeof(quad);

    upload_buffer = create_upload_buffer(device, sizeof(zero_data), &zero_data);

    so_buffer = create_default_buffer(device, sizeof(zero_data),
            D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST);

    ID3D12GraphicsCommandList_CopyBufferRegion(command_list, so_buffer, 0,
            upload_buffer, 0, sizeof(zero_data));
    transition_resource_state(command_list, so_buffer,
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_STREAM_OUT);

    sobv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(so_buffer);
    sobv.SizeInBytes = 1024;
    sobv.BufferFilledSizeLocation = sobv.BufferLocation + sobv.SizeInBytes;

    for (i = 0; i < ARRAY_SIZE(constant.tess_factors); ++i)
        constant.tess_factors[i] = 1.0f;
    for (i = 0; i < ARRAY_SIZE(constant.inside_tess_factors); ++i)
        constant.inside_tess_factors[i] = 1.0f;

    pso_desc.HS = hs_quad_ccw;
    hr = ID3D12Device_CreateGraphicsPipelineState(device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
    ok(hr == S_OK, "Failed to create state, hr %#x.\n", hr);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_4_CONTROL_POINT_PATCHLIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 0, 6, &constant, 0);
    ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, 1, &vbv);
    ID3D12GraphicsCommandList_SOSetTargets(command_list, 0, 1, &sobv);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xffffffff, 0);

    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    transition_resource_state(command_list, so_buffer,
            D3D12_RESOURCE_STATE_STREAM_OUT, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_buffer_readback_with_command_list(so_buffer, DXGI_FORMAT_UNKNOWN, &rb, queue, command_list);
    check_triangles(&rb.rb, expected_quad_ccw, ARRAY_SIZE(expected_quad_ccw));
    release_resource_readback(&rb);

    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, so_buffer,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_COPY_DEST);
    ID3D12GraphicsCommandList_CopyBufferRegion(command_list, so_buffer, 0,
            upload_buffer, 0, sizeof(zero_data));
    transition_resource_state(command_list, so_buffer,
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_STREAM_OUT);

    ID3D12PipelineState_Release(context.pipeline_state);
    pso_desc.HS = hs_quad_cw;
    hr = ID3D12Device_CreateGraphicsPipelineState(device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
    ok(hr == S_OK, "Failed to create state, hr %#x.\n", hr);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_4_CONTROL_POINT_PATCHLIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 0, 6, &constant, 0);
    ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, 1, &vbv);
    ID3D12GraphicsCommandList_SOSetTargets(command_list, 0, 1, &sobv);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);

    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    transition_resource_state(command_list, so_buffer,
            D3D12_RESOURCE_STATE_STREAM_OUT, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_buffer_readback_with_command_list(so_buffer, DXGI_FORMAT_UNKNOWN, &rb, queue, command_list);
    check_triangles(&rb.rb, expected_quad_cw, ARRAY_SIZE(expected_quad_cw));
    release_resource_readback(&rb);

    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, so_buffer,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_COPY_DEST);
    ID3D12GraphicsCommandList_CopyBufferRegion(command_list, so_buffer, 0,
            upload_buffer, 0, sizeof(zero_data));
    transition_resource_state(command_list, so_buffer,
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_STREAM_OUT);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_4_CONTROL_POINT_PATCHLIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, 1, &vbv);
    ID3D12GraphicsCommandList_SOSetTargets(command_list, 0, 1, &sobv);

    ID3D12GraphicsCommandList_BeginQuery(command_list, query_heap, D3D12_QUERY_TYPE_SO_STATISTICS_STREAM0, 0);

    for (i = 0; i < ARRAY_SIZE(constant.tess_factors); ++i)
        constant.tess_factors[i] = 2.0f;
    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 0, 6, &constant, 0);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 1, 0, 0);

    constant.tess_factors[0] = 0.0f; /* A patch is discarded. */
    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 0, 6, &constant, 0);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 1, 0, 0);

    ID3D12GraphicsCommandList_EndQuery(command_list, query_heap, D3D12_QUERY_TYPE_SO_STATISTICS_STREAM0, 0);

    ID3D12GraphicsCommandList_BeginQuery(command_list, query_heap, D3D12_QUERY_TYPE_SO_STATISTICS_STREAM0, 1);

    constant.tess_factors[0] = 5.0f;
    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 0, 6, &constant, 0);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 1, 0, 0);

    ID3D12GraphicsCommandList_EndQuery(command_list, query_heap, D3D12_QUERY_TYPE_SO_STATISTICS_STREAM0, 1);

    readback_buffer = create_readback_buffer(device, 2 * sizeof(*so_statistics));
    ID3D12GraphicsCommandList_ResolveQueryData(command_list,
            query_heap, D3D12_QUERY_TYPE_SO_STATISTICS_STREAM0, 0, 2, readback_buffer, 0);

    get_buffer_readback_with_command_list(readback_buffer, DXGI_FORMAT_UNKNOWN, &rb, queue, command_list);
    so_statistics = get_readback_data(&rb.rb, 0, 0, 0, sizeof(*so_statistics));
    ok(so_statistics[0].NumPrimitivesWritten == 8, "Got unexpected primitives written %u.\n",
            (unsigned int)so_statistics[0].NumPrimitivesWritten);
    ok(so_statistics[0].PrimitivesStorageNeeded == 8, "Got unexpected primitives storage needed %u.\n",
            (unsigned int)so_statistics[0].PrimitivesStorageNeeded);
    ok(so_statistics[1].NumPrimitivesWritten == 11, "Got unexpected primitives written %u.\n",
            (unsigned int)so_statistics[1].NumPrimitivesWritten);
    ok(so_statistics[1].PrimitivesStorageNeeded == 11, "Got unexpected primitives storage needed %u.\n",
            (unsigned int)so_statistics[1].PrimitivesStorageNeeded);
    release_resource_readback(&rb);

    ID3D12Resource_Release(readback_buffer);
    ID3D12Resource_Release(so_buffer);
    ID3D12Resource_Release(upload_buffer);
    ID3D12Resource_Release(vb);
    ID3D12QueryHeap_Release(query_heap);
    destroy_test_context(&context);
}

static void test_tessellation_dcl_index_range(void)
{
    static const DWORD vs_code[] =
    {
#if 0
        void main(float4 in_position : POSITION, out float4 out_position : SV_POSITION)
        {
            out_position = in_position;
        }
#endif
        0x43425844, 0xa7a2f22d, 0x83ff2560, 0xe61638bd, 0x87e3ce90, 0x00000001, 0x000000d8, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000f0f, 0x49534f50, 0x4e4f4954, 0xababab00,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000001, 0x00000003,
        0x00000000, 0x0000000f, 0x505f5653, 0x5449534f, 0x004e4f49, 0x52444853, 0x0000003c, 0x00010040,
        0x0000000f, 0x0300005f, 0x001010f2, 0x00000000, 0x04000067, 0x001020f2, 0x00000000, 0x00000001,
        0x05000036, 0x001020f2, 0x00000000, 0x00101e46, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE vs = {vs_code, sizeof(vs_code)};
#if 0
    struct point_data
    {
        float4 position : SV_POSITION;
    };

    struct patch_constant_data
    {
        float edges[4] : SV_TessFactor;
        float inside[2] : SV_InsideTessFactor;
    };

    patch_constant_data patch_constant(InputPatch<point_data, 4> input)
    {
        patch_constant_data output;

        output.edges[0] = 1.0f;
        output.edges[1] = 1.0f;
        output.edges[2] = 1.0f;
        output.edges[3] = 1.0f;
        output.inside[0] = 1.0f;
        output.inside[1] = 1.0f;

        return output;
    }

    [domain("quad")]
    [outputcontrolpoints(4)]
    [outputtopology("triangle_cw")]
    [partitioning("integer")]
    [patchconstantfunc("patch_constant")]
    point_data hs_main(InputPatch<point_data, 4> input,
            uint i : SV_OutputControlPointID)
    {
        return input[i];
    }

    [domain("quad")]
    point_data ds_main(patch_constant_data input,
            float2 tess_coord : SV_DomainLocation,
            const OutputPatch<point_data, 4> patch)
    {
        point_data output;

        float4 a = lerp(patch[0].position, patch[1].position, tess_coord.x);
        float4 b = lerp(patch[2].position, patch[3].position, tess_coord.x);
        output.position = lerp(a, b, tess_coord.y);

        return output;
    }
#endif
    static const DWORD hs_code[] =
    {
        0x43425844, 0x0a619042, 0x424471f9, 0x9f0f4ff1, 0x065efacc, 0x00000001, 0x0000029c, 0x00000004,
        0x00000030, 0x00000064, 0x00000098, 0x0000015c, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008,
        0x00000020, 0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x00000f0f, 0x505f5653, 0x5449534f,
        0x004e4f49, 0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000001,
        0x00000003, 0x00000000, 0x0000000f, 0x505f5653, 0x5449534f, 0x004e4f49, 0x47534350, 0x000000bc,
        0x00000006, 0x00000008, 0x00000098, 0x00000000, 0x0000000b, 0x00000003, 0x00000000, 0x00000e01,
        0x00000098, 0x00000001, 0x0000000b, 0x00000003, 0x00000001, 0x00000e01, 0x00000098, 0x00000002,
        0x0000000b, 0x00000003, 0x00000002, 0x00000e01, 0x00000098, 0x00000003, 0x0000000b, 0x00000003,
        0x00000003, 0x00000e01, 0x000000a6, 0x00000000, 0x0000000c, 0x00000003, 0x00000004, 0x00000e01,
        0x000000a6, 0x00000001, 0x0000000c, 0x00000003, 0x00000005, 0x00000e01, 0x545f5653, 0x46737365,
        0x6f746361, 0x56530072, 0x736e495f, 0x54656469, 0x46737365, 0x6f746361, 0xabab0072, 0x58454853,
        0x00000138, 0x00030050, 0x0000004e, 0x01000071, 0x01002093, 0x01002094, 0x01001895, 0x01000896,
        0x01001897, 0x0100086a, 0x01000073, 0x02000099, 0x00000004, 0x0200005f, 0x00017000, 0x04000067,
        0x00102012, 0x00000000, 0x0000000b, 0x04000067, 0x00102012, 0x00000001, 0x0000000c, 0x04000067,
        0x00102012, 0x00000002, 0x0000000d, 0x04000067, 0x00102012, 0x00000003, 0x0000000e, 0x02000068,
        0x00000001, 0x0400005b, 0x00102012, 0x00000000, 0x00000004, 0x04000036, 0x00100012, 0x00000000,
        0x0001700a, 0x06000036, 0x00902012, 0x0010000a, 0x00000000, 0x00004001, 0x3f800000, 0x0100003e,
        0x01000073, 0x02000099, 0x00000002, 0x0200005f, 0x00017000, 0x04000067, 0x00102012, 0x00000004,
        0x0000000f, 0x04000067, 0x00102012, 0x00000005, 0x00000010, 0x02000068, 0x00000001, 0x0400005b,
        0x00102012, 0x00000004, 0x00000002, 0x04000036, 0x00100012, 0x00000000, 0x0001700a, 0x07000036,
        0x00d02012, 0x00000004, 0x0010000a, 0x00000000, 0x00004001, 0x3f800000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE hs = {hs_code, sizeof(hs_code)};
    static const DWORD ds_code[] =
    {
        0x43425844, 0x4f187d50, 0x6743fe93, 0x10dfbe63, 0xf8cfd202, 0x00000001, 0x00000284, 0x00000004,
        0x00000030, 0x00000064, 0x00000128, 0x0000015c, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008,
        0x00000020, 0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x00000f0f, 0x505f5653, 0x5449534f,
        0x004e4f49, 0x47534350, 0x000000bc, 0x00000006, 0x00000008, 0x00000098, 0x00000000, 0x0000000b,
        0x00000003, 0x00000000, 0x00000001, 0x00000098, 0x00000001, 0x0000000b, 0x00000003, 0x00000001,
        0x00000001, 0x00000098, 0x00000002, 0x0000000b, 0x00000003, 0x00000002, 0x00000001, 0x00000098,
        0x00000003, 0x0000000b, 0x00000003, 0x00000003, 0x00000001, 0x000000a6, 0x00000000, 0x0000000c,
        0x00000003, 0x00000004, 0x00000001, 0x000000a6, 0x00000001, 0x0000000c, 0x00000003, 0x00000005,
        0x00000001, 0x545f5653, 0x46737365, 0x6f746361, 0x56530072, 0x736e495f, 0x54656469, 0x46737365,
        0x6f746361, 0xabab0072, 0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000,
        0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x505f5653, 0x5449534f, 0x004e4f49, 0x58454853,
        0x00000120, 0x00040050, 0x00000048, 0x01002093, 0x01001895, 0x0100086a, 0x0200005f, 0x0001c032,
        0x0400005f, 0x002190f2, 0x00000004, 0x00000000, 0x04000067, 0x001020f2, 0x00000000, 0x00000001,
        0x02000068, 0x00000002, 0x0a000000, 0x001000f2, 0x00000000, 0x80219e46, 0x00000041, 0x00000002,
        0x00000000, 0x00219e46, 0x00000003, 0x00000000, 0x09000032, 0x001000f2, 0x00000000, 0x0001c006,
        0x00100e46, 0x00000000, 0x00219e46, 0x00000002, 0x00000000, 0x0a000000, 0x001000f2, 0x00000001,
        0x80219e46, 0x00000041, 0x00000000, 0x00000000, 0x00219e46, 0x00000001, 0x00000000, 0x09000032,
        0x001000f2, 0x00000001, 0x0001c006, 0x00100e46, 0x00000001, 0x00219e46, 0x00000000, 0x00000000,
        0x08000000, 0x001000f2, 0x00000000, 0x00100e46, 0x00000000, 0x80100e46, 0x00000041, 0x00000001,
        0x08000032, 0x001020f2, 0x00000000, 0x0001c556, 0x00100e46, 0x00000000, 0x00100e46, 0x00000001,
        0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ds = {ds_code, sizeof(ds_code)};
    static const struct vec4 quad[] =
    {
        {-1.0f, -1.0f, 0.0f, 1.0f},
        {-1.0f,  1.0f, 0.0f, 1.0f},
        { 1.0f, -1.0f, 0.0f, 1.0f},
        { 1.0f,  1.0f, 0.0f, 1.0f},
    };
    static const D3D12_INPUT_ELEMENT_DESC layout_desc[] =
    {
        {"POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
    };
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};

    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
    ID3D12GraphicsCommandList *command_list;
    D3D12_INPUT_LAYOUT_DESC input_layout;
    struct test_context_desc desc;
    D3D12_VERTEX_BUFFER_VIEW vbv;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Device *device;
    ID3D12Resource *vb;
    HRESULT hr;

    memset(&desc, 0, sizeof(desc));
    desc.root_signature_flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
    desc.no_pipeline = true;
    if (!init_test_context(&context, &desc))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    input_layout.pInputElementDescs = layout_desc;
    input_layout.NumElements = ARRAY_SIZE(layout_desc);

    init_pipeline_state_desc(&pso_desc, context.root_signature,
            context.render_target_desc.Format, NULL, NULL, &input_layout);
    pso_desc.VS = vs;
    pso_desc.HS = hs;
    pso_desc.DS = ds;
    pso_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_PATCH;
    hr = ID3D12Device_CreateGraphicsPipelineState(device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
    ok(hr == S_OK, "Failed to create state, hr %#x.\n", hr);

    vb = create_upload_buffer(device, sizeof(quad), quad);

    vbv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vb);
    vbv.StrideInBytes = sizeof(*quad);
    vbv.SizeInBytes = sizeof(quad);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_4_CONTROL_POINT_PATCHLIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, 1, &vbv);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    bug_if(is_mvk_device(device))
    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);

    ID3D12Resource_Release(vb);
    destroy_test_context(&context);
}

static void test_hull_shader_control_point_phase(void)
{
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
    ID3D12GraphicsCommandList *command_list;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12CommandQueue *queue;
    HRESULT hr;

    static const DWORD vs_code[] =
    {
#if 0
        void main()
        {
        }
#endif
        0x43425844, 0x590b08ae, 0x11d28adb, 0x825a5628, 0x34c0c208, 0x00000001, 0x00000064, 0x00000003,
        0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x00000010, 0x00010050, 0x00000004, 0x0100086a,
        0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE vs = {vs_code, sizeof(vs_code)};
#if 0
    struct data
    {
        float4 position : SV_Position;
    };

    struct patch_constant_data
    {
        float edges[3] : SV_TessFactor;
        float inside : SV_InsideTessFactor;
    };

    void patch_constant(out patch_constant_data output)
    {
        output.edges[0] = output.edges[1] = output.edges[2] = 1.0f;
        output.inside = 1.0f;
    }

    [domain("tri")]
    [outputcontrolpoints(3)]
    [partitioning("integer")]
    [outputtopology("triangle_cw")]
    [patchconstantfunc("patch_constant")]
    data hs_main(uint i : SV_OutputControlPointID)
    {
        data output;

        if (i == 0)
            output.position = float4(-1, 1, 0, 1);
        else if (i == 1)
            output.position = float4(3, 1, 0, 1);
        else
            output.position = float4(-1, -3, 0, 1);

        return output;
    }

    [domain("tri")]
    void ds_main(patch_constant_data input,
            float3 tess_coord : SV_DomainLocation,
            const OutputPatch<data, 3> patch,
            out data output)
    {
        uint index = uint(tess_coord.y + 2 * tess_coord.z);
        output.position = patch[index].position;
    }
#endif
    static const DWORD hs_code[] =
    {
        0x43425844, 0x4204890e, 0x43f4f8e0, 0xbff7edfd, 0x0a48b715, 0x00000001, 0x0000028c, 0x00000004,
        0x00000030, 0x00000040, 0x00000074, 0x00000108, 0x4e475349, 0x00000008, 0x00000000, 0x00000008,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000001, 0x00000003,
        0x00000000, 0x0000000f, 0x505f5653, 0x7469736f, 0x006e6f69, 0x47534350, 0x0000008c, 0x00000004,
        0x00000008, 0x00000068, 0x00000000, 0x0000000d, 0x00000003, 0x00000000, 0x00000e01, 0x00000068,
        0x00000001, 0x0000000d, 0x00000003, 0x00000001, 0x00000e01, 0x00000068, 0x00000002, 0x0000000d,
        0x00000003, 0x00000002, 0x00000e01, 0x00000076, 0x00000000, 0x0000000e, 0x00000003, 0x00000003,
        0x00000e01, 0x545f5653, 0x46737365, 0x6f746361, 0x56530072, 0x736e495f, 0x54656469, 0x46737365,
        0x6f746361, 0xabab0072, 0x58454853, 0x0000017c, 0x00030050, 0x0000005f, 0x01000071, 0x01000893,
        0x01001894, 0x01001095, 0x01000896, 0x01001897, 0x0100086a, 0x01000072, 0x0200005f, 0x00016000,
        0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000001, 0x06000020, 0x00100012, 0x00000000,
        0x00016001, 0x00004001, 0x00000001, 0x0f000037, 0x001000f2, 0x00000000, 0x00100006, 0x00000000,
        0x00004002, 0x40400000, 0x3f800000, 0x00000000, 0x3f800000, 0x00004002, 0xbf800000, 0xc0400000,
        0x00000000, 0x3f800000, 0x0b000037, 0x001020f2, 0x00000000, 0x00016001, 0x00100e46, 0x00000000,
        0x00004002, 0xbf800000, 0x3f800000, 0x00000000, 0x3f800000, 0x0100003e, 0x01000073, 0x02000099,
        0x00000003, 0x0200005f, 0x00017000, 0x04000067, 0x00102012, 0x00000000, 0x00000011, 0x04000067,
        0x00102012, 0x00000001, 0x00000012, 0x04000067, 0x00102012, 0x00000002, 0x00000013, 0x02000068,
        0x00000001, 0x0400005b, 0x00102012, 0x00000000, 0x00000003, 0x04000036, 0x00100012, 0x00000000,
        0x0001700a, 0x06000036, 0x00902012, 0x0010000a, 0x00000000, 0x00004001, 0x3f800000, 0x0100003e,
        0x01000073, 0x04000067, 0x00102012, 0x00000003, 0x00000014, 0x05000036, 0x00102012, 0x00000003,
        0x00004001, 0x3f800000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE hs = {hs_code, sizeof(hs_code)};
    static const DWORD ds_code[] =
    {
        0x43425844, 0x33c6120a, 0x2d46da82, 0x2c17dddf, 0x252ae7e0, 0x00000001, 0x000001c8, 0x00000004,
        0x00000030, 0x00000064, 0x000000f8, 0x0000012c, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008,
        0x00000020, 0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x00000f0f, 0x505f5653, 0x7469736f,
        0x006e6f69, 0x47534350, 0x0000008c, 0x00000004, 0x00000008, 0x00000068, 0x00000000, 0x0000000d,
        0x00000003, 0x00000000, 0x00000001, 0x00000068, 0x00000001, 0x0000000d, 0x00000003, 0x00000001,
        0x00000001, 0x00000068, 0x00000002, 0x0000000d, 0x00000003, 0x00000002, 0x00000001, 0x00000076,
        0x00000000, 0x0000000e, 0x00000003, 0x00000003, 0x00000001, 0x545f5653, 0x46737365, 0x6f746361,
        0x56530072, 0x736e495f, 0x54656469, 0x46737365, 0x6f746361, 0xabab0072, 0x4e47534f, 0x0000002c,
        0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f,
        0x505f5653, 0x7469736f, 0x006e6f69, 0x58454853, 0x00000094, 0x00040050, 0x00000025, 0x01001893,
        0x01001095, 0x0100086a, 0x0200005f, 0x0001c062, 0x0400005f, 0x002190f2, 0x00000003, 0x00000000,
        0x04000067, 0x001020f2, 0x00000000, 0x00000001, 0x02000068, 0x00000001, 0x07000032, 0x00100012,
        0x00000000, 0x0001c02a, 0x00004001, 0x40000000, 0x0001c01a, 0x0500001c, 0x00100012, 0x00000000,
        0x0010000a, 0x00000000, 0x07000036, 0x001020f2, 0x00000000, 0x00a19e46, 0x0010000a, 0x00000000,
        0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ds = {ds_code, sizeof(ds_code)};

    memset(&desc, 0, sizeof(desc));
    desc.no_pipeline = true;
    if (!init_test_context(&context, &desc))
        return;
    command_list = context.list;
    queue = context.queue;

    init_pipeline_state_desc(&pso_desc, context.root_signature,
            context.render_target_desc.Format, NULL, NULL, NULL);
    pso_desc.VS = vs;
    pso_desc.HS = hs;
    pso_desc.DS = ds;
    pso_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_PATCH;
    hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
    ok(hr == S_OK, "Failed to create state, hr %#x.\n", hr);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);

    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);

    destroy_test_context(&context);
}

static void test_hull_shader_fork_phase(void)
{
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
    ID3D12GraphicsCommandList *command_list;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12CommandQueue *queue;
    HRESULT hr;

    static const DWORD vs_code[] =
    {
#if 0
        float4 main() : SV_Position
        {
            return float4(-1, -1, 0, 0);
        }
#endif
        0x43425844, 0x9f5f70e7, 0x57507df4, 0xbf1a4a34, 0xdb2531df, 0x00000001, 0x000000b8, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000001, 0x00000003, 0x00000000,
        0x0000000f, 0x505f5653, 0x7469736f, 0x006e6f69, 0x58454853, 0x00000040, 0x00010050, 0x00000010,
        0x0100086a, 0x04000067, 0x001020f2, 0x00000000, 0x00000001, 0x08000036, 0x001020f2, 0x00000000,
        0x00004002, 0xbf800000, 0xbf800000, 0x00000000, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE vs = {vs_code, sizeof(vs_code)};
#if 0
    struct data
    {
        float4 position : SV_Position;
    };

    struct patch_constant_data
    {
        float edges[4] : SV_TessFactor;
        float inside[2] : SV_InsideTessFactor;
        float3 center : CENTER;
    };

    void patch_constant(OutputPatch<data, 4> control_points,
            out patch_constant_data output)
    {
        uint i;

        output = (patch_constant_data)0;

        for (i = 0; i < 4; ++i)
        {
            output.edges[i] = 1.0;
        }

        for (i = 0; i < 2; ++i)
        {
            output.inside[i] = 1.0;
        }

        for (i = 0; i < 4; ++i)
        {
            output.center += control_points[i].position.xyz;
        }
        output.center /= 4;
    }

    [domain("quad")]
    [outputcontrolpoints(4)]
    [partitioning("integer")]
    [outputtopology("triangle_cw")]
    [patchconstantfunc("patch_constant")]
    data hs_main(InputPatch<data, 1> input,
            uint i : SV_OutputControlPointID)
    {
        data o = (data)0;
        const float4 vertices[] =
        {
            float4(0, 0, 0, 1),
            float4(0, 2, 0, 1),
            float4(2, 0, 0, 1),
            float4(2, 2, 0, 1),
        };

        o.position = input[0].position + vertices[i];

        return o;
    }

    [domain("quad")]
    void ds_main(patch_constant_data input,
            float2 tess_coord : SV_DomainLocation,
            const OutputPatch<data, 4> patch,
            out float4 position : SV_Position,
            out float4 color : COLOR)
    {
        float4 a = lerp(patch[0].position, patch[1].position, tess_coord.x);
        float4 b = lerp(patch[2].position, patch[3].position, tess_coord.x);
        position = lerp(a, b, tess_coord.y);

        color = float4(input.center, 1.0);
    }
#endif
    static const DWORD hs_code[] =
    {
        0x43425844, 0xcecdcc7a, 0x1e0454ae, 0x8c490947, 0x7e33bb11, 0x00000001, 0x000005a4, 0x00000004,
        0x00000030, 0x00000064, 0x00000098, 0x0000017c, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008,
        0x00000020, 0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x00000f0f, 0x505f5653, 0x7469736f,
        0x006e6f69, 0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000001,
        0x00000003, 0x00000000, 0x0000000f, 0x505f5653, 0x7469736f, 0x006e6f69, 0x47534350, 0x000000dc,
        0x00000007, 0x00000008, 0x000000b0, 0x00000000, 0x0000000b, 0x00000003, 0x00000000, 0x00000e01,
        0x000000be, 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x0000010e, 0x000000b0, 0x00000001,
        0x0000000b, 0x00000003, 0x00000001, 0x00000e01, 0x000000b0, 0x00000002, 0x0000000b, 0x00000003,
        0x00000002, 0x00000e01, 0x000000b0, 0x00000003, 0x0000000b, 0x00000003, 0x00000003, 0x00000e01,
        0x000000c5, 0x00000000, 0x0000000c, 0x00000003, 0x00000004, 0x00000e01, 0x000000c5, 0x00000001,
        0x0000000c, 0x00000003, 0x00000005, 0x00000e01, 0x545f5653, 0x46737365, 0x6f746361, 0x45430072,
        0x5245544e, 0x5f565300, 0x69736e49, 0x65546564, 0x61467373, 0x726f7463, 0xababab00, 0x58454853,
        0x00000420, 0x00030050, 0x00000108, 0x01000071, 0x01000893, 0x01002094, 0x01001895, 0x01000896,
        0x01001897, 0x0100086a, 0x00001835, 0x00000012, 0x3f800000, 0x00000000, 0x00000000, 0x00000000,
        0x3f800000, 0x00000000, 0x40000000, 0x00000000, 0x3f800000, 0x40000000, 0x00000000, 0x00000000,
        0x3f800000, 0x40000000, 0x40000000, 0x00000000, 0x01000072, 0x0200005f, 0x00016000, 0x0400005f,
        0x002010f2, 0x00000001, 0x00000000, 0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000001,
        0x04000036, 0x00100012, 0x00000000, 0x00016001, 0x06000036, 0x00100032, 0x00000000, 0x00909596,
        0x0010000a, 0x00000000, 0x08000036, 0x001000c2, 0x00000000, 0x00004002, 0x00000000, 0x00000000,
        0x00000000, 0x3f800000, 0x08000000, 0x001020f2, 0x00000000, 0x00100e46, 0x00000000, 0x00201e46,
        0x00000000, 0x00000000, 0x0100003e, 0x01000073, 0x02000099, 0x00000004, 0x0200005f, 0x00017000,
        0x04000067, 0x00102012, 0x00000000, 0x0000000b, 0x04000067, 0x00102012, 0x00000001, 0x0000000c,
        0x04000067, 0x00102012, 0x00000002, 0x0000000d, 0x04000067, 0x00102012, 0x00000003, 0x0000000e,
        0x02000068, 0x00000001, 0x0400005b, 0x00102012, 0x00000000, 0x00000004, 0x04000036, 0x00100012,
        0x00000000, 0x0001700a, 0x06000036, 0x00902012, 0x0010000a, 0x00000000, 0x00004001, 0x3f800000,
        0x0100003e, 0x01000073, 0x02000099, 0x00000002, 0x0200005f, 0x00017000, 0x04000067, 0x00102012,
        0x00000004, 0x0000000f, 0x04000067, 0x00102012, 0x00000005, 0x00000010, 0x02000068, 0x00000001,
        0x0400005b, 0x00102012, 0x00000004, 0x00000002, 0x04000036, 0x00100012, 0x00000000, 0x0001700a,
        0x07000036, 0x00d02012, 0x00000004, 0x0010000a, 0x00000000, 0x00004001, 0x3f800000, 0x0100003e,
        0x01000073, 0x0400005f, 0x0021a012, 0x00000004, 0x00000000, 0x03000065, 0x00102022, 0x00000000,
        0x02000068, 0x00000001, 0x09000000, 0x00100012, 0x00000000, 0x0021a00a, 0x00000001, 0x00000000,
        0x0021a00a, 0x00000000, 0x00000000, 0x08000000, 0x00100012, 0x00000000, 0x0010000a, 0x00000000,
        0x0021a00a, 0x00000002, 0x00000000, 0x08000000, 0x00100012, 0x00000000, 0x0010000a, 0x00000000,
        0x0021a00a, 0x00000003, 0x00000000, 0x07000038, 0x00102022, 0x00000000, 0x0010000a, 0x00000000,
        0x00004001, 0x3e800000, 0x0100003e, 0x01000073, 0x0400005f, 0x0021a022, 0x00000004, 0x00000000,
        0x03000065, 0x00102042, 0x00000000, 0x02000068, 0x00000001, 0x09000000, 0x00100012, 0x00000000,
        0x0021a01a, 0x00000001, 0x00000000, 0x0021a01a, 0x00000000, 0x00000000, 0x08000000, 0x00100012,
        0x00000000, 0x0010000a, 0x00000000, 0x0021a01a, 0x00000002, 0x00000000, 0x08000000, 0x00100012,
        0x00000000, 0x0010000a, 0x00000000, 0x0021a01a, 0x00000003, 0x00000000, 0x07000038, 0x00102042,
        0x00000000, 0x0010000a, 0x00000000, 0x00004001, 0x3e800000, 0x0100003e, 0x01000073, 0x0400005f,
        0x0021a042, 0x00000004, 0x00000000, 0x03000065, 0x00102082, 0x00000000, 0x02000068, 0x00000001,
        0x09000000, 0x00100012, 0x00000000, 0x0021a02a, 0x00000001, 0x00000000, 0x0021a02a, 0x00000000,
        0x00000000, 0x08000000, 0x00100012, 0x00000000, 0x0010000a, 0x00000000, 0x0021a02a, 0x00000002,
        0x00000000, 0x08000000, 0x00100012, 0x00000000, 0x0010000a, 0x00000000, 0x0021a02a, 0x00000003,
        0x00000000, 0x07000038, 0x00102082, 0x00000000, 0x0010000a, 0x00000000, 0x00004001, 0x3e800000,
        0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE hs = {hs_code, sizeof(hs_code)};
    static const DWORD ds_code[] =
    {
        0x43425844, 0x6a721eb5, 0xa3ba6439, 0x3f31e765, 0x07e272c6, 0x00000001, 0x00000304, 0x00000004,
        0x00000030, 0x00000064, 0x00000148, 0x0000019c, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008,
        0x00000020, 0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x00000f0f, 0x505f5653, 0x7469736f,
        0x006e6f69, 0x47534350, 0x000000dc, 0x00000007, 0x00000008, 0x000000b0, 0x00000000, 0x0000000b,
        0x00000003, 0x00000000, 0x00000001, 0x000000be, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x00000e0e, 0x000000b0, 0x00000001, 0x0000000b, 0x00000003, 0x00000001, 0x00000001, 0x000000b0,
        0x00000002, 0x0000000b, 0x00000003, 0x00000002, 0x00000001, 0x000000b0, 0x00000003, 0x0000000b,
        0x00000003, 0x00000003, 0x00000001, 0x000000c5, 0x00000000, 0x0000000c, 0x00000003, 0x00000004,
        0x00000001, 0x000000c5, 0x00000001, 0x0000000c, 0x00000003, 0x00000005, 0x00000001, 0x545f5653,
        0x46737365, 0x6f746361, 0x45430072, 0x5245544e, 0x5f565300, 0x69736e49, 0x65546564, 0x61467373,
        0x726f7463, 0xababab00, 0x4e47534f, 0x0000004c, 0x00000002, 0x00000008, 0x00000038, 0x00000000,
        0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x00000044, 0x00000000, 0x00000000, 0x00000003,
        0x00000001, 0x0000000f, 0x505f5653, 0x7469736f, 0x006e6f69, 0x4f4c4f43, 0xabab0052, 0x58454853,
        0x00000160, 0x00040050, 0x00000058, 0x01002093, 0x01001895, 0x0100086a, 0x0300005f, 0x0011b0e2,
        0x00000000, 0x0200005f, 0x0001c032, 0x0400005f, 0x002190f2, 0x00000004, 0x00000000, 0x04000067,
        0x001020f2, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000001, 0x02000068, 0x00000002,
        0x0a000000, 0x001000f2, 0x00000000, 0x80219e46, 0x00000041, 0x00000002, 0x00000000, 0x00219e46,
        0x00000003, 0x00000000, 0x09000032, 0x001000f2, 0x00000000, 0x0001c006, 0x00100e46, 0x00000000,
        0x00219e46, 0x00000002, 0x00000000, 0x0a000000, 0x001000f2, 0x00000001, 0x80219e46, 0x00000041,
        0x00000000, 0x00000000, 0x00219e46, 0x00000001, 0x00000000, 0x09000032, 0x001000f2, 0x00000001,
        0x0001c006, 0x00100e46, 0x00000001, 0x00219e46, 0x00000000, 0x00000000, 0x08000000, 0x001000f2,
        0x00000000, 0x00100e46, 0x00000000, 0x80100e46, 0x00000041, 0x00000001, 0x08000032, 0x001020f2,
        0x00000000, 0x0001c556, 0x00100e46, 0x00000000, 0x00100e46, 0x00000001, 0x05000036, 0x00102072,
        0x00000001, 0x0011b796, 0x00000000, 0x05000036, 0x00102082, 0x00000001, 0x00004001, 0x3f800000,
        0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ds = {ds_code, sizeof(ds_code)};
    static const DWORD ps_code[] =
    {
#if 0
        float4 main(in float4 p : SV_Position, in float4 color : COLOR) : SV_Target
        {
            return color;
        }
#endif
        0x43425844, 0xbd83f517, 0x8974e87a, 0xaf402223, 0xaec7f351, 0x00000001, 0x000000f8, 0x00000003,
        0x0000002c, 0x00000080, 0x000000b4, 0x4e475349, 0x0000004c, 0x00000002, 0x00000008, 0x00000038,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x00000044, 0x00000000, 0x00000000,
        0x00000003, 0x00000001, 0x00000f0f, 0x505f5653, 0x7469736f, 0x006e6f69, 0x4f4c4f43, 0xabab0052,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x0000003c, 0x00000050,
        0x0000000f, 0x0100086a, 0x03001062, 0x001010f2, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x05000036, 0x001020f2, 0x00000000, 0x00101e46, 0x00000001, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};

    memset(&desc, 0, sizeof(desc));
    desc.no_pipeline = true;
    if (!init_test_context(&context, &desc))
        return;
    command_list = context.list;
    queue = context.queue;

    init_pipeline_state_desc(&pso_desc, context.root_signature,
            context.render_target_desc.Format, NULL, NULL, NULL);
    pso_desc.VS = vs;
    pso_desc.HS = hs;
    pso_desc.DS = ds;
    pso_desc.PS = ps;
    pso_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_PATCH;
    hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
    ok(hr == S_OK, "Failed to create state, hr %#x.\n", hr);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_1_CONTROL_POINT_PATCHLIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 1, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    bug_if(is_mvk_device(context.device))
    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff000000, 0);

    destroy_test_context(&context);
}

static void test_line_tessellation(void)
{
    ID3D12Resource *vb, *so_buffer, *readback_buffer;
    D3D12_QUERY_DATA_SO_STATISTICS *so_statistics;
    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
    ID3D12GraphicsCommandList *command_list;
    D3D12_QUERY_HEAP_DESC query_heap_desc;
    D3D12_STREAM_OUTPUT_BUFFER_VIEW sobv[2];
    D3D12_INPUT_LAYOUT_DESC input_layout;
    struct d3d12_resource_readback rb;
    struct test_context_desc desc;
    D3D12_VERTEX_BUFFER_VIEW vbv;
    ID3D12QueryHeap *query_heap;
    struct test_context context;
    const struct vec4 *expected;
    ID3D12CommandQueue *queue;
    struct vec4 *data;
    bool broken_warp;
    unsigned int i;
    HRESULT hr;

#if 0
    struct data
    {
        float4 position : SV_Position;
        float3 color : COLOR;
        float line_density : LINE_DENSITY;
        float line_detail : LINE_DETAIL;
    };

    data vs_main(data input)
    {
        return input;
    }

    struct patch_constant_data
    {
        float tess_factor[2] : SV_TessFactor;
        float3 color : COLOR;
        uint prim_id : PRIMITIVE_ID;
    };

    void patch_constant(OutputPatch<data, 1> control_points,
            uint prim_id : SV_PrimitiveID,
            out patch_constant_data output)
    {
        output.tess_factor[0] = control_points[0].line_density;
        output.tess_factor[1] = control_points[0].line_detail;
        output.color = control_points[0].color;
        output.prim_id = prim_id;
    }

    [domain("isoline")]
    [outputcontrolpoints(1)]
    [partitioning("integer")]
    [outputtopology("line")]
    [patchconstantfunc("patch_constant")]
    data hs_main(InputPatch<data, 1> input)
    {
        return input[0];
    }

    [domain("isoline")]
    void ds_main(patch_constant_data input,
            float tess_factor[2] : SV_TessFactor,
            float2 tess_coord : SV_DomainLocation,
            float3 color : COLOR,
            uint prim_id : PRIMITIVE_ID,
            const OutputPatch<data, 1> patch,
            out float4 position : SV_Position,
            out float4 out_color : COLOR,
            out float4 out_prim_id : PRIMITIVE_ID)
    {
        position = patch[0].position;
        out_color = float4(color, 1.0);
        out_prim_id = prim_id;
    }
#endif
    static const DWORD vs_code[] =
    {
        0x43425844, 0x43d2f821, 0xc4bcdf60, 0xadbf5ed0, 0xe55b715d, 0x00000001, 0x00000230, 0x00000003,
        0x0000002c, 0x000000c8, 0x00000164, 0x4e475349, 0x00000094, 0x00000004, 0x00000008, 0x00000068,
        0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000f0f, 0x00000074, 0x00000000, 0x00000000,
        0x00000003, 0x00000001, 0x00000707, 0x0000007a, 0x00000000, 0x00000000, 0x00000003, 0x00000002,
        0x00000101, 0x00000087, 0x00000000, 0x00000000, 0x00000003, 0x00000003, 0x00000101, 0x505f5653,
        0x7469736f, 0x006e6f69, 0x4f4c4f43, 0x494c0052, 0x445f454e, 0x49534e45, 0x4c005954, 0x5f454e49,
        0x41544544, 0xab004c49, 0x4e47534f, 0x00000094, 0x00000004, 0x00000008, 0x00000068, 0x00000000,
        0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x00000074, 0x00000000, 0x00000000, 0x00000003,
        0x00000001, 0x00000807, 0x0000007a, 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x00000708,
        0x00000087, 0x00000000, 0x00000000, 0x00000003, 0x00000002, 0x00000e01, 0x505f5653, 0x7469736f,
        0x006e6f69, 0x4f4c4f43, 0x494c0052, 0x445f454e, 0x49534e45, 0x4c005954, 0x5f454e49, 0x41544544,
        0xab004c49, 0x58454853, 0x000000c4, 0x00010050, 0x00000031, 0x0100086a, 0x0300005f, 0x001010f2,
        0x00000000, 0x0300005f, 0x00101072, 0x00000001, 0x0300005f, 0x00101012, 0x00000002, 0x0300005f,
        0x00101012, 0x00000003, 0x04000067, 0x001020f2, 0x00000000, 0x00000001, 0x03000065, 0x00102072,
        0x00000001, 0x03000065, 0x00102082, 0x00000001, 0x03000065, 0x00102012, 0x00000002, 0x05000036,
        0x001020f2, 0x00000000, 0x00101e46, 0x00000000, 0x05000036, 0x00102072, 0x00000001, 0x00101246,
        0x00000001, 0x05000036, 0x00102082, 0x00000001, 0x0010100a, 0x00000002, 0x05000036, 0x00102012,
        0x00000002, 0x0010100a, 0x00000003, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE vs = {vs_code, sizeof(vs_code)};
    static const DWORD hs_code[] =
    {
        0x43425844, 0x130f1b8c, 0x02c24095, 0x18ab42ad, 0xbb861b50, 0x00000001, 0x00000448, 0x00000004,
        0x00000030, 0x000000cc, 0x00000168, 0x000001fc, 0x4e475349, 0x00000094, 0x00000004, 0x00000008,
        0x00000068, 0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x00000f0f, 0x00000074, 0x00000000,
        0x00000000, 0x00000003, 0x00000001, 0x00000707, 0x0000007a, 0x00000000, 0x00000000, 0x00000003,
        0x00000001, 0x00000808, 0x00000087, 0x00000000, 0x00000000, 0x00000003, 0x00000002, 0x00000101,
        0x505f5653, 0x7469736f, 0x006e6f69, 0x4f4c4f43, 0x494c0052, 0x445f454e, 0x49534e45, 0x4c005954,
        0x5f454e49, 0x41544544, 0xab004c49, 0x4e47534f, 0x00000094, 0x00000004, 0x00000008, 0x00000068,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x00000074, 0x00000000, 0x00000000,
        0x00000003, 0x00000001, 0x00000807, 0x0000007a, 0x00000000, 0x00000000, 0x00000003, 0x00000001,
        0x00000708, 0x00000087, 0x00000000, 0x00000000, 0x00000003, 0x00000002, 0x00000e01, 0x505f5653,
        0x7469736f, 0x006e6f69, 0x4f4c4f43, 0x494c0052, 0x445f454e, 0x49534e45, 0x4c005954, 0x5f454e49,
        0x41544544, 0xab004c49, 0x47534350, 0x0000008c, 0x00000004, 0x00000008, 0x00000068, 0x00000000,
        0x00000010, 0x00000003, 0x00000000, 0x00000e01, 0x00000076, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000010e, 0x00000068, 0x00000001, 0x0000000f, 0x00000003, 0x00000001, 0x00000e01,
        0x0000007c, 0x00000000, 0x00000000, 0x00000001, 0x00000001, 0x00000d02, 0x545f5653, 0x46737365,
        0x6f746361, 0x4f430072, 0x00524f4c, 0x4d495250, 0x56495449, 0x44495f45, 0xababab00, 0x58454853,
        0x00000244, 0x00030050, 0x00000091, 0x01000071, 0x01000893, 0x01000894, 0x01000895, 0x01000896,
        0x01001097, 0x0100086a, 0x01000072, 0x0400005f, 0x002010f2, 0x00000001, 0x00000000, 0x0400005f,
        0x00201072, 0x00000001, 0x00000001, 0x0400005f, 0x00201082, 0x00000001, 0x00000001, 0x0400005f,
        0x00201012, 0x00000001, 0x00000002, 0x03000065, 0x001020f2, 0x00000000, 0x03000065, 0x00102072,
        0x00000001, 0x03000065, 0x00102082, 0x00000001, 0x03000065, 0x00102012, 0x00000002, 0x06000036,
        0x001020f2, 0x00000000, 0x00201e46, 0x00000000, 0x00000000, 0x06000036, 0x001020f2, 0x00000001,
        0x00201e46, 0x00000000, 0x00000001, 0x06000036, 0x00102012, 0x00000002, 0x0020100a, 0x00000000,
        0x00000002, 0x0100003e, 0x01000073, 0x0400005f, 0x0021a082, 0x00000001, 0x00000001, 0x04000067,
        0x00102012, 0x00000000, 0x00000016, 0x06000036, 0x00102012, 0x00000000, 0x0021a03a, 0x00000000,
        0x00000001, 0x0100003e, 0x01000073, 0x0400005f, 0x0021a012, 0x00000001, 0x00000002, 0x04000067,
        0x00102012, 0x00000001, 0x00000015, 0x06000036, 0x00102012, 0x00000001, 0x0021a00a, 0x00000000,
        0x00000002, 0x0100003e, 0x01000073, 0x0400005f, 0x0021a012, 0x00000001, 0x00000001, 0x03000065,
        0x00102022, 0x00000000, 0x06000036, 0x00102022, 0x00000000, 0x0021a00a, 0x00000000, 0x00000001,
        0x0100003e, 0x01000073, 0x0400005f, 0x0021a022, 0x00000001, 0x00000001, 0x03000065, 0x00102042,
        0x00000000, 0x06000036, 0x00102042, 0x00000000, 0x0021a01a, 0x00000000, 0x00000001, 0x0100003e,
        0x01000073, 0x0400005f, 0x0021a042, 0x00000001, 0x00000001, 0x03000065, 0x00102082, 0x00000000,
        0x06000036, 0x00102082, 0x00000000, 0x0021a02a, 0x00000000, 0x00000001, 0x0100003e, 0x01000073,
        0x0200005f, 0x0000b000, 0x03000065, 0x00102022, 0x00000001, 0x04000036, 0x00102022, 0x00000001,
        0x0000b001, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE hs = {hs_code, sizeof(hs_code)};
    static const DWORD ds_code[] =
    {
        0x43425844, 0xc78d05dd, 0x4b270467, 0xb480a2fb, 0x02f0edc7, 0x00000001, 0x0000029c, 0x00000004,
        0x00000030, 0x000000cc, 0x00000160, 0x000001d8, 0x4e475349, 0x00000094, 0x00000004, 0x00000008,
        0x00000068, 0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x00000f0f, 0x00000074, 0x00000000,
        0x00000000, 0x00000003, 0x00000001, 0x00000007, 0x0000007a, 0x00000000, 0x00000000, 0x00000003,
        0x00000001, 0x00000008, 0x00000087, 0x00000000, 0x00000000, 0x00000003, 0x00000002, 0x00000001,
        0x505f5653, 0x7469736f, 0x006e6f69, 0x4f4c4f43, 0x494c0052, 0x445f454e, 0x49534e45, 0x4c005954,
        0x5f454e49, 0x41544544, 0xab004c49, 0x47534350, 0x0000008c, 0x00000004, 0x00000008, 0x00000068,
        0x00000000, 0x00000010, 0x00000003, 0x00000000, 0x00000001, 0x00000076, 0x00000000, 0x00000000,
        0x00000003, 0x00000000, 0x00000e0e, 0x00000068, 0x00000001, 0x0000000f, 0x00000003, 0x00000001,
        0x00000001, 0x0000007c, 0x00000000, 0x00000000, 0x00000001, 0x00000001, 0x00000202, 0x545f5653,
        0x46737365, 0x6f746361, 0x4f430072, 0x00524f4c, 0x4d495250, 0x56495449, 0x44495f45, 0xababab00,
        0x4e47534f, 0x00000070, 0x00000003, 0x00000008, 0x00000050, 0x00000000, 0x00000001, 0x00000003,
        0x00000000, 0x0000000f, 0x0000005c, 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x0000000f,
        0x00000062, 0x00000000, 0x00000000, 0x00000003, 0x00000002, 0x0000000f, 0x505f5653, 0x7469736f,
        0x006e6f69, 0x4f4c4f43, 0x52500052, 0x54494d49, 0x5f455649, 0xab004449, 0x58454853, 0x000000bc,
        0x00040050, 0x0000002f, 0x01000893, 0x01000895, 0x0100086a, 0x0300005f, 0x0011b0e2, 0x00000000,
        0x0300005f, 0x0011b022, 0x00000001, 0x0400005f, 0x002190f2, 0x00000001, 0x00000000, 0x04000067,
        0x001020f2, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000001, 0x03000065, 0x001020f2,
        0x00000002, 0x06000036, 0x001020f2, 0x00000000, 0x00219e46, 0x00000000, 0x00000000, 0x05000036,
        0x00102072, 0x00000001, 0x0011b796, 0x00000000, 0x05000036, 0x00102082, 0x00000001, 0x00004001,
        0x3f800000, 0x05000056, 0x001020f2, 0x00000002, 0x0011b556, 0x00000001, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ds = {ds_code, sizeof(ds_code)};
    static const D3D12_INPUT_ELEMENT_DESC layout_desc[] =
    {
        {"SV_POSITION",  0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0,  0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
        {"COLOR",        0, DXGI_FORMAT_R32G32B32_FLOAT,    0, 16, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
        {"LINE_DENSITY", 0, DXGI_FORMAT_R32_FLOAT,          0, 32, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
        {"LINE_DETAIL",  0, DXGI_FORMAT_R32_FLOAT,          0, 36, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
    };
    static const D3D12_SO_DECLARATION_ENTRY so_declaration[] =
    {
        {0, "SV_POSITION",  0, 0, 4, 0},
        {0, "COLOR",        0, 0, 4, 1},
        {0, "PRIMITIVE_ID", 0, 0, 4, 1},
    };
    unsigned int strides[] = {16, 32};
    static const struct
    {
        struct vec4 position;
        struct vec4 color;
        float line_density;
        float line_detail;
    }
    vertices[] =
    {
        {{0.0f, 0.0f, 0.0f, 1.0f}, {0.0f, 1.0f, 0.0f}, 1.0f, 1.0f},
        {{1.0f, 0.0f, 0.0f, 1.0f}, {0.0f, 0.5f, 0.0f}, 2.0f, 1.0f},
        {{2.0f, 0.0f, 0.0f, 1.0f}, {1.0f, 1.0f, 1.0f}, 1.0f, 2.0f},
        {{3.0f, 0.0f, 0.0f, 1.0f}, {0.5f, 0.5f, 0.5f}, 2.0f, 2.0f},
    };
    static const struct vec4 expected_data[] =
    {
        {0.0f, 0.0f, 0.0f, 1.0f}, {0.0f, 1.0f, 0.0f, 1.0f}, {0.0f, 0.0f, 0.0f, 0.0f},
        {0.0f, 0.0f, 0.0f, 1.0f}, {0.0f, 1.0f, 0.0f, 1.0f}, {0.0f, 0.0f, 0.0f, 0.0f},

        {1.0f, 0.0f, 0.0f, 1.0f}, {0.0f, 0.5f, 0.0f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f},
        {1.0f, 0.0f, 0.0f, 1.0f}, {0.0f, 0.5f, 0.0f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f},
        {1.0f, 0.0f, 0.0f, 1.0f}, {0.0f, 0.5f, 0.0f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f},
        {1.0f, 0.0f, 0.0f, 1.0f}, {0.0f, 0.5f, 0.0f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f},

        {2.0f, 0.0f, 0.0f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}, {2.0f, 2.0f, 2.0f, 2.0f},
        {2.0f, 0.0f, 0.0f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}, {2.0f, 2.0f, 2.0f, 2.0f},
        {2.0f, 0.0f, 0.0f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}, {2.0f, 2.0f, 2.0f, 2.0f},
        {2.0f, 0.0f, 0.0f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}, {2.0f, 2.0f, 2.0f, 2.0f},

        {3.0f, 0.0f, 0.0f, 1.0f}, {0.5f, 0.5f, 0.5f, 1.0f}, {3.0f, 3.0f, 3.0f, 3.0f},
        {3.0f, 0.0f, 0.0f, 1.0f}, {0.5f, 0.5f, 0.5f, 1.0f}, {3.0f, 3.0f, 3.0f, 3.0f},
        {3.0f, 0.0f, 0.0f, 1.0f}, {0.5f, 0.5f, 0.5f, 1.0f}, {3.0f, 3.0f, 3.0f, 3.0f},
        {3.0f, 0.0f, 0.0f, 1.0f}, {0.5f, 0.5f, 0.5f, 1.0f}, {3.0f, 3.0f, 3.0f, 3.0f},
        {3.0f, 0.0f, 0.0f, 1.0f}, {0.5f, 0.5f, 0.5f, 1.0f}, {3.0f, 3.0f, 3.0f, 3.0f},
        {3.0f, 0.0f, 0.0f, 1.0f}, {0.5f, 0.5f, 0.5f, 1.0f}, {3.0f, 3.0f, 3.0f, 3.0f},
        {3.0f, 0.0f, 0.0f, 1.0f}, {0.5f, 0.5f, 0.5f, 1.0f}, {3.0f, 3.0f, 3.0f, 3.0f},
        {3.0f, 0.0f, 0.0f, 1.0f}, {0.5f, 0.5f, 0.5f, 1.0f}, {3.0f, 3.0f, 3.0f, 3.0f},
    };

    memset(&desc, 0, sizeof(desc));
    desc.root_signature_flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT
            | D3D12_ROOT_SIGNATURE_FLAG_ALLOW_STREAM_OUTPUT;
    desc.no_pipeline = true;
    if (!init_test_context(&context, &desc))
        return;
    command_list = context.list;
    queue = context.queue;

    query_heap_desc.Type = D3D12_QUERY_HEAP_TYPE_SO_STATISTICS;
    query_heap_desc.Count = 2;
    query_heap_desc.NodeMask = 0;
    hr = ID3D12Device_CreateQueryHeap(context.device, &query_heap_desc, &IID_ID3D12QueryHeap, (void **)&query_heap);
    if (hr == E_NOTIMPL)
    {
        skip("Stream output is not supported.\n");
        destroy_test_context(&context);
        return;
    }
    ok(hr == S_OK, "Failed to create query heap, hr %#x.\n", hr);

    input_layout.pInputElementDescs = layout_desc;
    input_layout.NumElements = ARRAY_SIZE(layout_desc);
    init_pipeline_state_desc(&pso_desc, context.root_signature,
            DXGI_FORMAT_UNKNOWN, NULL, NULL, &input_layout);
    pso_desc.VS = vs;
    pso_desc.HS = hs;
    pso_desc.DS = ds;
    memset(&pso_desc.PS, 0, sizeof(pso_desc.PS));
    pso_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_PATCH;
    pso_desc.StreamOutput.NumEntries = ARRAY_SIZE(so_declaration);
    pso_desc.StreamOutput.pSODeclaration = so_declaration;
    pso_desc.StreamOutput.pBufferStrides = strides;
    pso_desc.StreamOutput.NumStrides = ARRAY_SIZE(strides);
    pso_desc.StreamOutput.RasterizedStream = D3D12_SO_NO_RASTERIZED_STREAM;
    hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
    ok(hr == S_OK, "Failed to create state, hr %#x.\n", hr);

    vb = create_upload_buffer(context.device, sizeof(vertices), vertices);
    vbv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vb);
    vbv.StrideInBytes = sizeof(*vertices);
    vbv.SizeInBytes = sizeof(vertices);

    so_buffer = create_default_buffer(context.device, 4096,
            D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_STREAM_OUT);

    for (i = 0; i < ARRAY_SIZE(sobv); ++i)
    {
        sobv[i].BufferLocation = ID3D12Resource_GetGPUVirtualAddress(so_buffer) + i * 2048;
        sobv[i].SizeInBytes = 1024;
        sobv[i].BufferFilledSizeLocation = sobv[i].BufferLocation + sobv[i].SizeInBytes;
    }

    ID3D12GraphicsCommandList_BeginQuery(command_list, query_heap, D3D12_QUERY_TYPE_SO_STATISTICS_STREAM0, 0);

    ID3D12GraphicsCommandList_SOSetTargets(command_list, 0, 2, sobv);

    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_1_CONTROL_POINT_PATCHLIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, 1, &vbv);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 1, 0, 0);

    ID3D12GraphicsCommandList_EndQuery(command_list, query_heap, D3D12_QUERY_TYPE_SO_STATISTICS_STREAM0, 0);

    readback_buffer = create_readback_buffer(context.device, sizeof(*so_statistics));
    ID3D12GraphicsCommandList_ResolveQueryData(command_list,
            query_heap, D3D12_QUERY_TYPE_SO_STATISTICS_STREAM0, 0, 1, readback_buffer, 0);

    get_buffer_readback_with_command_list(readback_buffer, DXGI_FORMAT_UNKNOWN, &rb, queue, command_list);
    so_statistics = get_readback_data(&rb.rb, 0, 0, 0, sizeof(*so_statistics));
    broken_warp = broken_on_warp(so_statistics[0].NumPrimitivesWritten != 9);
    ok(so_statistics[0].NumPrimitivesWritten == 9 || broken_warp, "Got unexpected primitives written %u.\n",
            (unsigned int)so_statistics[0].NumPrimitivesWritten);
    ok(so_statistics[0].PrimitivesStorageNeeded == 9 || broken_warp, "Got unexpected primitives storage needed %u.\n",
            (unsigned int)so_statistics[0].PrimitivesStorageNeeded);
    release_resource_readback(&rb);

    if (broken_warp)
    {
        skip("Broken on WARP.\n");
        goto done;
    }

    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, so_buffer,
            D3D12_RESOURCE_STATE_STREAM_OUT, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_buffer_readback_with_command_list(so_buffer, DXGI_FORMAT_UNKNOWN, &rb, queue, command_list);
    for (i = 0; i < ARRAY_SIZE(expected_data) / 3 ; ++i)
    {
        data = get_readback_data(&rb.rb, i, 0, 0, sizeof(*data));
        expected = &expected_data[3 * i + 0];
        ok(compare_vec4(data, expected, 1),
                "Got position {%.8e, %.8e, %.8e, %.8e}, expected {%.8e, %.8e, %.8e, %.8e} at %u.\n",
                data->x, data->y, data->z, data->w, expected->x, expected->y, expected->z, expected->w, i);
        data = get_readback_data(&rb.rb, i + 2048 / (2 * sizeof(*data)), 0, 0, 2 * sizeof(*data));
        expected = &expected_data[3 * i + 1];
        bug_if(is_nvidia_device(context.device))
        ok(compare_vec4(data, expected, 1),
                "Got color {%.8e, %.8e, %.8e, %.8e}, expected {%.8e, %.8e, %.8e, %.8e} at %u.\n",
                data->x, data->y, data->z, data->w, expected->x, expected->y, expected->z, expected->w, i);
        ++data;
        expected = &expected_data[3 * i + 2];
        ok(compare_vec4(data, expected, 1),
                "Got primitive ID {%.8e, %.8e, %.8e, %.8e}, expected {%.8e, %.8e, %.8e, %.8e} at %u.\n",
                data->x, data->y, data->z, data->w, expected->x, expected->y, expected->z, expected->w, i);
    }
    release_resource_readback(&rb);

done:
    ID3D12QueryHeap_Release(query_heap);
    ID3D12Resource_Release(readback_buffer);
    ID3D12Resource_Release(so_buffer);
    ID3D12Resource_Release(vb);
    destroy_test_context(&context);
}

static void test_tessellation_primitive_id(void)
{
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
    D3D12_ROOT_PARAMETER root_parameters[1];
    ID3D12GraphicsCommandList *command_list;
    D3D12_INPUT_LAYOUT_DESC input_layout;
    struct test_context_desc desc;
    D3D12_VERTEX_BUFFER_VIEW vbv;
    struct test_context context;
    ID3D12Resource *raw_buffer;
    ID3D12CommandQueue *queue;
    ID3D12Resource *vb;
    HRESULT hr;

#if 0
    ByteAddressBuffer b;

    struct data
    {
        float4 position : POSITION;
        float ref_buffer_data : REF_BUFFER_DATA;
    };

    struct ds_data
    {
        float4 position : POSITION;
        float ref_buffer_data : REF_BUFFER_DATA;
        uint primitive_id : PRIM_ID;
        uint invocation_id : CP_ID;
    };

    struct ps_data
    {
        float4 position : SV_POSITION;
        float4 color : COLOR;
    };

    struct patch_constant_data
    {
        float edges[3] : SV_TessFactor;
        float inside : SV_InsideTessFactor;
        float buffer_data : BUFFER_DATA;
        uint primitive_id : PATCH_PRIM_ID;
    };

    data vs_main(in data input)
    {
        return input;
    }

    void patch_constant(uint prim_id : SV_PrimitiveID, out patch_constant_data output)
    {
        output.edges[0] = output.edges[1] = output.edges[2] = 4.0f;
        output.inside = 4.0f;
        output.buffer_data = b.Load(4 * prim_id);
        output.primitive_id = prim_id;
    }

    [domain("tri")]
    [outputcontrolpoints(3)]
    [partitioning("integer")]
    [outputtopology("triangle_cw")]
    [patchconstantfunc("patch_constant")]
    ds_data hs_main(const InputPatch<data, 3> input,
            uint prim_id : SV_PrimitiveID, uint i : SV_OutputControlPointID)
    {
        ds_data output;
        output.position = input[i].position;
        output.ref_buffer_data = input[i].ref_buffer_data;
        output.primitive_id = prim_id;
        output.invocation_id = i;
        return output;
    }

    [domain("tri")]
    void ds_main(patch_constant_data input,
            float3 tess_coord : SV_DomainLocation,
            const OutputPatch<ds_data, 3> patch,
            out ps_data output)
    {
        uint i;

        output.position = tess_coord.x * patch[0].position
                + tess_coord.y * patch[1].position
                + tess_coord.z * patch[2].position;

        for (i = 0; i < 3; ++i)
        {
            if (patch[i].ref_buffer_data != input.buffer_data)
            {
                output.color = float4(1, patch[i].ref_buffer_data / 255.0f, input.buffer_data / 255.0f, 0);
                return;
            }
        }

        for (i = 0; i < 3; ++i)
        {
            if (patch[i].primitive_id != input.primitive_id)
            {
                output.color = float4(1, 0, 1, 1);
                return;
            }
        }

        if (patch[0].invocation_id != 0 || patch[1].invocation_id != 1 || patch[2].invocation_id != 2)
        {
            output.color = float4(1, 1, 0, 1);
            return;
        }

        output.color = float4(0, 1, 0, 1);
    }
#endif
    static const DWORD vs_code[] =
    {
        0x43425844, 0x1cf34a89, 0x09f0ca02, 0x1d9d7e25, 0x4161cddd, 0x00000001, 0x00000154, 0x00000003,
        0x0000002c, 0x00000088, 0x000000e4, 0x4e475349, 0x00000054, 0x00000002, 0x00000008, 0x00000038,
        0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000f0f, 0x00000041, 0x00000000, 0x00000000,
        0x00000003, 0x00000001, 0x00000101, 0x49534f50, 0x4e4f4954, 0x46455200, 0x4655425f, 0x5f524546,
        0x41544144, 0xababab00, 0x4e47534f, 0x00000054, 0x00000002, 0x00000008, 0x00000038, 0x00000000,
        0x00000000, 0x00000003, 0x00000000, 0x0000000f, 0x00000041, 0x00000000, 0x00000000, 0x00000003,
        0x00000001, 0x00000e01, 0x49534f50, 0x4e4f4954, 0x46455200, 0x4655425f, 0x5f524546, 0x41544144,
        0xababab00, 0x58454853, 0x00000068, 0x00010050, 0x0000001a, 0x0100086a, 0x0300005f, 0x001010f2,
        0x00000000, 0x0300005f, 0x00101012, 0x00000001, 0x03000065, 0x001020f2, 0x00000000, 0x03000065,
        0x00102012, 0x00000001, 0x05000036, 0x001020f2, 0x00000000, 0x00101e46, 0x00000000, 0x05000036,
        0x00102012, 0x00000001, 0x0010100a, 0x00000001, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE vs = {vs_code, sizeof(vs_code)};
    static const DWORD hs_code[] =
    {
        0x43425844, 0x23a919a7, 0xf5fdd1b4, 0x4f5a835f, 0xca389c71, 0x00000001, 0x00000464, 0x00000004,
        0x00000030, 0x0000008c, 0x00000124, 0x00000200, 0x4e475349, 0x00000054, 0x00000002, 0x00000008,
        0x00000038, 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000f0f, 0x00000041, 0x00000000,
        0x00000000, 0x00000003, 0x00000001, 0x00000101, 0x49534f50, 0x4e4f4954, 0x46455200, 0x4655425f,
        0x5f524546, 0x41544144, 0xababab00, 0x4e47534f, 0x00000090, 0x00000004, 0x00000008, 0x00000068,
        0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x0000000f, 0x00000071, 0x00000000, 0x00000000,
        0x00000003, 0x00000001, 0x00000e01, 0x00000081, 0x00000000, 0x00000000, 0x00000001, 0x00000002,
        0x00000e01, 0x00000089, 0x00000000, 0x00000000, 0x00000001, 0x00000002, 0x00000d02, 0x49534f50,
        0x4e4f4954, 0x46455200, 0x4655425f, 0x5f524546, 0x41544144, 0x49525000, 0x44495f4d, 0x5f504300,
        0xab004449, 0x47534350, 0x000000d4, 0x00000006, 0x00000008, 0x00000098, 0x00000000, 0x0000000d,
        0x00000003, 0x00000000, 0x00000e01, 0x000000a6, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x00000d02, 0x00000098, 0x00000001, 0x0000000d, 0x00000003, 0x00000001, 0x00000e01, 0x000000b2,
        0x00000000, 0x00000000, 0x00000001, 0x00000001, 0x00000d02, 0x00000098, 0x00000002, 0x0000000d,
        0x00000003, 0x00000002, 0x00000e01, 0x000000c0, 0x00000000, 0x0000000e, 0x00000003, 0x00000003,
        0x00000e01, 0x545f5653, 0x46737365, 0x6f746361, 0x55420072, 0x52454646, 0x5441445f, 0x41500041,
        0x5f484354, 0x4d495250, 0x0044495f, 0x495f5653, 0x6469736e, 0x73655465, 0x63614673, 0x00726f74,
        0x58454853, 0x0000025c, 0x00030050, 0x00000097, 0x01000071, 0x01001893, 0x01001894, 0x01001095,
        0x01000896, 0x01001897, 0x0100086a, 0x030000a1, 0x00107000, 0x00000000, 0x01000072, 0x0200005f,
        0x00016000, 0x0200005f, 0x0000b000, 0x0400005f, 0x002010f2, 0x00000003, 0x00000000, 0x0400005f,
        0x00201012, 0x00000003, 0x00000001, 0x03000065, 0x001020f2, 0x00000000, 0x03000065, 0x00102012,
        0x00000001, 0x03000065, 0x00102012, 0x00000002, 0x03000065, 0x00102022, 0x00000002, 0x02000068,
        0x00000001, 0x04000036, 0x00100012, 0x00000000, 0x00016001, 0x07000036, 0x001020f2, 0x00000000,
        0x00a01e46, 0x0010000a, 0x00000000, 0x00000000, 0x07000036, 0x00102012, 0x00000001, 0x00a0100a,
        0x0010000a, 0x00000000, 0x00000001, 0x04000036, 0x00102012, 0x00000002, 0x0000b001, 0x04000036,
        0x00102022, 0x00000002, 0x00016001, 0x0100003e, 0x01000073, 0x02000099, 0x00000003, 0x0200005f,
        0x00017000, 0x04000067, 0x00102012, 0x00000000, 0x00000011, 0x04000067, 0x00102012, 0x00000001,
        0x00000012, 0x04000067, 0x00102012, 0x00000002, 0x00000013, 0x02000068, 0x00000001, 0x0400005b,
        0x00102012, 0x00000000, 0x00000003, 0x04000036, 0x00100012, 0x00000000, 0x0001700a, 0x06000036,
        0x00902012, 0x0010000a, 0x00000000, 0x00004001, 0x40800000, 0x0100003e, 0x01000073, 0x04000067,
        0x00102012, 0x00000003, 0x00000014, 0x05000036, 0x00102012, 0x00000003, 0x00004001, 0x40800000,
        0x0100003e, 0x01000073, 0x0200005f, 0x0000b000, 0x03000065, 0x00102022, 0x00000000, 0x02000068,
        0x00000001, 0x06000029, 0x00100012, 0x00000000, 0x0000b001, 0x00004001, 0x00000002, 0x890000a5,
        0x800002c2, 0x00199983, 0x00100012, 0x00000000, 0x0010000a, 0x00000000, 0x00107006, 0x00000000,
        0x05000056, 0x00102022, 0x00000000, 0x0010000a, 0x00000000, 0x0100003e, 0x01000073, 0x0200005f,
        0x0000b000, 0x03000065, 0x00102022, 0x00000001, 0x04000036, 0x00102022, 0x00000001, 0x0000b001,
        0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE hs = {hs_code, sizeof(hs_code)};
    static const DWORD ds_code[] =
    {
        0x43425844, 0x4b659bb4, 0x3eb61f16, 0xaa8397b6, 0xd60e8c43, 0x00000001, 0x000005ec, 0x00000004,
        0x00000030, 0x000000c8, 0x000001a4, 0x000001f8, 0x4e475349, 0x00000090, 0x00000004, 0x00000008,
        0x00000068, 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000f0f, 0x00000071, 0x00000000,
        0x00000000, 0x00000003, 0x00000001, 0x00000101, 0x00000081, 0x00000000, 0x00000000, 0x00000001,
        0x00000002, 0x00000101, 0x00000089, 0x00000000, 0x00000000, 0x00000001, 0x00000002, 0x00000202,
        0x49534f50, 0x4e4f4954, 0x46455200, 0x4655425f, 0x5f524546, 0x41544144, 0x49525000, 0x44495f4d,
        0x5f504300, 0xab004449, 0x47534350, 0x000000d4, 0x00000006, 0x00000008, 0x00000098, 0x00000000,
        0x0000000d, 0x00000003, 0x00000000, 0x00000001, 0x000000a6, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x00000202, 0x00000098, 0x00000001, 0x0000000d, 0x00000003, 0x00000001, 0x00000001,
        0x000000b2, 0x00000000, 0x00000000, 0x00000001, 0x00000001, 0x00000202, 0x00000098, 0x00000002,
        0x0000000d, 0x00000003, 0x00000002, 0x00000001, 0x000000c0, 0x00000000, 0x0000000e, 0x00000003,
        0x00000003, 0x00000001, 0x545f5653, 0x46737365, 0x6f746361, 0x55420072, 0x52454646, 0x5441445f,
        0x41500041, 0x5f484354, 0x4d495250, 0x0044495f, 0x495f5653, 0x6469736e, 0x73655465, 0x63614673,
        0x00726f74, 0x4e47534f, 0x0000004c, 0x00000002, 0x00000008, 0x00000038, 0x00000000, 0x00000001,
        0x00000003, 0x00000000, 0x0000000f, 0x00000044, 0x00000000, 0x00000000, 0x00000003, 0x00000001,
        0x0000000f, 0x505f5653, 0x5449534f, 0x004e4f49, 0x4f4c4f43, 0xabab0052, 0x58454853, 0x000003ec,
        0x00040050, 0x000000fb, 0x01001893, 0x01001095, 0x0100086a, 0x0300005f, 0x0011b022, 0x00000000,
        0x0300005f, 0x0011b022, 0x00000001, 0x0200005f, 0x0001c072, 0x0400005f, 0x002190f2, 0x00000003,
        0x00000000, 0x0400005f, 0x00219012, 0x00000003, 0x00000001, 0x0400005f, 0x00219012, 0x00000003,
        0x00000002, 0x0400005f, 0x00219022, 0x00000003, 0x00000002, 0x04000067, 0x001020f2, 0x00000000,
        0x00000001, 0x03000065, 0x001020f2, 0x00000001, 0x02000068, 0x00000002, 0x07000038, 0x001000f2,
        0x00000000, 0x0001c556, 0x00219e46, 0x00000001, 0x00000000, 0x09000032, 0x001000f2, 0x00000000,
        0x0001c006, 0x00219e46, 0x00000000, 0x00000000, 0x00100e46, 0x00000000, 0x09000032, 0x001000f2,
        0x00000000, 0x0001caa6, 0x00219e46, 0x00000002, 0x00000000, 0x00100e46, 0x00000000, 0x05000036,
        0x00100012, 0x00000001, 0x00004001, 0x00000000, 0x01000030, 0x07000050, 0x00100022, 0x00000001,
        0x0010000a, 0x00000001, 0x00004001, 0x00000003, 0x03040003, 0x0010001a, 0x00000001, 0x09000039,
        0x00100022, 0x00000001, 0x0011b01a, 0x00000000, 0x00a1900a, 0x0010000a, 0x00000001, 0x00000001,
        0x0304001f, 0x0010001a, 0x00000001, 0x05000036, 0x001020f2, 0x00000000, 0x00100e46, 0x00000000,
        0x08000036, 0x00102092, 0x00000001, 0x00004002, 0x3f800000, 0x00000000, 0x00000000, 0x00000000,
        0x09000038, 0x00102022, 0x00000001, 0x00004001, 0x3b808081, 0x00a1900a, 0x0010000a, 0x00000001,
        0x00000001, 0x07000038, 0x00102042, 0x00000001, 0x0011b01a, 0x00000000, 0x00004001, 0x3b808081,
        0x0100003e, 0x01000015, 0x0700001e, 0x00100012, 0x00000001, 0x0010000a, 0x00000001, 0x00004001,
        0x00000001, 0x01000016, 0x05000036, 0x00100012, 0x00000001, 0x00004001, 0x00000000, 0x01000030,
        0x07000050, 0x00100022, 0x00000001, 0x0010000a, 0x00000001, 0x00004001, 0x00000003, 0x03040003,
        0x0010001a, 0x00000001, 0x09000027, 0x00100022, 0x00000001, 0x0011b01a, 0x00000001, 0x00a1900a,
        0x0010000a, 0x00000001, 0x00000002, 0x0304001f, 0x0010001a, 0x00000001, 0x05000036, 0x001020f2,
        0x00000000, 0x00100e46, 0x00000000, 0x08000036, 0x001020f2, 0x00000001, 0x00004002, 0x3f800000,
        0x00000000, 0x3f800000, 0x3f800000, 0x0100003e, 0x01000015, 0x0700001e, 0x00100012, 0x00000001,
        0x0010000a, 0x00000001, 0x00004001, 0x00000001, 0x01000016, 0x08000027, 0x00100012, 0x00000001,
        0x00004001, 0x00000000, 0x0021901a, 0x00000000, 0x00000002, 0x08000027, 0x00100022, 0x00000001,
        0x00004001, 0x00000001, 0x0021901a, 0x00000001, 0x00000002, 0x0700003c, 0x00100012, 0x00000001,
        0x0010001a, 0x00000001, 0x0010000a, 0x00000001, 0x08000027, 0x00100022, 0x00000001, 0x00004001,
        0x00000002, 0x0021901a, 0x00000002, 0x00000002, 0x0700003c, 0x00100012, 0x00000001, 0x0010001a,
        0x00000001, 0x0010000a, 0x00000001, 0x0304001f, 0x0010000a, 0x00000001, 0x05000036, 0x001020f2,
        0x00000000, 0x00100e46, 0x00000000, 0x08000036, 0x001020f2, 0x00000001, 0x00004002, 0x3f800000,
        0x3f800000, 0x00000000, 0x3f800000, 0x0100003e, 0x01000015, 0x05000036, 0x001020f2, 0x00000000,
        0x00100e46, 0x00000000, 0x08000036, 0x001020f2, 0x00000001, 0x00004002, 0x00000000, 0x3f800000,
        0x00000000, 0x3f800000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ds = {ds_code, sizeof(ds_code)};
    static const DWORD ps_code[] =
    {
#if 0
        float4 main(in float4 p : SV_Position, in float4 color : COLOR) : SV_Target
        {
            return color;
        }
#endif
        0x43425844, 0xbd83f517, 0x8974e87a, 0xaf402223, 0xaec7f351, 0x00000001, 0x000000f8, 0x00000003,
        0x0000002c, 0x00000080, 0x000000b4, 0x4e475349, 0x0000004c, 0x00000002, 0x00000008, 0x00000038,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x00000044, 0x00000000, 0x00000000,
        0x00000003, 0x00000001, 0x00000f0f, 0x505f5653, 0x7469736f, 0x006e6f69, 0x4f4c4f43, 0xabab0052,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x0000003c, 0x00000050,
        0x0000000f, 0x0100086a, 0x03001062, 0x001010f2, 0x00000001, 0x03000065, 0x001020f2, 0x00000000,
        0x05000036, 0x001020f2, 0x00000000, 0x00101e46, 0x00000001, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};
    static const D3D12_INPUT_ELEMENT_DESC layout_desc[] =
    {
        {"POSITION",        0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0,  0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
        {"REF_BUFFER_DATA", 0, DXGI_FORMAT_R32_FLOAT,          0, 16, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
    };
    static const struct
    {
        struct vec4 position;
        float ref_buffer_data;
    }
    vertices[] =
    {
        {{-1.0f, -1.0f, 0.0f, 1.0f}, 1.0f},
        {{-1.0f,  1.0f, 0.0f, 1.0f}, 1.0f},
        {{ 1.0f, -1.0f, 0.0f, 1.0f}, 1.0f},

        {{-1.0f,  1.0f, 0.0f, 1.0f}, 2.0f},
        {{ 1.0f,  1.0f, 0.0f, 1.0f}, 2.0f},
        {{ 1.0f, -1.0f, 0.0f, 1.0f}, 2.0f},
    };
    static const uint32_t buffer_data[] = {1, 2};

    memset(&desc, 0, sizeof(desc));
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    command_list = context.list;
    queue = context.queue;

    root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_SRV;
    root_parameters[0].Descriptor.ShaderRegister = 0;
    root_parameters[0].Descriptor.RegisterSpace = 0;
    root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_HULL;
    memset(&root_signature_desc, 0, sizeof(root_signature_desc));
    root_signature_desc.NumParameters = ARRAY_SIZE(root_parameters);
    root_signature_desc.pParameters = root_parameters;
    root_signature_desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
    hr = create_root_signature(context.device, &root_signature_desc, &context.root_signature);
    ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr);

    input_layout.pInputElementDescs = layout_desc;
    input_layout.NumElements = ARRAY_SIZE(layout_desc);
    init_pipeline_state_desc(&pso_desc, context.root_signature,
            context.render_target_desc.Format, NULL, NULL, &input_layout);
    pso_desc.VS = vs;
    pso_desc.HS = hs;
    pso_desc.DS = ds;
    pso_desc.PS = ps;
    pso_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_PATCH;
    hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
    ok(hr == S_OK, "Failed to create state, hr %#x.\n", hr);

    vb = create_upload_buffer(context.device, sizeof(vertices), vertices);
    vbv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vb);
    vbv.StrideInBytes = sizeof(*vertices);
    vbv.SizeInBytes = sizeof(vertices);

    raw_buffer = create_upload_buffer(context.device, sizeof(buffer_data), buffer_data);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, 1, &vbv);
    ID3D12GraphicsCommandList_SetGraphicsRootShaderResourceView(command_list,
            0, ID3D12Resource_GetGPUVirtualAddress(raw_buffer));
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 6, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    bug_if(is_nvidia_device(context.device) || is_mvk_device(context.device))
    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);

    ID3D12Resource_Release(vb);
    ID3D12Resource_Release(raw_buffer);
    destroy_test_context(&context);
}

static void test_domain_shader_inputs(void)
{
    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
    ID3D12GraphicsCommandList *command_list;
    D3D12_STREAM_OUTPUT_BUFFER_VIEW sobv;
    D3D12_INPUT_LAYOUT_DESC input_layout;
    struct d3d12_resource_readback rb;
    float got = 0.0f, expected = 0.0f;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12Resource *so_buffer;
    ID3D12CommandQueue *queue;
    const float *elems = NULL;
    unsigned int x, y;
    bool fail = false;
    HRESULT hr;

#if 0
    struct data
    {
        float4 position : SV_Position;
    };

    data vs_main()
    {
        data d;
        d.position = float4(1.0f, 2.0f, 3.0f, 4.0f);
        return d;
    }

    struct patch_constant_data
    {
        float tess_factor[2] : SV_TessFactor;
        float3 a : A;
        float3 b : B;
    };

    void patch_constant(OutputPatch<data, 1> control_points,
            uint prim_id : SV_PrimitiveID,
            out patch_constant_data output)
    {
        output.tess_factor[0] = 2.0f;
        output.tess_factor[1] = 1.0f;
        output.a = float3(2.0f, 4.0f, 10.0f);
        output.b = float3(3.0f, 3.0f, 12.0f);
    }

    [domain("isoline")]
    [outputcontrolpoints(1)]
    [partitioning("integer")]
    [outputtopology("line")]
    [patchconstantfunc("patch_constant")]
    data hs_main(InputPatch<data, 1> input)
    {
        return input[0];
    }

    [domain("isoline")]
    void ds_main(float tess_factor[2] : SV_TessFactor,
            float2 tess_coord : SV_DomainLocation,
            float3 a : A,
            float3 b : B,
            const OutputPatch<data, 1> patch,
            out float4 position : SV_Position,
            out float4 out_color : COLOR)
    {
        position = float4(patch[0].position.xy, tess_coord);
        out_color = float4(0.5f * (a.xy + b.yz), tess_factor[0], tess_factor[1]);
    }
#endif
    static const DWORD vs_code[] =
    {
        0x43425844, 0xcb24c840, 0x8a22891b, 0xcf0dbe1d, 0x7b0a4fdb, 0x00000001, 0x000000b8, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000001, 0x00000003, 0x00000000,
        0x0000000f, 0x505f5653, 0x7469736f, 0x006e6f69, 0x58454853, 0x00000040, 0x00010050, 0x00000010,
        0x0100086a, 0x04000067, 0x001020f2, 0x00000000, 0x00000001, 0x08000036, 0x001020f2, 0x00000000,
        0x00004002, 0x3f800000, 0x40000000, 0x40400000, 0x40800000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE vs = {vs_code, sizeof(vs_code)};
    static const DWORD hs_code[] =
    {
        0x43425844, 0x67db9c59, 0xff2c4d5e, 0xc2a4d15b, 0x0f71495f, 0x00000001, 0x00000384, 0x00000004,
        0x00000030, 0x00000064, 0x00000098, 0x0000011c, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008,
        0x00000020, 0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x00000f0f, 0x505f5653, 0x7469736f,
        0x006e6f69, 0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000001,
        0x00000003, 0x00000000, 0x0000000f, 0x505f5653, 0x7469736f, 0x006e6f69, 0x47534350, 0x0000007c,
        0x00000004, 0x00000008, 0x00000068, 0x00000000, 0x00000010, 0x00000003, 0x00000000, 0x00000e01,
        0x00000076, 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x0000010e, 0x00000068, 0x00000001,
        0x0000000f, 0x00000003, 0x00000001, 0x00000e01, 0x00000078, 0x00000000, 0x00000000, 0x00000003,
        0x00000001, 0x0000010e, 0x545f5653, 0x46737365, 0x6f746361, 0x00410072, 0xabab0042, 0x58454853,
        0x00000260, 0x00030050, 0x00000098, 0x01000071, 0x01000893, 0x01000894, 0x01000895, 0x01000896,
        0x01001097, 0x0100086a, 0x00001835, 0x0000000a, 0x41200000, 0x40800000, 0x40000000, 0x40000000,
        0x41400000, 0x40400000, 0x40400000, 0x3f800000, 0x01000072, 0x0400005f, 0x002010f2, 0x00000001,
        0x00000000, 0x03000065, 0x001020f2, 0x00000000, 0x06000036, 0x001020f2, 0x00000000, 0x00201e46,
        0x00000000, 0x00000000, 0x0100003e, 0x01000073, 0x02000099, 0x00000002, 0x0200005f, 0x00017000,
        0x04000067, 0x00102012, 0x00000000, 0x00000016, 0x04000067, 0x00102012, 0x00000001, 0x00000015,
        0x02000068, 0x00000001, 0x0400005b, 0x00102012, 0x00000000, 0x00000002, 0x04000036, 0x00100012,
        0x00000000, 0x0001700a, 0x07000036, 0x00902012, 0x0010000a, 0x00000000, 0x0090903a, 0x0010000a,
        0x00000000, 0x0100003e, 0x01000073, 0x02000099, 0x00000002, 0x0200005f, 0x00017000, 0x03000065,
        0x00102022, 0x00000000, 0x03000065, 0x00102022, 0x00000001, 0x02000068, 0x00000001, 0x0400005b,
        0x00102022, 0x00000000, 0x00000002, 0x04000036, 0x00100012, 0x00000000, 0x0001700a, 0x07000036,
        0x00902022, 0x0010000a, 0x00000000, 0x0090902a, 0x0010000a, 0x00000000, 0x0100003e, 0x01000073,
        0x02000099, 0x00000002, 0x0200005f, 0x00017000, 0x03000065, 0x00102042, 0x00000000, 0x03000065,
        0x00102042, 0x00000001, 0x02000068, 0x00000001, 0x0400005b, 0x00102042, 0x00000000, 0x00000002,
        0x04000036, 0x00100012, 0x00000000, 0x0001700a, 0x07000036, 0x00902042, 0x0010000a, 0x00000000,
        0x0090901a, 0x0010000a, 0x00000000, 0x0100003e, 0x01000073, 0x02000099, 0x00000002, 0x0200005f,
        0x00017000, 0x03000065, 0x00102082, 0x00000000, 0x03000065, 0x00102082, 0x00000001, 0x02000068,
        0x00000001, 0x0400005b, 0x00102082, 0x00000000, 0x00000002, 0x04000036, 0x00100012, 0x00000000,
        0x0001700a, 0x07000036, 0x00902082, 0x0010000a, 0x00000000, 0x0090900a, 0x0010000a, 0x00000000,
        0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE hs = {hs_code, sizeof(hs_code)};
    static const DWORD ds_code[] =
    {
        0x43425844, 0xef67f8be, 0x1be137b6, 0x0e4a6b01, 0xf0cd92d8, 0x00000001, 0x00000264, 0x00000004,
        0x00000030, 0x00000064, 0x000000e8, 0x0000013c, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008,
        0x00000020, 0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000030f, 0x505f5653, 0x7469736f,
        0x006e6f69, 0x47534350, 0x0000007c, 0x00000004, 0x00000008, 0x00000068, 0x00000000, 0x00000010,
        0x00000003, 0x00000000, 0x00000101, 0x00000076, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000060e, 0x00000068, 0x00000001, 0x0000000f, 0x00000003, 0x00000001, 0x00000101, 0x00000078,
        0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x00000c0e, 0x545f5653, 0x46737365, 0x6f746361,
        0x00410072, 0xabab0042, 0x4e47534f, 0x0000004c, 0x00000002, 0x00000008, 0x00000038, 0x00000000,
        0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x00000044, 0x00000000, 0x00000000, 0x00000003,
        0x00000001, 0x0000000f, 0x505f5653, 0x7469736f, 0x006e6f69, 0x4f4c4f43, 0xabab0052, 0x58454853,
        0x00000120, 0x00040050, 0x00000048, 0x01000893, 0x01000895, 0x0100086a, 0x04000061, 0x0011b012,
        0x00000000, 0x00000016, 0x0300005f, 0x0011b062, 0x00000000, 0x04000061, 0x0011b012, 0x00000001,
        0x00000015, 0x0300005f, 0x0011b0c2, 0x00000001, 0x0200005f, 0x0001c032, 0x0400005f, 0x00219032,
        0x00000001, 0x00000000, 0x04000067, 0x001020f2, 0x00000000, 0x00000001, 0x03000065, 0x001020f2,
        0x00000001, 0x02000068, 0x00000001, 0x06000036, 0x00102032, 0x00000000, 0x00219046, 0x00000000,
        0x00000000, 0x04000036, 0x001020c2, 0x00000000, 0x0001c406, 0x07000000, 0x00100032, 0x00000000,
        0x0011b596, 0x00000000, 0x0011bae6, 0x00000001, 0x0a000038, 0x00102032, 0x00000001, 0x00100046,
        0x00000000, 0x00004002, 0x3f000000, 0x3f000000, 0x00000000, 0x00000000, 0x05000036, 0x00102042,
        0x00000001, 0x0011b00a, 0x00000000, 0x05000036, 0x00102082, 0x00000001, 0x0011b00a, 0x00000001,
        0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ds = {ds_code, sizeof(ds_code)};
    static const D3D12_SO_DECLARATION_ENTRY so_declaration[] =
    {
        {0, "SV_POSITION",  0, 0, 4, 0},
        {0, "COLOR",        0, 0, 4, 0},
    };
    static const float reference[][8] =
    {
        {1.0f, 2.0f, 0.0f, 0.0f, 2.5f, 8.0f, 2.0f, 1.0f},
        {1.0f, 2.0f, 1.0f, 0.0f, 2.5f, 8.0f, 2.0f, 1.0f},
        {1.0f, 2.0f, 0.0f, 0.5f, 2.5f, 8.0f, 2.0f, 1.0f},
        {1.0f, 2.0f, 1.0f, 0.5f, 2.5f, 8.0f, 2.0f, 1.0f},
    };
    static const unsigned int stride = sizeof(*reference);

    memset(&desc, 0, sizeof(desc));
    desc.root_signature_flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT
            | D3D12_ROOT_SIGNATURE_FLAG_ALLOW_STREAM_OUTPUT;
    desc.no_pipeline = true;
    if (!init_test_context(&context, &desc))
        return;
    command_list = context.list;
    queue = context.queue;

    memset(&input_layout, 0, sizeof(input_layout));

    init_pipeline_state_desc(&pso_desc, context.root_signature, DXGI_FORMAT_UNKNOWN, &vs, NULL, &input_layout);
    pso_desc.HS = hs;
    pso_desc.DS = ds;
    memset(&pso_desc.PS, 0, sizeof(pso_desc.PS));
    pso_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_PATCH;
    pso_desc.StreamOutput.NumEntries = ARRAY_SIZE(so_declaration);
    pso_desc.StreamOutput.pSODeclaration = so_declaration;
    pso_desc.StreamOutput.pBufferStrides = &stride;
    pso_desc.StreamOutput.NumStrides = 1;
    pso_desc.StreamOutput.RasterizedStream = D3D12_SO_NO_RASTERIZED_STREAM;
    hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
    bug_if(is_mvk_device(context.device))
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);

    so_buffer = create_default_buffer(context.device, 2048,
            D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_STREAM_OUT);
    sobv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(so_buffer);
    sobv.SizeInBytes = 1024;
    sobv.BufferFilledSizeLocation = sobv.BufferLocation + sobv.SizeInBytes;

    ID3D12GraphicsCommandList_SOSetTargets(command_list, 0, 1, &sobv);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_1_CONTROL_POINT_PATCHLIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 1, 0, 0);

    transition_resource_state(command_list, so_buffer,
            D3D12_RESOURCE_STATE_STREAM_OUT, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_buffer_readback_with_command_list(so_buffer, DXGI_FORMAT_UNKNOWN, &rb, queue, command_list);
    for (y = 0; y < ARRAY_SIZE(reference); ++y)
    {
        elems = get_readback_data(&rb.rb, y, 0, 0, stride);
        for (x = 0; x < ARRAY_SIZE(*reference); ++x)
        {
            got = elems[x];
            expected = reference[y][x];
            if (!compare_float(expected, got, 0))
            {
                fail = true;
                break;
            }
        }

        if (fail)
            break;
    }
    bug_if(is_mvk_device(context.device))
    ok(!fail, "Got unexpected value %.8e for [%u][%u], expected %.8e.\n", got, y, x, expected);
    release_resource_readback(&rb);

    ID3D12Resource_Release(so_buffer);
    destroy_test_context(&context);
}

static void test_domain_shader_one_patch_constant_input(void)
{
    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
    ID3D12GraphicsCommandList *command_list;
    D3D12_STREAM_OUTPUT_BUFFER_VIEW sobv;
    D3D12_INPUT_LAYOUT_DESC input_layout;
    struct d3d12_resource_readback rb;
    float got = 0.0f, expected = 0.0f;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12Resource *so_buffer;
    ID3D12CommandQueue *queue;
    const float *elems = NULL;
    unsigned int x, y;
    bool fail = false;
    HRESULT hr;

#if 0
    struct data
    {
        float4 position : SV_Position;
    };

    data vs_main()
    {
        data d;
        d.position = float4(1.0f, 2.0f, 3.0f, 4.0f);
        return d;
    }

    struct patch_constant_data
    {
        float tess_factor[2] : SV_TessFactor;
    };

    void patch_constant(OutputPatch<data, 1> control_points,
            uint prim_id : SV_PrimitiveID,
            out patch_constant_data output)
    {
        output.tess_factor[0] = 2.0f;
        output.tess_factor[1] = 1.0f;
    }

    [domain("isoline")]
    [outputcontrolpoints(1)]
    [partitioning("integer")]
    [outputtopology("line")]
    [patchconstantfunc("patch_constant")]
    data hs_main(InputPatch<data, 1> input)
    {
        return input[0];
    }

    [domain("isoline")]
    void ds_main(float tess_factor[2] : SV_TessFactor,
            const OutputPatch<data, 1> patch,
            out float4 position : SV_Position)
    {
        position = float4(patch[0].position.xyz, tess_factor[1]);
    }
#endif
    static const DWORD vs_code[] =
    {
        0x43425844, 0xcb24c840, 0x8a22891b, 0xcf0dbe1d, 0x7b0a4fdb, 0x00000001, 0x000000b8, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000001, 0x00000003, 0x00000000,
        0x0000000f, 0x505f5653, 0x7469736f, 0x006e6f69, 0x58454853, 0x00000040, 0x00010050, 0x00000010,
        0x0100086a, 0x04000067, 0x001020f2, 0x00000000, 0x00000001, 0x08000036, 0x001020f2, 0x00000000,
        0x00004002, 0x3f800000, 0x40000000, 0x40400000, 0x40800000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE vs = {vs_code, sizeof(vs_code)};
    static const DWORD hs_code[] =
    {
        0x43425844, 0x36132cb6, 0x45a8f125, 0x2ff97810, 0xf60e439e, 0x00000001, 0x000001f4, 0x00000004,
        0x00000030, 0x00000064, 0x00000098, 0x000000e8, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008,
        0x00000020, 0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x00000f0f, 0x505f5653, 0x7469736f,
        0x006e6f69, 0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000001,
        0x00000003, 0x00000000, 0x0000000f, 0x505f5653, 0x7469736f, 0x006e6f69, 0x47534350, 0x00000048,
        0x00000002, 0x00000008, 0x00000038, 0x00000000, 0x00000010, 0x00000003, 0x00000000, 0x00000e01,
        0x00000038, 0x00000001, 0x0000000f, 0x00000003, 0x00000001, 0x00000e01, 0x545f5653, 0x46737365,
        0x6f746361, 0xabab0072, 0x58454853, 0x00000104, 0x00030050, 0x00000041, 0x01000071, 0x01000893,
        0x01000894, 0x01000895, 0x01000896, 0x01001097, 0x0100086a, 0x00001835, 0x0000000a, 0x40000000,
        0x00000000, 0x00000000, 0x00000000, 0x3f800000, 0x00000000, 0x00000000, 0x00000000, 0x01000072,
        0x0400005f, 0x002010f2, 0x00000001, 0x00000000, 0x03000065, 0x001020f2, 0x00000000, 0x06000036,
        0x001020f2, 0x00000000, 0x00201e46, 0x00000000, 0x00000000, 0x0100003e, 0x01000073, 0x02000099,
        0x00000002, 0x0200005f, 0x00017000, 0x04000067, 0x00102012, 0x00000000, 0x00000016, 0x04000067,
        0x00102012, 0x00000001, 0x00000015, 0x02000068, 0x00000001, 0x0400005b, 0x00102012, 0x00000000,
        0x00000002, 0x04000036, 0x00100012, 0x00000000, 0x0001700a, 0x07000036, 0x00902012, 0x0010000a,
        0x00000000, 0x0090900a, 0x0010000a, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE hs = {hs_code, sizeof(hs_code)};
    static const DWORD ds_code[] =
    {
        0x43425844, 0x3ac40533, 0x2aa65785, 0x41df1859, 0xb3b2294b, 0x00000001, 0x00000164, 0x00000004,
        0x00000030, 0x00000064, 0x000000b4, 0x000000e8, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008,
        0x00000020, 0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000070f, 0x505f5653, 0x7469736f,
        0x006e6f69, 0x47534350, 0x00000048, 0x00000002, 0x00000008, 0x00000038, 0x00000000, 0x00000010,
        0x00000003, 0x00000000, 0x00000001, 0x00000038, 0x00000001, 0x0000000f, 0x00000003, 0x00000001,
        0x00000101, 0x545f5653, 0x46737365, 0x6f746361, 0xabab0072, 0x4e47534f, 0x0000002c, 0x00000001,
        0x00000008, 0x00000020, 0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x505f5653,
        0x7469736f, 0x006e6f69, 0x58454853, 0x00000074, 0x00040050, 0x0000001d, 0x01000893, 0x01000895,
        0x0100086a, 0x04000061, 0x0011b012, 0x00000001, 0x00000015, 0x0400005f, 0x00219072, 0x00000001,
        0x00000000, 0x04000067, 0x001020f2, 0x00000000, 0x00000001, 0x06000036, 0x00102072, 0x00000000,
        0x00219246, 0x00000000, 0x00000000, 0x05000036, 0x00102082, 0x00000000, 0x0011b00a, 0x00000001,
        0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ds = {ds_code, sizeof(ds_code)};
    static const D3D12_SO_DECLARATION_ENTRY so_declaration[] =
    {
        {0, "SV_POSITION",  0, 0, 4, 0},
    };
    static const float reference[][4] =
    {
        {1.0f, 2.0f, 3.0f, 1.0f},
        {1.0f, 2.0f, 3.0f, 1.0f},
        {1.0f, 2.0f, 3.0f, 1.0f},
        {1.0f, 2.0f, 3.0f, 1.0f},
    };
    static const unsigned int stride = sizeof(*reference);

    memset(&desc, 0, sizeof(desc));
    desc.root_signature_flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT
            | D3D12_ROOT_SIGNATURE_FLAG_ALLOW_STREAM_OUTPUT;
    desc.no_pipeline = true;
    if (!init_test_context(&context, &desc))
        return;
    command_list = context.list;
    queue = context.queue;

    memset(&input_layout, 0, sizeof(input_layout));

    init_pipeline_state_desc(&pso_desc, context.root_signature, DXGI_FORMAT_UNKNOWN, &vs, NULL, &input_layout);
    pso_desc.HS = hs;
    pso_desc.DS = ds;
    memset(&pso_desc.PS, 0, sizeof(pso_desc.PS));
    pso_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_PATCH;
    pso_desc.StreamOutput.NumEntries = ARRAY_SIZE(so_declaration);
    pso_desc.StreamOutput.pSODeclaration = so_declaration;
    pso_desc.StreamOutput.pBufferStrides = &stride;
    pso_desc.StreamOutput.NumStrides = 1;
    pso_desc.StreamOutput.RasterizedStream = D3D12_SO_NO_RASTERIZED_STREAM;
    hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
    bug_if(is_mvk_device(context.device))
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);

    so_buffer = create_default_buffer(context.device, 2048,
            D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_STREAM_OUT);
    sobv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(so_buffer);
    sobv.SizeInBytes = 1024;
    sobv.BufferFilledSizeLocation = sobv.BufferLocation + sobv.SizeInBytes;

    ID3D12GraphicsCommandList_SOSetTargets(command_list, 0, 1, &sobv);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_1_CONTROL_POINT_PATCHLIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 1, 0, 0);

    transition_resource_state(command_list, so_buffer,
            D3D12_RESOURCE_STATE_STREAM_OUT, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_buffer_readback_with_command_list(so_buffer, DXGI_FORMAT_UNKNOWN, &rb, queue, command_list);
    for (y = 0; y < ARRAY_SIZE(reference); ++y)
    {
        elems = get_readback_data(&rb.rb, y, 0, 0, stride);
        for (x = 0; x < ARRAY_SIZE(*reference); ++x)
        {
            got = elems[x];
            expected = reference[y][x];
            if (!compare_float(expected, got, 0))
            {
                fail = true;
                break;
            }
        }

        if (fail)
            break;
    }
    bug_if(is_mvk_device(context.device))
    ok(!fail, "Got unexpected value %.8e for [%u][%u], expected %.8e.\n", got, y, x, expected);
    release_resource_readback(&rb);

    ID3D12Resource_Release(so_buffer);
    destroy_test_context(&context);
}

static void test_render_a8(void)
{
    static const float black[] = {0.0f, 0.0f, 0.0f, 0.0f};
    ID3D12GraphicsCommandList *command_list;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12CommandQueue *queue;

    static const DWORD ps_code[] =
    {
#if 0
        void main(out float4 target : SV_Target)
        {
            target = float4(0.0f, 0.25f, 0.5f, 1.0f);
        }
#endif
        0x43425844, 0x2f09e5ff, 0xaa135d5e, 0x7860f4b5, 0x5c7b8cbc, 0x00000001, 0x000000b4, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x0000003c, 0x00000050, 0x0000000f,
        0x0100086a, 0x03000065, 0x001020f2, 0x00000000, 0x08000036, 0x001020f2, 0x00000000, 0x00004002,
        0x00000000, 0x3e800000, 0x3f000000, 0x3f800000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};

    memset(&desc, 0, sizeof(desc));
    desc.rt_format = DXGI_FORMAT_A8_UNORM;
    desc.ps = &ps;
    if (!init_test_context(&context, &desc))
        return;
    command_list = context.list;
    queue = context.queue;

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, black, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);

    check_sub_resource_uint8(context.render_target, 0, queue, command_list, 0xff, 0);

    destroy_test_context(&context);
}

static void test_cpu_descriptors_lifetime(void)
{
    static const float blue[] = {0.0f, 0.0f, 1.0f, 1.0f};
    static const float red[] = {1.0f, 0.0f, 0.0f, 1.0f};
    ID3D12Resource *red_resource, *blue_resource;
    ID3D12GraphicsCommandList *command_list;
    D3D12_CPU_DESCRIPTOR_HANDLE rtv_handle;
    D3D12_HEAP_PROPERTIES heap_properties;
    D3D12_RESOURCE_DESC resource_desc;
    ID3D12DescriptorHeap *rtv_heap;
    D3D12_CLEAR_VALUE clear_value;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Device *device;
    HRESULT hr;

    if (!init_test_context(&context, NULL))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    rtv_heap = create_cpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_RTV, 1);
    rtv_handle = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(rtv_heap);

    memset(&heap_properties, 0, sizeof(heap_properties));
    heap_properties.Type = D3D12_HEAP_TYPE_DEFAULT;
    resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
    resource_desc.Alignment = 0;
    resource_desc.Width = 32;
    resource_desc.Height = 32;
    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;
    clear_value.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    clear_value.Color[0] = 1.0f;
    clear_value.Color[1] = 0.0f;
    clear_value.Color[2] = 0.0f;
    clear_value.Color[3] = 1.0f;
    hr = ID3D12Device_CreateCommittedResource(device,
            &heap_properties, D3D12_HEAP_FLAG_NONE, &resource_desc,
            D3D12_RESOURCE_STATE_RENDER_TARGET, &clear_value,
            &IID_ID3D12Resource, (void **)&red_resource);
    ok(hr == S_OK, "Failed to create texture, hr %#x.\n", hr);
    clear_value.Color[0] = 0.0f;
    clear_value.Color[1] = 0.0f;
    clear_value.Color[2] = 1.0f;
    clear_value.Color[3] = 1.0f;
    hr = ID3D12Device_CreateCommittedResource(device,
            &heap_properties, D3D12_HEAP_FLAG_NONE, &resource_desc,
            D3D12_RESOURCE_STATE_RENDER_TARGET, &clear_value,
            &IID_ID3D12Resource, (void **)&blue_resource);
    ok(hr == S_OK, "Failed to create texture, hr %#x.\n", hr);

    ID3D12Device_CreateRenderTargetView(device, red_resource, NULL, rtv_handle);
    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, rtv_handle, red, 0, NULL);
    /* Destroy the previous RTV and create a new one in its place. */
    ID3D12Device_CreateRenderTargetView(device, blue_resource, NULL, rtv_handle);
    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, rtv_handle, blue, 0, NULL);

    /* Destroy the CPU descriptor heap. */
    ID3D12DescriptorHeap_Release(rtv_heap);

    transition_resource_state(command_list, red_resource,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    transition_resource_state(command_list, blue_resource,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_uint(red_resource, 0, queue, command_list, 0xff0000ff, 0);
    reset_command_list(command_list, context.allocator);
    check_sub_resource_uint(blue_resource, 0, queue, command_list, 0xffff0000, 0);

    rtv_heap = create_cpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_RTV, 1);
    rtv_handle = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(rtv_heap);

    /* Try again with OMSetRenderTargets(). */
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, red_resource,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
    transition_resource_state(command_list, blue_resource,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    ID3D12Device_CreateRenderTargetView(device, red_resource, NULL, rtv_handle);
    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, rtv_handle, red, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &rtv_handle, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    /* Destroy the previous RTV and create a new one in its place. */
    ID3D12Device_CreateRenderTargetView(device, blue_resource, NULL, rtv_handle);
    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, rtv_handle, blue, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &rtv_handle, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    /* Destroy the previous RTV and create a new one in its place. */
    ID3D12Device_CreateRenderTargetView(device, red_resource, NULL, rtv_handle);

    /* Destroy the CPU descriptor heap. */
    ID3D12DescriptorHeap_Release(rtv_heap);

    transition_resource_state(command_list, red_resource,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    transition_resource_state(command_list, blue_resource,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_uint(red_resource, 0, queue, command_list, 0xff00ff00, 0);
    reset_command_list(command_list, context.allocator);
    check_sub_resource_uint(blue_resource, 0, queue, command_list, 0xff00ff00, 0);

    ID3D12Resource_Release(blue_resource);
    ID3D12Resource_Release(red_resource);
    destroy_test_context(&context);
}

static void check_clip_distance(struct test_context *context, ID3D12PipelineState *pso,
        D3D12_PRIMITIVE_TOPOLOGY topology, D3D12_VERTEX_BUFFER_VIEW vbv[2], ID3D12Resource *vb,
        ID3D12Resource *vs_cb, ID3D12Resource *tess_cb, ID3D12Resource *gs_cb)
{
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    struct vertex
    {
        float clip_distance0;
        float clip_distance1;
    };

    ID3D12GraphicsCommandList *command_list = context->list;
    ID3D12CommandQueue *queue = context->queue;
    struct d3d12_resource_readback rb;
    struct vertex vertices[4];
    unsigned int i;
    D3D12_BOX box;

    for (i = 0; i < ARRAY_SIZE(vertices); ++i)
        vertices[i].clip_distance0 = 1.0f;
    update_buffer_data(vb, 0, sizeof(vertices), vertices);
    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context->rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context->root_signature);
    ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 0,
            ID3D12Resource_GetGPUVirtualAddress(vs_cb));
    ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 1,
            ID3D12Resource_GetGPUVirtualAddress(tess_cb));
    ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 2,
            ID3D12Resource_GetGPUVirtualAddress(tess_cb));
    ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 3,
            ID3D12Resource_GetGPUVirtualAddress(gs_cb));
    ID3D12GraphicsCommandList_SetPipelineState(command_list, pso);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, topology);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context->viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context->scissor_rect);
    ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, 2, vbv);
    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context->rtv, white, 0, NULL);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 1, 0, 0);
    transition_resource_state(command_list, context->render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_uint(context->render_target, 0, queue, command_list, 0xff00ff00, 0);

    reset_command_list(command_list, context->allocator);
    transition_resource_state(command_list, context->render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    for (i = 0; i < ARRAY_SIZE(vertices); ++i)
        vertices[i].clip_distance0 = 0.0f;
    update_buffer_data(vb, 0, sizeof(vertices), vertices);
    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context->rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context->root_signature);
    ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 0,
            ID3D12Resource_GetGPUVirtualAddress(vs_cb));
    ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 1,
            ID3D12Resource_GetGPUVirtualAddress(tess_cb));
    ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 2,
            ID3D12Resource_GetGPUVirtualAddress(tess_cb));
    ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 3,
            ID3D12Resource_GetGPUVirtualAddress(gs_cb));
    ID3D12GraphicsCommandList_SetPipelineState(command_list, pso);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, topology);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context->viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context->scissor_rect);
    ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, 2, vbv);
    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context->rtv, white, 0, NULL);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 1, 0, 0);
    transition_resource_state(command_list, context->render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_uint(context->render_target, 0, queue, command_list, 0xff00ff00, 0);

    reset_command_list(command_list, context->allocator);
    transition_resource_state(command_list, context->render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    for (i = 0; i < ARRAY_SIZE(vertices); ++i)
        vertices[i].clip_distance0 = -1.0f;
    update_buffer_data(vb, 0, sizeof(vertices), vertices);
    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context->rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context->root_signature);
    ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 0,
            ID3D12Resource_GetGPUVirtualAddress(vs_cb));
    ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 1,
            ID3D12Resource_GetGPUVirtualAddress(tess_cb));
    ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 2,
            ID3D12Resource_GetGPUVirtualAddress(tess_cb));
    ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 3,
            ID3D12Resource_GetGPUVirtualAddress(gs_cb));
    ID3D12GraphicsCommandList_SetPipelineState(command_list, pso);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, topology);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context->viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context->scissor_rect);
    ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, 2, vbv);
    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context->rtv, white, 0, NULL);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 1, 0, 0);
    transition_resource_state(command_list, context->render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_uint(context->render_target, 0, queue, command_list, 0xffffffff, 0);

    reset_command_list(command_list, context->allocator);
    transition_resource_state(command_list, context->render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    for (i = 0; i < ARRAY_SIZE(vertices); ++i)
        vertices[i].clip_distance0 = i < 2 ? 1.0f : -1.0f;
    update_buffer_data(vb, 0, sizeof(vertices), vertices);
    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context->rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context->root_signature);
    ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 0,
            ID3D12Resource_GetGPUVirtualAddress(vs_cb));
    ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 1,
            ID3D12Resource_GetGPUVirtualAddress(tess_cb));
    ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 2,
            ID3D12Resource_GetGPUVirtualAddress(tess_cb));
    ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 3,
            ID3D12Resource_GetGPUVirtualAddress(gs_cb));
    ID3D12GraphicsCommandList_SetPipelineState(command_list, pso);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, topology);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context->viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context->scissor_rect);
    ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, 2, vbv);
    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context->rtv, white, 0, NULL);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 1, 0, 0);
    transition_resource_state(command_list, context->render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_resource_readback_with_command_list(context->render_target, 0, &rb, queue, command_list);
    set_box(&box, 0, 0, 0, 320, 480, 1);
    check_readback_data_uint(&rb.rb, &box, 0xff00ff00, 1);
    set_box(&box, 320, 0, 0, 320, 480, 1);
    check_readback_data_uint(&rb.rb, &box, 0xffffffff, 1);
    release_resource_readback(&rb);

    reset_command_list(command_list, context->allocator);
    transition_resource_state(command_list, context->render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    for (i = 0; i < ARRAY_SIZE(vertices); ++i)
        vertices[i].clip_distance0 = i % 2 ? 1.0f : -1.0f;
    update_buffer_data(vb, 0, sizeof(vertices), vertices);
    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context->rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context->root_signature);
    ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 0,
            ID3D12Resource_GetGPUVirtualAddress(vs_cb));
    ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 1,
            ID3D12Resource_GetGPUVirtualAddress(tess_cb));
    ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 2,
            ID3D12Resource_GetGPUVirtualAddress(tess_cb));
    ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 3,
            ID3D12Resource_GetGPUVirtualAddress(gs_cb));
    ID3D12GraphicsCommandList_SetPipelineState(command_list, pso);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, topology);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context->viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context->scissor_rect);
    ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, 2, vbv);
    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context->rtv, white, 0, NULL);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 1, 0, 0);
    transition_resource_state(command_list, context->render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_resource_readback_with_command_list(context->render_target, 0, &rb, queue, command_list);
    set_box(&box, 0, 0, 0, 640, 240, 1);
    check_readback_data_uint(&rb.rb, &box, 0xff00ff00, 0);
    set_box(&box, 0, 240, 0, 640, 240, 1);
    check_readback_data_uint(&rb.rb, &box, 0xffffffff, 0);
    release_resource_readback(&rb);

    reset_command_list(command_list, context->allocator);
    transition_resource_state(command_list, context->render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
}

static void test_clip_distance(void)
{
    ID3D12Resource *vs_cb, *tess_cb, *gs_cb, *vb[2];
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
    D3D12_ROOT_PARAMETER root_parameters[4];
    ID3D12GraphicsCommandList *command_list;
    D3D12_INPUT_LAYOUT_DESC input_layout;
    struct d3d12_resource_readback rb;
    D3D12_VERTEX_BUFFER_VIEW vbv[2];
    struct test_context_desc desc;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12PipelineState *pso;
    ID3D12Device *device;
    unsigned int i;
    D3D12_BOX box;
    HRESULT hr;

    static const DWORD vs_code[] =
    {
#if 0
        bool use_constant;
        float clip_distance;

        struct input
        {
            float4 position : POSITION;
            float distance0 : CLIP_DISTANCE0;
            float distance1 : CLIP_DISTANCE1;
        };

        struct vertex
        {
            float4 position : SV_POSITION;
            float user_clip : CLIP_DISTANCE;
            float clip : SV_ClipDistance;
        };

        void main(input vin, out vertex vertex)
        {
            vertex.position = vin.position;
            vertex.user_clip = vin.distance0;
            vertex.clip = vin.distance0;
            if (use_constant)
                vertex.clip = clip_distance;
        }
#endif
        0x43425844, 0x09dfef58, 0x88570f2e, 0x1ebcf953, 0x9f97e22a, 0x00000001, 0x000001dc, 0x00000003,
        0x0000002c, 0x0000009c, 0x00000120, 0x4e475349, 0x00000068, 0x00000003, 0x00000008, 0x00000050,
        0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000f0f, 0x00000059, 0x00000000, 0x00000000,
        0x00000003, 0x00000001, 0x00000101, 0x00000059, 0x00000001, 0x00000000, 0x00000003, 0x00000002,
        0x00000001, 0x49534f50, 0x4e4f4954, 0x494c4300, 0x49445f50, 0x4e415453, 0xab004543, 0x4e47534f,
        0x0000007c, 0x00000003, 0x00000008, 0x00000050, 0x00000000, 0x00000001, 0x00000003, 0x00000000,
        0x0000000f, 0x0000005c, 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x00000e01, 0x0000006a,
        0x00000000, 0x00000002, 0x00000003, 0x00000002, 0x00000e01, 0x505f5653, 0x5449534f, 0x004e4f49,
        0x50494c43, 0x5349445f, 0x434e4154, 0x56530045, 0x696c435f, 0x73694470, 0x636e6174, 0xabab0065,
        0x52444853, 0x000000b4, 0x00010040, 0x0000002d, 0x04000059, 0x00208e46, 0x00000000, 0x00000001,
        0x0300005f, 0x001010f2, 0x00000000, 0x0300005f, 0x00101012, 0x00000001, 0x04000067, 0x001020f2,
        0x00000000, 0x00000001, 0x03000065, 0x00102012, 0x00000001, 0x04000067, 0x00102012, 0x00000002,
        0x00000002, 0x05000036, 0x001020f2, 0x00000000, 0x00101e46, 0x00000000, 0x05000036, 0x00102012,
        0x00000001, 0x0010100a, 0x00000001, 0x0b000037, 0x00102012, 0x00000002, 0x0020800a, 0x00000000,
        0x00000000, 0x0020801a, 0x00000000, 0x00000000, 0x0010100a, 0x00000001, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE vs = {vs_code, sizeof(vs_code)};
    static const DWORD vs_multiple_code[] =
    {
#if 0
        bool use_constant;
        float clip_distance0;
        float clip_distance1;

        struct input
        {
            float4 position : POSITION;
            float distance0 : CLIP_DISTANCE0;
            float distance1 : CLIP_DISTANCE1;
        };

        struct vertex
        {
            float4 position : SV_POSITION;
            float user_clip : CLIP_DISTANCE;
            float2 clip : SV_ClipDistance;
        };

        void main(input vin, out vertex vertex)
        {
            vertex.position = vin.position;
            vertex.user_clip = vin.distance0;
            vertex.clip.x = vin.distance0;
            if (use_constant)
                vertex.clip.x = clip_distance0;
            vertex.clip.y = vin.distance1;
            if (use_constant)
                vertex.clip.y = clip_distance1;
        }
#endif
        0x43425844, 0xef5cc236, 0xe2fbfa69, 0x560b6591, 0x23037999, 0x00000001, 0x00000214, 0x00000003,
        0x0000002c, 0x0000009c, 0x00000120, 0x4e475349, 0x00000068, 0x00000003, 0x00000008, 0x00000050,
        0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000f0f, 0x00000059, 0x00000000, 0x00000000,
        0x00000003, 0x00000001, 0x00000101, 0x00000059, 0x00000001, 0x00000000, 0x00000003, 0x00000002,
        0x00000101, 0x49534f50, 0x4e4f4954, 0x494c4300, 0x49445f50, 0x4e415453, 0xab004543, 0x4e47534f,
        0x0000007c, 0x00000003, 0x00000008, 0x00000050, 0x00000000, 0x00000001, 0x00000003, 0x00000000,
        0x0000000f, 0x0000005c, 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x00000e01, 0x0000006a,
        0x00000000, 0x00000002, 0x00000003, 0x00000002, 0x00000c03, 0x505f5653, 0x5449534f, 0x004e4f49,
        0x50494c43, 0x5349445f, 0x434e4154, 0x56530045, 0x696c435f, 0x73694470, 0x636e6174, 0xabab0065,
        0x52444853, 0x000000ec, 0x00010040, 0x0000003b, 0x04000059, 0x00208e46, 0x00000000, 0x00000001,
        0x0300005f, 0x001010f2, 0x00000000, 0x0300005f, 0x00101012, 0x00000001, 0x0300005f, 0x00101012,
        0x00000002, 0x04000067, 0x001020f2, 0x00000000, 0x00000001, 0x03000065, 0x00102012, 0x00000001,
        0x04000067, 0x00102032, 0x00000002, 0x00000002, 0x05000036, 0x001020f2, 0x00000000, 0x00101e46,
        0x00000000, 0x05000036, 0x00102012, 0x00000001, 0x0010100a, 0x00000001, 0x0b000037, 0x00102012,
        0x00000002, 0x0020800a, 0x00000000, 0x00000000, 0x0020801a, 0x00000000, 0x00000000, 0x0010100a,
        0x00000001, 0x0b000037, 0x00102022, 0x00000002, 0x0020800a, 0x00000000, 0x00000000, 0x0020802a,
        0x00000000, 0x00000000, 0x0010100a, 0x00000002, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE vs_multiple = {vs_multiple_code, sizeof(vs_multiple_code)};
#if 0
    bool use_constant;
    float clip_distance0;
    float clip_distance1;
    float tessellation_factor;

    struct vertex
    {
        float4 position : SV_POSITION;
        float user_clip : CLIP_DISTANCE;
        float clip : SV_ClipDistance;
    };

    struct patch_constant_data
    {
        float edges[4] : SV_TessFactor;
        float inside[2] : SV_InsideTessFactor;
    };

    patch_constant_data patch_constant()
    {
        patch_constant_data output;

        output.edges[0] = tessellation_factor;
        output.edges[1] = tessellation_factor;
        output.edges[2] = tessellation_factor;
        output.edges[3] = tessellation_factor;
        output.inside[0] = tessellation_factor;
        output.inside[1] = tessellation_factor;

        return output;
    }

    [domain("quad")]
    [outputcontrolpoints(4)]
    [outputtopology("triangle_cw")]
    [partitioning("pow2")]
    [patchconstantfunc("patch_constant")]
    vertex hs_main(InputPatch<vertex, 4> input,
            uint i : SV_OutputControlPointID)
    {
        vertex o;
        o.position = input[i].position;
        o.user_clip = input[i].user_clip;
        o.clip = input[i].user_clip;
        return o;
    }

    float4 interpolate_vec(float4 a, float4 b, float4 c, float4 d, float2 tess_coord)
    {
        float4 e = lerp(a, b, tess_coord.x);
        float4 f = lerp(c, d, tess_coord.x);
        return lerp(e, f, tess_coord.y);
    }

    float interpolate(float a, float b, float c, float d, float2 tess_coord)
    {
        float e = lerp(a, b, tess_coord.x);
        float f = lerp(c, d, tess_coord.x);
        return lerp(e, f, tess_coord.y);
    }

    [domain("quad")]
    vertex ds_main(patch_constant_data input,
            float2 tess_coord : SV_DomainLocation,
            const OutputPatch<vertex, 4> patch)
    {
        vertex output;

        output.position = interpolate_vec(patch[0].position, patch[1].position,
                patch[2].position, patch[3].position, tess_coord);
        output.user_clip = interpolate(patch[0].user_clip, patch[1].user_clip,
                patch[2].user_clip, patch[3].user_clip, tess_coord);
        output.clip = interpolate(patch[0].clip, patch[1].clip,
                patch[2].clip, patch[3].clip, tess_coord);
        if (use_constant)
            output.clip = clip_distance0;

        return output;
    }
#endif
    static const DWORD hs_code[] =
    {
        0x43425844, 0x5a6d7564, 0x5f30a6c9, 0x2cf3b848, 0x5b4c6dca, 0x00000001, 0x00000414, 0x00000004,
        0x00000030, 0x000000b4, 0x00000138, 0x000001fc, 0x4e475349, 0x0000007c, 0x00000003, 0x00000008,
        0x00000050, 0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x00000f0f, 0x0000005c, 0x00000000,
        0x00000000, 0x00000003, 0x00000001, 0x00000101, 0x0000006a, 0x00000000, 0x00000002, 0x00000003,
        0x00000002, 0x00000001, 0x505f5653, 0x5449534f, 0x004e4f49, 0x50494c43, 0x5349445f, 0x434e4154,
        0x56530045, 0x696c435f, 0x73694470, 0x636e6174, 0xabab0065, 0x4e47534f, 0x0000007c, 0x00000003,
        0x00000008, 0x00000050, 0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x0000005c,
        0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x00000e01, 0x0000006a, 0x00000000, 0x00000002,
        0x00000003, 0x00000002, 0x00000e01, 0x505f5653, 0x5449534f, 0x004e4f49, 0x50494c43, 0x5349445f,
        0x434e4154, 0x56530045, 0x696c435f, 0x73694470, 0x636e6174, 0xabab0065, 0x47534350, 0x000000bc,
        0x00000006, 0x00000008, 0x00000098, 0x00000000, 0x0000000b, 0x00000003, 0x00000000, 0x00000e01,
        0x00000098, 0x00000001, 0x0000000b, 0x00000003, 0x00000001, 0x00000e01, 0x00000098, 0x00000002,
        0x0000000b, 0x00000003, 0x00000002, 0x00000e01, 0x00000098, 0x00000003, 0x0000000b, 0x00000003,
        0x00000003, 0x00000e01, 0x000000a6, 0x00000000, 0x0000000c, 0x00000003, 0x00000004, 0x00000e01,
        0x000000a6, 0x00000001, 0x0000000c, 0x00000003, 0x00000005, 0x00000e01, 0x545f5653, 0x46737365,
        0x6f746361, 0x56530072, 0x736e495f, 0x54656469, 0x46737365, 0x6f746361, 0xabab0072, 0x58454853,
        0x00000210, 0x00030050, 0x00000084, 0x01000071, 0x01002093, 0x01002094, 0x01001895, 0x01001096,
        0x01001897, 0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x01000072, 0x0200005f,
        0x00016000, 0x0400005f, 0x002010f2, 0x00000004, 0x00000000, 0x0400005f, 0x00201012, 0x00000004,
        0x00000001, 0x03000065, 0x001020f2, 0x00000000, 0x03000065, 0x00102012, 0x00000001, 0x03000065,
        0x00102012, 0x00000002, 0x02000068, 0x00000001, 0x04000036, 0x00100012, 0x00000000, 0x00016001,
        0x07000036, 0x001020f2, 0x00000000, 0x00a01e46, 0x0010000a, 0x00000000, 0x00000000, 0x07000036,
        0x00102012, 0x00000001, 0x00a0100a, 0x0010000a, 0x00000000, 0x00000001, 0x07000036, 0x00102012,
        0x00000002, 0x00a0100a, 0x0010000a, 0x00000000, 0x00000001, 0x0100003e, 0x01000073, 0x02000099,
        0x00000004, 0x0200005f, 0x00017000, 0x04000067, 0x00102012, 0x00000000, 0x0000000b, 0x04000067,
        0x00102012, 0x00000001, 0x0000000c, 0x04000067, 0x00102012, 0x00000002, 0x0000000d, 0x04000067,
        0x00102012, 0x00000003, 0x0000000e, 0x02000068, 0x00000001, 0x0400005b, 0x00102012, 0x00000000,
        0x00000004, 0x04000036, 0x00100012, 0x00000000, 0x0001700a, 0x07000036, 0x00902012, 0x0010000a,
        0x00000000, 0x0020803a, 0x00000000, 0x00000000, 0x0100003e, 0x01000073, 0x02000099, 0x00000002,
        0x0200005f, 0x00017000, 0x04000067, 0x00102012, 0x00000004, 0x0000000f, 0x04000067, 0x00102012,
        0x00000005, 0x00000010, 0x02000068, 0x00000001, 0x0400005b, 0x00102012, 0x00000004, 0x00000002,
        0x04000036, 0x00100012, 0x00000000, 0x0001700a, 0x08000036, 0x00d02012, 0x00000004, 0x0010000a,
        0x00000000, 0x0020803a, 0x00000000, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE hs = {hs_code, sizeof(hs_code)};
    static const DWORD ds_code[] =
    {
        0x43425844, 0xc54dc020, 0x063a9622, 0x6f649eb9, 0xceb1dd36, 0x00000001, 0x0000054c, 0x00000004,
        0x00000030, 0x000000b4, 0x00000178, 0x000001fc, 0x4e475349, 0x0000007c, 0x00000003, 0x00000008,
        0x00000050, 0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x00000f0f, 0x0000005c, 0x00000000,
        0x00000000, 0x00000003, 0x00000001, 0x00000101, 0x0000006a, 0x00000000, 0x00000002, 0x00000003,
        0x00000002, 0x00000101, 0x505f5653, 0x5449534f, 0x004e4f49, 0x50494c43, 0x5349445f, 0x434e4154,
        0x56530045, 0x696c435f, 0x73694470, 0x636e6174, 0xabab0065, 0x47534350, 0x000000bc, 0x00000006,
        0x00000008, 0x00000098, 0x00000000, 0x0000000b, 0x00000003, 0x00000000, 0x00000001, 0x00000098,
        0x00000001, 0x0000000b, 0x00000003, 0x00000001, 0x00000001, 0x00000098, 0x00000002, 0x0000000b,
        0x00000003, 0x00000002, 0x00000001, 0x00000098, 0x00000003, 0x0000000b, 0x00000003, 0x00000003,
        0x00000001, 0x000000a6, 0x00000000, 0x0000000c, 0x00000003, 0x00000004, 0x00000001, 0x000000a6,
        0x00000001, 0x0000000c, 0x00000003, 0x00000005, 0x00000001, 0x545f5653, 0x46737365, 0x6f746361,
        0x56530072, 0x736e495f, 0x54656469, 0x46737365, 0x6f746361, 0xabab0072, 0x4e47534f, 0x0000007c,
        0x00000003, 0x00000008, 0x00000050, 0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f,
        0x0000005c, 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x00000e01, 0x0000006a, 0x00000000,
        0x00000002, 0x00000003, 0x00000002, 0x00000e01, 0x505f5653, 0x5449534f, 0x004e4f49, 0x50494c43,
        0x5349445f, 0x434e4154, 0x56530045, 0x696c435f, 0x73694470, 0x636e6174, 0xabab0065, 0x58454853,
        0x00000348, 0x00040050, 0x000000d2, 0x01002093, 0x01001895, 0x0100086a, 0x04000059, 0x00208e46,
        0x00000000, 0x00000001, 0x0200005f, 0x0001c032, 0x0400005f, 0x002190f2, 0x00000004, 0x00000000,
        0x0400005f, 0x00219012, 0x00000004, 0x00000001, 0x0400005f, 0x00219012, 0x00000004, 0x00000002,
        0x04000067, 0x001020f2, 0x00000000, 0x00000001, 0x03000065, 0x00102012, 0x00000001, 0x04000067,
        0x00102012, 0x00000002, 0x00000002, 0x02000068, 0x00000002, 0x0a000000, 0x001000f2, 0x00000000,
        0x80219e46, 0x00000041, 0x00000002, 0x00000000, 0x00219e46, 0x00000003, 0x00000000, 0x09000032,
        0x001000f2, 0x00000000, 0x0001c006, 0x00100e46, 0x00000000, 0x00219e46, 0x00000002, 0x00000000,
        0x0a000000, 0x001000f2, 0x00000001, 0x80219e46, 0x00000041, 0x00000000, 0x00000000, 0x00219e46,
        0x00000001, 0x00000000, 0x09000032, 0x001000f2, 0x00000001, 0x0001c006, 0x00100e46, 0x00000001,
        0x00219e46, 0x00000000, 0x00000000, 0x08000000, 0x001000f2, 0x00000000, 0x00100e46, 0x00000000,
        0x80100e46, 0x00000041, 0x00000001, 0x08000032, 0x001020f2, 0x00000000, 0x0001c556, 0x00100e46,
        0x00000000, 0x00100e46, 0x00000001, 0x0a000000, 0x00100012, 0x00000000, 0x8021900a, 0x00000041,
        0x00000002, 0x00000001, 0x0021900a, 0x00000003, 0x00000001, 0x09000032, 0x00100012, 0x00000000,
        0x0001c00a, 0x0010000a, 0x00000000, 0x0021900a, 0x00000002, 0x00000001, 0x0a000000, 0x00100022,
        0x00000000, 0x8021900a, 0x00000041, 0x00000000, 0x00000001, 0x0021900a, 0x00000001, 0x00000001,
        0x09000032, 0x00100022, 0x00000000, 0x0001c00a, 0x0010001a, 0x00000000, 0x0021900a, 0x00000000,
        0x00000001, 0x08000000, 0x00100012, 0x00000000, 0x8010001a, 0x00000041, 0x00000000, 0x0010000a,
        0x00000000, 0x08000032, 0x00102012, 0x00000001, 0x0001c01a, 0x0010000a, 0x00000000, 0x0010001a,
        0x00000000, 0x0a000000, 0x00100012, 0x00000000, 0x8021900a, 0x00000041, 0x00000002, 0x00000002,
        0x0021900a, 0x00000003, 0x00000002, 0x09000032, 0x00100012, 0x00000000, 0x0001c00a, 0x0010000a,
        0x00000000, 0x0021900a, 0x00000002, 0x00000002, 0x0a000000, 0x00100022, 0x00000000, 0x8021900a,
        0x00000041, 0x00000000, 0x00000002, 0x0021900a, 0x00000001, 0x00000002, 0x09000032, 0x00100022,
        0x00000000, 0x0001c00a, 0x0010001a, 0x00000000, 0x0021900a, 0x00000000, 0x00000002, 0x08000000,
        0x00100012, 0x00000000, 0x8010001a, 0x00000041, 0x00000000, 0x0010000a, 0x00000000, 0x08000032,
        0x00100012, 0x00000000, 0x0001c01a, 0x0010000a, 0x00000000, 0x0010001a, 0x00000000, 0x0b000037,
        0x00102012, 0x00000002, 0x0020800a, 0x00000000, 0x00000000, 0x0020801a, 0x00000000, 0x00000000,
        0x0010000a, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ds = {ds_code, sizeof(ds_code)};
    static const DWORD gs_code[] =
    {
#if 0
        bool use_constant;
        float clip_distance;

        struct vertex
        {
            float4 position : SV_POSITION;
            float user_clip : CLIP_DISTANCE;
            float clip : SV_ClipDistance;
        };

        [maxvertexcount(3)]
        void main(triangle vertex input[3], inout TriangleStream<vertex> output)
        {
            vertex o;
            o = input[0];
            o.clip = input[0].user_clip;
            if (use_constant)
                o.clip = clip_distance;
            output.Append(o);
            o = input[1];
            o.clip = input[1].user_clip;
            if (use_constant)
                o.clip = clip_distance;
            output.Append(o);
            o = input[2];
            o.clip = input[2].user_clip;
            if (use_constant)
                o.clip = clip_distance;
            output.Append(o);
        }
#endif
        0x43425844, 0x9b0823e9, 0xab3ed100, 0xba0ff618, 0x1bbd1cb8, 0x00000001, 0x00000338, 0x00000003,
        0x0000002c, 0x000000b0, 0x00000134, 0x4e475349, 0x0000007c, 0x00000003, 0x00000008, 0x00000050,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x00000f0f, 0x0000005c, 0x00000000, 0x00000000,
        0x00000003, 0x00000001, 0x00000101, 0x0000006a, 0x00000000, 0x00000002, 0x00000003, 0x00000002,
        0x00000001, 0x505f5653, 0x5449534f, 0x004e4f49, 0x50494c43, 0x5349445f, 0x434e4154, 0x56530045,
        0x696c435f, 0x73694470, 0x636e6174, 0xabab0065, 0x4e47534f, 0x0000007c, 0x00000003, 0x00000008,
        0x00000050, 0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x0000005c, 0x00000000,
        0x00000000, 0x00000003, 0x00000001, 0x00000e01, 0x0000006a, 0x00000000, 0x00000002, 0x00000003,
        0x00000002, 0x00000e01, 0x505f5653, 0x5449534f, 0x004e4f49, 0x50494c43, 0x5349445f, 0x434e4154,
        0x56530045, 0x696c435f, 0x73694470, 0x636e6174, 0xabab0065, 0x52444853, 0x000001fc, 0x00020040,
        0x0000007f, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x05000061, 0x002010f2, 0x00000003,
        0x00000000, 0x00000001, 0x0400005f, 0x00201012, 0x00000003, 0x00000001, 0x0400005f, 0x00201012,
        0x00000003, 0x00000002, 0x02000068, 0x00000001, 0x0100185d, 0x0100285c, 0x04000067, 0x001020f2,
        0x00000000, 0x00000001, 0x03000065, 0x00102012, 0x00000001, 0x04000067, 0x00102012, 0x00000002,
        0x00000002, 0x0200005e, 0x00000003, 0x06000036, 0x001020f2, 0x00000000, 0x00201e46, 0x00000000,
        0x00000000, 0x06000036, 0x00102012, 0x00000001, 0x0020100a, 0x00000000, 0x00000001, 0x0c000037,
        0x00100012, 0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x0020801a, 0x00000000, 0x00000000,
        0x0020100a, 0x00000000, 0x00000001, 0x05000036, 0x00102012, 0x00000002, 0x0010000a, 0x00000000,
        0x01000013, 0x06000036, 0x001020f2, 0x00000000, 0x00201e46, 0x00000001, 0x00000000, 0x06000036,
        0x00102012, 0x00000001, 0x0020100a, 0x00000001, 0x00000001, 0x0c000037, 0x00100012, 0x00000000,
        0x0020800a, 0x00000000, 0x00000000, 0x0020801a, 0x00000000, 0x00000000, 0x0020100a, 0x00000001,
        0x00000001, 0x05000036, 0x00102012, 0x00000002, 0x0010000a, 0x00000000, 0x01000013, 0x06000036,
        0x001020f2, 0x00000000, 0x00201e46, 0x00000002, 0x00000000, 0x06000036, 0x00102012, 0x00000001,
        0x0020100a, 0x00000002, 0x00000001, 0x0c000037, 0x00100012, 0x00000000, 0x0020800a, 0x00000000,
        0x00000000, 0x0020801a, 0x00000000, 0x00000000, 0x0020100a, 0x00000002, 0x00000001, 0x05000036,
        0x00102012, 0x00000002, 0x0010000a, 0x00000000, 0x01000013, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE gs = {gs_code, sizeof(gs_code)};
    static const D3D12_INPUT_ELEMENT_DESC layout_desc[] =
    {
        {"POSITION",      0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
        {"CLIP_DISTANCE", 0, DXGI_FORMAT_R32_FLOAT,    1, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
        {"CLIP_DISTANCE", 1, DXGI_FORMAT_R32_FLOAT,    1, 4, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
    };
    static const struct vec4 quad[] =
    {
        {-1.0f, -1.0f},
        {-1.0f,  1.0f},
        { 1.0f, -1.0f},
        { 1.0f,  1.0f},
    };
    struct
    {
        float clip_distance0;
        float clip_distance1;
    }
    vertices[] =
    {
        {1.0f, 1.0f},
        {1.0f, 1.0f},
        {1.0f, 1.0f},
        {1.0f, 1.0f},
    };
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    struct
    {
        bool use_constant;
        float clip_distance0;
        float clip_distance1;
        float tessellation_factor;
    } cb_data;

    memset(&desc, 0, sizeof(desc));
    desc.rt_width = 640;
    desc.rt_height = 480;
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
    root_parameters[0].Descriptor.ShaderRegister = 0;
    root_parameters[0].Descriptor.RegisterSpace = 0;
    root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;

    root_parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
    root_parameters[1].Descriptor.ShaderRegister = 0;
    root_parameters[1].Descriptor.RegisterSpace = 0;
    root_parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_HULL;

    root_parameters[2].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
    root_parameters[2].Descriptor.ShaderRegister = 0;
    root_parameters[2].Descriptor.RegisterSpace = 0;
    root_parameters[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_DOMAIN;

    root_parameters[3].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
    root_parameters[3].Descriptor.ShaderRegister = 0;
    root_parameters[3].Descriptor.RegisterSpace = 0;
    root_parameters[3].ShaderVisibility = D3D12_SHADER_VISIBILITY_GEOMETRY;

    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_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
    hr = create_root_signature(device, &root_signature_desc, &context.root_signature);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);

    input_layout.pInputElementDescs = layout_desc;
    input_layout.NumElements = ARRAY_SIZE(layout_desc);

    init_pipeline_state_desc(&pso_desc, context.root_signature,
            context.render_target_desc.Format, &vs, NULL, &input_layout);
    hr = ID3D12Device_CreateGraphicsPipelineState(device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&pso);
    ok(hr == S_OK, "Failed to create pipeline state, hr %#x.\n", hr);

    vb[0] = create_upload_buffer(device, sizeof(quad), quad);
    vbv[0].BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vb[0]);
    vbv[0].StrideInBytes = sizeof(*quad);
    vbv[0].SizeInBytes = sizeof(quad);

    vb[1] = create_upload_buffer(device, sizeof(vertices), vertices);
    vbv[1].BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vb[1]);
    vbv[1].StrideInBytes = sizeof(*vertices);
    vbv[1].SizeInBytes = sizeof(vertices);

    memset(&cb_data, 0, sizeof(cb_data));
    cb_data.tessellation_factor = 1.0f;
    vs_cb = create_upload_buffer(device, sizeof(cb_data), &cb_data);
    tess_cb = create_upload_buffer(device, sizeof(cb_data), &cb_data);
    gs_cb = create_upload_buffer(device, sizeof(cb_data), &cb_data);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 0,
            ID3D12Resource_GetGPUVirtualAddress(vs_cb));
    ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 1,
            ID3D12Resource_GetGPUVirtualAddress(tess_cb));
    ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 2,
            ID3D12Resource_GetGPUVirtualAddress(tess_cb));
    ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 3,
            ID3D12Resource_GetGPUVirtualAddress(gs_cb));
    ID3D12GraphicsCommandList_SetPipelineState(command_list, pso);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, ARRAY_SIZE(vbv), vbv);

    /* vertex shader */
    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 1, 0, 0);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);

    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
    check_clip_distance(&context, pso, D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, vbv, vb[1], vs_cb, tess_cb, gs_cb);

    cb_data.use_constant = true;
    cb_data.clip_distance0 = -1.0f;
    update_buffer_data(vs_cb, 0, sizeof(cb_data), &cb_data);

    ID3D12PipelineState_Release(pso);

    if (is_mvk_device(device))
    {
        skip("Clipping not supported in tessellation and geometry shaders on MoltenVK.\n");
    }
    else
    {
        /* tessellation shaders */
        pso_desc.HS = hs;
        pso_desc.DS = ds;
        pso_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_PATCH;
        hr = ID3D12Device_CreateGraphicsPipelineState(device, &pso_desc,
                &IID_ID3D12PipelineState, (void **)&pso);
        ok(hr == S_OK, "Failed to create pipeline state, hr %#x.\n", hr);

        check_clip_distance(&context, pso, D3D_PRIMITIVE_TOPOLOGY_4_CONTROL_POINT_PATCHLIST,
                vbv, vb[1], vs_cb, tess_cb, gs_cb);

        cb_data.use_constant = false;
        cb_data.tessellation_factor = 2.0f;
        update_buffer_data(tess_cb, 0, sizeof(cb_data), &cb_data);

        for (i = 0; i < ARRAY_SIZE(vertices); ++i)
            vertices[i].clip_distance0 = 1.0f;
        update_buffer_data(vb[1], 0, sizeof(vertices), &vertices);

        ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
        ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 0,
                ID3D12Resource_GetGPUVirtualAddress(vs_cb));
        ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 1,
                ID3D12Resource_GetGPUVirtualAddress(tess_cb));
        ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 2,
                ID3D12Resource_GetGPUVirtualAddress(tess_cb));
        ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 3,
                ID3D12Resource_GetGPUVirtualAddress(gs_cb));
        ID3D12GraphicsCommandList_SetPipelineState(command_list, pso);
        ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_4_CONTROL_POINT_PATCHLIST);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
        ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
        ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, ARRAY_SIZE(vbv), vbv);
        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 1, 0, 0);
        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
        check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);

        ID3D12PipelineState_Release(pso);
        reset_command_list(command_list, context.allocator);
        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

        cb_data.use_constant = true;
        cb_data.clip_distance0 = -1.0f;
        update_buffer_data(tess_cb, 0, sizeof(cb_data), &cb_data);

        /* geometry shader */
        pso_desc.GS = gs;
        hr = ID3D12Device_CreateGraphicsPipelineState(device, &pso_desc,
                &IID_ID3D12PipelineState, (void **)&pso);
        ok(hr == S_OK, "Failed to create pipeline state, hr %#x.\n", hr);

        check_clip_distance(&context, pso, D3D_PRIMITIVE_TOPOLOGY_4_CONTROL_POINT_PATCHLIST,
                vbv, vb[1], vs_cb, tess_cb, gs_cb);

        cb_data.use_constant = true;
        cb_data.clip_distance0 = 1.0f;
        update_buffer_data(gs_cb, 0, sizeof(cb_data), &cb_data);
        ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
        ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 0,
                ID3D12Resource_GetGPUVirtualAddress(vs_cb));
        ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 1,
                ID3D12Resource_GetGPUVirtualAddress(tess_cb));
        ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 2,
                ID3D12Resource_GetGPUVirtualAddress(tess_cb));
        ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 3,
                ID3D12Resource_GetGPUVirtualAddress(gs_cb));
        ID3D12GraphicsCommandList_SetPipelineState(command_list, pso);
        ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_4_CONTROL_POINT_PATCHLIST);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
        ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
        ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, ARRAY_SIZE(vbv), vbv);
        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 1, 0, 0);
        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
        check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);

        ID3D12PipelineState_Release(pso);
        reset_command_list(command_list, context.allocator);
        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
    }

    /* multiple clip distances */
    pso_desc.VS = vs_multiple;
    memset(&pso_desc.HS, 0, sizeof(pso_desc.HS));
    memset(&pso_desc.DS, 0, sizeof(pso_desc.DS));
    memset(&pso_desc.GS, 0, sizeof(pso_desc.GS));
    pso_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
    hr = ID3D12Device_CreateGraphicsPipelineState(device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&pso);
    ok(hr == S_OK, "Failed to create pipeline state, hr %#x.\n", hr);

    cb_data.use_constant = false;
    update_buffer_data(vs_cb, 0, sizeof(cb_data), &cb_data);

    for (i = 0; i < ARRAY_SIZE(vertices); ++i)
        vertices[i].clip_distance0 = 1.0f;
    update_buffer_data(vb[1], 0, sizeof(vertices), vertices);
    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 0,
            ID3D12Resource_GetGPUVirtualAddress(vs_cb));
    ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 1,
            ID3D12Resource_GetGPUVirtualAddress(tess_cb));
    ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 2,
            ID3D12Resource_GetGPUVirtualAddress(tess_cb));
    ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 3,
            ID3D12Resource_GetGPUVirtualAddress(gs_cb));
    ID3D12GraphicsCommandList_SetPipelineState(command_list, pso);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, ARRAY_SIZE(vbv), vbv);
    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 1, 0, 0);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);

    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    for (i = 0; i < ARRAY_SIZE(vertices); ++i)
    {
        vertices[i].clip_distance0 = i < 2 ? 1.0f : -1.0f;
        vertices[i].clip_distance1 = i % 2 ? 1.0f : -1.0f;
    }
    update_buffer_data(vb[1], 0, sizeof(vertices), vertices);
    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 0,
            ID3D12Resource_GetGPUVirtualAddress(vs_cb));
    ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 1,
            ID3D12Resource_GetGPUVirtualAddress(tess_cb));
    ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 2,
            ID3D12Resource_GetGPUVirtualAddress(tess_cb));
    ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 3,
            ID3D12Resource_GetGPUVirtualAddress(gs_cb));
    ID3D12GraphicsCommandList_SetPipelineState(command_list, pso);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, ARRAY_SIZE(vbv), vbv);
    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 1, 0, 0);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);

    get_resource_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
    set_box(&box, 0, 0, 0, 320, 240, 1);
    check_readback_data_uint(&rb.rb, &box, 0xff00ff00, 1);
    set_box(&box, 0, 240, 0, 320, 480, 1);
    check_readback_data_uint(&rb.rb, &box, 0xffffffff, 1);
    set_box(&box, 320, 0, 0, 640, 480, 1);
    check_readback_data_uint(&rb.rb, &box, 0xffffffff, 1);
    release_resource_readback(&rb);

    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    cb_data.use_constant = true;
    cb_data.clip_distance0 = 0.0f;
    cb_data.clip_distance1 = 0.0f;
    update_buffer_data(vs_cb, 0, sizeof(cb_data), &cb_data);
    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 0,
            ID3D12Resource_GetGPUVirtualAddress(vs_cb));
    ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 1,
            ID3D12Resource_GetGPUVirtualAddress(tess_cb));
    ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 2,
            ID3D12Resource_GetGPUVirtualAddress(tess_cb));
    ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 3,
            ID3D12Resource_GetGPUVirtualAddress(gs_cb));
    ID3D12GraphicsCommandList_SetPipelineState(command_list, pso);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, ARRAY_SIZE(vbv), vbv);
    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 1, 0, 0);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);

    ID3D12PipelineState_Release(pso);
    for (i = 0; i < ARRAY_SIZE(vb); ++i)
        ID3D12Resource_Release(vb[i]);
    ID3D12Resource_Release(vs_cb);
    ID3D12Resource_Release(tess_cb);
    ID3D12Resource_Release(gs_cb);
    destroy_test_context(&context);
}

static void test_combined_clip_and_cull_distances(void)
{
    ID3D12GraphicsCommandList *command_list;
    D3D12_INPUT_LAYOUT_DESC input_layout;
    struct d3d12_resource_readback rb;
    D3D12_VERTEX_BUFFER_VIEW vbv[2];
    struct test_context_desc desc;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Resource *vb[2];
    ID3D12Device *device;
    unsigned int i, j, k;

    static const DWORD vs_code[] =
    {
#if 0
        struct input
        {
            float4 position : POSITION;
            float clip0 : CLIP_DISTANCE0;
            float clip1 : CLIP_DISTANCE1;
            float clip2 : CLIP_DISTANCE2;
            float clip3 : CLIP_DISTANCE3;
            float cull0 : CULL_DISTANCE0;
            float cull1 : CULL_DISTANCE1;
            float cull2 : CULL_DISTANCE2;
            float cull3 : CULL_DISTANCE3;
        };

        struct vertex
        {
            float4 position : SV_Position;
            float3 clip0 : SV_ClipDistance1;
            float3 cull0 : SV_CullDistance1;
            float clip1 : SV_ClipDistance2;
            float cull1 : SV_CullDistance2;
        };

        void main(input vin, out vertex vertex)
        {
            vertex.position = vin.position;
            vertex.clip0 = float3(vin.clip0, vin.clip1, vin.clip2);
            vertex.cull0 = float3(vin.cull0, vin.cull1, vin.cull2);
            vertex.clip1 = vin.clip3;
            vertex.cull1 = vin.cull3;
        }
#endif
        0x43425844, 0xa24fb3ea, 0x92e2c2b0, 0xb599b1b9, 0xd671f830, 0x00000001, 0x00000374, 0x00000003,
        0x0000002c, 0x0000013c, 0x000001f0, 0x4e475349, 0x00000108, 0x00000009, 0x00000008, 0x000000e0,
        0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000f0f, 0x000000e9, 0x00000000, 0x00000000,
        0x00000003, 0x00000001, 0x00000101, 0x000000e9, 0x00000001, 0x00000000, 0x00000003, 0x00000002,
        0x00000101, 0x000000e9, 0x00000002, 0x00000000, 0x00000003, 0x00000003, 0x00000101, 0x000000e9,
        0x00000003, 0x00000000, 0x00000003, 0x00000004, 0x00000101, 0x000000f7, 0x00000000, 0x00000000,
        0x00000003, 0x00000005, 0x00000101, 0x000000f7, 0x00000001, 0x00000000, 0x00000003, 0x00000006,
        0x00000101, 0x000000f7, 0x00000002, 0x00000000, 0x00000003, 0x00000007, 0x00000101, 0x000000f7,
        0x00000003, 0x00000000, 0x00000003, 0x00000008, 0x00000101, 0x49534f50, 0x4e4f4954, 0x494c4300,
        0x49445f50, 0x4e415453, 0x43004543, 0x5f4c4c55, 0x54534944, 0x45434e41, 0xababab00, 0x4e47534f,
        0x000000ac, 0x00000005, 0x00000008, 0x00000080, 0x00000000, 0x00000001, 0x00000003, 0x00000000,
        0x0000000f, 0x0000008c, 0x00000000, 0x00000002, 0x00000003, 0x00000001, 0x00000807, 0x0000008c,
        0x00000001, 0x00000002, 0x00000003, 0x00000001, 0x00000708, 0x0000009c, 0x00000000, 0x00000003,
        0x00000003, 0x00000002, 0x00000807, 0x0000009c, 0x00000001, 0x00000003, 0x00000003, 0x00000002,
        0x00000708, 0x505f5653, 0x7469736f, 0x006e6f69, 0x435f5653, 0x4470696c, 0x61747369, 0x0065636e,
        0x435f5653, 0x446c6c75, 0x61747369, 0x0065636e, 0x52444853, 0x0000017c, 0x00010040, 0x0000005f,
        0x0300005f, 0x001010f2, 0x00000000, 0x0300005f, 0x00101012, 0x00000001, 0x0300005f, 0x00101012,
        0x00000002, 0x0300005f, 0x00101012, 0x00000003, 0x0300005f, 0x00101012, 0x00000004, 0x0300005f,
        0x00101012, 0x00000005, 0x0300005f, 0x00101012, 0x00000006, 0x0300005f, 0x00101012, 0x00000007,
        0x0300005f, 0x00101012, 0x00000008, 0x04000067, 0x001020f2, 0x00000000, 0x00000001, 0x04000067,
        0x00102072, 0x00000001, 0x00000002, 0x04000067, 0x00102082, 0x00000001, 0x00000002, 0x04000067,
        0x00102072, 0x00000002, 0x00000003, 0x04000067, 0x00102082, 0x00000002, 0x00000003, 0x05000036,
        0x001020f2, 0x00000000, 0x00101e46, 0x00000000, 0x05000036, 0x00102012, 0x00000001, 0x0010100a,
        0x00000001, 0x05000036, 0x00102022, 0x00000001, 0x0010100a, 0x00000002, 0x05000036, 0x00102042,
        0x00000001, 0x0010100a, 0x00000003, 0x05000036, 0x00102082, 0x00000001, 0x0010100a, 0x00000004,
        0x05000036, 0x00102012, 0x00000002, 0x0010100a, 0x00000005, 0x05000036, 0x00102022, 0x00000002,
        0x0010100a, 0x00000006, 0x05000036, 0x00102042, 0x00000002, 0x0010100a, 0x00000007, 0x05000036,
        0x00102082, 0x00000002, 0x0010100a, 0x00000008, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE vs = {vs_code, sizeof(vs_code)};
    static const D3D12_INPUT_ELEMENT_DESC layout_desc[] =
    {
        {"POSITION",      0, DXGI_FORMAT_R32G32_FLOAT, 0,  0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
        {"CLIP_DISTANCE", 0, DXGI_FORMAT_R32_FLOAT,    1,  0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
        {"CLIP_DISTANCE", 1, DXGI_FORMAT_R32_FLOAT,    1,  4, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
        {"CLIP_DISTANCE", 2, DXGI_FORMAT_R32_FLOAT,    1,  8, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
        {"CLIP_DISTANCE", 3, DXGI_FORMAT_R32_FLOAT,    1, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
        {"CULL_DISTANCE", 0, DXGI_FORMAT_R32_FLOAT,    1, 16, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
        {"CULL_DISTANCE", 1, DXGI_FORMAT_R32_FLOAT,    1, 20, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
        {"CULL_DISTANCE", 2, DXGI_FORMAT_R32_FLOAT,    1, 24, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
        {"CULL_DISTANCE", 3, DXGI_FORMAT_R32_FLOAT,    1, 28, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
    };
    static const struct vec4 quad[] =
    {
        {-1.0f, -1.0f},
        {-1.0f,  1.0f},
        { 1.0f, -1.0f},
        { 1.0f,  1.0f},
    };
    struct
    {
        float clip_distance[4];
        float cull_distance[4];
    }
    vertices[4] =
    {
        {{1.0f, 1.0f, 1.0f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}},
        {{1.0f, 1.0f, 1.0f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}},
        {{1.0f, 1.0f, 1.0f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}},
        {{1.0f, 1.0f, 1.0f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}},
    };
    static const struct test
    {
        float vertices[4];
        bool triangle_visible[2];
    }
    cull_distance_tests[] =
    {
        {{-1.0f,  1.0f,  1.0f,  1.0f}, {true, true}},
        {{ 1.0f, -1.0f,  1.0f,  1.0f}, {true, true}},
        {{ 1.0f,  1.0f,  1.0f, -1.0f}, {true, true}},
        {{-1.0f, -1.0f,  1.0f,  1.0f}, {true, true}},
        {{-1.0f,  1.0f, -1.0f,  1.0f}, {true, true}},
        {{-1.0f,  1.0f,  1.0f, -1.0f}, {true, true}},
        {{ 1.0f, -1.0f, -1.0f,  1.0f}, {true, true}},
        {{ 1.0f, -1.0f,  1.0f, -1.0f}, {true, true}},
        {{ 1.0f,  1.0f, -1.0f, -1.0f}, {true, true}},

        {{-1.0f, -1.0f, -1.0f,  1.0f}, {false, true}},
        {{-1.0f, -1.0f,  1.0f, -1.0f}, {true,  true}},
        {{-1.0f, -1.0f,  1.0f, -1.0f}, {true,  true}},
        {{-1.0f,  1.0f, -1.0f, -1.0f}, {true,  true}},
        {{ 1.0f, -1.0f, -1.0f, -1.0f}, {true,  false}},

        {{-1.0f, -1.0f, -1.0f, -1.0f}, {false, false}},
    };
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};

    memset(&desc, 0, sizeof(desc));
    desc.rt_width = 640;
    desc.rt_height = 480;
    desc.root_signature_flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
    desc.no_pipeline = true;
    if (!init_test_context(&context, &desc))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    if (is_mvk_device(device))
    {
        skip("Cull distance not supported on MoltenVK.\n");
        destroy_test_context(&context);
        return;
    }

    input_layout.pInputElementDescs = layout_desc;
    input_layout.NumElements = ARRAY_SIZE(layout_desc);

    context.pipeline_state = create_pipeline_state(device, context.root_signature,
            context.render_target_desc.Format, &vs, NULL, &input_layout);

    vb[0] = create_upload_buffer(device, sizeof(quad), quad);
    vbv[0].BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vb[0]);
    vbv[0].StrideInBytes = sizeof(*quad);
    vbv[0].SizeInBytes = sizeof(quad);

    vb[1] = create_upload_buffer(device, sizeof(vertices), vertices);
    vbv[1].BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vb[1]);
    vbv[1].StrideInBytes = sizeof(*vertices);
    vbv[1].SizeInBytes = sizeof(vertices);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, ARRAY_SIZE(vbv), vbv);
    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 1, 0, 0);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);

    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    for (i = 0; i < ARRAY_SIZE(vertices->cull_distance); ++i)
    {
        for (j = 0; j < ARRAY_SIZE(cull_distance_tests); ++j)
        {
            const struct test *test = &cull_distance_tests[j];
            unsigned int expected_color[ARRAY_SIZE(test->triangle_visible)];
            unsigned int color;

            for (k = 0; k < ARRAY_SIZE(vertices); ++k)
                vertices[k].cull_distance[i] = test->vertices[k];
            update_buffer_data(vb[1], 0, sizeof(vertices), vertices);

            ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
            ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
            ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
            ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
            ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
            ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
            ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, ARRAY_SIZE(vbv), vbv);
            ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
            ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 1, 0, 0);
            transition_resource_state(command_list, context.render_target,
                    D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);

            for (k = 0; k < ARRAY_SIZE(expected_color); ++k)
                expected_color[k] = test->triangle_visible[k] ? 0xff00ff00 : 0xffffffff;

            if (expected_color[0] == expected_color[1])
            {
                check_sub_resource_uint(context.render_target, 0, queue, command_list, expected_color[0], 0);
            }
            else
            {
                get_resource_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
                color = get_readback_uint(&rb.rb, 160, 240, 0);
                ok(color == expected_color[0], "Got unexpected color 0x%08x.\n", color);
                color = get_readback_uint(&rb.rb, 480, 240, 0);
                ok(color == expected_color[1], "Got unexpected color 0x%08x.\n", color);
                release_resource_readback(&rb);
            }

            reset_command_list(command_list, context.allocator);
            transition_resource_state(command_list, context.render_target,
                    D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
        }

        for (j = 0; j < ARRAY_SIZE(vertices); ++j)
            vertices[j].cull_distance[i] = 1.0f;
    }

    for (i = 0; i < ARRAY_SIZE(vertices->clip_distance); ++i)
    {
        for (j = 0; j < ARRAY_SIZE(vertices); ++j)
            vertices[j].clip_distance[i] = -1.0f;
        update_buffer_data(vb[1], 0, sizeof(vertices), vertices);

        ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
        ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
        ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
        ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
        ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, ARRAY_SIZE(vbv), vbv);
        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 1, 0, 0);
        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);

        check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xffffffff, 0);

        reset_command_list(command_list, context.allocator);
        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

        for (j = 0; j < ARRAY_SIZE(vertices); ++j)
            vertices[j].clip_distance[i] = 1.0f;
    }

    memset(vertices, 0, sizeof(vertices));
    update_buffer_data(vb[1], 0, sizeof(vertices), vertices);
    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, ARRAY_SIZE(vbv), vbv);
    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 1, 0, 0);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);

    for (i = 0; i < ARRAY_SIZE(vb); ++i)
        ID3D12Resource_Release(vb[i]);
    destroy_test_context(&context);
}

static void test_resource_allocation_info(void)
{
    D3D12_RESOURCE_ALLOCATION_INFO info;
    D3D12_RESOURCE_DESC desc;
    ID3D12Device8 *device8;
    ID3D12Device4 *device4;
    ID3D12Device *device;
    uint64_t total = 0;
    unsigned int i, j;
    ULONG refcount;

    static const unsigned int alignments[] =
    {
        0,
        D3D12_SMALL_RESOURCE_PLACEMENT_ALIGNMENT,
        D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT,
        D3D12_DEFAULT_MSAA_RESOURCE_PLACEMENT_ALIGNMENT,
    };
    static const unsigned int buffer_sizes[] =
    {
        1,
        16,
        256,
        1024,
        D3D12_SMALL_RESOURCE_PLACEMENT_ALIGNMENT,
        D3D12_SMALL_RESOURCE_PLACEMENT_ALIGNMENT + 1,
        D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT,
        D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT + 1,
        D3D12_DEFAULT_MSAA_RESOURCE_PLACEMENT_ALIGNMENT,
        D3D12_DEFAULT_MSAA_RESOURCE_PLACEMENT_ALIGNMENT + 1,
    };
    static const struct
    {
        unsigned int width;
        unsigned int height;
        unsigned int array_size;
        unsigned int miplevels;
        DXGI_FORMAT format;
    }
    texture_tests[] =
    {
        { 4,  4, 1, 1, DXGI_FORMAT_R8_UINT},
        { 8,  8, 1, 1, DXGI_FORMAT_R8G8B8A8_UNORM},
        {16, 16, 1, 1, DXGI_FORMAT_R8G8B8A8_UNORM},
        {16, 16, 1024, 1, DXGI_FORMAT_R8G8B8A8_UNORM},
        {256, 512, 1, 10, DXGI_FORMAT_BC1_UNORM},
        {256, 512, 64, 1, DXGI_FORMAT_BC1_UNORM},

        {1024, 1024, 1, 1, DXGI_FORMAT_R8G8B8A8_UNORM},
        {1024, 1024, 1, 2, DXGI_FORMAT_R8G8B8A8_UNORM},
        {1024, 1024, 1, 3, DXGI_FORMAT_R8G8B8A8_UNORM},
        {1024, 1024, 1, 0, DXGI_FORMAT_R8G8B8A8_UNORM},
        {260, 512, 1, 1, DXGI_FORMAT_BC1_UNORM},
    };
    D3D12_RESOURCE_ALLOCATION_INFO1 infos1[ARRAY_SIZE(texture_tests)] = {0};
    D3D12_RESOURCE_DESC1 desc_array1[ARRAY_SIZE(texture_tests)];
    D3D12_RESOURCE_DESC desc_array[ARRAY_SIZE(texture_tests)];
    uint64_t sizes[ARRAY_SIZE(texture_tests)];

    if (!(device = create_device()))
    {
        skip("Failed to create device.\n");
        return;
    }

    info = ID3D12Device_GetResourceAllocationInfo(device, 0, 0, &desc);
    ok(info.SizeInBytes == 0, "Got unexpected size %"PRIu64".\n", info.SizeInBytes);
    /* Alignment on AMD Windows is 1, but this doesn't seem important to check. */

    desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
    desc.Alignment = 0;
    desc.Width = 32;
    desc.Height = 1;
    desc.DepthOrArraySize = 1;
    desc.MipLevels = 1;
    desc.Format = DXGI_FORMAT_UNKNOWN;
    desc.SampleDesc.Count = 1;
    desc.SampleDesc.Quality = 0;
    desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
    desc.Flags = 0;

    for (i = 0; i < ARRAY_SIZE(alignments); ++i)
    {
        for (j = 0; j < ARRAY_SIZE(buffer_sizes); ++j)
        {
            desc.Alignment = alignments[i];
            desc.Width = buffer_sizes[j];
            info = ID3D12Device_GetResourceAllocationInfo(device, 0, 1, &desc);
            if (!desc.Alignment || desc.Alignment == D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT)
            {
                check_alignment(info.SizeInBytes, info.Alignment);
            }
            else
            {
                ok(info.SizeInBytes == ~(uint64_t)0,
                        "Got unexpected size %"PRIu64".\n", info.SizeInBytes);
                ok(info.Alignment == D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT,
                        "Got unexpected alignment %"PRIu64".\n", info.Alignment);
            }
        }
    }

    desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
    desc.SampleDesc.Count = 1;
    desc.SampleDesc.Quality = 0;
    desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
    desc.Flags = 0;

    for (i = 0; i < ARRAY_SIZE(texture_tests); ++i)
    {
        desc.Width = texture_tests[i].width;
        desc.Height = texture_tests[i].height;
        desc.DepthOrArraySize = texture_tests[i].array_size;
        desc.MipLevels = texture_tests[i].miplevels;
        desc.Format = texture_tests[i].format;

        desc.Alignment = 0;
        info = ID3D12Device_GetResourceAllocationInfo(device, 0, 1, &desc);
        ok(info.Alignment >= D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT,
                "Got unexpected alignment %"PRIu64".\n", info.Alignment);
        check_alignment(info.SizeInBytes, info.Alignment);

        desc.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
        info = ID3D12Device_GetResourceAllocationInfo(device, 0, 1, &desc);
        ok(info.Alignment >= D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT,
                "Got unexpected alignment %"PRIu64".\n", info.Alignment);
        check_alignment(info.SizeInBytes, info.Alignment);
        if (i >= 6)
        {
            total += info.SizeInBytes;
            sizes[i] = info.SizeInBytes;
        }

        desc.Alignment = D3D12_SMALL_RESOURCE_PLACEMENT_ALIGNMENT;
        info = ID3D12Device_GetResourceAllocationInfo(device, 0, 1, &desc);
        ok(info.Alignment >= D3D12_SMALL_RESOURCE_PLACEMENT_ALIGNMENT,
                "Got unexpected alignment %"PRIu64".\n", info.Alignment);
        if (i < 6)
        {
            check_alignment(info.SizeInBytes, info.Alignment);
            total += info.SizeInBytes;
            sizes[i] = info.SizeInBytes;
        }
        else
        {
            ok(info.SizeInBytes == ~(uint64_t)0,
                    "Got unexpected size %"PRIu64".\n", info.SizeInBytes);
            ok(info.Alignment >= D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT,
                    "Got unexpected alignment %"PRIu64".\n", info.Alignment);
        }
    }

    for (i = 0; i < ARRAY_SIZE(texture_tests); ++i)
    {
        desc_array[i] = desc;
        desc_array[i].Width = texture_tests[i].width;
        desc_array[i].Height = texture_tests[i].height;
        desc_array[i].DepthOrArraySize = texture_tests[i].array_size;
        desc_array[i].MipLevels = texture_tests[i].miplevels;
        desc_array[i].Format = texture_tests[i].format;
        desc_array[i].Alignment = (i < 6) ? D3D12_SMALL_RESOURCE_PLACEMENT_ALIGNMENT
                : D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
    }
    info = ID3D12Device_GetResourceAllocationInfo(device, 0, ARRAY_SIZE(desc_array), desc_array);
    ok(info.Alignment >= D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT,
            "Got unexpected alignment %"PRIu64".\n", info.Alignment);
    check_alignment(info.SizeInBytes, info.Alignment);
    ok(info.SizeInBytes >= total, "Got unexpected size %"PRIu64".\n", info.SizeInBytes);

    if (SUCCEEDED(ID3D12Device_QueryInterface(device, &IID_ID3D12Device4, (void **)&device4)))
    {
        info = ID3D12Device4_GetResourceAllocationInfo1(device4, 0, ARRAY_SIZE(desc_array), desc_array, infos1);
        ok(info.Alignment >= D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT,
                "Got unexpected alignment %"PRIu64".\n", info.Alignment);
        check_alignment(info.SizeInBytes, info.Alignment);
        ok(info.SizeInBytes >= total, "Got unexpected size %"PRIu64".\n", info.SizeInBytes);
        ok(!infos1[0].Offset, "Got unexpected offset %"PRIu64".\n", infos1[0].Offset);

        for (i = 0; i < ARRAY_SIZE(infos1); ++i)
        {
            vkd3d_test_push_context("Test %u", i);

            ok(infos1[i].Alignment >= desc_array[i].Alignment,
                    "Got unexpected alignment %"PRIu64".\n", infos1[i].Alignment);
            check_alignment(infos1[i].Offset, infos1[i].Alignment);
            check_alignment(infos1[i].SizeInBytes, infos1[i].Alignment);
            ok(infos1[i].SizeInBytes == sizes[i], "Got unexpected size %"PRIu64".\n", infos1[i].SizeInBytes);

            if (!i)
            {
                vkd3d_test_pop_context();
                continue;
            }

            ok(infos1[i].Offset - infos1[i - 1].Offset >= infos1[i - 1].SizeInBytes,
                    "Got unexpected prev size %"PRIu64", prev offset %"PRIu64", offset %"PRIu64".\n",
                    infos1[i - 1].SizeInBytes, infos1[i - 1].Offset, infos1[i].Offset);

            vkd3d_test_pop_context();
        }

        ID3D12Device4_Release(device4);
    }

    if (FAILED(ID3D12Device_QueryInterface(device, &IID_ID3D12Device8, (void **)&device8)))
    {
        skip("ID3D12Device8 not available; skipping GetResourceAllocationInfo2() tests.");
        goto release;
    }

    for (i = 0; i < ARRAY_SIZE(texture_tests); ++i)
    {
        desc_array1[i].Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
        desc_array1[i].Alignment = (i < 6) ? D3D12_SMALL_RESOURCE_PLACEMENT_ALIGNMENT
                : D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
        desc_array1[i].Width = texture_tests[i].width;
        desc_array1[i].Height = texture_tests[i].height;
        desc_array1[i].DepthOrArraySize = texture_tests[i].array_size;
        desc_array1[i].MipLevels = texture_tests[i].miplevels;
        desc_array1[i].Format = texture_tests[i].format;
        desc_array1[i].SampleDesc.Count = 1;
        desc_array1[i].SampleDesc.Quality = 0;
        desc_array1[i].Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
        desc_array1[i].Flags = 0;
        memset(&desc_array1[i].SamplerFeedbackMipRegion, 0, sizeof(desc_array1[i].SamplerFeedbackMipRegion));
    }

    info = ID3D12Device8_GetResourceAllocationInfo2(device8, 0, ARRAY_SIZE(desc_array1), desc_array1, infos1);
    ok(info.Alignment >= D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT,
            "Got unexpected alignment %"PRIu64".\n", info.Alignment);
    check_alignment(info.SizeInBytes, info.Alignment);
    ok(info.SizeInBytes >= total, "Got unexpected size %"PRIu64".\n", info.SizeInBytes);
    ok(!infos1[0].Offset, "Got unexpected offset %"PRIu64".\n", infos1[0].Offset);

    for (i = 0; i < ARRAY_SIZE(infos1); ++i)
    {
        vkd3d_test_push_context("Test %u", i);

        ok(infos1[i].Alignment >= desc_array1[i].Alignment,
                "Got unexpected alignment %"PRIu64".\n", infos1[i].Alignment);
        check_alignment(infos1[i].Offset, infos1[i].Alignment);
        check_alignment(infos1[i].SizeInBytes, infos1[i].Alignment);
        ok(infos1[i].SizeInBytes == sizes[i], "Got unexpected size %"PRIu64".\n", infos1[i].SizeInBytes);

        if (!i)
        {
            vkd3d_test_pop_context();
            continue;
        }

        ok(infos1[i].Offset - infos1[i - 1].Offset >= infos1[i - 1].SizeInBytes,
                "Got unexpected prev size %"PRIu64", prev offset %"PRIu64", offset %"PRIu64".\n",
                infos1[i - 1].SizeInBytes, infos1[i - 1].Offset, infos1[i].Offset);

        vkd3d_test_pop_context();
    }

    ID3D12Device8_Release(device8);

release:
    refcount = ID3D12Device_Release(device);
    ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount);
}

static void test_64kb_texture_alignment(void)
{
    ID3D12GraphicsCommandList *command_list;
    D3D12_RESOURCE_ALLOCATION_INFO info;
    D3D12_SUBRESOURCE_DATA texture_data;
    struct d3d12_resource_readback rb;
    D3D12_RESOURCE_DESC resource_desc;
    struct test_context context;
    ID3D12Resource *textures[2];
    D3D12_HEAP_DESC heap_desc;
    ID3D12CommandQueue *queue;
    uint32_t *upload_buffer;
    ID3D12Device *device;
    ID3D12Heap *heap;
    unsigned int i;
    D3D12_BOX box;
    HRESULT hr;

    if (!init_test_context(&context, NULL))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    /* This results in an alignment of 0x20000 with RX580/RADV, but
     * only with D3D12_TEXTURE_LAYOUT_UNKNOWN. Other cards/drivers may
     * specify smaller alignments compatible with the 64kb D3D12 default. */
    resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
    resource_desc.Alignment = 0;
    resource_desc.Width = 1024;
    resource_desc.Height = 1024;
    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 = 0;

    info = ID3D12Device_GetResourceAllocationInfo(device, 0, 1, &resource_desc);

    heap_desc.SizeInBytes = info.SizeInBytes * 2 + D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
    memset(&heap_desc.Properties, 0, sizeof(heap_desc.Properties));
    heap_desc.Properties.Type = D3D12_HEAP_TYPE_DEFAULT;
    heap_desc.Alignment = 0;
    heap_desc.Flags = D3D12_HEAP_FLAG_DENY_BUFFERS | D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES;
    hr = ID3D12Device_CreateHeap(device, &heap_desc, &IID_ID3D12Heap, (void **)&heap);
    ok(hr == S_OK, "Failed to create heap, hr %#x.\n", hr);

    /* Padding of info.SizeInBytes is (vulkan_alignment - d3d12_alignment), so this heap_offset calculation
     * always results in a Vulkan-aligned offset which won't be adjusted upwards. */
    hr = ID3D12Device_CreatePlacedResource(device, heap, D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT + info.SizeInBytes,
            &resource_desc, D3D12_RESOURCE_STATE_COPY_DEST, NULL, &IID_ID3D12Resource, (void **)&textures[1]);
    ok(hr == S_OK, "Failed to create placed resource, hr %#x.\n", hr);

    /* Some apps, e.g. WoW, ignore the alignment returned by GetResourceAllocationInfo() and use
     * D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT. D3D12 requires support for this alignment. */
    hr = ID3D12Device_CreatePlacedResource(device, heap, D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT,
            &resource_desc, D3D12_RESOURCE_STATE_COPY_DEST, NULL, &IID_ID3D12Resource, (void **)&textures[0]);
    ok(hr == S_OK, "Failed to create placed resource, hr %#x.\n", hr);

    upload_buffer = malloc(info.SizeInBytes);
    ok(upload_buffer, "Failed to allocate memory.\n");

    for (i = 0; i < resource_desc.Width * resource_desc.Height; ++i)
        upload_buffer[i] = 0xcafef00d;
    texture_data.pData = upload_buffer;
    texture_data.RowPitch = resource_desc.Width * sizeof(uint32_t);
    texture_data.SlicePitch = texture_data.RowPitch * resource_desc.Height;
    upload_texture_data(textures[1], &texture_data, 1, queue, command_list);

    for (i = 0; i < resource_desc.Width * resource_desc.Height; ++i)
        upload_buffer[i] = 0xdeadbeef;
    texture_data.SlicePitch = texture_data.RowPitch * resource_desc.Height;
    reset_command_list(command_list, context.allocator);
    /* Write data to the first texture to check if the second is overwritten.
     * Resource overlap may still go undetected depending on the actual layout
     * used by the driver. */
    upload_texture_data(textures[0], &texture_data, 1, queue, command_list);

    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, textures[1],
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_resource_readback_with_command_list(textures[1], 0, &rb, queue, command_list);
    set_box(&box, 0, 0, 0, resource_desc.Width, resource_desc.Height, 1);
    check_readback_data_uint(&rb.rb, &box, 0xcafef00d, 0);
    release_resource_readback(&rb);

    ID3D12Resource_Release(textures[1]);
    /* Create an aliased texture. */
    hr = ID3D12Device_CreatePlacedResource(device, heap, D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT,
            &resource_desc, D3D12_RESOURCE_STATE_COPY_DEST, NULL, &IID_ID3D12Resource, (void **)&textures[1]);
    ok(hr == S_OK, "Failed to create placed resource, hr %#x.\n", hr);

    /* If the heap could not be used, the texture is not aliased. */
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, textures[1],
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_resource_readback_with_command_list(textures[1], 0, &rb, queue, command_list);
    todo_if(is_mvk_device_lt(device, 1, 2, 12))
    check_readback_data_uint(&rb.rb, &box, 0xdeadbeef, 0);
    release_resource_readback(&rb);

    free(upload_buffer);
    ID3D12Resource_Release(textures[0]);
    ID3D12Resource_Release(textures[1]);
    ID3D12Heap_Release(heap);
    destroy_test_context(&context);
}

static void test_suballocate_small_textures(void)
{
    D3D12_GPU_VIRTUAL_ADDRESS gpu_address;
    D3D12_RESOURCE_ALLOCATION_INFO info;
    D3D12_RESOURCE_DESC resource_desc;
    ID3D12Resource *textures[10];
    D3D12_HEAP_DESC heap_desc;
    ID3D12Device *device;
    ID3D12Heap *heap;
    unsigned int i;
    ULONG refcount;
    HRESULT hr;

    if (!(device = create_device()))
    {
        skip("Failed to create device.\n");
        return;
    }

    resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
    resource_desc.Alignment = 0;
    resource_desc.Width = 32;
    resource_desc.Height = 32;
    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 = 0;

    resource_desc.Alignment = D3D12_SMALL_RESOURCE_PLACEMENT_ALIGNMENT;

    info = ID3D12Device_GetResourceAllocationInfo(device, 0, 1, &resource_desc);
    trace("Size %"PRIu64", alignment %"PRIu64".\n", info.SizeInBytes, info.Alignment);
    check_alignment(info.SizeInBytes, info.Alignment);
    if (info.Alignment != D3D12_SMALL_RESOURCE_PLACEMENT_ALIGNMENT)
    {
        resource_desc.Alignment = 0;
        info = ID3D12Device_GetResourceAllocationInfo(device, 0, 1, &resource_desc);
        trace("Size %"PRIu64", alignment %"PRIu64".\n", info.SizeInBytes, info.Alignment);
        check_alignment(info.SizeInBytes, info.Alignment);
    }

    ok(info.Alignment >= D3D12_SMALL_RESOURCE_PLACEMENT_ALIGNMENT, "Got alignment %"PRIu64".\n", info.Alignment);

    heap_desc.SizeInBytes = ARRAY_SIZE(textures) * info.SizeInBytes;
    memset(&heap_desc.Properties, 0, sizeof(heap_desc.Properties));
    heap_desc.Properties.Type = D3D12_HEAP_TYPE_DEFAULT;
    heap_desc.Alignment = 0;
    heap_desc.Flags = D3D12_HEAP_FLAG_DENY_BUFFERS | D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES;
    hr = ID3D12Device_CreateHeap(device, &heap_desc, &IID_ID3D12Heap, (void **)&heap);
    ok(hr == S_OK, "Failed to create heap, hr %#x.\n", hr);

    for (i = 0; i < ARRAY_SIZE(textures); ++i)
    {
        hr = ID3D12Device_CreatePlacedResource(device, heap, i * info.SizeInBytes,
                &resource_desc, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE,
                NULL, &IID_ID3D12Resource, (void **)&textures[i]);
        ok(hr == S_OK, "Failed to create placed resource %u, hr %#x.\n", i, hr);

        check_interface(textures[i], &IID_ID3D12Object, true);
        check_interface(textures[i], &IID_ID3D12DeviceChild, true);
        check_interface(textures[i], &IID_ID3D12Pageable, true);
        check_interface(textures[i], &IID_ID3D12Resource, true);

        gpu_address = ID3D12Resource_GetGPUVirtualAddress(textures[i]);
        ok(!gpu_address, "Got unexpected GPU virtual address %#"PRIx64".\n", gpu_address);
    }

    refcount = get_refcount(heap);
    ok(refcount == 1, "Got unexpected refcount %u.\n", (unsigned int)refcount);

    for (i = 0; i < ARRAY_SIZE(textures); ++i)
    {
        refcount = ID3D12Resource_Release(textures[i]);
        ok(!refcount, "ID3D12Resource has %u references left.\n", (unsigned int)refcount);
    }

    refcount = ID3D12Heap_Release(heap);
    ok(!refcount, "ID3D12Heap has %u references left.\n", (unsigned int)refcount);
    refcount = ID3D12Device_Release(device);
    ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount);
}

static void test_command_list_initial_pipeline_state(void)
{
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    ID3D12GraphicsCommandList *command_list;
    ID3D12PipelineState *pipeline_state;
    ID3D12CommandAllocator *allocator;
    struct test_context context;
    ID3D12CommandQueue *queue;
    HRESULT hr;

    static const DWORD ps_code[] =
    {
#if 0
        void main(out float4 target : SV_Target)
        {
            target = float4(0.0f, 0.25f, 0.5f, 1.0f);
        }
#endif
        0x43425844, 0x2f09e5ff, 0xaa135d5e, 0x7860f4b5, 0x5c7b8cbc, 0x00000001, 0x000000b4, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x0000003c, 0x00000050, 0x0000000f,
        0x0100086a, 0x03000065, 0x001020f2, 0x00000000, 0x08000036, 0x001020f2, 0x00000000, 0x00004002,
        0x00000000, 0x3e800000, 0x3f000000, 0x3f800000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};

    if (!init_test_context(&context, NULL))
        return;
    queue = context.queue;

    pipeline_state = create_pipeline_state(context.device,
            context.root_signature, context.render_target_desc.Format, NULL, &ps, NULL);

    hr = ID3D12Device_CreateCommandAllocator(context.device, D3D12_COMMAND_LIST_TYPE_DIRECT,
            &IID_ID3D12CommandAllocator, (void **)&allocator);
    ok(hr == S_OK, "Failed to create command allocator, hr %#x.\n", hr);
    hr = ID3D12Device_CreateCommandList(context.device, 0, D3D12_COMMAND_LIST_TYPE_DIRECT,
            allocator, pipeline_state, &IID_ID3D12GraphicsCommandList, (void **)&command_list);
    ok(hr == S_OK, "Failed to create command list, hr %#x.\n", hr);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff804000, 1);

    hr = ID3D12CommandAllocator_Reset(allocator);
    ok(hr == S_OK, "Failed to reset command allocator, hr %#x.\n", hr);
    hr = ID3D12GraphicsCommandList_Reset(command_list, allocator, context.pipeline_state);
    ok(hr == S_OK, "Failed to reset command list, hr %#x.\n", hr);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);

    ID3D12CommandAllocator_Release(allocator);
    ID3D12GraphicsCommandList_Release(command_list);
    ID3D12PipelineState_Release(pipeline_state);
    destroy_test_context(&context);
}

static void test_blend_factor(void)
{
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
    ID3D12GraphicsCommandList *command_list;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12CommandQueue *queue;
    unsigned int i;
    HRESULT hr;

    static const struct
    {
        float blend_factor[4];
        unsigned int expected_color;
    }
    tests[] =
    {
        {{0.0f, 0.0f, 0.0f, 0.0f}, 0xffffffff},
        {{0.0f, 1.0f, 0.0f, 1.0f}, 0xffffffff},
        {{0.5f, 0.5f, 0.5f, 0.5f}, 0xff80ff80},
        {{1.0f, 1.0f, 1.0f, 1.0f}, 0xff00ff00},
    };

    memset(&desc, 0, sizeof(desc));
    desc.no_pipeline = true;
    if (!init_test_context(&context, &desc))
        return;
    command_list = context.list;
    queue = context.queue;

    init_pipeline_state_desc(&pso_desc, context.root_signature,
            context.render_target_desc.Format, NULL, NULL, NULL);
    pso_desc.BlendState.RenderTarget[0].BlendEnable = true;
    pso_desc.BlendState.RenderTarget[0].SrcBlend = D3D12_BLEND_BLEND_FACTOR;
    pso_desc.BlendState.RenderTarget[0].DestBlend = D3D12_BLEND_INV_BLEND_FACTOR;
    pso_desc.BlendState.RenderTarget[0].BlendOp = D3D12_BLEND_OP_ADD;
    pso_desc.BlendState.RenderTarget[0].SrcBlendAlpha = D3D12_BLEND_BLEND_FACTOR;
    pso_desc.BlendState.RenderTarget[0].DestBlendAlpha = D3D12_BLEND_INV_BLEND_FACTOR;
    pso_desc.BlendState.RenderTarget[0].BlendOpAlpha = D3D12_BLEND_OP_ADD;
    hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
    ok(hr == S_OK, "Failed to create pipeline, hr %#x.\n", hr);

    for (i = 0; i < ARRAY_SIZE(tests); ++i)
    {
        vkd3d_test_push_context("Test %u", i);

        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
        ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
        ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
        ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
        ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
        ID3D12GraphicsCommandList_OMSetBlendFactor(command_list, tests[i].blend_factor);
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);
        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
        check_sub_resource_uint(context.render_target, 0, queue, command_list, tests[i].expected_color, 1);

        reset_command_list(command_list, context.allocator);
        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

        vkd3d_test_pop_context();
    }

    destroy_test_context(&context);
}

static void test_dual_source_blending(void)
{
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
    ID3D12GraphicsCommandList *command_list;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12CommandQueue *queue;
    unsigned int i;
    HRESULT hr;

    static const DWORD ps_code[] =
    {
#if 0
        float4 c0;
        float4 c1;

        void main(out float4 o0 : SV_Target0, out float4 o1 : SV_Target1)
        {
            o0 = c0;
            o1 = c1;
        }
#endif
        0x43425844, 0x823a4ecf, 0xd409abf6, 0xe76697ab, 0x9b53c9a5, 0x00000001, 0x000000f8, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000088, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x00000044, 0x00000002, 0x00000008, 0x00000038, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x00000038, 0x00000001, 0x00000000, 0x00000003, 0x00000001, 0x0000000f, 0x545f5653,
        0x65677261, 0xabab0074, 0x58454853, 0x00000068, 0x00000050, 0x0000001a, 0x0100086a, 0x04000059,
        0x00208e46, 0x00000000, 0x00000002, 0x03000065, 0x001020f2, 0x00000000, 0x03000065, 0x001020f2,
        0x00000001, 0x06000036, 0x001020f2, 0x00000000, 0x00208e46, 0x00000000, 0x00000000, 0x06000036,
        0x001020f2, 0x00000001, 0x00208e46, 0x00000000, 0x00000001, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};
    static const struct
    {
        struct
        {
            struct vec4 c0;
            struct vec4 c1;
        } constants;
        unsigned int expected_color;
    }
    tests[] =
    {
        {{{0.0f, 0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f, 0.0f}}, 0x00000000},
        {{{0.0f, 0.0f, 0.0f, 0.0f}, {1.0f, 0.0f, 0.0f, 1.0f}}, 0xff0000ff},
        {{{1.0f, 0.0f, 0.0f, 1.0f}, {0.0f, 0.0f, 0.0f, 0.0f}}, 0xff0000ff},
        {{{1.0f, 0.0f, 0.0f, 1.0f}, {0.0f, 1.0f, 1.0f, 0.0f}}, 0xffffffff},
    };

    memset(&desc, 0, sizeof(desc));
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    command_list = context.list;
    queue = context.queue;

    context.root_signature = create_32bit_constants_root_signature(context.device,
            0, sizeof(tests->constants) / sizeof(uint32_t), D3D12_SHADER_VISIBILITY_PIXEL);

    init_pipeline_state_desc(&pso_desc, context.root_signature,
            context.render_target_desc.Format, NULL, &ps, NULL);
    pso_desc.BlendState.RenderTarget[0].BlendEnable = true;
    pso_desc.BlendState.RenderTarget[0].SrcBlend = D3D12_BLEND_SRC_COLOR;
    pso_desc.BlendState.RenderTarget[0].DestBlend = D3D12_BLEND_SRC1_COLOR;
    pso_desc.BlendState.RenderTarget[0].BlendOp = D3D12_BLEND_OP_ADD;
    pso_desc.BlendState.RenderTarget[0].SrcBlendAlpha = D3D12_BLEND_SRC_ALPHA;
    pso_desc.BlendState.RenderTarget[0].DestBlendAlpha = D3D12_BLEND_SRC1_ALPHA;
    pso_desc.BlendState.RenderTarget[0].BlendOpAlpha = D3D12_BLEND_OP_ADD;
    hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
    ok(hr == S_OK, "Failed to create pipeline, hr %#x.\n", hr);

    for (i = 0; i < ARRAY_SIZE(tests); ++i)
    {
        vkd3d_test_push_context("Test %u", i);

        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
        ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
        ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
        ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
        ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
        ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 0, 8, &tests[i].constants, 0);
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);
        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
        check_sub_resource_uint(context.render_target, 0, queue, command_list, tests[i].expected_color, 1);

        reset_command_list(command_list, context.allocator);
        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

        vkd3d_test_pop_context();
    }

    ID3D12PipelineState_Release(context.pipeline_state);
    pso_desc.BlendState.IndependentBlendEnable = true;
    hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
    ok(hr == S_OK, "Failed to create pipeline, hr %#x.\n", hr);
    ID3D12PipelineState_Release(context.pipeline_state);
    context.pipeline_state = NULL;

    pso_desc.BlendState.RenderTarget[1] = pso_desc.BlendState.RenderTarget[0];
    hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    pso_desc.BlendState.RenderTarget[1].DestBlendAlpha = D3D12_BLEND_SRC_ALPHA;
    pso_desc.BlendState.RenderTarget[1].DestBlend = D3D12_BLEND_SRC_COLOR;
    hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    context.pipeline_state = NULL;

    pso_desc.NumRenderTargets = 2;
    pso_desc.RTVFormats[1] = pso_desc.RTVFormats[0];
    pso_desc.BlendState.IndependentBlendEnable = false;
    pso_desc.BlendState.RenderTarget[0].BlendEnable = true;
    pso_desc.BlendState.RenderTarget[0].SrcBlend = D3D12_BLEND_SRC_COLOR;
    pso_desc.BlendState.RenderTarget[0].DestBlend = D3D12_BLEND_SRC1_COLOR;
    pso_desc.BlendState.RenderTarget[0].BlendOp = D3D12_BLEND_OP_ADD;
    pso_desc.BlendState.RenderTarget[0].SrcBlendAlpha = D3D12_BLEND_SRC_ALPHA;
    pso_desc.BlendState.RenderTarget[0].DestBlendAlpha = D3D12_BLEND_SRC1_ALPHA;
    pso_desc.BlendState.RenderTarget[0].BlendOpAlpha = D3D12_BLEND_OP_ADD;
    hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    destroy_test_context(&context);
}

static void test_output_merger_logic_op(void)
{
    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
    ID3D12GraphicsCommandList *command_list;
    ID3D12PipelineState *logic_pso;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12CommandQueue *queue;
    unsigned int i, do_test;
    HRESULT hr;

    static const DWORD ps_code[] =
    {
#if 0
        uint4 c0;

        void main(const in float4 position : SV_Position, out uint4 target : SV_Target0)
        {
            target = c0;
        }
#endif
        0x43425844, 0xb1d5d133, 0xbd9958bf, 0xe4a12856, 0x0197481b, 0x00000001, 0x000000e0, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x505f5653, 0x7469736f, 0x006e6f69,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000044, 0x00000050,
        0x00000011, 0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2,
        0x00000000, 0x06000036, 0x001020f2, 0x00000000, 0x00208e46, 0x00000000, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};
    static const uint32_t clear_values[] = {0xffffffff, 0x00000000, 0xffffffff, 0x00000000};
    static const uint32_t test_values[] = {0xffffffff, 0xffffffff, 0x00000000, 0x00000000};
    static const struct
    {
        D3D12_LOGIC_OP logic_op;
        unsigned int expected_colour;
    }
    tests[] =
    {
        {D3D12_LOGIC_OP_CLEAR,         0x00000000},
        {D3D12_LOGIC_OP_SET,           0xffffffff},
        {D3D12_LOGIC_OP_COPY,          0x0000ffff},
        {D3D12_LOGIC_OP_COPY_INVERTED, 0xffff0000},
        {D3D12_LOGIC_OP_NOOP,          0x00ff00ff},
        {D3D12_LOGIC_OP_INVERT,        0xff00ff00},
        {D3D12_LOGIC_OP_AND,           0x000000ff},
        {D3D12_LOGIC_OP_NAND,          0xffffff00},
        {D3D12_LOGIC_OP_OR,            0x00ffffff},
        {D3D12_LOGIC_OP_NOR,           0xff000000},
        {D3D12_LOGIC_OP_XOR,           0x00ffff00},
        {D3D12_LOGIC_OP_EQUIV,         0xff0000ff},
        {D3D12_LOGIC_OP_AND_REVERSE,   0x0000ff00},
        {D3D12_LOGIC_OP_AND_INVERTED,  0x00ff0000},
        {D3D12_LOGIC_OP_OR_REVERSE,    0xff00ffff},
        {D3D12_LOGIC_OP_OR_INVERTED,   0xffff00ff},
    };

    memset(&desc, 0, sizeof(desc));
    desc.no_root_signature = true;
    desc.ps = &ps;
    desc.rt_format = DXGI_FORMAT_R8G8B8A8_UINT;
    if (!init_test_context(&context, &desc))
        return;
    command_list = context.list;
    queue = context.queue;

    if (!is_output_merger_logic_op_supported(context.device))
    {
        skip("The device does not support output merger logic ops.\n");
        destroy_test_context(&context);
        return;
    }

    context.root_signature = create_32bit_constants_root_signature(context.device,
            0, ARRAY_SIZE(clear_values), D3D12_SHADER_VISIBILITY_PIXEL);

    init_pipeline_state_desc(&pso_desc, context.root_signature,
            context.render_target_desc.Format, NULL, &ps, NULL);
    hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);

    pso_desc.BlendState.RenderTarget[0].LogicOpEnable = true;

    for (i = 0; i < ARRAY_SIZE(tests); ++i)
    {
        pso_desc.BlendState.RenderTarget[0].LogicOp = tests[i].logic_op;
        hr = ID3D12Device_CreateGraphicsPipelineState(context.device,
                &pso_desc, &IID_ID3D12PipelineState, (void **)&logic_pso);
        ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);

        for (do_test = 0; do_test < 2; ++do_test)
        {
            vkd3d_test_push_context(do_test ? "Test %u" : "Clear %u", i);

            ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
            ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
            ID3D12GraphicsCommandList_SetPipelineState(command_list, do_test ? logic_pso : context.pipeline_state);
            ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list,
                    0, 4, do_test ? test_values : clear_values, 0);
            ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
            ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
            ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
            ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

            transition_resource_state(command_list, context.render_target,
                    D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
            check_sub_resource_uint(context.render_target, 0, queue, command_list,
                    do_test ? tests[i].expected_colour : 0x00ff00ff, 1);
            reset_command_list(command_list, context.allocator);
            transition_resource_state(command_list, context.render_target,
                    D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

            vkd3d_test_pop_context();
        }

        ID3D12PipelineState_Release(logic_pso);
    }

    destroy_test_context(&context);
}

static void test_multisample_rendering(void)
{
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
    ID3D12GraphicsCommandList *command_list;
    ID3D12PipelineState *ms_pipeline_state;
    D3D12_CPU_DESCRIPTOR_HANDLE ms_rtv;
    ID3D12Resource *ms_render_target;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12DescriptorHeap *heap;
    ID3D12CommandQueue *queue;
    uint32_t sample;
    unsigned int i;
    HRESULT hr;

    static const DWORD ps_color_code[] =
    {
#if 0
        float4 main(uint id : SV_SampleIndex) : SV_Target
        {
            switch (id)
            {
                case 0:  return float4(1.0f, 0.0f, 0.0f, 1.0f);
                case 1:  return float4(0.0f, 1.0f, 0.0f, 1.0f);
                case 2:  return float4(0.0f, 0.0f, 1.0f, 1.0f);
                default: return float4(0.0f, 0.0f, 0.0f, 1.0f);
            }
        }
#endif
        0x43425844, 0x94c35f48, 0x04c6b0f7, 0x407d8214, 0xc24f01e5, 0x00000001, 0x00000194, 0x00000003,
        0x0000002c, 0x00000064, 0x00000098, 0x4e475349, 0x00000030, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x0000000a, 0x00000001, 0x00000000, 0x00000101, 0x535f5653, 0x6c706d61, 0x646e4965,
        0xab007865, 0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000,
        0x00000003, 0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x000000f4,
        0x00000050, 0x0000003d, 0x0100086a, 0x04000863, 0x00101012, 0x00000000, 0x0000000a, 0x03000065,
        0x001020f2, 0x00000000, 0x0300004c, 0x0010100a, 0x00000000, 0x03000006, 0x00004001, 0x00000000,
        0x08000036, 0x001020f2, 0x00000000, 0x00004002, 0x3f800000, 0x00000000, 0x00000000, 0x3f800000,
        0x0100003e, 0x03000006, 0x00004001, 0x00000001, 0x08000036, 0x001020f2, 0x00000000, 0x00004002,
        0x00000000, 0x3f800000, 0x00000000, 0x3f800000, 0x0100003e, 0x03000006, 0x00004001, 0x00000002,
        0x08000036, 0x001020f2, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x3f800000, 0x3f800000,
        0x0100003e, 0x0100000a, 0x08000036, 0x001020f2, 0x00000000, 0x00004002, 0x00000000, 0x00000000,
        0x00000000, 0x3f800000, 0x0100003e, 0x01000017, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_color = {ps_color_code, sizeof(ps_color_code)};
    static const DWORD ps_resolve_code[] =
    {
#if 0
        Texture2DMS<float4> t;

        uint sample;
        uint rt_size;

        float4 main(float4 position : SV_Position) : SV_Target
        {
            float3 p;
            t.GetDimensions(p.x, p.y, p.z);
            p *= float3(position.x / rt_size, position.y / rt_size, 0);
            return t.Load((int2)p.xy, sample);
        }
#endif
        0x43425844, 0x68a4590b, 0xc1ec3070, 0x1b957c43, 0x0c080741, 0x00000001, 0x000001c8, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000030f, 0x505f5653, 0x7469736f, 0x006e6f69,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x0000012c, 0x00000050,
        0x0000004b, 0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x04002058, 0x00107000,
        0x00000000, 0x00005555, 0x04002064, 0x00101032, 0x00000000, 0x00000001, 0x03000065, 0x001020f2,
        0x00000000, 0x02000068, 0x00000001, 0x06000056, 0x00100012, 0x00000000, 0x0020801a, 0x00000000,
        0x00000000, 0x0700000e, 0x00100032, 0x00000000, 0x00101046, 0x00000000, 0x00100006, 0x00000000,
        0x8900003d, 0x80000102, 0x00155543, 0x001000c2, 0x00000000, 0x00004001, 0x00000000, 0x001074e6,
        0x00000000, 0x07000038, 0x00100032, 0x00000000, 0x00100046, 0x00000000, 0x00100ae6, 0x00000000,
        0x0500001b, 0x00100032, 0x00000000, 0x00100046, 0x00000000, 0x08000036, 0x001000c2, 0x00000000,
        0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x8c00002e, 0x80000102, 0x00155543,
        0x001020f2, 0x00000000, 0x00100e46, 0x00000000, 0x00107e46, 0x00000000, 0x0020800a, 0x00000000,
        0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_resolve = {ps_resolve_code, sizeof(ps_resolve_code)};
    static const unsigned int expected_colors[] = {0xff0000ff, 0xff00ff00, 0xffff0000, 0xff000000};

    if (test_options.use_warp_device)
    {
        skip("Sample shading tests fail on WARP.\n");
        return;
    }

    memset(&desc, 0, sizeof(desc));
    desc.rt_width = desc.rt_height = 32;
    desc.rt_descriptor_count = 2;
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    command_list = context.list;
    queue = context.queue;

    context.root_signature = create_texture_root_signature(context.device,
            D3D12_SHADER_VISIBILITY_PIXEL, 2, 0);

    init_pipeline_state_desc(&pso_desc, context.root_signature,
            context.render_target_desc.Format, NULL, &ps_resolve, NULL);
    hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
    ok(hr == S_OK, "Failed to create pipeline, hr %#x.\n", hr);

    pso_desc.PS = ps_color;
    pso_desc.SampleDesc.Count = 4;
    hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&ms_pipeline_state);
    ok(hr == S_OK, "Failed to create pipeline, hr %#x.\n", hr);

    ms_rtv = get_cpu_rtv_handle(&context, context.rtv_heap, 1);
    desc.sample_desc.Count = 4;
    create_render_target(&context, &desc, &ms_render_target, &ms_rtv);

    heap = create_gpu_descriptor_heap(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1);
    ID3D12Device_CreateShaderResourceView(context.device, ms_render_target, NULL,
            get_cpu_descriptor_handle(&context, heap, 0));

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, ms_rtv, white, 0, NULL);
    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &ms_rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, ms_pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &heap);
    ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0,
            get_gpu_descriptor_handle(&context, heap, 0));
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, ms_render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_RESOLVE_SOURCE);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_RESOLVE_DEST);
    ID3D12GraphicsCommandList_ResolveSubresource(command_list,
            context.render_target, 0, ms_render_target, 0, context.render_target_desc.Format);
    transition_resource_state(command_list, ms_render_target,
            D3D12_RESOURCE_STATE_RESOLVE_SOURCE, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RESOLVE_DEST, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff404040, 2);

    for (i = 0; i < ARRAY_SIZE(expected_colors); ++i)
    {
        reset_command_list(command_list, context.allocator);
        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
        ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
        ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
        ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
        ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
        ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &heap);
        ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0,
                get_gpu_descriptor_handle(&context, heap, 0));
        ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 1, 1, &desc.rt_width, 1);

        sample = i;
        ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 1, 1, &sample, 0);
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
        check_sub_resource_uint(context.render_target, 0, queue, command_list, expected_colors[i], 0);
    }

    ID3D12DescriptorHeap_Release(heap);
    ID3D12Resource_Release(ms_render_target);
    ID3D12PipelineState_Release(ms_pipeline_state);
    destroy_test_context(&context);
}

static void test_sample_mask(void)
{
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
    ID3D12GraphicsCommandList *command_list;
    D3D12_CPU_DESCRIPTOR_HANDLE ms_rtv;
    ID3D12Resource *ms_render_target;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12CommandQueue *queue;
    uint32_t sample_mask;
    HRESULT hr;

    static const DWORD ps_code[] =
    {
#if 0
        uint mask;

        float4 main(in float4 pos : SV_Position, out uint sample_mask : SV_Coverage) : SV_Target
        {
            sample_mask = mask;
            return float4(0.0, 1.0, 0.0, 1.0);
        }
#endif
        0x43425844, 0xfab05c6c, 0xeba1b017, 0xf4493502, 0x72ce5d05, 0x00000001, 0x00000128, 0x00000003,
        0x0000002c, 0x00000060, 0x000000b8, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x505f5653, 0x7469736f, 0x006e6f69,
        0x4e47534f, 0x00000050, 0x00000002, 0x00000008, 0x00000038, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x00000042, 0x00000000, 0x00000000, 0x00000001, 0xffffffff, 0x00000e01,
        0x545f5653, 0x65677261, 0x56530074, 0x766f435f, 0x67617265, 0xabab0065, 0x58454853, 0x00000068,
        0x00000050, 0x0000001a, 0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065,
        0x001020f2, 0x00000000, 0x02000065, 0x0000f000, 0x08000036, 0x001020f2, 0x00000000, 0x00004002,
        0x00000000, 0x3f800000, 0x00000000, 0x3f800000, 0x05000036, 0x0000f001, 0x0020800a, 0x00000000,
        0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};

    memset(&desc, 0, sizeof(desc));
    desc.rt_descriptor_count = 2;
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    command_list = context.list;
    queue = context.queue;

    context.root_signature = create_32bit_constants_root_signature(context.device,
            0, 1, D3D12_SHADER_VISIBILITY_PIXEL);

    init_pipeline_state_desc(&pso_desc, context.root_signature,
            context.render_target_desc.Format, NULL, &ps, NULL);
    pso_desc.SampleDesc.Count = 4;
    hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
    ok(hr == S_OK, "Failed to create pipeline, hr %#x.\n", hr);

    ms_rtv = get_cpu_rtv_handle(&context, context.rtv_heap, 1);
    desc.sample_desc.Count = 4;
    create_render_target(&context, &desc, &ms_render_target, &ms_rtv);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, ms_rtv, white, 0, NULL);
    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &ms_rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    sample_mask = 0xa;
    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 0, 1, &sample_mask, 0);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, ms_render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_RESOLVE_SOURCE);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_RESOLVE_DEST);
    ID3D12GraphicsCommandList_ResolveSubresource(command_list,
            context.render_target, 0, ms_render_target, 0, context.render_target_desc.Format);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RESOLVE_DEST, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff7fff7f, 2);

    ID3D12Resource_Release(ms_render_target);
    destroy_test_context(&context);
}

static void test_coverage(void)
{
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
    D3D12_DESCRIPTOR_RANGE descriptor_ranges[1];
    ID3D12DescriptorHeap *cpu_heap, *gpu_heap;
    ID3D12GraphicsCommandList *command_list;
    D3D12_ROOT_PARAMETER root_parameters[1];
    struct test_context_desc desc;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Resource *texture;
    unsigned int i;
    HRESULT hr;

    static const float black[4];
    static const unsigned int zero[4];
    static const DWORD ps_code[] =
    {
#if 0
        RWTexture2D<uint> u;

        float4 main(float4 position : SV_Position, uint coverage : SV_Coverage) : SV_Target
        {
            InterlockedOr(u[uint2(position.x, position.y)], coverage);
            return float4(0.0, 1.0, 0.0, 1.0);
        }
#endif
        0x43425844, 0x53236006, 0x68a61a42, 0x5d0a06e7, 0x05a9405b, 0x00000001, 0x00000134, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000030f, 0x505f5653, 0x7469736f, 0x006e6f69,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000098, 0x00000050,
        0x00000026, 0x0100086a, 0x0400189c, 0x0011e000, 0x00000001, 0x00004444, 0x04002064, 0x00101032,
        0x00000000, 0x00000001, 0x0200005f, 0x00023001, 0x03000065, 0x001020f2, 0x00000000, 0x02000068,
        0x00000001, 0x0500001c, 0x00100032, 0x00000000, 0x00101046, 0x00000000, 0x060000aa, 0x0011e000,
        0x00000001, 0x00100046, 0x00000000, 0x0002300a, 0x08000036, 0x001020f2, 0x00000000, 0x00004002,
        0x00000000, 0x3f800000, 0x00000000, 0x3f800000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};

    static const struct
    {
        unsigned int sample_mask;
        unsigned int expected_color;
    }
    tests[] =
    {
        {0x01, 0x40004000},
        {0x03, 0x7f007f00},
        {0x07, 0xbf00bf00},
        {0x09, 0x7f007f00},
        {0x0d, 0xbf00bf00},
        {0x0f, 0xff00ff00},
        {0xff, 0xff00ff00},
        { ~0u, 0xff00ff00},
    };

    if (test_options.use_warp_device)
    {
        skip("Sample shading tests fail on WARP.\n");
        return;
    }

    memset(&desc, 0, sizeof(desc));
    desc.rt_width = desc.rt_height = 32;
    desc.sample_desc.Count = 4;
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    command_list = context.list;
    queue = context.queue;

    descriptor_ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
    descriptor_ranges[0].NumDescriptors = 1;
    descriptor_ranges[0].BaseShaderRegister = 1;
    descriptor_ranges[0].RegisterSpace = 0;
    descriptor_ranges[0].OffsetInDescriptorsFromTableStart = 0;
    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_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, &context.root_signature);
    ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr);

    texture = create_default_texture(context.device, desc.rt_width, desc.rt_height, DXGI_FORMAT_R32_UINT,
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

    cpu_heap = create_cpu_descriptor_heap(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1);
    gpu_heap = create_gpu_descriptor_heap(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1);
    ID3D12Device_CreateUnorderedAccessView(context.device, texture, NULL, NULL,
            get_cpu_descriptor_handle(&context, cpu_heap, 0));
    ID3D12Device_CopyDescriptorsSimple(context.device, 1,
            get_cpu_descriptor_handle(&context, gpu_heap, 0),
            get_cpu_descriptor_handle(&context, cpu_heap, 0),
            D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);

    for (i = 0; i < ARRAY_SIZE(tests); ++i)
    {
        vkd3d_test_push_context("sample mask %#x", tests[i].sample_mask);

        if (context.pipeline_state)
            ID3D12PipelineState_Release(context.pipeline_state);

        init_pipeline_state_desc(&pso_desc, context.root_signature,
                context.render_target_desc.Format, NULL, &ps, NULL);
        pso_desc.SampleMask = tests[i].sample_mask;
        pso_desc.SampleDesc.Count = desc.sample_desc.Count;
        hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
                &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
        ok(hr == S_OK, "Failed to create pipeline, hr %#x.\n", hr);

        ID3D12GraphicsCommandList_ClearUnorderedAccessViewUint(command_list,
                get_gpu_descriptor_handle(&context, gpu_heap, 0),
                get_cpu_descriptor_handle(&context, cpu_heap, 0),
                texture, zero, 0, NULL);

        uav_barrier(command_list, texture);

        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, black, 0, NULL);
        ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
        ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
        ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
        ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
        ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &gpu_heap);
        ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list,
                0, get_gpu_descriptor_handle(&context, gpu_heap, 0));
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_RESOLVE_SOURCE);
        check_sub_resource_uint(context.render_target, 0, queue, command_list, tests[i].expected_color, 2);

        reset_command_list(command_list, context.allocator);
        transition_resource_state(command_list, texture,
                D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
        bug_if(is_mvk_device(context.device))
        check_sub_resource_uint(texture, 0, queue, command_list, tests[i].sample_mask & 0xf, 0);

        reset_command_list(command_list, context.allocator);
        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_RESOLVE_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
        transition_resource_state(command_list, texture,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

        vkd3d_test_pop_context();
    }

    ID3D12DescriptorHeap_Release(cpu_heap);
    ID3D12DescriptorHeap_Release(gpu_heap);
    ID3D12Resource_Release(texture);
    destroy_test_context(&context);
}

static void test_shader_get_render_target_sample_count(void)
{
    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
    ID3D12GraphicsCommandList *command_list;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12CommandQueue *queue;
    struct vec4 sample_count;
    HRESULT hr;

    static const float black[4];
    static const DWORD ps_code[] =
    {
#if 0
        float4 main() : SV_Target
        {
            return GetRenderTargetSampleCount();
        }

#endif
        0x43425844, 0x74404d37, 0xad6f88e4, 0xb006ea57, 0xf07d9e2a, 0x00000001, 0x000000a4, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x0000002c, 0x00000050, 0x0000000b,
        0x0100086a, 0x03000065, 0x001020f2, 0x00000000, 0x0400006f, 0x001020f2, 0x00000000, 0x0000e00a,
        0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};

    memset(&desc, 0, sizeof(desc));
    desc.rt_format = DXGI_FORMAT_R32G32B32A32_FLOAT;
    desc.sample_desc.Count = 8;
    desc.no_pipeline = true;
    desc.check_multisampling = true;
    if (!init_test_context(&context, &desc))
        return;
    command_list = context.list;
    queue = context.queue;
    bug_if(is_mvk_device(context.device) || is_llvmpipe_device(context.device))
    ok(context.render_target_desc.SampleDesc.Count == 8, "Failed to create render target with MSAA 8, got %u.\n",
            context.render_target_desc.SampleDesc.Count);

    sample_count.x = context.render_target_desc.SampleDesc.Count;
    sample_count.y = context.render_target_desc.SampleDesc.Count;
    sample_count.z = context.render_target_desc.SampleDesc.Count;
    sample_count.w = context.render_target_desc.SampleDesc.Count;

    init_pipeline_state_desc(&pso_desc, context.root_signature,
            context.render_target_desc.Format, NULL, &ps, NULL);
    pso_desc.SampleDesc.Count = context.render_target_desc.SampleDesc.Count;
    hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
    ok(hr == S_OK, "Failed to create pipeline, hr %#x.\n", hr);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, black, 0, NULL);
    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_RESOLVE_SOURCE);
    check_sub_resource_vec4(context.render_target, 0, queue, command_list, &sample_count, 0);

    destroy_test_context(&context);
}

static void test_shader_sample_position(void)
{
    D3D12_TEXTURE_COPY_LOCATION src_location, dst_location;
    ID3D12Resource *texture, *readback_texture;
    ID3D12GraphicsCommandList *command_list;
    D3D12_HEAP_PROPERTIES heap_properties;
    struct d3d12_resource_readback rb;
    D3D12_RESOURCE_DESC resource_desc;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12DescriptorHeap *heap;
    ID3D12CommandQueue *queue;
    uint32_t sample_index;
    unsigned int i;
    D3D12_BOX box;
    HRESULT hr;

    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    static const DWORD ps_code[] =
    {
#if 0
        uint index;
        Texture2DMS<float4> t;

        float4 main() : SV_Target
        {
            return float4(t.GetSamplePosition(index), 0, 0);
        }
#endif
        0x43425844, 0x89611945, 0x2b7e06f0, 0x953a72bb, 0x1590618f, 0x00000001, 0x000000f8, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000080, 0x00000050, 0x00000020,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x04002058, 0x00107000, 0x00000000,
        0x00005555, 0x03000065, 0x001020f2, 0x00000000, 0x0900006e, 0x00102032, 0x00000000, 0x00107046,
        0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x00000000, 0x08000036, 0x001020c2, 0x00000000,
        0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};

    memset(&desc, 0, sizeof(desc));
    desc.rt_format = DXGI_FORMAT_R32G32B32A32_FLOAT;
    desc.rt_width = desc.rt_height = 1;
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    command_list = context.list;
    queue = context.queue;

    context.root_signature = create_texture_root_signature(context.device,
            D3D12_SHADER_VISIBILITY_PIXEL, 1, 0);
    context.pipeline_state = create_pipeline_state(context.device,
            context.root_signature, context.render_target_desc.Format, NULL, &ps, NULL);

    memset(&heap_properties, 0, sizeof(heap_properties));
    heap_properties.Type = D3D12_HEAP_TYPE_DEFAULT;
    resource_desc = ID3D12Resource_GetDesc(context.render_target);
    resource_desc.SampleDesc.Count = 4;
    hr = ID3D12Device_CreateCommittedResource(context.device,
            &heap_properties, D3D12_HEAP_FLAG_NONE, &resource_desc,
            D3D12_RESOURCE_STATE_COMMON, NULL, &IID_ID3D12Resource, (void **)&texture);
    ok(hr == S_OK, "Failed to create texture, hr %#x.\n", hr);

    heap = create_gpu_descriptor_heap(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 16);
    readback_texture = create_default_texture(context.device, 4, 1, DXGI_FORMAT_R32G32B32A32_FLOAT,
            D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST);
    ID3D12Device_CreateShaderResourceView(context.device, texture, NULL,
            ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(heap));

    transition_resource_state(command_list,
            texture, D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);

    for (sample_index = 0; sample_index < resource_desc.SampleDesc.Count; ++sample_index)
    {
        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
        ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
        ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
        ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
        ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
        ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &heap);
        ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0,
                get_gpu_descriptor_handle(&context, heap, 0));
        ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 1, 1, &sample_index, 0);
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
        src_location.pResource = context.render_target;
        src_location.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
        src_location.SubresourceIndex = 0;
        dst_location.pResource = readback_texture;
        dst_location.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
        dst_location.SubresourceIndex = 0;
        set_box(&box, 0, 0, 0, 1, 1, 1);
        ID3D12GraphicsCommandList_CopyTextureRegion(command_list, &dst_location, sample_index, 0, 0, &src_location, &box);
        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);
    }

    transition_resource_state(command_list, readback_texture,
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COPY_SOURCE);

    get_resource_readback_with_command_list(readback_texture, 0, &rb, queue, command_list);
    for (i = 0; i < resource_desc.SampleDesc.Count; ++i)
    {
        const struct vec4 *position = get_readback_vec4(&rb.rb, i, 0);

        vkd3d_test_push_context("Sample %u", i);

        ok(-1.0f <= position->x && position->x <= 1.0f, "Unexpected x %.8e.\n", position->x);
        ok(-1.0f <= position->y && position->y <= 1.0f, "Unexpected y %.8e.\n", position->y);
        ok(!position->z, "Unexpected z %.8e.\n", position->z);
        ok(!position->w, "Unexpected w %.8e.\n", position->w);

        if (vkd3d_test_state.debug_level > 0)
            trace("Position {%.8e, %.8e}.\n", position->x, position->y);

        vkd3d_test_pop_context();
    }
    release_resource_readback(&rb);

    ID3D12DescriptorHeap_Release(heap);
    ID3D12Resource_Release(texture);
    ID3D12Resource_Release(readback_texture);
    destroy_test_context(&context);
}

static void test_shader_eval_attribute(void)
{
    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
    ID3D12GraphicsCommandList *command_list;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12CommandQueue *queue;
    HRESULT hr;

    static const DWORD vs_code[] =
    {
#if 0
        void main(uint id : SV_VertexID, out float4 position : SV_Position,
                out float2 attr : ATTR, out centroid float2 ref : REF)
        {
            float2 coords = float2((id << 1) & 2, id & 2);
            position = float4(coords * float2(2, -2) + float2(-1, 1), 0, 1);
            attr = ref = position.xy;
        }
#endif
        0x43425844, 0x9289815f, 0xc6ff580d, 0xa7184c61, 0x4920e2eb, 0x00000001, 0x0000021c, 0x00000003,
        0x0000002c, 0x00000060, 0x000000d0, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000006, 0x00000001, 0x00000000, 0x00000101, 0x565f5653, 0x65747265, 0x00444978,
        0x4e47534f, 0x00000068, 0x00000003, 0x00000008, 0x00000050, 0x00000000, 0x00000001, 0x00000003,
        0x00000000, 0x0000000f, 0x0000005c, 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x00000c03,
        0x00000061, 0x00000000, 0x00000000, 0x00000003, 0x00000002, 0x00000c03, 0x505f5653, 0x7469736f,
        0x006e6f69, 0x52545441, 0x46455200, 0xababab00, 0x58454853, 0x00000144, 0x00010050, 0x00000051,
        0x0100086a, 0x04000060, 0x00101012, 0x00000000, 0x00000006, 0x04000067, 0x001020f2, 0x00000000,
        0x00000001, 0x03000065, 0x00102032, 0x00000001, 0x03000065, 0x00102032, 0x00000002, 0x02000068,
        0x00000001, 0x0b00008c, 0x00100012, 0x00000000, 0x00004001, 0x00000001, 0x00004001, 0x00000001,
        0x0010100a, 0x00000000, 0x00004001, 0x00000000, 0x07000001, 0x00100042, 0x00000000, 0x0010100a,
        0x00000000, 0x00004001, 0x00000002, 0x05000056, 0x00100032, 0x00000000, 0x00100086, 0x00000000,
        0x0f000032, 0x00100032, 0x00000000, 0x00100046, 0x00000000, 0x00004002, 0x40000000, 0xc0000000,
        0x00000000, 0x00000000, 0x00004002, 0xbf800000, 0x3f800000, 0x00000000, 0x00000000, 0x05000036,
        0x00102032, 0x00000000, 0x00100046, 0x00000000, 0x08000036, 0x001020c2, 0x00000000, 0x00004002,
        0x00000000, 0x00000000, 0x00000000, 0x3f800000, 0x05000036, 0x00102032, 0x00000001, 0x00100046,
        0x00000000, 0x05000036, 0x00102032, 0x00000002, 0x00100046, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE vs = {vs_code, sizeof(vs_code)};
    static const DWORD ps_eval_sample_index_code[] =
    {
#if 0
        float4 main(float4 p : SV_Position, float2 attr : ATTR,
                sample float2 ref : REF, uint sample_id : SV_SampleIndex) : SV_Target
        {
            return float4(EvaluateAttributeAtSample(attr, sample_id) - ref, 0, 1);
        }
#endif
        0x43425844, 0x65f268a1, 0x2c1a3d53, 0xd39689a5, 0x2f556a12, 0x00000001, 0x000001a4, 0x00000003,
        0x0000002c, 0x000000c0, 0x000000f4, 0x4e475349, 0x0000008c, 0x00000004, 0x00000008, 0x00000068,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x00000074, 0x00000000, 0x00000000,
        0x00000003, 0x00000001, 0x00000303, 0x00000079, 0x00000000, 0x00000000, 0x00000003, 0x00000002,
        0x00000303, 0x0000007d, 0x00000000, 0x0000000a, 0x00000001, 0x00000003, 0x00000101, 0x505f5653,
        0x7469736f, 0x006e6f69, 0x52545441, 0x46455200, 0x5f565300, 0x706d6153, 0x6e49656c, 0x00786564,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x000000a8, 0x00000050,
        0x0000002a, 0x0100086a, 0x03001062, 0x00101032, 0x00000001, 0x03003062, 0x00101032, 0x00000002,
        0x04000863, 0x00101012, 0x00000003, 0x0000000a, 0x03000065, 0x001020f2, 0x00000000, 0x02000068,
        0x00000001, 0x070000cc, 0x00100032, 0x00000000, 0x00101046, 0x00000001, 0x0010100a, 0x00000003,
        0x08000000, 0x00102032, 0x00000000, 0x00100046, 0x00000000, 0x80101046, 0x00000041, 0x00000002,
        0x08000036, 0x001020c2, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x3f800000,
        0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_eval_sample_index = {ps_eval_sample_index_code, sizeof(ps_eval_sample_index_code)};
    static const DWORD vs_eval_centroid_code[] =
    {
#if 0
        void main(uint id : SV_VertexID, out float4 position : SV_Position,
                out float2 attr : ATTR, out float2 attr2 : ATTR2, out centroid float2 ref : REF)
        {
            float2 coords = float2((id << 1) & 2, id & 2);
            position = float4(coords * float2(2, -2) + float2(-1, 1), 0, 1);
            attr = attr2 = ref = position.xy;
        }
#endif
        0x43425844, 0xed41033d, 0xa2906698, 0x319dcb84, 0x41750935, 0x00000001, 0x00000240, 0x00000003,
        0x0000002c, 0x00000060, 0x000000e8, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000006, 0x00000001, 0x00000000, 0x00000101, 0x565f5653, 0x65747265, 0x00444978,
        0x4e47534f, 0x00000080, 0x00000004, 0x00000008, 0x00000068, 0x00000000, 0x00000001, 0x00000003,
        0x00000000, 0x0000000f, 0x00000074, 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x00000c03,
        0x00000074, 0x00000002, 0x00000000, 0x00000003, 0x00000001, 0x0000030c, 0x00000079, 0x00000000,
        0x00000000, 0x00000003, 0x00000002, 0x00000c03, 0x505f5653, 0x7469736f, 0x006e6f69, 0x52545441,
        0x46455200, 0xababab00, 0x58454853, 0x00000150, 0x00010050, 0x00000054, 0x0100086a, 0x04000060,
        0x00101012, 0x00000000, 0x00000006, 0x04000067, 0x001020f2, 0x00000000, 0x00000001, 0x03000065,
        0x00102032, 0x00000001, 0x03000065, 0x001020c2, 0x00000001, 0x03000065, 0x00102032, 0x00000002,
        0x02000068, 0x00000001, 0x0b00008c, 0x00100012, 0x00000000, 0x00004001, 0x00000001, 0x00004001,
        0x00000001, 0x0010100a, 0x00000000, 0x00004001, 0x00000000, 0x07000001, 0x00100042, 0x00000000,
        0x0010100a, 0x00000000, 0x00004001, 0x00000002, 0x05000056, 0x00100032, 0x00000000, 0x00100086,
        0x00000000, 0x0f000032, 0x00100032, 0x00000000, 0x00100046, 0x00000000, 0x00004002, 0x40000000,
        0xc0000000, 0x00000000, 0x00000000, 0x00004002, 0xbf800000, 0x3f800000, 0x00000000, 0x00000000,
        0x05000036, 0x00102032, 0x00000000, 0x00100046, 0x00000000, 0x08000036, 0x001020c2, 0x00000000,
        0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x3f800000, 0x05000036, 0x001020f2, 0x00000001,
        0x00100446, 0x00000000, 0x05000036, 0x00102032, 0x00000002, 0x00100046, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE vs_eval_centroid = {vs_eval_centroid_code, sizeof(vs_eval_centroid_code)};
    static const DWORD ps_eval_centroid_code[] =
    {
#if 0
        float4 main(float4 p : SV_Position, float2 attr : ATTR, float2 attr2 : ATTR2, centroid float2 ref : REF) : SV_Target
        {
            return float4(EvaluateAttributeCentroid(attr) - ref, 0, 1);
        }
#endif
        0x43425844, 0x8ec53803, 0xdfd9505b, 0x8d4ce8ad, 0xbbefe3d4, 0x00000001, 0x00000180, 0x00000003,
        0x0000002c, 0x000000b4, 0x000000e8, 0x4e475349, 0x00000080, 0x00000004, 0x00000008, 0x00000068,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x00000074, 0x00000000, 0x00000000,
        0x00000003, 0x00000001, 0x00000303, 0x00000074, 0x00000002, 0x00000000, 0x00000003, 0x00000001,
        0x0000000c, 0x00000079, 0x00000000, 0x00000000, 0x00000003, 0x00000002, 0x00000303, 0x505f5653,
        0x7469736f, 0x006e6f69, 0x52545441, 0x46455200, 0xababab00, 0x4e47534f, 0x0000002c, 0x00000001,
        0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x0000000f, 0x545f5653,
        0x65677261, 0xabab0074, 0x58454853, 0x00000090, 0x00000050, 0x00000024, 0x0100086a, 0x03001062,
        0x00101032, 0x00000001, 0x03001862, 0x00101032, 0x00000002, 0x03000065, 0x001020f2, 0x00000000,
        0x02000068, 0x00000001, 0x050000cd, 0x00100032, 0x00000000, 0x00101046, 0x00000001, 0x08000000,
        0x00102032, 0x00000000, 0x00100046, 0x00000000, 0x80101046, 0x00000041, 0x00000002, 0x08000036,
        0x001020c2, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x3f800000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_eval_centroid = {ps_eval_centroid_code, sizeof(ps_eval_centroid_code)};
    static const struct vec4 expected_vec4 = {0.0f, 0.0f, 0.0f, 1.0f};
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};

    if (test_options.use_warp_device)
    {
        skip("Sample shading tests fail on WARP.\n");
        return;
    }

    memset(&desc, 0, sizeof(desc));
    desc.rt_format = DXGI_FORMAT_R32G32B32A32_FLOAT;
    desc.rt_width = desc.rt_height = 32;
    desc.sample_desc.Count = 4;
    desc.no_pipeline = true;
    if (!init_test_context(&context, &desc))
        return;
    command_list = context.list;
    queue = context.queue;

    init_pipeline_state_desc(&pso_desc, context.root_signature,
            context.render_target_desc.Format, &vs, &ps_eval_sample_index, NULL);
    pso_desc.SampleDesc.Count = desc.sample_desc.Count;
    hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
    ok(hr == S_OK, "Failed to create pipeline, hr %#x.\n", hr);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_RESOLVE_SOURCE);
    check_sub_resource_vec4(context.render_target, 0, queue, command_list, &expected_vec4, 0);

    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RESOLVE_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    ID3D12PipelineState_Release(context.pipeline_state);
    pso_desc.VS = vs_eval_centroid;
    pso_desc.PS = ps_eval_centroid;
    hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
    ok(hr == S_OK, "Failed to create pipeline, hr %#x.\n", hr);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_RESOLVE_SOURCE);
    check_sub_resource_vec4(context.render_target, 0, queue, command_list, &expected_vec4, 0);

    destroy_test_context(&context);
}

static void test_primitive_restart(void)
{
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
    ID3D12GraphicsCommandList *command_list;
    D3D12_INPUT_LAYOUT_DESC input_layout;
    struct d3d12_resource_readback rb;
    struct test_context_desc desc;
    D3D12_VERTEX_BUFFER_VIEW vbv;
    struct test_context context;
    D3D12_INDEX_BUFFER_VIEW ibv;
    ID3D12CommandQueue *queue;
    unsigned int index_count;
    ID3D12Resource *ib, *vb;
    size_t buffer_size;
    unsigned int i;
    D3D12_BOX box;
    HRESULT hr;
    void *ptr;

    static const DWORD vs_code[] =
    {
#if 0
        float4 main(int4 p : POSITION) : SV_Position
        {
            return p;
        }
#endif
        0x43425844, 0x3fd50ab1, 0x580a1d14, 0x28f5f602, 0xd1083e3a, 0x00000001, 0x000000d8, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000000, 0x00000002, 0x00000000, 0x00000f0f, 0x49534f50, 0x4e4f4954, 0xababab00,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000001, 0x00000003,
        0x00000000, 0x0000000f, 0x505f5653, 0x7469736f, 0x006e6f69, 0x52444853, 0x0000003c, 0x00010040,
        0x0000000f, 0x0300005f, 0x001010f2, 0x00000000, 0x04000067, 0x001020f2, 0x00000000, 0x00000001,
        0x0500002b, 0x001020f2, 0x00000000, 0x00101e46, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE vs = {vs_code, sizeof(vs_code)};
    static const struct
    {
        int8_t x, y;
    }
    quad[] =
    {
        {-1, -1},
        {-1,  1},
        { 1, -1},
        { 1,  1},
    };
    static const D3D12_INPUT_ELEMENT_DESC layout_desc[] =
    {
        {"position", 0, DXGI_FORMAT_R8G8_SINT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
    };
    static const uint16_t indices16[] = {0, 1, 2, 3};
    static const uint32_t indices[] = {0, 1, 2, 3};
    static const uint16_t indices16_max[] = {0, 1, 2, 0xffff};
    static const uint32_t indices_max16[] = {0, 1, 2, 0xffff};
    static const uint16_t indices16_restart[] = {0, 1, 2, 0xffff, 2, 1, 3};
    static const uint32_t indices_restart[] = {0, 1, 2, 0xffffffff, 2, 1, 3};
    static const struct
    {
        D3D12_INDEX_BUFFER_STRIP_CUT_VALUE strip_cut_value;
        DXGI_FORMAT ib_format;
        const void *indices;
        size_t indices_size;
        unsigned int last_index;
        bool full_quad;
        bool is_todo;
        bool is_mvk_bug;
    }
    tests[] =
    {
        {D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_DISABLED, DXGI_FORMAT_R16_UINT,     indices16,     sizeof(indices16), 0x0003, true},
        /* Metal, and therefore MoltenVK, doesn't support disabling
         * primitive restart. */
        {D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_DISABLED, DXGI_FORMAT_R16_UINT, indices16_max, sizeof(indices16_max), 0xffff, true, false, true},
        {D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_DISABLED, DXGI_FORMAT_R32_UINT,       indices,       sizeof(indices), 0x0003, true},
        {D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_DISABLED, DXGI_FORMAT_R32_UINT, indices_max16, sizeof(indices_max16), 0xffff, true},

        {D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_0xFFFF, DXGI_FORMAT_R16_UINT,     indices16,     sizeof(indices16), 0x0003, true},
        {D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_0xFFFF, DXGI_FORMAT_R16_UINT, indices16_max, sizeof(indices16_max), 0xffff, false},
        {D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_0xFFFF, DXGI_FORMAT_R16_UINT, indices16_restart, sizeof(indices16_restart), 0x0003, true},
        {D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_0xFFFF, DXGI_FORMAT_R32_UINT,       indices,       sizeof(indices), 0x0003, true},
        {D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_0xFFFF, DXGI_FORMAT_R32_UINT, indices_max16, sizeof(indices_max16), 0xffff, false, true},

        {D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_0xFFFFFFFF, DXGI_FORMAT_R16_UINT,     indices16,     sizeof(indices16), 0x0003, true},
        {D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_0xFFFFFFFF, DXGI_FORMAT_R16_UINT, indices16_max, sizeof(indices16_max), 0xffff, true, true},
        {D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_0xFFFFFFFF, DXGI_FORMAT_R32_UINT,       indices,       sizeof(indices), 0x0003, true},
        {D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_0xFFFFFFFF, DXGI_FORMAT_R32_UINT, indices_max16, sizeof(indices_max16), 0xffff, true},
        {D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_0xFFFFFFFF, DXGI_FORMAT_R32_UINT, indices_restart, sizeof(indices_restart), 0x0003, true},
    };

    memset(&desc, 0, sizeof(desc));
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    command_list = context.list;
    queue = context.queue;

    context.root_signature = create_empty_root_signature(context.device,
            D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
    input_layout.pInputElementDescs = layout_desc;
    input_layout.NumElements = ARRAY_SIZE(layout_desc);
    init_pipeline_state_desc(&pso_desc, context.root_signature,
            context.render_target_desc.Format, &vs, NULL, &input_layout);

    for (i = 0; i < ARRAY_SIZE(tests); ++i)
    {
        vkd3d_test_push_context("Test %u", i);

        buffer_size = (tests[i].last_index + 1) * sizeof(*quad);

        vb = create_upload_buffer(context.device, buffer_size, NULL);
        vbv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vb);
        vbv.StrideInBytes = sizeof(*quad);
        vbv.SizeInBytes = buffer_size;

        pso_desc.IBStripCutValue = tests[i].strip_cut_value;
        hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
                &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
        ok(hr == S_OK, "Failed to create pipeline, hr %#x.\n", hr);

        ibv.Format = tests[i].ib_format;
        ib = create_upload_buffer(context.device, tests[i].indices_size, tests[i].indices);
        ibv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(ib);
        ibv.SizeInBytes = tests[i].indices_size;
        index_count = tests[i].indices_size / format_size(ibv.Format);

        hr = ID3D12Resource_Map(vb, 0, NULL, &ptr);
        ok(hr == S_OK, "Failed to map buffer, hr %#x.\n", hr);
        memcpy(ptr, quad, (ARRAY_SIZE(quad) - 1) * sizeof(*quad));
        memcpy((BYTE *)ptr + tests[i].last_index * sizeof(*quad), &quad[ARRAY_SIZE(quad) - 1], sizeof(*quad));
        ID3D12Resource_Unmap(vb, 0, NULL);

        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

        ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
        ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
        ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
        ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, 1, &vbv);
        ID3D12GraphicsCommandList_IASetIndexBuffer(command_list, &ibv);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
        ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
        ID3D12GraphicsCommandList_DrawIndexedInstanced(command_list, index_count, 1, 0, 0, 0);

        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
        get_resource_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
        if (tests[i].full_quad)
        {
            bug_if(is_mvk_device(context.device) && tests[i].is_mvk_bug)
            todo_if(tests[i].is_todo)
            check_readback_data_uint(&rb.rb, NULL, 0xff00ff00, 0);
        }
        else
        {
            set_box(&box, 16, 0, 0, 32, 10, 1);
            todo_if(tests[i].is_todo)
            check_readback_data_uint(&rb.rb, &box, 0xffffffff, 0);
            set_box(&box, 0, 16, 0, 16, 32, 1);
            check_readback_data_uint(&rb.rb, &box, 0xff00ff00, 0);
        }
        release_resource_readback(&rb);

        reset_command_list(command_list, context.allocator);
        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

        ID3D12Resource_Release(ib);
        ID3D12Resource_Release(vb);
        ID3D12PipelineState_Release(context.pipeline_state);
        context.pipeline_state = NULL;
        vkd3d_test_pop_context();
    }

    destroy_test_context(&context);
}

static void test_vertex_shader_stream_output(void)
{
    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
    ID3D12Resource *counter_buffer, *so_buffer;
    ID3D12GraphicsCommandList *command_list;
    D3D12_STREAM_OUTPUT_BUFFER_VIEW sobv;
    struct d3d12_resource_readback rb;
    ID3D12Resource *upload_buffer;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12CommandQueue *queue;
    unsigned int counter, i;
    const struct vec4 *data;
    ID3D12Device *device;
    HRESULT hr;

    static const D3D12_SO_DECLARATION_ENTRY so_declaration[] =
    {
        {0, "SV_Position", 0, 0, 4, 0},
    };
    static const struct vec4 expected_output[] =
    {
        {-1.0f, 1.0f, 0.0f, 1.0f},
        { 3.0f, 1.0f, 0.0f, 1.0f},
        {-1.0f,-3.0f, 0.0f, 1.0f},
    };
    unsigned int strides[] = {16};

    memset(&desc, 0, sizeof(desc));
    desc.root_signature_flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_STREAM_OUTPUT;
    desc.no_pipeline = true;
    if (!init_test_context(&context, &desc))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    init_pipeline_state_desc(&pso_desc, context.root_signature, 0, NULL, NULL, NULL);
    pso_desc.StreamOutput.NumEntries = ARRAY_SIZE(so_declaration);
    pso_desc.StreamOutput.pSODeclaration = so_declaration;
    pso_desc.StreamOutput.pBufferStrides = strides;
    pso_desc.StreamOutput.NumStrides = ARRAY_SIZE(strides);
    pso_desc.StreamOutput.RasterizedStream = D3D12_SO_NO_RASTERIZED_STREAM;
    hr = ID3D12Device_CreateGraphicsPipelineState(device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
    if (hr == E_NOTIMPL)
    {
        skip("Stream output is not supported.\n");
        destroy_test_context(&context);
        return;
    }
    ok(hr == S_OK, "Failed to create graphics pipeline state, hr %#x.\n", hr);

    counter = 0;
    upload_buffer = create_upload_buffer(device, sizeof(counter), &counter);

    counter_buffer = create_default_buffer(device, 32,
            D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST);
    so_buffer = create_default_buffer(device, 1024,
            D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_STREAM_OUT);
    sobv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(so_buffer);
    sobv.SizeInBytes = 1024;
    sobv.BufferFilledSizeLocation = ID3D12Resource_GetGPUVirtualAddress(counter_buffer);

    ID3D12GraphicsCommandList_CopyBufferRegion(command_list, counter_buffer, 0,
            upload_buffer, 0, sizeof(counter));

    transition_resource_state(command_list, counter_buffer,
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_STREAM_OUT);

    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_SOSetTargets(command_list, 0, 1, &sobv);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, counter_buffer,
            D3D12_RESOURCE_STATE_STREAM_OUT, D3D12_RESOURCE_STATE_COPY_SOURCE);
    transition_resource_state(command_list, so_buffer,
            D3D12_RESOURCE_STATE_STREAM_OUT, D3D12_RESOURCE_STATE_COPY_SOURCE);

    get_buffer_readback_with_command_list(counter_buffer, DXGI_FORMAT_R32_UINT, &rb, queue, command_list);
    counter = get_readback_uint(&rb.rb, 0, 0, 0);
    ok(counter == 3 * sizeof(struct vec4), "Got unexpected counter %u.\n", counter);
    release_resource_readback(&rb);
    reset_command_list(command_list, context.allocator);
    get_buffer_readback_with_command_list(so_buffer, DXGI_FORMAT_UNKNOWN, &rb, queue, command_list);
    for (i = 0; i < ARRAY_SIZE(expected_output); ++i)
    {
        const struct vec4 *expected = &expected_output[i];
        data = get_readback_vec4(&rb.rb, i, 0);
        ok(compare_vec4(data, expected, 1),
                "Got {%.8e, %.8e, %.8e, %.8e}, expected {%.8e, %.8e, %.8e, %.8e}.\n",
                data->x, data->y, data->z, data->w, expected->x, expected->y, expected->z, expected->w);
    }
    release_resource_readback(&rb);

    ID3D12Resource_Release(counter_buffer);
    ID3D12Resource_Release(upload_buffer);
    ID3D12Resource_Release(so_buffer);
    destroy_test_context(&context);
}

static void test_read_write_subresource(void)
{
    D3D12_TEXTURE_COPY_LOCATION src_location, dst_location;
    uint32_t *dst_buffer, *zero_buffer, *ptr;
    ID3D12GraphicsCommandList *command_list;
    D3D12_HEAP_PROPERTIES heap_properties;
    D3D12_SUBRESOURCE_DATA texture_data;
    struct d3d12_resource_readback rb;
    D3D12_RESOURCE_DESC resource_desc;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12Resource *src_texture;
    ID3D12Resource *dst_texture;
    ID3D12CommandQueue *queue;
    ID3D12Resource *rb_buffer;
    unsigned int buffer_size;
    unsigned int slice_pitch;
    unsigned int x, y, z, i;
    unsigned int row_pitch;
    uint32_t got, expected;
    ID3D12Device *device;
    D3D12_BOX box;
    HRESULT hr;

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

    row_pitch = 128 * sizeof(unsigned int);
    slice_pitch = row_pitch * 100;
    buffer_size = slice_pitch * 64;

    /* Buffers are not supported */
    rb_buffer = create_readback_buffer(device, buffer_size);
    dst_buffer = malloc(buffer_size);
    ok(dst_buffer, "Failed to allocate memory.\n");
    zero_buffer = malloc(buffer_size);
    ok(zero_buffer, "Failed to allocate memory.\n");
    memset(zero_buffer, 0, buffer_size);

    set_box(&box, 0, 0, 0, 1, 1, 1);
    hr = ID3D12Resource_WriteToSubresource(rb_buffer, 0, &box, dst_buffer, row_pitch, slice_pitch);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    hr = ID3D12Resource_ReadFromSubresource(rb_buffer, dst_buffer, row_pitch, slice_pitch, 0, &box);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    ID3D12Resource_Release(rb_buffer);

    /* Only texture on custom heaps is legal for ReadFromSubresource/WriteToSubresource */
    resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE3D;
    resource_desc.Alignment = 0;
    resource_desc.Width = 128;
    resource_desc.Height = 100;
    resource_desc.DepthOrArraySize = 64;
    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 = 0;

    memset(&heap_properties, 0, sizeof(heap_properties));
    heap_properties.Type = D3D12_HEAP_TYPE_CUSTOM;
    heap_properties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_WRITE_BACK;
    heap_properties.MemoryPoolPreference = D3D12_MEMORY_POOL_L0;
    hr = ID3D12Device_CreateCommittedResource(device, &heap_properties, D3D12_HEAP_FLAG_NONE,
            &resource_desc, D3D12_RESOURCE_STATE_COMMON, NULL, &IID_ID3D12Resource, (void **)&src_texture);
    if (FAILED(hr))
    {
        skip("Failed to create texture on custom heap.\n");
        goto done;
    }

    /* Invalid box */
    set_box(&box, 0, 0, 0, 128, 100, 65);
    hr = ID3D12Resource_ReadFromSubresource(src_texture, dst_buffer, row_pitch, slice_pitch, 0, &box);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    set_box(&box, 0, 0, 65, 128, 100, 65);
    hr = ID3D12Resource_ReadFromSubresource(src_texture, dst_buffer, row_pitch, slice_pitch, 0, &box);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    set_box(&box, 128, 0, 0, 128, 100, 65);
    hr = ID3D12Resource_ReadFromSubresource(src_texture, dst_buffer, row_pitch, slice_pitch, 0, &box);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    /* NULL box */
    hr = ID3D12Resource_WriteToSubresource(src_texture, 0, NULL, dst_buffer, row_pitch, slice_pitch);
    todo_if(is_nvidia_device(device) || is_mvk_device(device))
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);

    hr = ID3D12Resource_ReadFromSubresource(src_texture, dst_buffer, row_pitch, slice_pitch, 0, NULL);
    todo_if(is_nvidia_device(device) || is_mvk_device(device))
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);

    /* Empty box */
    set_box(&box, 128, 100, 64, 128, 100, 64);
    hr = ID3D12Resource_ReadFromSubresource(src_texture, dst_buffer, row_pitch, slice_pitch, 0, &box);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);

    set_box(&box, 0, 0, 0, 0, 0, 0);
    hr = ID3D12Resource_WriteToSubresource(src_texture, 0, &box, dst_buffer, row_pitch, slice_pitch);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);

    hr = ID3D12Resource_ReadFromSubresource(src_texture, dst_buffer, row_pitch, slice_pitch, 0, &box);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);

    for (i = 0; i < 2; ++i)
    {
        vkd3d_test_push_context("Test %u", i);

        for (z = 0; z < 64; ++z)
        {
            for (y = 0; y < 100; ++y)
            {
                for (x = 0; x < 128; ++x)
                {
                    ptr = &dst_buffer[z * 128 * 100 + y * 128 + x];
                    if (x < 2 && y< 2 && z < 2) /* Region 1 */
                        *ptr = (z + 1) << 16 | (y + 1) << 8 | (x + 1);
                    else if (2 <= x && x < 11 && 2 <= y && y < 13 && 2 <= z && z < 17) /* Region 2 */
                        *ptr = (z + 2) << 16 | (y + 2) << 8 | (x + 2);
                    else
                        *ptr = 0xdeadbeef;
                }
            }
        }

        if (i)
        {
            hr = ID3D12Resource_WriteToSubresource(src_texture, 0, NULL, zero_buffer, row_pitch, slice_pitch);
            todo_if(is_nvidia_device(device) || is_mvk_device(device))
            ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);

            /* Write region 1 */
            set_box(&box, 0, 0, 0, 2, 2, 2);
            hr = ID3D12Resource_WriteToSubresource(src_texture, 0, &box, dst_buffer, row_pitch, slice_pitch);
            todo_if(is_nvidia_device(device) || is_mvk_device(device))
            ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);

            /* Write region 2 */
            set_box(&box, 2, 2, 2, 11, 13, 17);
            hr = ID3D12Resource_WriteToSubresource(src_texture, 0, &box, &dst_buffer[2 * 128 * 100 + 2 * 128 + 2],
                    row_pitch, slice_pitch);
            todo_if(is_nvidia_device(device) || is_mvk_device(device))
            ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
        }
        else
        {
            /* Upload the test data */
            transition_resource_state(command_list, src_texture,
                    D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_COPY_DEST);
            texture_data.pData = dst_buffer;
            texture_data.RowPitch = row_pitch;
            texture_data.SlicePitch = slice_pitch;
            upload_texture_data(src_texture, &texture_data, 1, queue, command_list);
            reset_command_list(command_list, context.allocator);
            transition_resource_state(command_list, src_texture,
                    D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COMMON);
        }

        memset(dst_buffer, 0, buffer_size);

        /* Read region 1 */
        set_box(&box, 0, 0, 0, 2, 2, 2);
        hr = ID3D12Resource_ReadFromSubresource(src_texture, dst_buffer, row_pitch, slice_pitch, 0, &box);
        todo_if(is_nvidia_device(device) || is_mvk_device(device))
        ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);

        /* Read region 2 */
        set_box(&box, 2, 2, 2, 11, 13, 17);
        hr = ID3D12Resource_ReadFromSubresource(src_texture, &dst_buffer[2 * 128 * 100 + 2 * 128 + 2], row_pitch,
                slice_pitch, 0, &box);
        todo_if(is_nvidia_device(device) || is_mvk_device(device))
        ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);

        for (z = 0; z < 64; ++z)
        {
            for (y = 0; y < 100; ++y)
            {
                for (x = 0; x < 128; ++x)
                {
                    if (x < 2 && y < 2 && z < 2) /* Region 1 */
                        expected = (z + 1) << 16 | (y + 1) << 8 | (x + 1);
                    else if (2 <= x && x < 11 && 2 <= y && y < 13 && 2 <= z && z < 17) /* Region 2 */
                        expected = (z + 2) << 16 | (y + 2) << 8 | (x + 2);
                    else /* Untouched */
                        expected = 0;

                    got = dst_buffer[z * 128 * 100 + y * 128 + x];
                    if (got != expected)
                        break;
                }
                if (got != expected)
                    break;
            }
            if (got != expected)
                break;
        }
        todo_if(is_nvidia_device(device) || is_mvk_device(device))
        ok(got == expected, "Got unexpected value 0x%08x at (%u, %u, %u), expected 0x%08x.\n", got, x, y, z, expected);
        vkd3d_test_pop_context();
    }

    /* Test layout is the same */
    dst_texture = create_default_texture3d(device, 128, 100, 64, 1, DXGI_FORMAT_R8G8B8A8_UNORM, 0,
            D3D12_RESOURCE_STATE_COPY_DEST);
    memset(dst_buffer, 0, buffer_size);
    texture_data.pData = dst_buffer;
    texture_data.RowPitch = row_pitch;
    texture_data.SlicePitch = slice_pitch;
    upload_texture_data(dst_texture, &texture_data, 1, queue, command_list);
    reset_command_list(command_list, context.allocator);

    src_location.pResource = src_texture;
    src_location.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
    src_location.SubresourceIndex = 0;
    dst_location.pResource = dst_texture;
    dst_location.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
    dst_location.SubresourceIndex = 0;
    set_box(&box, 0, 0, 0, 128, 100, 64);
    ID3D12GraphicsCommandList_CopyTextureRegion(command_list, &dst_location, 0, 0, 0, &src_location, &box);

    transition_resource_state(command_list, dst_texture,
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_resource_readback_with_command_list(dst_texture, 0, &rb, queue, command_list);
    for (z = 0; z < 64; ++z)
    {
        for (y = 0; y < 100; ++y)
        {
            for (x = 0; x < 128; ++x)
            {
                if (x < 2 && y < 2 && z < 2) /* Region 1 */
                    expected = (z + 1) << 16 | (y + 1) << 8 | (x + 1);
                else if (2 <= x && x < 11 && 2 <= y && y < 13 && 2 <= z && z < 17) /* Region 2 */
                    expected = (z + 2) << 16 | (y + 2) << 8 | (x + 2);
                else /* Untouched */
                    expected = 0;

                got = get_readback_uint(&rb.rb, x, y, z);
                if (got != expected)
                    break;
            }
            if (got != expected)
                break;
        }
        if (got != expected)
            break;
    }
    todo_if(is_nvidia_device(device) || is_mvk_device(device))
    ok(got == expected, "Got unexpected value 0x%08x at (%u, %u, %u), expected 0x%08x.\n", got, x, y, z, expected);
    release_resource_readback(&rb);

    ID3D12Resource_Release(src_texture);
    ID3D12Resource_Release(dst_texture);

    /* Invalid box */
    resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
    resource_desc.Alignment = 0;
    resource_desc.Width = 64;
    resource_desc.Height = 32;
    resource_desc.DepthOrArraySize = 1;
    resource_desc.MipLevels = 1;
    resource_desc.Format = DXGI_FORMAT_BC1_UNORM;
    resource_desc.SampleDesc.Count = 1;
    resource_desc.SampleDesc.Quality = 0;
    resource_desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
    resource_desc.Flags = 0;

    memset(&heap_properties, 0, sizeof(heap_properties));
    heap_properties.Type = D3D12_HEAP_TYPE_CUSTOM;
    heap_properties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_WRITE_BACK;
    heap_properties.MemoryPoolPreference = D3D12_MEMORY_POOL_L0;
    hr = ID3D12Device_CreateCommittedResource(device, &heap_properties, D3D12_HEAP_FLAG_NONE,
            &resource_desc, D3D12_RESOURCE_STATE_COMMON, NULL, &IID_ID3D12Resource, (void **)&src_texture);
    ok(hr == S_OK, "Failed to create resource, hr %#x.\n", hr);

    /* Unaligned coordinates for BC format */
    set_box(&box, 0, 0, 0, 2, 2, 1);
    hr = ID3D12Resource_ReadFromSubresource(src_texture, dst_buffer, row_pitch, slice_pitch, 0, &box);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    set_box(&box, 2, 2, 0, 4, 4, 1);
    hr = ID3D12Resource_ReadFromSubresource(src_texture, dst_buffer, row_pitch, slice_pitch, 0, &box);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    set_box(&box, 2, 2, 0, 6, 6, 1);
    hr = ID3D12Resource_ReadFromSubresource(src_texture, dst_buffer, row_pitch, slice_pitch, 0, &box);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    /* Invalid coordinates for resource dimensions */
    set_box(&box, 0, 0, 0, 64, 32, 2);
    hr = ID3D12Resource_ReadFromSubresource(src_texture, dst_buffer, row_pitch, slice_pitch, 0, &box);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    set_box(&box, 0, 0, 0, 68, 32, 1);
    hr = ID3D12Resource_ReadFromSubresource(src_texture, dst_buffer, row_pitch, slice_pitch, 0, &box);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    ID3D12Resource_Release(src_texture);

done:
    free(dst_buffer);
    free(zero_buffer);
    destroy_test_context(&context);
}

static void test_queue_wait(void)
{
    D3D12_TEXTURE_COPY_LOCATION dst_location, src_location;
    ID3D12GraphicsCommandList *command_list;
    ID3D12Resource *readback_buffer, *cb;
    ID3D12CommandQueue *queue, *queue2;
    struct d3d12_resource_readback rb;
    D3D12_RESOURCE_DESC resource_desc;
    uint64_t row_pitch, buffer_size;
    struct test_context_desc desc;
    ID3D12Fence *fence, *fence2;
    struct test_context context;
    ID3D12Device *device;
    unsigned int ret;
    uint64_t value;
    float color[4];
    HANDLE event;
    HRESULT hr;

    static const float blue[] = {0.0f, 0.0f, 1.0f, 1.0f};
    static const float green[] = {0.0f, 1.0f, 0.0f, 1.0f};
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    static const DWORD ps_code[] =
    {
#if 0
        float4 color;

        float4 main(float4 position : SV_POSITION) : SV_Target
        {
            return color;
        }
#endif
        0x43425844, 0xd18ead43, 0x8b8264c1, 0x9c0a062d, 0xfc843226, 0x00000001, 0x000000e0, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x505f5653, 0x5449534f, 0x004e4f49,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000044, 0x00000050,
        0x00000011, 0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2,
        0x00000000, 0x06000036, 0x001020f2, 0x00000000, 0x00208e46, 0x00000000, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};

    memset(&desc, 0, sizeof(desc));
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    queue2 = create_command_queue(device, D3D12_COMMAND_LIST_TYPE_DIRECT, D3D12_COMMAND_QUEUE_PRIORITY_NORMAL);

    event = create_event();
    ok(event, "Failed to create event.\n");

    hr = ID3D12Device_CreateFence(device, 1, D3D12_FENCE_FLAG_NONE, &IID_ID3D12Fence, (void **)&fence);
    ok(hr == S_OK, "Failed to create fence, hr %#x.\n", hr);
    hr = ID3D12Device_CreateFence(device, 0, D3D12_FENCE_FLAG_NONE, &IID_ID3D12Fence, (void **)&fence2);
    ok(hr == S_OK, "Failed to create fence, hr %#x.\n", hr);

    context.root_signature = create_cb_root_signature(context.device,
            0, D3D12_SHADER_VISIBILITY_PIXEL, D3D12_ROOT_SIGNATURE_FLAG_NONE);
    context.pipeline_state = create_pipeline_state(context.device,
            context.root_signature, context.render_target_desc.Format, NULL, &ps, NULL);

    cb = create_upload_buffer(device, sizeof(color), NULL);

    resource_desc = ID3D12Resource_GetDesc(context.render_target);

    row_pitch = align(resource_desc.Width * format_size(resource_desc.Format), D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
    buffer_size = row_pitch * resource_desc.Height;
    readback_buffer = create_readback_buffer(device, buffer_size);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 0,
            ID3D12Resource_GetGPUVirtualAddress(cb));
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);

    dst_location.pResource = readback_buffer;
    dst_location.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
    dst_location.PlacedFootprint.Offset = 0;
    dst_location.PlacedFootprint.Footprint.Format = resource_desc.Format;
    dst_location.PlacedFootprint.Footprint.Width = resource_desc.Width;
    dst_location.PlacedFootprint.Footprint.Height = resource_desc.Height;
    dst_location.PlacedFootprint.Footprint.Depth = 1;
    dst_location.PlacedFootprint.Footprint.RowPitch = row_pitch;
    src_location.pResource = context.render_target;
    src_location.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
    src_location.SubresourceIndex = 0;
    ID3D12GraphicsCommandList_CopyTextureRegion(command_list, &dst_location, 0, 0, 0, &src_location, NULL);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    hr = ID3D12GraphicsCommandList_Close(command_list);
    ok(hr == S_OK, "Failed to close command list, hr %#x.\n", hr);

    /* Wait() with signaled fence */
    update_buffer_data(cb, 0, sizeof(green), &green);
    queue_wait(queue, fence, 1);
    exec_command_list(queue, command_list);
    wait_queue_idle(device, queue);
    init_readback(&rb, readback_buffer, buffer_size, resource_desc.Width, resource_desc.Height, 1, row_pitch);
    check_readback_data_uint(&rb.rb, NULL, 0xff00ff00, 0);
    release_resource_readback(&rb);

    /* Wait() before CPU signal */
    update_buffer_data(cb, 0, sizeof(blue), &blue);
    queue_wait(queue, fence, 2);
    exec_command_list(queue, command_list);
    queue_signal(queue, fence2, 1);
    hr = ID3D12Fence_SetEventOnCompletion(fence2, 1, event);
    ok(hr == S_OK, "Failed to set event on completion, hr %#x.\n", hr);
    ret = wait_event(event, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
    init_readback(&rb, readback_buffer, buffer_size, resource_desc.Width, resource_desc.Height, 1, row_pitch);
    check_readback_data_uint(&rb.rb, NULL, 0xff00ff00, 0);
    release_resource_readback(&rb);
    value = ID3D12Fence_GetCompletedValue(fence2);
    ok(value == 0, "Got unexpected value %"PRIu64".\n", value);

    hr = ID3D12Fence_Signal(fence, 2);
    ok(hr == S_OK, "Failed to signal fence, hr %#x.\n", hr);
    ret = wait_event(event, INFINITE);
    ok(ret == WAIT_OBJECT_0, "Got unexpected return value %#x.\n", ret);
    ret = wait_event(event, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
    init_readback(&rb, readback_buffer, buffer_size, resource_desc.Width, resource_desc.Height, 1, row_pitch);
    check_readback_data_uint(&rb.rb, NULL, 0xffff0000, 0);
    release_resource_readback(&rb);
    value = ID3D12Fence_GetCompletedValue(fence2);
    ok(value == 1, "Got unexpected value %"PRIu64".\n", value);

    /* Wait() before GPU signal */
    update_buffer_data(cb, 0, sizeof(green), &green);
    queue_wait(queue, fence, 3);
    exec_command_list(queue, command_list);
    queue_signal(queue, fence2, 2);
    hr = ID3D12Fence_SetEventOnCompletion(fence2, 2, event);
    ok(hr == S_OK, "Failed to set event on completion, hr %#x.\n", hr);
    ret = wait_event(event, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
    init_readback(&rb, readback_buffer, buffer_size, resource_desc.Width, resource_desc.Height, 1, row_pitch);
    check_readback_data_uint(&rb.rb, NULL, 0xffff0000, 0);
    release_resource_readback(&rb);
    value = ID3D12Fence_GetCompletedValue(fence2);
    ok(value == 1, "Got unexpected value %"PRIu64".\n", value);

    queue_signal(queue2, fence, 3);
    ret = wait_event(event, INFINITE);
    ok(ret == WAIT_OBJECT_0, "Got unexpected return value %#x.\n", ret);
    ret = wait_event(event, 0);
    ok(ret == WAIT_TIMEOUT, "Got unexpected return value %#x.\n", ret);
    init_readback(&rb, readback_buffer, buffer_size, resource_desc.Width, resource_desc.Height, 1, row_pitch);
    check_readback_data_uint(&rb.rb, NULL, 0xff00ff00, 0);
    release_resource_readback(&rb);
    value = ID3D12Fence_GetCompletedValue(fence2);
    ok(value == 2, "Got unexpected value %"PRIu64".\n", value);

    /* update constant buffer after Wait() */
    queue_wait(queue, fence, 5);
    exec_command_list(queue, command_list);
    hr = ID3D12Fence_Signal(fence, 4);
    ok(hr == S_OK, "Failed to signal fence, hr %#x.\n", hr);
    update_buffer_data(cb, 0, sizeof(blue), &blue);
    hr = ID3D12Fence_Signal(fence, 5);
    ok(hr == S_OK, "Failed to signal fence, hr %#x.\n", hr);
    wait_queue_idle(device, queue);
    init_readback(&rb, readback_buffer, buffer_size, resource_desc.Width, resource_desc.Height, 1, row_pitch);
    check_readback_data_uint(&rb.rb, NULL, 0xffff0000, 0);
    release_resource_readback(&rb);

    /* signal to a lower value after a GPU wait was used (test timeline semaphore replacement) */
    hr = ID3D12Fence_Signal(fence, 0);
    ok(hr == S_OK, "Failed to signal fence, hr %#x.\n", hr);
    value = ID3D12Fence_GetCompletedValue(fence);
    ok(value == 0, "Got unexpected value %"PRIu64".\n", value);

    hr = ID3D12Fence_Signal(fence, 6);
    ok(hr == S_OK, "Failed to signal fence, hr %#x.\n", hr);
    update_buffer_data(cb, 0, sizeof(green), &green);
    exec_command_list(queue, command_list);
    wait_queue_idle(device, queue);
    init_readback(&rb, readback_buffer, buffer_size, resource_desc.Width, resource_desc.Height, 1, row_pitch);
    check_readback_data_uint(&rb.rb, NULL, 0xff00ff00, 0);
    release_resource_readback(&rb);

    /* Signal() and Wait() in the same command queue */
    update_buffer_data(cb, 0, sizeof(blue), &blue);
    queue_signal(queue, fence, 7);
    queue_wait(queue, fence, 7);
    exec_command_list(queue, command_list);
    wait_queue_idle(device, queue);
    init_readback(&rb, readback_buffer, buffer_size, resource_desc.Width, resource_desc.Height, 1, row_pitch);
    check_readback_data_uint(&rb.rb, NULL, 0xffff0000, 0);
    release_resource_readback(&rb);

    value = ID3D12Fence_GetCompletedValue(fence);
    ok(value == 7, "Got unexpected value %"PRIu64".\n", value);

    destroy_event(event);
    ID3D12Fence_Release(fence);
    ID3D12Fence_Release(fence2);
    ID3D12Resource_Release(cb);
    ID3D12CommandQueue_Release(queue2);
    ID3D12Resource_Release(readback_buffer);
    destroy_test_context(&context);
}

static void test_graphics_compute_queue_synchronization(void)
{
    ID3D12GraphicsCommandList *graphics_lists[2], *compute_lists[2];
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    ID3D12PipelineState *compute_pipeline_state;
    D3D12_UNORDERED_ACCESS_VIEW_DESC uav_desc;
    ID3D12CommandQueue *queue, *compute_queue;
    ID3D12DescriptorHeap *cpu_heap, *gpu_heap;
    ID3D12GraphicsCommandList *command_list;
    D3D12_ROOT_PARAMETER root_parameters[2];
    ID3D12CommandAllocator *allocators[3];
    struct d3d12_resource_readback rb;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12Fence *fence, *fence2;
    ID3D12Resource *buffer;
    ID3D12Device *device;
    uint32_t value;
    unsigned int i;
    HRESULT hr;

    unsigned int clear_value[4] = {0};
    static const DWORD cs_code[] =
    {
#if 0
        uint expected_value;
        RWByteAddressBuffer u : register(u1);

        [numthreads(64, 1, 1)]
        void main(void)
        {
            u.InterlockedCompareStore(0, expected_value, expected_value + 10);
        }
#endif
        0x43425844, 0x7909aab0, 0xf8576455, 0x58f9dd61, 0x3e7e64f0, 0x00000001, 0x000000e0, 0x00000003,
        0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x0000008c, 0x00050050, 0x00000023, 0x0100086a,
        0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x0300009d, 0x0011e000, 0x00000001, 0x02000068,
        0x00000001, 0x0400009b, 0x00000040, 0x00000001, 0x00000001, 0x0800001e, 0x00100012, 0x00000000,
        0x0020800a, 0x00000000, 0x00000000, 0x00004001, 0x0000000a, 0x0a0000ac, 0x0011e000, 0x00000001,
        0x00004001, 0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x0010000a, 0x00000000, 0x0100003e,
    };
    static const DWORD ps_code[] =
    {
#if 0
        uint expected_value;
        RWByteAddressBuffer u;

        float4 main(void) : SV_Target
        {
            u.InterlockedCompareStore(0, expected_value, expected_value + 2);
            return float4(0, 1, 0, 1);
        }
#endif
        0x43425844, 0x82fbce04, 0xa014204c, 0xc4d46d91, 0x1081c7a7, 0x00000001, 0x00000120, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x000000a8, 0x00000050, 0x0000002a,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x0300009d, 0x0011e000, 0x00000001,
        0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000001, 0x0800001e, 0x00100012, 0x00000000,
        0x0020800a, 0x00000000, 0x00000000, 0x00004001, 0x00000002, 0x0a0000ac, 0x0011e000, 0x00000001,
        0x00004001, 0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x0010000a, 0x00000000, 0x08000036,
        0x001020f2, 0x00000000, 0x00004002, 0x00000000, 0x3f800000, 0x00000000, 0x3f800000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};

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

    if (is_mvk_paravirtualized_device(device))
    {
        skip("GPU hangs on paravirtualized MVK.\n");
        destroy_test_context(&context);
        return;
    }

    root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV;
    root_parameters[0].Descriptor.ShaderRegister = 1;
    root_parameters[0].Descriptor.RegisterSpace = 0;
    root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
    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 = 1;
    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(device, &root_signature_desc, &context.root_signature);
    ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr);

    compute_pipeline_state = create_compute_pipeline_state(device, context.root_signature,
            shader_bytecode(cs_code, sizeof(cs_code)));
    context.pipeline_state = create_pipeline_state(context.device,
            context.root_signature, context.render_target_desc.Format, NULL, &ps, NULL);

    compute_queue = create_command_queue(device, D3D12_COMMAND_LIST_TYPE_COMPUTE, D3D12_COMMAND_QUEUE_PRIORITY_NORMAL);
    hr = ID3D12Device_CreateFence(device, 0, D3D12_FENCE_FLAG_NONE, &IID_ID3D12Fence, (void **)&fence);
    ok(hr == S_OK, "Failed to create fence, hr %#x.\n", hr);
    hr = ID3D12Device_CreateFence(device, 0, D3D12_FENCE_FLAG_NONE, &IID_ID3D12Fence, (void **)&fence2);
    ok(hr == S_OK, "Failed to create fence, hr %#x.\n", hr);

    buffer = create_default_buffer(device, 1024,
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

    hr = ID3D12Device_CreateCommandAllocator(device, D3D12_COMMAND_LIST_TYPE_DIRECT,
            &IID_ID3D12CommandAllocator, (void **)&allocators[0]);
    ok(hr == S_OK, "Failed to create command allocator, hr %#x.\n", hr);
    hr = ID3D12Device_CreateCommandList(device, 0, D3D12_COMMAND_LIST_TYPE_DIRECT,
            allocators[0], NULL, &IID_ID3D12GraphicsCommandList, (void **)&graphics_lists[0]);
    ok(hr == S_OK, "Failed to create command list, hr %#x.\n", hr);

    graphics_lists[1] = context.list;
    ID3D12GraphicsCommandList_AddRef(graphics_lists[1]);

    for (i = 0; i < ARRAY_SIZE(compute_lists); ++i)
    {
        hr = ID3D12Device_CreateCommandAllocator(device, D3D12_COMMAND_LIST_TYPE_COMPUTE,
                &IID_ID3D12CommandAllocator, (void **)&allocators[i + 1]);
        ok(hr == S_OK, "Failed to create command allocator, hr %#x.\n", hr);
        hr = ID3D12Device_CreateCommandList(device, 0, D3D12_COMMAND_LIST_TYPE_COMPUTE,
                allocators[i + 1], NULL, &IID_ID3D12GraphicsCommandList, (void **)&compute_lists[i]);
        ok(hr == S_OK, "Failed to create command list, hr %#x.\n", hr);
    }

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

    memset(&uav_desc, 0, sizeof(uav_desc));
    uav_desc.Format = DXGI_FORMAT_R32_UINT;
    uav_desc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
    uav_desc.Buffer.NumElements = 1024 / sizeof(uint32_t);
    ID3D12Device_CreateUnorderedAccessView(device, buffer, NULL, &uav_desc,
            get_cpu_descriptor_handle(&context, cpu_heap, 0));
    ID3D12Device_CreateUnorderedAccessView(device, buffer, NULL, &uav_desc,
            get_cpu_descriptor_handle(&context, gpu_heap, 0));

    ID3D12GraphicsCommandList_ClearUnorderedAccessViewUint(compute_lists[0],
            get_gpu_descriptor_handle(&context, gpu_heap, 0),
            get_cpu_descriptor_handle(&context, cpu_heap, 0),
            buffer, clear_value, 0, NULL);
    uav_barrier(compute_lists[0], buffer);

    value = 0;
    for (i = 0; i < ARRAY_SIZE(compute_lists); ++i)
    {
        command_list = compute_lists[i];

        ID3D12GraphicsCommandList_SetComputeRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetPipelineState(command_list, compute_pipeline_state);
        ID3D12GraphicsCommandList_SetComputeRootUnorderedAccessView(command_list,
                0, ID3D12Resource_GetGPUVirtualAddress(buffer));
        ID3D12GraphicsCommandList_SetComputeRoot32BitConstants(command_list, 1, 1, &value, 0);
        ID3D12GraphicsCommandList_Dispatch(command_list, 1, 1, 1);
        hr = ID3D12GraphicsCommandList_Close(command_list);
        ok(hr == S_OK, "Failed to close command list, hr %#x.\n", hr);

        value += 10;

        command_list = graphics_lists[i];

        ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
        ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
        ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
        ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
        ID3D12GraphicsCommandList_SetGraphicsRootUnorderedAccessView(command_list,
                0, ID3D12Resource_GetGPUVirtualAddress(buffer));
        ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 1, 1, &value, 0);
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);
        hr = ID3D12GraphicsCommandList_Close(command_list);
        ok(hr == S_OK, "Failed to close command list, hr %#x.\n", hr);

        value += 2;
    }

    exec_command_list(compute_queue, compute_lists[0]);
    queue_signal(compute_queue, fence, 1);

    queue_wait(queue, fence, 1);
    exec_command_list(queue, graphics_lists[0]);
    queue_signal(queue, fence2, 1);

    queue_wait(compute_queue, fence2, 1);
    exec_command_list(compute_queue, compute_lists[1]);
    queue_signal(compute_queue, fence, 2);

    queue_wait(queue, fence, 2);
    exec_command_list(queue, graphics_lists[1]);

    hr = wait_for_fence(fence2, 1);
    ok(hr == S_OK, "Failed to wait for fence, hr %#x.\n", hr);
    reset_command_list(graphics_lists[0], allocators[0]);
    command_list = graphics_lists[0];

    transition_resource_state(command_list, buffer,
            D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_buffer_readback_with_command_list(buffer, DXGI_FORMAT_R32_UINT, &rb, queue, command_list);
    value = get_readback_uint(&rb.rb, 0, 0, 0);
    bug_if(is_mvk_device(device))
    ok(value == 24, "Got unexpected value %u.\n", value);
    release_resource_readback(&rb);

    ID3D12DescriptorHeap_Release(cpu_heap);
    ID3D12DescriptorHeap_Release(gpu_heap);
    for (i = 0; i < ARRAY_SIZE(graphics_lists); ++i)
        ID3D12GraphicsCommandList_Release(graphics_lists[i]);
    for (i = 0; i < ARRAY_SIZE(compute_lists); ++i)
        ID3D12GraphicsCommandList_Release(compute_lists[i]);
    for (i = 0; i < ARRAY_SIZE(allocators); ++i)
        ID3D12CommandAllocator_Release(allocators[i]);
    ID3D12Fence_Release(fence);
    ID3D12Fence_Release(fence2);
    ID3D12Resource_Release(buffer);
    ID3D12CommandQueue_Release(compute_queue);
    ID3D12PipelineState_Release(compute_pipeline_state);
    destroy_test_context(&context);
}

static void test_early_depth_stencil_tests(void)
{
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
    ID3D12DescriptorHeap *cpu_heap, *gpu_heap;
    ID3D12GraphicsCommandList *command_list;
    D3D12_DESCRIPTOR_RANGE descriptor_range;
    D3D12_ROOT_PARAMETER root_parameter;
    struct depth_stencil_resource ds;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Resource *texture;
    HRESULT hr;

    static const DWORD ps_code[] =
    {
#if 0
        RWTexture2D<int> u;

        [earlydepthstencil]
        void main()
        {
            InterlockedAdd(u[uint2(0, 0)], 1);
        }
#endif
        0x43425844, 0xd8c9f845, 0xadb9dbe2, 0x4e8aea86, 0x80f0b053, 0x00000001, 0x0000009c, 0x00000003,
        0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x00000048, 0x00000050, 0x00000012, 0x0100286a,
        0x0400189c, 0x0011e000, 0x00000000, 0x00003333, 0x0a0000ad, 0x0011e000, 0x00000000, 0x00004002,
        0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00004001, 0x00000001, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};
    static const UINT values[4] = {0};

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

    descriptor_range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
    descriptor_range.NumDescriptors = 1;
    descriptor_range.BaseShaderRegister = 0;
    descriptor_range.RegisterSpace = 0;
    descriptor_range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
    root_parameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    root_parameter.DescriptorTable.NumDescriptorRanges = 1;
    root_parameter.DescriptorTable.pDescriptorRanges = &descriptor_range;
    root_parameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
    root_signature_desc.NumParameters = 1;
    root_signature_desc.pParameters = &root_parameter;
    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, &context.root_signature);
    ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr);

    init_pipeline_state_desc(&pso_desc, context.root_signature, 0, NULL, &ps, NULL);
    pso_desc.NumRenderTargets = 0;
    pso_desc.DSVFormat = DXGI_FORMAT_D32_FLOAT;
    pso_desc.DepthStencilState.DepthEnable = true;
    pso_desc.DepthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ZERO;
    pso_desc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL;
    hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
    ok(hr == S_OK, "Failed to create graphics pipeline state, hr %#x.\n", hr);

    init_depth_stencil(&ds, context.device, 1, 1, 1, 1, DXGI_FORMAT_D32_FLOAT, 0, NULL);
    set_rect(&context.scissor_rect, 0, 0, 1, 1);

    texture = create_default_texture(context.device, 1, 1, DXGI_FORMAT_R32_SINT,
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
    cpu_heap = create_cpu_descriptor_heap(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1);
    gpu_heap = create_gpu_descriptor_heap(context.device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1);
    ID3D12Device_CreateUnorderedAccessView(context.device, texture, NULL, NULL,
            get_cpu_descriptor_handle(&context, cpu_heap, 0));
    ID3D12Device_CreateUnorderedAccessView(context.device, texture, NULL, NULL,
            get_cpu_descriptor_handle(&context, gpu_heap, 0));

    set_viewport(&context.viewport, 0.0f, 0.0f, 1.0f, 100.0f, 0.5f, 0.5f);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 0, NULL, false, &ds.dsv_handle);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &gpu_heap);
    ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0,
            get_gpu_descriptor_handle(&context, gpu_heap, 0));

    ID3D12GraphicsCommandList_ClearUnorderedAccessViewUint(command_list,
            get_gpu_descriptor_handle(&context, gpu_heap, 0),
            get_cpu_descriptor_handle(&context, cpu_heap, 0), texture, values, 0, NULL);

    ID3D12GraphicsCommandList_ClearDepthStencilView(command_list, ds.dsv_handle,
            D3D12_CLEAR_FLAG_DEPTH, 0.6f, 0, 0, NULL);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, ds.texture,
            D3D12_RESOURCE_STATE_DEPTH_WRITE, D3D12_RESOURCE_STATE_COPY_SOURCE);
    transition_resource_state(command_list, texture,
            D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_float(ds.texture, 0, queue, command_list, 0.6f, 1);
    reset_command_list(command_list, context.allocator);
    bug_if(is_mvk_device(context.device))
    check_sub_resource_uint(texture, 0, queue, command_list, 2, 1);

    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, ds.texture,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_DEPTH_WRITE);
    transition_resource_state(command_list, texture,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 0, NULL, false, &ds.dsv_handle);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &gpu_heap);
    ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0,
            get_gpu_descriptor_handle(&context, gpu_heap, 0));

    ID3D12GraphicsCommandList_ClearDepthStencilView(command_list, ds.dsv_handle,
            D3D12_CLEAR_FLAG_DEPTH, 0.3f, 0, 0, NULL);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    ID3D12GraphicsCommandList_ClearDepthStencilView(command_list, ds.dsv_handle,
            D3D12_CLEAR_FLAG_DEPTH, 0.55f, 0, 0, NULL);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    ID3D12GraphicsCommandList_ClearDepthStencilView(command_list, ds.dsv_handle,
            D3D12_CLEAR_FLAG_DEPTH, 0.5f, 0, 0, NULL);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, ds.texture,
            D3D12_RESOURCE_STATE_DEPTH_WRITE, D3D12_RESOURCE_STATE_COPY_SOURCE);
    transition_resource_state(command_list, texture,
            D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_float(ds.texture, 0, queue, command_list, 0.5f, 1);
    reset_command_list(command_list, context.allocator);
    bug_if(is_mvk_device(context.device))
    check_sub_resource_uint(texture, 0, queue, command_list, 4, 1);

    ID3D12Resource_Release(texture);
    ID3D12DescriptorHeap_Release(cpu_heap);
    ID3D12DescriptorHeap_Release(gpu_heap);
    destroy_depth_stencil(&ds);
    destroy_test_context(&context);
}

static void prepare_instanced_draw(struct test_context *context)
{
    ID3D12GraphicsCommandList *command_list = context->list;

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context->rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context->root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context->pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context->viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context->scissor_rect);
}

static void test_conditional_rendering(void)
{
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    ID3D12Resource *conditions, *upload_buffer;
    ID3D12CommandSignature *command_signature;
    ID3D12GraphicsCommandList *command_list;
    D3D12_ROOT_PARAMETER root_parameters[2];
    ID3D12Resource *texture, *texture_copy;
    D3D12_RENDER_TARGET_VIEW_DESC rtv_desc;
    D3D12_HEAP_PROPERTIES heap_properties;
    ID3D12PipelineState *pipeline_state;
    ID3D12RootSignature *root_signature;
    struct d3d12_resource_readback rb;
    D3D12_RESOURCE_DESC resource_desc;
    struct test_context context;
    ID3D12Resource *buffer, *cb;
    ID3D12CommandQueue *queue;
    unsigned int i;
    uint32_t value;
    HRESULT hr;

    static const uint64_t predicate_args[] = {0, 1, (uint64_t)1 << 32};
    static const uint32_t r8g8b8a8_data[] = {0x28384858, 0x39495969};
    static const D3D12_DRAW_ARGUMENTS draw_args = {3, 1, 0, 0};
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    static const float green[] = {0.0f, 1.0f, 0.0f, 1.0f};
    static const float ms_color[] = {0.345f, 0.282f, 0.219f, 0.156f};
    static const uint32_t init_value = 0xdeadbeef;
    static const D3D12_SUBRESOURCE_DATA copy_data[] =
    {
        {&r8g8b8a8_data[0], sizeof(r8g8b8a8_data[0]), sizeof(r8g8b8a8_data[0])},
        {&r8g8b8a8_data[1], sizeof(r8g8b8a8_data[1]), sizeof(r8g8b8a8_data[1])}
    };
    static const DWORD cs_code[] =
    {
#if 0
        cbuffer cb
        {
            unsigned int offset;
            unsigned int value;
        };

        RWByteAddressBuffer b;

        [numthreads(1, 1, 1)]
        void main()
        {
            b.Store(4 * offset, value);
        }
#endif
        0x43425844, 0xaadc5460, 0x88c27e90, 0x2acacf4e, 0x4e06019a, 0x00000001, 0x000000d8, 0x00000003,
        0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x00000084, 0x00050050, 0x00000021, 0x0100086a,
        0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x0300009d, 0x0011e000, 0x00000000, 0x02000068,
        0x00000001, 0x0400009b, 0x00000001, 0x00000001, 0x00000001, 0x08000029, 0x00100012, 0x00000000,
        0x0020800a, 0x00000000, 0x00000000, 0x00004001, 0x00000002, 0x080000a6, 0x0011e012, 0x00000000,
        0x0010000a, 0x00000000, 0x0020801a, 0x00000000, 0x00000000, 0x0100003e,
    };
    static const struct
    {
        uint32_t offset;
        uint32_t value;
        uint32_t uav_offset;
    }
    input = {0, 4, 0};

    if (!init_test_context(&context, NULL))
        return;
    command_list = context.list;
    queue = context.queue;

    if (is_intel_windows_device(context.device))
    {
        skip("Predicated rendering is broken on Intel.\n");
        destroy_test_context(&context);
        return;
    }

    conditions = create_default_buffer(context.device, sizeof(predicate_args),
            D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST);
    upload_buffer_data(conditions, 0, sizeof(predicate_args), &predicate_args, queue, command_list);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, conditions,
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PREDICATION);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

    /* Skip draw on zero. */
    prepare_instanced_draw(&context);
    ID3D12GraphicsCommandList_SetPredication(command_list, conditions, 0, D3D12_PREDICATION_OP_EQUAL_ZERO);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);
    ID3D12GraphicsCommandList_SetPredication(command_list, NULL, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);

    bug_if(is_mvk_device(context.device))
    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xffffffff, 0);

    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    /* Skip draw on non-zero. */
    prepare_instanced_draw(&context);
    ID3D12GraphicsCommandList_SetPredication(command_list, conditions,
            sizeof(uint64_t), D3D12_PREDICATION_OP_NOT_EQUAL_ZERO);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);
    /* Don't reset predication to test automatic reset on next SetPredication() call. */

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);

    ID3D12GraphicsCommandList_SetPredication(command_list, conditions,
            sizeof(uint64_t), D3D12_PREDICATION_OP_EQUAL_ZERO);
    bug_if(is_mvk_device(context.device))
    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xffffffff, 0);

    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    /* Skip clear on zero. */
    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
    ID3D12GraphicsCommandList_SetPredication(command_list, conditions, 0, D3D12_PREDICATION_OP_EQUAL_ZERO);
    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, green, 0, NULL);
    ID3D12GraphicsCommandList_SetPredication(command_list, NULL, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);

    get_resource_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
    bug_if(is_radv_device(context.device))
    todo check_readback_data_uint(&rb.rb, NULL, 0xffffffff, 0);
    release_resource_readback(&rb);

    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    /* Draw on zero. */
    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
    prepare_instanced_draw(&context);
    ID3D12GraphicsCommandList_SetPredication(command_list, conditions, 0, D3D12_PREDICATION_OP_NOT_EQUAL_ZERO);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);
    ID3D12GraphicsCommandList_SetPredication(command_list, NULL, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);

    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);

    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    /* Draw on non-zero. */
    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
    prepare_instanced_draw(&context);
    ID3D12GraphicsCommandList_SetPredication(command_list, conditions,
            sizeof(uint64_t), D3D12_PREDICATION_OP_EQUAL_ZERO);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);
    ID3D12GraphicsCommandList_SetPredication(command_list, NULL, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);

    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);

    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    /* 64-bit conditional 0x100000000 - fails due to Vulkan 32-bit values. */
    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
    prepare_instanced_draw(&context);
    ID3D12GraphicsCommandList_SetPredication(command_list, conditions,
            2 * sizeof(uint64_t), D3D12_PREDICATION_OP_NOT_EQUAL_ZERO);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);
    ID3D12GraphicsCommandList_SetPredication(command_list, NULL, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);

    get_resource_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
    todo check_readback_data_uint(&rb.rb, NULL, 0xffffffff, 0);
    release_resource_readback(&rb);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    /* Direct3D latches the value of the predicate upon beginning predicated rendering. However
     * Vulkan doesn't mandate any behavior, so some drivers behave differently. */
    buffer = create_default_buffer(context.device, sizeof(predicate_args),
            D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST);
    transition_resource_state(command_list, conditions,
            D3D12_RESOURCE_STATE_PREDICATION, D3D12_RESOURCE_STATE_COPY_SOURCE);
    ID3D12GraphicsCommandList_CopyResource(command_list, buffer, conditions);
    transition_resource_state(command_list,
            buffer, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PREDICATION);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
    prepare_instanced_draw(&context);
    ID3D12GraphicsCommandList_SetPredication(command_list, buffer, 0, D3D12_PREDICATION_OP_NOT_EQUAL_ZERO);

    transition_resource_state(command_list, buffer,
            D3D12_RESOURCE_STATE_PREDICATION, D3D12_RESOURCE_STATE_COPY_DEST);
    ID3D12GraphicsCommandList_CopyBufferRegion(command_list, buffer, 0, conditions, sizeof(uint64_t), sizeof(uint64_t));
    transition_resource_state(command_list,
            buffer, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PREDICATION);
    transition_resource_state(command_list,
            conditions, D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_PREDICATION);

    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);
    ID3D12GraphicsCommandList_SetPredication(command_list, NULL, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    todo_if(is_llvmpipe_device(context.device))
    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);

    ID3D12Resource_Release(buffer);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    /* SetPredication() and upload buffer. */
    upload_buffer = create_upload_buffer(context.device, sizeof(predicate_args), predicate_args);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
    prepare_instanced_draw(&context);
    /* Skip. */
    ID3D12GraphicsCommandList_SetPredication(command_list, upload_buffer,
            0, D3D12_PREDICATION_OP_EQUAL_ZERO);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    ID3D12GraphicsCommandList_SetPredication(command_list, upload_buffer,
            0, D3D12_PREDICATION_OP_NOT_EQUAL_ZERO);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    bug_if(is_mvk_device(context.device))
    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xffffffff, 0);

    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    /* ExecuteIndirect(). */
    buffer = create_upload_buffer(context.device, sizeof(draw_args), &draw_args);

    command_signature = create_command_signature(context.device, D3D12_INDIRECT_ARGUMENT_TYPE_DRAW);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
    prepare_instanced_draw(&context);
    /* Skip. */
    ID3D12GraphicsCommandList_SetPredication(command_list, conditions, 0, D3D12_PREDICATION_OP_EQUAL_ZERO);
    ID3D12GraphicsCommandList_ExecuteIndirect(command_list, command_signature, 1, buffer, 0, NULL, 0);
    ID3D12GraphicsCommandList_SetPredication(command_list, NULL, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);

    bug_if(is_mvk_device(context.device))
    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xffffffff, 0);

    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    prepare_instanced_draw(&context);
    /* Draw. */
    ID3D12GraphicsCommandList_SetPredication(command_list, conditions, 0, D3D12_PREDICATION_OP_NOT_EQUAL_ZERO);
    ID3D12GraphicsCommandList_ExecuteIndirect(command_list, command_signature, 1, buffer, 0, NULL, 0);
    ID3D12GraphicsCommandList_SetPredication(command_list, NULL, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);

    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);

    ID3D12Resource_Release(buffer);
    ID3D12CommandSignature_Release(command_signature);
    reset_command_list(command_list, context.allocator);

    /* CopyResource(). */
    texture = create_default_texture(context.device,
            1, 1, DXGI_FORMAT_R8G8B8A8_UNORM, 0, D3D12_RESOURCE_STATE_COPY_DEST);
    upload_texture_data(texture, &copy_data[0], 1, queue, command_list);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, texture,
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COPY_SOURCE);

    texture_copy = create_default_texture(context.device,
                1, 1, DXGI_FORMAT_R8G8B8A8_UNORM, 0, D3D12_RESOURCE_STATE_COPY_DEST);
    upload_texture_data(texture_copy, &copy_data[1], 1, queue, command_list);
    reset_command_list(command_list, context.allocator);

    /* Skip. */
    ID3D12GraphicsCommandList_SetPredication(command_list, conditions, 0, D3D12_PREDICATION_OP_EQUAL_ZERO);
    ID3D12GraphicsCommandList_CopyResource(command_list, texture_copy, texture);
    ID3D12GraphicsCommandList_SetPredication(command_list, NULL, 0, 0);

    transition_resource_state(command_list, texture_copy,
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COPY_SOURCE);

    get_resource_readback_with_command_list(texture_copy, 0, &rb, queue, command_list);
    todo check_readback_data_uint(&rb.rb, NULL, r8g8b8a8_data[1], 0);
    release_resource_readback(&rb);

    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, texture_copy,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_COPY_DEST);

    /* Copy. */
    ID3D12GraphicsCommandList_SetPredication(command_list, conditions, 0, D3D12_PREDICATION_OP_NOT_EQUAL_ZERO);
    ID3D12GraphicsCommandList_CopyResource(command_list, texture_copy, texture);
    ID3D12GraphicsCommandList_SetPredication(command_list, NULL, 0, 0);

    transition_resource_state(command_list, texture_copy,
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COPY_SOURCE);

    check_sub_resource_uint(texture_copy, 0, queue, command_list, r8g8b8a8_data[0], 0);

    /* Multisample texture. */
    ID3D12Resource_Release(texture);
    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_TEXTURE2D;
    resource_desc.Width = 1;
    resource_desc.Height = 1;
    resource_desc.DepthOrArraySize = 1;
    resource_desc.MipLevels = 1;
    resource_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    resource_desc.SampleDesc.Count = 4;
    resource_desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
    hr = ID3D12Device_CreateCommittedResource(context.device, &heap_properties, D3D12_HEAP_FLAG_NONE,
            &resource_desc, D3D12_RESOURCE_STATE_RENDER_TARGET, NULL, &IID_ID3D12Resource, (void **)&texture);
    ok(hr == S_OK, "Failed to create texture, hr %#x.\n", hr);

    memset(&rtv_desc, 0, sizeof(rtv_desc));
    rtv_desc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DMS;
    rtv_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    ID3D12Device_CreateRenderTargetView(context.device, texture, &rtv_desc,
            get_cpu_rtv_handle(&context, context.rtv_heap, 0));

    reset_command_list(command_list, context.allocator);
    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, ms_color, 0, NULL);

    /* ResolveSubresource(). */
    transition_resource_state(command_list, texture_copy,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_COPY_DEST);
    upload_texture_data(texture_copy, &copy_data[1], 1, queue, command_list);

    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, texture_copy,
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_RESOLVE_DEST);
    transition_resource_state(command_list, texture,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_RESOLVE_SOURCE);

    /* Skip. */
    ID3D12GraphicsCommandList_SetPredication(command_list, conditions, 0, D3D12_PREDICATION_OP_EQUAL_ZERO);
    ID3D12GraphicsCommandList_ResolveSubresource(command_list,
            texture_copy, 0, texture, 0, DXGI_FORMAT_R8G8B8A8_UNORM);
    ID3D12GraphicsCommandList_SetPredication(command_list, NULL, 0, 0);

    transition_resource_state(command_list, texture_copy,
            D3D12_RESOURCE_STATE_RESOLVE_DEST, D3D12_RESOURCE_STATE_COPY_SOURCE);

    get_resource_readback_with_command_list(texture_copy, 0, &rb, queue, command_list);
    bug_if(is_radv_device(context.device) || is_llvmpipe_device(context.device))
    todo check_readback_data_uint(&rb.rb, NULL, r8g8b8a8_data[1], 0);
    release_resource_readback(&rb);

    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, texture_copy,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RESOLVE_DEST);

    /* Resolve. */
    ID3D12GraphicsCommandList_SetPredication(command_list, conditions, 0, D3D12_PREDICATION_OP_NOT_EQUAL_ZERO);
    ID3D12GraphicsCommandList_ResolveSubresource(command_list,
            texture_copy, 0, texture, 0, DXGI_FORMAT_R8G8B8A8_UNORM);
    ID3D12GraphicsCommandList_SetPredication(command_list, NULL, 0, 0);

    transition_resource_state(command_list, texture_copy,
            D3D12_RESOURCE_STATE_RESOLVE_DEST, D3D12_RESOURCE_STATE_COPY_SOURCE);

    check_sub_resource_uint(texture_copy, 0, queue, command_list, r8g8b8a8_data[0], 2);

    reset_command_list(command_list, context.allocator);

    /* Dispatch(). */
    cb = create_upload_buffer(context.device, sizeof(input), &input);

    buffer = create_default_buffer(context.device, 512,
            D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_DEST);
    upload_buffer_data(buffer, 0, sizeof(init_value), &init_value, queue, command_list);
    reset_command_list(command_list, context.allocator);
    transition_sub_resource_state(command_list, buffer, 0,
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

    root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
    root_parameters[0].Descriptor.ShaderRegister = 0;
    root_parameters[0].Descriptor.RegisterSpace = 0;
    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;
    memset(&root_signature_desc, 0, sizeof(root_signature_desc));
    root_signature_desc.NumParameters = 2;
    root_signature_desc.pParameters = root_parameters;
    hr = create_root_signature(context.device, &root_signature_desc, &root_signature);
    ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr);

    pipeline_state = create_compute_pipeline_state(context.device, root_signature,
            shader_bytecode(cs_code, sizeof(cs_code)));

    for (i = 0; i < 2; ++i)
    {
        if (i == 0 && is_nvidia_device(context.device))
        {
            /* I can reproduce that on an NVIDIA Quadro P2000, driver version 535.161.08. */
            skip("Triggers a GPU freeze on NVIDIA.\n");
            continue;
        }

        ID3D12GraphicsCommandList_SetPipelineState(command_list, pipeline_state);
        ID3D12GraphicsCommandList_SetComputeRootSignature(command_list, root_signature);
        ID3D12GraphicsCommandList_SetComputeRootConstantBufferView(command_list,
                0, ID3D12Resource_GetGPUVirtualAddress(cb));
        ID3D12GraphicsCommandList_SetComputeRootUnorderedAccessView(command_list,
                1, ID3D12Resource_GetGPUVirtualAddress(buffer));
        ID3D12GraphicsCommandList_SetPredication(command_list, conditions, i * sizeof(uint64_t),
                D3D12_PREDICATION_OP_EQUAL_ZERO);
        ID3D12GraphicsCommandList_Dispatch(command_list, 1, 1, 1);
        ID3D12GraphicsCommandList_SetPredication(command_list, NULL, 0, 0);

        transition_sub_resource_state(command_list, buffer, 0,
                D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);

        get_buffer_readback_with_command_list(buffer, DXGI_FORMAT_R32_UINT, &rb, queue, command_list);
        value = get_readback_uint(&rb.rb, 0, 0, 0);
        bug_if(is_mvk_device(context.device))
        ok(value == (!i ? init_value : input.value), "Got %#x, expected %#x.\n", value, !i ? init_value : input.value);
        release_resource_readback(&rb);
        reset_command_list(command_list, context.allocator);

        transition_sub_resource_state(command_list, buffer, 0,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
    }

    ID3D12Resource_Release(texture);
    ID3D12Resource_Release(texture_copy);
    ID3D12Resource_Release(conditions);
    ID3D12Resource_Release(cb);
    ID3D12Resource_Release(buffer);
    ID3D12Resource_Release(upload_buffer);
    ID3D12RootSignature_Release(root_signature);
    ID3D12PipelineState_Release(pipeline_state);
    destroy_test_context(&context);
}
static void test_bufinfo_instruction(void)
{
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
    const D3D12_SHADER_BYTECODE *current_shader;
    D3D12_UNORDERED_ACCESS_VIEW_DESC uav_desc;
    D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc;
    ID3D12GraphicsCommandList *command_list;
    D3D12_DESCRIPTOR_RANGE descriptor_range;
    D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle;
    D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle;
    D3D12_INPUT_LAYOUT_DESC input_layout;
    D3D12_ROOT_PARAMETER root_parameter;
    struct test_context_desc desc;
    D3D12_VERTEX_BUFFER_VIEW vbv;
    struct test_context context;
    ID3D12Resource *vb, *buffer;
    ID3D12DescriptorHeap *heap;
    ID3D12CommandQueue *queue;
    ID3D12Device *device;
    HRESULT hr;
    int i;
    static const D3D12_INPUT_ELEMENT_DESC layout_desc[] =
    {
        {"POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
    };
    static const DWORD vs_code[] =
    {
#if 0
        void main(float4 in_position : POSITION, out float4 out_position : SV_POSITION)
        {
            out_position = in_position;
        }
#endif
        0x43425844, 0xa7a2f22d, 0x83ff2560, 0xe61638bd, 0x87e3ce90, 0x00000001, 0x000000d8, 0x00000003,
        0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000f0f, 0x49534f50, 0x4e4f4954, 0xababab00,
        0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000001, 0x00000003,
        0x00000000, 0x0000000f, 0x505f5653, 0x5449534f, 0x004e4f49, 0x52444853, 0x0000003c, 0x00010040,
        0x0000000f, 0x0300005f, 0x001010f2, 0x00000000, 0x04000067, 0x001020f2, 0x00000000, 0x00000001,
        0x05000036, 0x001020f2, 0x00000000, 0x00101e46, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE vs = {vs_code, sizeof(vs_code)};
    static const DWORD ps_srv_raw_code[] =
    {
#if 0
        ByteAddressBuffer b;

        uint4 main(void) : SV_Target
        {
            uint width;
            b.GetDimensions(width);
            return width;
        }
#endif
        0x43425844, 0x934bc27a, 0x3251cc9d, 0xa129bdd3, 0xf7cedcc4, 0x00000001, 0x000000d8, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000060, 0x00000050, 0x00000018,
        0x0100086a, 0x030000a1, 0x00107000, 0x00000000, 0x03000065, 0x001020f2, 0x00000000, 0x02000068,
        0x00000001, 0x87000079, 0x800002c2, 0x00199983, 0x00100012, 0x00000000, 0x00107e46, 0x00000000,
        0x05000036, 0x001020f2, 0x00000000, 0x00100006, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_srv_raw = {ps_srv_raw_code, sizeof(ps_srv_raw_code)};
    static const DWORD ps_srv_structured_code[] =
    {
#if 0
        StructuredBuffer<bool> b;

        uint4 main(void) : SV_Target
        {
            uint count, stride;
            b.GetDimensions(count, stride);
            return uint4(count, stride, 0, 1);
        }
#endif
        0x43425844, 0x313f910c, 0x2f60c646, 0x2d87455c, 0xb9988c2c, 0x00000001, 0x000000fc, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000084, 0x00000050, 0x00000021,
        0x0100086a, 0x040000a2, 0x00107000, 0x00000000, 0x00000004, 0x03000065, 0x001020f2, 0x00000000,
        0x02000068, 0x00000001, 0x87000079, 0x80002302, 0x00199983, 0x00100012, 0x00000000, 0x00107e46,
        0x00000000, 0x05000036, 0x00102012, 0x00000000, 0x0010000a, 0x00000000, 0x08000036, 0x001020e2,
        0x00000000, 0x00004002, 0x00000000, 0x00000004, 0x00000000, 0x00000001, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_srv_structured = {ps_srv_structured_code, sizeof(ps_srv_structured_code)};
    static const DWORD ps_srv_typed_code[] =
    {
#if 0
        Buffer<float> b;

        uint4 main(void) : SV_Target
        {
            uint width;
            b.GetDimensions(width);
            return width;
        }
#endif
        0x43425844, 0x6ae6dbb0, 0x6289d227, 0xaf4e708e, 0x111efed1, 0x00000001, 0x000000dc, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000064, 0x00000050, 0x00000019,
        0x0100086a, 0x04000858, 0x00107000, 0x00000000, 0x00005555, 0x03000065, 0x001020f2, 0x00000000,
        0x02000068, 0x00000001, 0x87000079, 0x80000042, 0x00155543, 0x00100012, 0x00000000, 0x00107e46,
        0x00000000, 0x05000036, 0x001020f2, 0x00000000, 0x00100006, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_srv_typed = {ps_srv_typed_code, sizeof(ps_srv_typed_code)};
    static const DWORD ps_uav_raw_code[] =
    {
#if 0
        RWByteAddressBuffer b;

        uint4 main(void) : SV_Target
        {
            uint width;
            b.GetDimensions(width);
            return width;
        }
#endif
        0x43425844, 0xb06e9715, 0x99733b00, 0xaa536550, 0x703a01c5, 0x00000001, 0x000000d8, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000060, 0x00000050, 0x00000018,
        0x0100086a, 0x0300009d, 0x0011e000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000, 0x02000068,
        0x00000001, 0x87000079, 0x800002c2, 0x00199983, 0x00100012, 0x00000000, 0x0011ee46, 0x00000001,
        0x05000036, 0x001020f2, 0x00000000, 0x00100006, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_uav_raw = {ps_uav_raw_code, sizeof(ps_uav_raw_code)};
    static const DWORD ps_uav_structured_code[] =
    {
#if 0
        struct s
        {
            uint4 u;
            bool b;
        };

        RWStructuredBuffer<s> b;

        uint4 main(void) : SV_Target
        {
            uint count, stride;
            b.GetDimensions(count, stride);
            return uint4(count, stride, 0, 1);
        }
#endif
        0x43425844, 0xe1900f85, 0x13c1f338, 0xbb19865e, 0x366df28f, 0x00000001, 0x000000fc, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000084, 0x00000050, 0x00000021,
        0x0100086a, 0x0400009e, 0x0011e000, 0x00000001, 0x00000014, 0x03000065, 0x001020f2, 0x00000000,
        0x02000068, 0x00000001, 0x87000079, 0x8000a302, 0x00199983, 0x00100012, 0x00000000, 0x0011ee46,
        0x00000001, 0x05000036, 0x00102012, 0x00000000, 0x0010000a, 0x00000000, 0x08000036, 0x001020e2,
        0x00000000, 0x00004002, 0x00000000, 0x00000014, 0x00000000, 0x00000001, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_uav_structured = {ps_uav_structured_code, sizeof(ps_uav_structured_code)};
    static const DWORD ps_uav_structured32_code[] =
    {
#if 0
        struct s
        {
            uint4 u;
            bool4 b;
        };

        RWStructuredBuffer<s> b;

        uint4 main(void) : SV_Target
        {
            uint count, stride;
            b.GetDimensions(count, stride);
            return uint4(count, stride, 0, 1);
        }
#endif
        0x43425844, 0xdd87a805, 0x28090470, 0xe4fa7c4d, 0x57963f52, 0x00000001, 0x000000fc, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000084, 0x00000050, 0x00000021,
        0x0100086a, 0x0400009e, 0x0011e000, 0x00000001, 0x00000020, 0x03000065, 0x001020f2, 0x00000000,
        0x02000068, 0x00000001, 0x87000079, 0x80010302, 0x00199983, 0x00100012, 0x00000000, 0x0011ee46,
        0x00000001, 0x05000036, 0x00102012, 0x00000000, 0x0010000a, 0x00000000, 0x08000036, 0x001020e2,
        0x00000000, 0x00004002, 0x00000000, 0x00000020, 0x00000000, 0x00000001, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_uav_structured32 = {ps_uav_structured32_code, sizeof(ps_uav_structured32_code)};
    static const DWORD ps_uav_typed_code[] =
    {
#if 0
        RWBuffer<float> b;

        uint4 main(void) : SV_Target
        {
            uint width;
            b.GetDimensions(width);
            return width;
        }
#endif
        0x43425844, 0x96b39f5f, 0x5fef24c7, 0xed404a41, 0x01c9d4fe, 0x00000001, 0x000000dc, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000064, 0x00000050, 0x00000019,
        0x0100086a, 0x0400089c, 0x0011e000, 0x00000001, 0x00005555, 0x03000065, 0x001020f2, 0x00000000,
        0x02000068, 0x00000001, 0x87000079, 0x80000042, 0x00155543, 0x00100012, 0x00000000, 0x0011ee46,
        0x00000001, 0x05000036, 0x001020f2, 0x00000000, 0x00100006, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps_uav_typed = {ps_uav_typed_code, sizeof(ps_uav_typed_code)};
    static const struct test
    {
        const D3D12_SHADER_BYTECODE *ps;
        BOOL uav;
        BOOL raw;
        unsigned int buffer_size;
        unsigned int buffer_structure_byte_stride;
        DXGI_FORMAT view_format;
        unsigned int view_element_idx;
        unsigned int view_element_count;
        struct uvec4 expected_result;
    }
    tests[] =
    {
        {&ps_srv_raw,          false, true,  100,  0, DXGI_FORMAT_R32_TYPELESS,  0, 25, {100, 100, 100, 100}},
        {&ps_srv_raw,          false, true,  500,  0, DXGI_FORMAT_R32_TYPELESS, 64,  4, { 16,  16,  16,  16}},
        {&ps_srv_structured,   false, false, 100,  4, DXGI_FORMAT_UNKNOWN,       0,  5, {  5,   4,   0,   1}},
        {&ps_srv_structured,   false, false, 100,  4, DXGI_FORMAT_UNKNOWN,       0,  2, {  2,   4,   0,   1}},
        {&ps_srv_structured,   false, false, 400,  4, DXGI_FORMAT_UNKNOWN,      64,  2, {  2,   4,   0,   1}},
        {&ps_srv_typed,        false, false, 200,  0, DXGI_FORMAT_R32_FLOAT,     0, 50, { 50,  50,  50,  50}},
        {&ps_srv_typed,        false, false, 400,  0, DXGI_FORMAT_R32_FLOAT,    64,  1, {  1,   1,   1,   1}},
        {&ps_srv_typed,        false, false, 100,  0, DXGI_FORMAT_R16_FLOAT,     0, 50, { 50,  50,  50,  50}},
        {&ps_srv_typed,        false, false, 400,  0, DXGI_FORMAT_R16_FLOAT,   128,  2, {  2,   2,   2,   2}},
        {&ps_uav_raw,          true,  true,  100,  0, DXGI_FORMAT_R32_TYPELESS,  0, 25, {100, 100, 100, 100}},
        {&ps_uav_raw,          true,  true,  512,  0, DXGI_FORMAT_R32_TYPELESS, 64, 64, {256, 256, 256, 256}},
        {&ps_uav_structured,   true,  false, 100, 20, DXGI_FORMAT_UNKNOWN,       0,  5, {  5,  20,   0,   1}},
        {&ps_uav_structured,   true,  false, 100, 20, DXGI_FORMAT_UNKNOWN,       0,  2, {  2,  20,   0,   1}},
        {&ps_uav_structured32, true,  false, 320, 32, DXGI_FORMAT_UNKNOWN,       8,  2, {  2,  32,   0,   1}},
        {&ps_uav_typed,        true,  false, 200,  0, DXGI_FORMAT_R32_FLOAT,     0, 50, { 50,  50,  50,  50}},
        {&ps_uav_typed,        true,  false, 400,  0, DXGI_FORMAT_R32_FLOAT,    64,  1, {  1,   1,   1,   1}},
        {&ps_uav_typed,        true,  false, 100,  0, DXGI_FORMAT_R16_FLOAT,     0, 50, { 50,  50,  50,  50}},
        {&ps_uav_typed,        true,  false, 400,  0, DXGI_FORMAT_R16_FLOAT,   128,  1, {  1,   1,   1,   1}},
    };
    static const struct vec4 quad[] =
    {
        {-1.0f, -1.0f, 0.0f, 1.0f},
        {-1.0f,  1.0f, 0.0f, 1.0f},
        { 1.0f, -1.0f, 0.0f, 1.0f},
        { 1.0f,  1.0f, 0.0f, 1.0f},
    };

    memset(&desc, 0, sizeof(desc));
    desc.rt_width = desc.rt_height = 64;
    desc.rt_format = DXGI_FORMAT_R32G32B32A32_UINT;
    desc.no_root_signature = true;
    desc.no_pipeline = true;
    if (!init_test_context(&context, &desc))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    heap = create_gpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1);
    cpu_handle = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(heap);
    gpu_handle = ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(heap);

    vb = create_upload_buffer(device, sizeof(quad), quad);
    vbv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vb);
    vbv.StrideInBytes = sizeof(*quad);
    vbv.SizeInBytes = sizeof(quad);

    current_shader = NULL;
    for (i = 0; i < ARRAY_SIZE(tests); ++i)
    {
        const struct test *test = &tests[i];

        vkd3d_test_push_context("Test %u", i);

        if (!context.root_signature || test->uav != tests[i - 1].uav)
        {
            if (context.root_signature)
                ID3D12RootSignature_Release(context.root_signature);

            descriptor_range.RangeType = test->uav
                    ? D3D12_DESCRIPTOR_RANGE_TYPE_UAV : D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
            descriptor_range.NumDescriptors = 1;
            descriptor_range.BaseShaderRegister = test->uav ? 1 : 0;
            descriptor_range.RegisterSpace = 0;
            descriptor_range.OffsetInDescriptorsFromTableStart = 0;
            root_parameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
            root_parameter.DescriptorTable.NumDescriptorRanges = 1;
            root_parameter.DescriptorTable.pDescriptorRanges = &descriptor_range;
            root_parameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
            memset(&root_signature_desc, 0, sizeof(root_signature_desc));
            root_signature_desc.NumParameters = 1;
            root_signature_desc.pParameters = &root_parameter;
            root_signature_desc.NumStaticSamplers = 0;
            root_signature_desc.pStaticSamplers = NULL;
            root_signature_desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
            hr = create_root_signature(context.device, &root_signature_desc, &context.root_signature);
            ok(SUCCEEDED(hr), "Failed to create root signature, hr %#x.\n", hr);
        }

        if (current_shader != test->ps)
        {
            if (context.pipeline_state)
                ID3D12PipelineState_Release(context.pipeline_state);

            input_layout.pInputElementDescs = layout_desc;
            input_layout.NumElements = ARRAY_SIZE(layout_desc);
            current_shader = test->ps;
            init_pipeline_state_desc(&pso_desc, context.root_signature,
                    context.render_target_desc.Format, &vs, current_shader, &input_layout);
            hr = ID3D12Device_CreateGraphicsPipelineState(device, &pso_desc,
                    &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
            ok(hr == S_OK, "Failed to create state, hr %#x.\n", hr);
        }

        if (test->uav)
        {
            buffer = create_default_buffer(device, test->buffer_size,
                    D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
            memset(&uav_desc, 0, sizeof(uav_desc));
            uav_desc.Format = test->view_format;
            uav_desc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
            uav_desc.Buffer.Flags = test->raw ? D3D12_BUFFER_UAV_FLAG_RAW : 0;
            uav_desc.Buffer.FirstElement = test->view_element_idx;
            uav_desc.Buffer.NumElements = test->view_element_count;
            uav_desc.Buffer.StructureByteStride = test->buffer_structure_byte_stride;
            ID3D12Device_CreateUnorderedAccessView(device, buffer, NULL, &uav_desc, cpu_handle);
        }
        else
        {
            buffer = create_default_buffer(device, test->buffer_size,
                    D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST);
            memset(&srv_desc, 0, sizeof(srv_desc));
            srv_desc.Format = test->view_format;
            srv_desc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER;
            srv_desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
            srv_desc.Buffer.Flags = test->raw ? D3D12_BUFFER_SRV_FLAG_RAW : 0;
            srv_desc.Buffer.FirstElement = test->view_element_idx;
            srv_desc.Buffer.NumElements = test->view_element_count;
            srv_desc.Buffer.StructureByteStride = test->buffer_structure_byte_stride;
            ID3D12Device_CreateShaderResourceView(device, buffer, &srv_desc, cpu_handle);
        }

        ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
        ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
        ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
        ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
        ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, 1, &vbv);
        ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
        ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
        ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &heap);
        ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list, 0, gpu_handle);
        ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 1, 0, 0);

        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
        check_sub_resource_uvec4(context.render_target, 0, queue, command_list, &test->expected_result);
        reset_command_list(command_list, context.allocator);
        transition_resource_state(command_list, context.render_target,
                D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

        ID3D12Resource_Release(buffer);

        vkd3d_test_pop_context();
    }

    ID3D12Resource_Release(vb);
    ID3D12DescriptorHeap_Release(heap);
    destroy_test_context(&context);
}

static void test_write_buffer_immediate(void)
{
    D3D12_WRITEBUFFERIMMEDIATE_PARAMETER parameters[2];
    ID3D12GraphicsCommandList2 *command_list2;
    D3D12_WRITEBUFFERIMMEDIATE_MODE modes[2];
    ID3D12GraphicsCommandList *command_list;
    struct d3d12_resource_readback rb;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Resource *buffer;
    ID3D12Device *device;
    unsigned int value;
    HRESULT hr;

    static const unsigned int data_values[] = {0xdeadbeef, 0xf00baa};

    if (!init_test_context(&context, NULL))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    if (FAILED(hr = ID3D12GraphicsCommandList_QueryInterface(command_list,
            &IID_ID3D12GraphicsCommandList2, (void **)&command_list2)))
    {
        skip("ID3D12GraphicsCommandList2 not implemented.\n");
        destroy_test_context(&context);
        return;
    }

    buffer = create_default_buffer(device, sizeof(data_values),
            D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST);
    upload_buffer_data(buffer, 0, sizeof(data_values), data_values, queue, command_list);
    reset_command_list(command_list, context.allocator);

    parameters[0].Dest = ID3D12Resource_GetGPUVirtualAddress(buffer);
    parameters[0].Value = 0x1020304;
    parameters[1].Dest = parameters[0].Dest + sizeof(data_values[0]);
    parameters[1].Value = 0xc0d0e0f;
    ID3D12GraphicsCommandList2_WriteBufferImmediate(command_list2, ARRAY_SIZE(parameters), parameters, NULL);
    hr = ID3D12GraphicsCommandList_Close(command_list);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    exec_command_list(queue, command_list);
    wait_queue_idle(device, queue);
    reset_command_list(command_list, context.allocator);

    get_buffer_readback_with_command_list(buffer, DXGI_FORMAT_R32_UINT, &rb, queue, command_list);
    value = get_readback_uint(&rb.rb, 0, 0, 0);
    todo ok(value == parameters[0].Value, "Got unexpected value %#x, expected %#x.\n", value, parameters[0].Value);
    value = get_readback_uint(&rb.rb, 1, 0, 0);
    todo ok(value == parameters[1].Value, "Got unexpected value %#x, expected %#x.\n", value, parameters[1].Value);
    release_resource_readback(&rb);
    reset_command_list(command_list, context.allocator);

    parameters[0].Value = 0x2030405;
    parameters[1].Value = 0xb0c0d0e;
    modes[0] = D3D12_WRITEBUFFERIMMEDIATE_MODE_MARKER_IN;
    modes[1] = D3D12_WRITEBUFFERIMMEDIATE_MODE_MARKER_OUT;
    ID3D12GraphicsCommandList2_WriteBufferImmediate(command_list2, ARRAY_SIZE(parameters), parameters, modes);
    hr = ID3D12GraphicsCommandList_Close(command_list);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    exec_command_list(queue, command_list);
    wait_queue_idle(device, queue);
    reset_command_list(command_list, context.allocator);

    get_buffer_readback_with_command_list(buffer, DXGI_FORMAT_R32_UINT, &rb, queue, command_list);
    value = get_readback_uint(&rb.rb, 0, 0, 0);
    todo ok(value == parameters[0].Value, "Got unexpected value %#x, expected %#x.\n", value, parameters[0].Value);
    value = get_readback_uint(&rb.rb, 1, 0, 0);
    todo ok(value == parameters[1].Value, "Got unexpected value %#x, expected %#x.\n", value, parameters[1].Value);
    release_resource_readback(&rb);
    reset_command_list(command_list, context.allocator);

    modes[0] = 0x7fffffff;
    ID3D12GraphicsCommandList2_WriteBufferImmediate(command_list2, ARRAY_SIZE(parameters), parameters, modes);
    hr = ID3D12GraphicsCommandList_Close(command_list);
    todo ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    ID3D12Resource_Release(buffer);
    ID3D12GraphicsCommandList2_Release(command_list2);
    destroy_test_context(&context);
}

static void test_register_space(void)
{
    ID3D12Resource *input_buffers[6], *output_buffers[8], *counter_buffers[2];
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    D3D12_UNORDERED_ACCESS_VIEW_DESC uav_desc;
    D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc;
    ID3D12GraphicsCommandList *command_list;
    unsigned int descriptor_count;
    struct test_context context;
    ID3D12DescriptorHeap *heap;
    ID3D12CommandQueue *queue;
    ID3D12Device *device;
    uint32_t counter;
    unsigned int i;
    HRESULT hr;

    static const D3D12_DESCRIPTOR_RANGE descriptor_ranges[] =
    {
        {D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 1, 2, 0},
        {D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 2, 1, 1, D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND},
        {D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 2, 2, 2, D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND},
        {D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 3, 1, D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND},

        {D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 1, 1, 1, D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND},
        {D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 2, 1, 2, D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND},
        {D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 2, 2, 1, D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND},
        {D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 1, 3, 2, D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND},
        {D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 1, 3, 4, D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND},
        {D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 1, 3, 3, D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND},
    };

    static const D3D12_ROOT_PARAMETER root_parameters[] =
    {
        {D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE,
                    .DescriptorTable = {ARRAY_SIZE(descriptor_ranges), descriptor_ranges}},
        {D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS, .Constants = {1, 1, 1}},
        {D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS, .Constants = {1, 2, 1}},
    };

    static const DWORD cs_code[] =
    {
#if 0
        cbuffer cb1 : register(b1, space1)
        {
            uint c1;
        }
        cbuffer cb2 : register(b1, space2)
        {
            uint c2;
        }

        ByteAddressBuffer t1 : register(t1, space2);
        ByteAddressBuffer t2 : register(t1, space1);

        Buffer<uint> t3 : register(t2, space1);
        Buffer<uint> t4 : register(t2, space2);

        StructuredBuffer<uint> t5 : register(t3, space1);
        StructuredBuffer<uint> t6 : register(t3, space2);

        RWByteAddressBuffer u1 : register(u1, space1);
        RWByteAddressBuffer u2 : register(u1, space2);
        RWByteAddressBuffer u3 : register(u2, space1);
        RWByteAddressBuffer u4 : register(u2, space2);

        RWBuffer<uint> u5 : register(u3, space1);
        RWBuffer<uint> u6 : register(u3, space2);

        RWStructuredBuffer<uint> u7 : register(u3, space4);
        RWStructuredBuffer<uint> u8 : register(u3, space3);

        [numthreads(1, 1, 1)]
        void main()
        {
            u1.Store(0, t1.Load(0));
            u2.Store(0, t2.Load(0));
            u3.Store(0, t3.Load(0));
            u4.Store(0, t4.Load(0));
            u5[0] = t5.Load(0);
            u6[0] = t6.Load(0);
            u7[0] = c1;
            u8[0] = c2;
            u7.IncrementCounter();
            u8.DecrementCounter();
        }
#endif
        0x43425844, 0x2a87323a, 0x4f83d345, 0x707e5d14, 0xdf41ce4a, 0x00000001, 0x00000474, 0x00000003,
        0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x00000420, 0x00050051, 0x00000108, 0x0100086a,
        0x07000059, 0x00308e46, 0x00000000, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x07000059,
        0x00308e46, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000002, 0x060000a1, 0x00307e46,
        0x00000000, 0x00000001, 0x00000001, 0x00000001, 0x07000858, 0x00307e46, 0x00000001, 0x00000002,
        0x00000002, 0x00004444, 0x00000001, 0x070000a2, 0x00307e46, 0x00000002, 0x00000003, 0x00000003,
        0x00000004, 0x00000001, 0x060000a1, 0x00307e46, 0x00000003, 0x00000001, 0x00000001, 0x00000002,
        0x07000858, 0x00307e46, 0x00000004, 0x00000002, 0x00000002, 0x00004444, 0x00000002, 0x070000a2,
        0x00307e46, 0x00000005, 0x00000003, 0x00000003, 0x00000004, 0x00000002, 0x0600009d, 0x0031ee46,
        0x00000000, 0x00000001, 0x00000001, 0x00000001, 0x0600009d, 0x0031ee46, 0x00000001, 0x00000002,
        0x00000002, 0x00000001, 0x0700089c, 0x0031ee46, 0x00000002, 0x00000003, 0x00000003, 0x00004444,
        0x00000001, 0x0600009d, 0x0031ee46, 0x00000003, 0x00000001, 0x00000001, 0x00000002, 0x0600009d,
        0x0031ee46, 0x00000004, 0x00000002, 0x00000002, 0x00000002, 0x0700089c, 0x0031ee46, 0x00000005,
        0x00000003, 0x00000003, 0x00004444, 0x00000002, 0x0780009e, 0x0031ee46, 0x00000006, 0x00000003,
        0x00000003, 0x00000004, 0x00000003, 0x0780009e, 0x0031ee46, 0x00000007, 0x00000003, 0x00000003,
        0x00000004, 0x00000004, 0x02000068, 0x00000001, 0x0400009b, 0x00000001, 0x00000001, 0x00000001,
        0x080000a5, 0x00100012, 0x00000000, 0x00004001, 0x00000000, 0x00207006, 0x00000003, 0x00000001,
        0x080000a6, 0x0021e012, 0x00000000, 0x00000001, 0x00004001, 0x00000000, 0x0010000a, 0x00000000,
        0x080000a5, 0x00100012, 0x00000000, 0x00004001, 0x00000000, 0x00207006, 0x00000000, 0x00000001,
        0x080000a6, 0x0021e012, 0x00000003, 0x00000001, 0x00004001, 0x00000000, 0x0010000a, 0x00000000,
        0x0b00002d, 0x00100012, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
        0x00207e46, 0x00000001, 0x00000002, 0x080000a6, 0x0021e012, 0x00000001, 0x00000002, 0x00004001,
        0x00000000, 0x0010000a, 0x00000000, 0x0b00002d, 0x00100012, 0x00000000, 0x00004002, 0x00000000,
        0x00000000, 0x00000000, 0x00000000, 0x00207e46, 0x00000004, 0x00000002, 0x080000a6, 0x0021e012,
        0x00000004, 0x00000002, 0x00004001, 0x00000000, 0x0010000a, 0x00000000, 0x0a0000a7, 0x00100012,
        0x00000000, 0x00004001, 0x00000000, 0x00004001, 0x00000000, 0x00207006, 0x00000002, 0x00000003,
        0x0b0000a4, 0x0021e0f2, 0x00000002, 0x00000003, 0x00004002, 0x00000000, 0x00000000, 0x00000000,
        0x00000000, 0x00100006, 0x00000000, 0x0a0000a7, 0x00100012, 0x00000000, 0x00004001, 0x00000000,
        0x00004001, 0x00000000, 0x00207006, 0x00000005, 0x00000003, 0x0b0000a4, 0x0021e0f2, 0x00000005,
        0x00000003, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00100006, 0x00000000,
        0x0c0000a8, 0x0021e012, 0x00000007, 0x00000003, 0x00004001, 0x00000000, 0x00004001, 0x00000000,
        0x0030800a, 0x00000000, 0x00000001, 0x00000000, 0x0c0000a8, 0x0021e012, 0x00000006, 0x00000003,
        0x00004001, 0x00000000, 0x00004001, 0x00000000, 0x0030800a, 0x00000001, 0x00000001, 0x00000000,
        0x060000b2, 0x00100012, 0x00000000, 0x0021e000, 0x00000007, 0x00000003, 0x060000b3, 0x00100012,
        0x00000000, 0x0021e000, 0x00000006, 0x00000003, 0x0100003e,
    };
    static const uint32_t uav_data[] = {100, 200, 400, 300, 600, 500, 700, 800};
    static const uint32_t srv_data[] = {100, 200, 300, 400, 500, 600};

    if (!init_compute_test_context(&context))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    memset(&root_signature_desc, 0, sizeof(root_signature_desc));
    root_signature_desc.NumParameters = ARRAY_SIZE(root_parameters);
    root_signature_desc.pParameters = root_parameters;
    hr = create_root_signature(device, &root_signature_desc, &context.root_signature);
    ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr);

    for (i = 0, descriptor_count = 0; i < ARRAY_SIZE(descriptor_ranges); ++i)
    {
        descriptor_count += descriptor_ranges[i].NumDescriptors;
    }
    heap = create_gpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, descriptor_count);

    for (i = 0; i < ARRAY_SIZE(input_buffers); ++i)
    {
        input_buffers[i] = create_default_buffer(device, sizeof(uint32_t),
                D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST);
        upload_buffer_data(input_buffers[i], 0, sizeof(srv_data[i]), &srv_data[i], queue, command_list);
        reset_command_list(command_list, context.allocator);
        transition_resource_state(command_list, input_buffers[i],
                D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);

        memset(&srv_desc, 0, sizeof(srv_desc));
        srv_desc.Format = DXGI_FORMAT_R32_UINT;
        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 = 1;
        ID3D12Device_CreateShaderResourceView(device, input_buffers[i], &srv_desc,
                get_cpu_descriptor_handle(&context, heap, i));
    }

    for (i = 0; i < ARRAY_SIZE(counter_buffers); ++i)
    {
        counter_buffers[i] = create_default_buffer(device, sizeof(uint32_t),
                D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_DEST);
        counter = 2;
        upload_buffer_data(counter_buffers[i], 0, sizeof(counter), &counter, queue, command_list);
        reset_command_list(command_list, context.allocator);
        transition_sub_resource_state(command_list, counter_buffers[i], 0,
                D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
    }

    for (i = 0; i < ARRAY_SIZE(output_buffers); ++i)
    {
        output_buffers[i] = create_default_buffer(device, sizeof(uint32_t),
                D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

        memset(&uav_desc, 0, sizeof(uav_desc));
        uav_desc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
        uav_desc.Buffer.FirstElement = 0;
        uav_desc.Buffer.NumElements = 1;
        if (i < 6)
        {
            uav_desc.Format = DXGI_FORMAT_R32_UINT;
        }
        else
        {
            uav_desc.Format = DXGI_FORMAT_UNKNOWN;
            uav_desc.Buffer.StructureByteStride = sizeof(uint32_t);
        }
        ID3D12Device_CreateUnorderedAccessView(device, output_buffers[i], i >= 6 ? counter_buffers[i - 6] : NULL,
                &uav_desc, get_cpu_descriptor_handle(&context, heap, ARRAY_SIZE(input_buffers) + i));
    }

    context.pipeline_state = create_compute_pipeline_state(device, context.root_signature,
            shader_bytecode(cs_code, sizeof(cs_code)));

    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_SetComputeRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &heap);
    ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(command_list,
            0, get_gpu_descriptor_handle(&context, heap, 0));
    ID3D12GraphicsCommandList_SetComputeRoot32BitConstant(command_list, 1, 700, 0);
    ID3D12GraphicsCommandList_SetComputeRoot32BitConstant(command_list, 2, 800, 0);

    ID3D12GraphicsCommandList_Dispatch(command_list, 1, 1, 1);

    for (i = 0; i < ARRAY_SIZE(output_buffers); ++i)
    {
        transition_sub_resource_state(command_list, output_buffers[i], 0,
                D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
        check_buffer_uint(output_buffers[i], queue, command_list, uav_data[i], 0);
        reset_command_list(command_list, context.allocator);
    }

    counter = read_uav_counter(&context, counter_buffers[0], 0);
    ok(counter == 3, "Got unexpected value %d.\n", counter);
    counter = read_uav_counter(&context, counter_buffers[1], 0);
    ok(counter == 1, "Got unexpected value %d.\n", counter);

    for (i = 0; i < ARRAY_SIZE(input_buffers); ++i)
        ID3D12Resource_Release(input_buffers[i]);
    for (i = 0; i < ARRAY_SIZE(output_buffers); ++i)
        ID3D12Resource_Release(output_buffers[i]);
    for (i = 0; i < ARRAY_SIZE(counter_buffers); ++i)
        ID3D12Resource_Release(counter_buffers[i]);
    ID3D12DescriptorHeap_Release(heap);
    destroy_test_context(&context);
}

static void test_sampler_register_space(void)
{
    static const struct test_context_desc desc = {.no_root_signature = true};
    ID3D12DescriptorHeap *heap, *sampler_heap, *heaps[2];
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    ID3D12GraphicsCommandList *command_list;
    D3D12_SAMPLER_DESC sampler_desc;
    D3D12_SUBRESOURCE_DATA data;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Resource *texture;
    ID3D12Device *device;
    HRESULT hr;

    static const D3D12_DESCRIPTOR_RANGE descriptor_ranges[] =
    {
        {D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0, 0, 0},
    };

    static const D3D12_DESCRIPTOR_RANGE sampler_ranges[] =
    {
        {D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 1, 1, 1, 0},
        {D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 1, 1, 2, D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND},
    };

    static const D3D12_ROOT_PARAMETER root_parameters[] =
    {
        {D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE,
                    .DescriptorTable = {ARRAY_SIZE(descriptor_ranges), descriptor_ranges}},
        {D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE,
                    .DescriptorTable = {ARRAY_SIZE(sampler_ranges), sampler_ranges}},
    };

    static const DWORD ps_code[] =
    {
#if 0
        Texture2D<float> t;
        SamplerState s1 : register(s1, space1);
        SamplerState s2 : register(s1, space2);

        float4 main() : SV_Target
        {
            float2 coords = float2(0.5, 0.5);
            return float4(t.Sample(s1, coords), t.Sample(s2, coords), 0, 1);
        }
#endif
        0x43425844, 0xa29c83c7, 0xd4c8eeff, 0x7b73ff7b, 0x6463d58c, 0x00000001, 0x0000018c, 0x00000003,
        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000114, 0x00000051, 0x00000045,
        0x0100086a, 0x0600005a, 0x00306e46, 0x00000000, 0x00000001, 0x00000001, 0x00000001, 0x0600005a,
        0x00306e46, 0x00000001, 0x00000001, 0x00000001, 0x00000002, 0x07001858, 0x00307e46, 0x00000000,
        0x00000000, 0x00000000, 0x00005555, 0x00000000, 0x03000065, 0x001020f2, 0x00000000, 0x02000068,
        0x00000001, 0x0e000045, 0x00100012, 0x00000000, 0x00004002, 0x3f000000, 0x3f000000, 0x00000000,
        0x00000000, 0x00207e46, 0x00000000, 0x00000000, 0x00206000, 0x00000000, 0x00000001, 0x0e000045,
        0x00100022, 0x00000000, 0x00004002, 0x3f000000, 0x3f000000, 0x00000000, 0x00000000, 0x00207e16,
        0x00000000, 0x00000000, 0x00206000, 0x00000001, 0x00000001, 0x05000036, 0x00102032, 0x00000000,
        0x00100046, 0x00000000, 0x08000036, 0x001020c2, 0x00000000, 0x00004002, 0x00000000, 0x00000000,
        0x00000000, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};
    static const float texture_data[4] = {0.0, 1.0, 0.0, 1.0};
    static const float blue[] = {0.0f, 0.0f, 1.0f, 1.0f};

    if (!init_test_context(&context, &desc))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    memset(&root_signature_desc, 0, sizeof(root_signature_desc));
    root_signature_desc.NumParameters = ARRAY_SIZE(root_parameters);
    root_signature_desc.pParameters = root_parameters;
    hr = create_root_signature(device, &root_signature_desc, &context.root_signature);
    ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr);

    heap = create_gpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, ARRAY_SIZE(descriptor_ranges));
    sampler_heap = create_gpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, ARRAY_SIZE(sampler_ranges));

    texture = create_default_texture(device, 2, 2, DXGI_FORMAT_R32_FLOAT, 0, D3D12_RESOURCE_STATE_COPY_DEST);
    data.pData = texture_data;
    data.SlicePitch = data.RowPitch = 2 * sizeof(*texture_data);
    upload_texture_data(texture, &data, 1, queue, command_list);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, texture,
            D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
    ID3D12Device_CreateShaderResourceView(device, texture, NULL, get_cpu_descriptor_handle(&context, heap, 0));

    memset(&sampler_desc, 0, sizeof(sampler_desc));
    sampler_desc.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
    sampler_desc.AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
    sampler_desc.AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
    sampler_desc.AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
    ID3D12Device_CreateSampler(device, &sampler_desc, get_cpu_sampler_handle(&context, sampler_heap, 0));
    sampler_desc.Filter = D3D12_FILTER_MIN_MAG_MIP_POINT;
    ID3D12Device_CreateSampler(device, &sampler_desc, get_cpu_sampler_handle(&context, sampler_heap, 1));

    context.pipeline_state = create_pipeline_state(device, context.root_signature,
            context.render_target_desc.Format, NULL, &ps, NULL);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, blue, 0, NULL);
    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    heaps[0] = heap; heaps[1] = sampler_heap;
    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, ARRAY_SIZE(heaps), heaps);
    ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list,
            0, get_gpu_descriptor_handle(&context, heap, 0));
    ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(command_list,
            1, get_gpu_sampler_handle(&context, sampler_heap, 0));
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0x0000ff80, 1);

    ID3D12Resource_Release(texture);
    ID3D12DescriptorHeap_Release(heap);
    ID3D12DescriptorHeap_Release(sampler_heap);
    destroy_test_context(&context);
}

static void test_hull_shader_relative_addressing(void)
{
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
    ID3D12GraphicsCommandList *command_list;
    D3D12_ROOT_PARAMETER root_parameters[1];
    D3D12_STREAM_OUTPUT_BUFFER_VIEW sobv;
    D3D12_INPUT_LAYOUT_DESC input_layout;
    struct d3d12_resource_readback rb;
    ID3D12Resource *vb, *so_buffer;
    struct test_context_desc desc;
    D3D12_VERTEX_BUFFER_VIEW vbv;
    struct test_context context;
    ID3D12CommandQueue *queue;
    HRESULT hr;

#if 0
    int2 index[4];

    struct patch_in_data
    {
        float4 position[2] : P;
    };

    struct patch_out_data
    {
        float4 position : SV_Position;
    };

    struct patch_constant_data
    {
        float edges[3] : SV_TessFactor;
        float inside : SV_InsideTessFactor;
    };

    void patch_constant(out patch_constant_data output)
    {
        output.edges[0] = 1.0f;
        output.edges[1] = 1.0f;
        output.edges[2] = 1.0f;
        output.inside = 1.0f;
    }

    patch_in_data vs_main(patch_in_data input)
    {
        return input;
    }

    [domain("tri")]
    [outputcontrolpoints(4)]
    [partitioning("integer")]
    [outputtopology("triangle_cw")]
    [patchconstantfunc("patch_constant")]
    patch_out_data hs_main(InputPatch<patch_in_data, 3> input, uint i : SV_OutputControlPointID)
    {
        patch_out_data output;

        output.position = input[index[i].x].position[index[i].y];

        return output;
    }

    [domain("tri")]
    float4 ds_main(patch_constant_data input, float3 tess_coord : SV_DomainLocation,
            const OutputPatch<patch_out_data, 4> patch) : SV_Position
    {
        float4 position;

        position = patch[3].position;
        position += patch[0].position * tess_coord.x;
        position += patch[1].position * tess_coord.y;
        position += patch[2].position * tess_coord.z;

        return position / 2;
    }
#endif
    static const DWORD vs_code[] =
    {
        0x43425844, 0x7ba6d35f, 0x9640af75, 0x43fbed3a, 0x564c554f, 0x00000001, 0x00000124, 0x00000003,
        0x0000002c, 0x00000070, 0x000000b4, 0x4e475349, 0x0000003c, 0x00000002, 0x00000008, 0x00000038,
        0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000f0f, 0x00000038, 0x00000001, 0x00000000,
        0x00000003, 0x00000001, 0x00000f0f, 0xabab0050, 0x4e47534f, 0x0000003c, 0x00000002, 0x00000008,
        0x00000038, 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x0000000f, 0x00000038, 0x00000001,
        0x00000000, 0x00000003, 0x00000001, 0x0000000f, 0xabab0050, 0x58454853, 0x00000068, 0x00010050,
        0x0000001a, 0x0100086a, 0x0300005f, 0x001010f2, 0x00000000, 0x0300005f, 0x001010f2, 0x00000001,
        0x03000065, 0x001020f2, 0x00000000, 0x03000065, 0x001020f2, 0x00000001, 0x05000036, 0x001020f2,
        0x00000000, 0x00101e46, 0x00000000, 0x05000036, 0x001020f2, 0x00000001, 0x00101e46, 0x00000001,
        0x0100003e,
    };
    static const DWORD hs_code[] =
    {
        0x43425844, 0xe1d76e8a, 0x5e615b9a, 0xac2b4cf5, 0xbd8708f0, 0x00000001, 0x000002ec, 0x00000004,
        0x00000030, 0x00000074, 0x000000a8, 0x0000013c, 0x4e475349, 0x0000003c, 0x00000002, 0x00000008,
        0x00000038, 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000f0f, 0x00000038, 0x00000001,
        0x00000000, 0x00000003, 0x00000001, 0x00000f0f, 0xabab0050, 0x4e47534f, 0x0000002c, 0x00000001,
        0x00000008, 0x00000020, 0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x505f5653,
        0x7469736f, 0x006e6f69, 0x47534350, 0x0000008c, 0x00000004, 0x00000008, 0x00000068, 0x00000000,
        0x0000000d, 0x00000003, 0x00000000, 0x00000e01, 0x00000068, 0x00000001, 0x0000000d, 0x00000003,
        0x00000001, 0x00000e01, 0x00000068, 0x00000002, 0x0000000d, 0x00000003, 0x00000002, 0x00000e01,
        0x00000076, 0x00000000, 0x0000000e, 0x00000003, 0x00000003, 0x00000e01, 0x545f5653, 0x46737365,
        0x6f746361, 0x56530072, 0x736e495f, 0x54656469, 0x46737365, 0x6f746361, 0xabab0072, 0x58454853,
        0x000001a8, 0x00030050, 0x0000006a, 0x01000071, 0x01001893, 0x01002094, 0x01001095, 0x01000896,
        0x01001897, 0x0100086a, 0x04000859, 0x00208e46, 0x00000000, 0x00000004, 0x01000072, 0x0200005f,
        0x00016000, 0x0400005f, 0x002010f2, 0x00000003, 0x00000000, 0x0400005f, 0x002010f2, 0x00000003,
        0x00000001, 0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000001, 0x0500005b, 0x002010f2,
        0x00000003, 0x00000000, 0x00000002, 0x04000036, 0x00100012, 0x00000000, 0x00016001, 0x07000036,
        0x00100022, 0x00000000, 0x0420801a, 0x00000000, 0x0010000a, 0x00000000, 0x07000036, 0x00100012,
        0x00000000, 0x0420800a, 0x00000000, 0x0010000a, 0x00000000, 0x08000036, 0x001020f2, 0x00000000,
        0x04a01e46, 0x0010000a, 0x00000000, 0x0010001a, 0x00000000, 0x0100003e, 0x01000073, 0x02000099,
        0x00000003, 0x0200005f, 0x00017000, 0x04000067, 0x00102012, 0x00000000, 0x00000011, 0x04000067,
        0x00102012, 0x00000001, 0x00000012, 0x04000067, 0x00102012, 0x00000002, 0x00000013, 0x02000068,
        0x00000001, 0x0400005b, 0x00102012, 0x00000000, 0x00000003, 0x04000036, 0x00100012, 0x00000000,
        0x0001700a, 0x06000036, 0x00902012, 0x0010000a, 0x00000000, 0x00004001, 0x3f800000, 0x0100003e,
        0x01000073, 0x04000067, 0x00102012, 0x00000003, 0x00000014, 0x05000036, 0x00102012, 0x00000003,
        0x00004001, 0x3f800000, 0x0100003e,
    };
    static const DWORD ds_code[] =
    {
        0x43425844, 0x95673c05, 0xbf512405, 0x69311baa, 0x46d18077, 0x00000001, 0x00000214, 0x00000004,
        0x00000030, 0x00000064, 0x000000f8, 0x0000012c, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008,
        0x00000020, 0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x00000f0f, 0x505f5653, 0x7469736f,
        0x006e6f69, 0x47534350, 0x0000008c, 0x00000004, 0x00000008, 0x00000068, 0x00000000, 0x0000000d,
        0x00000003, 0x00000000, 0x00000001, 0x00000068, 0x00000001, 0x0000000d, 0x00000003, 0x00000001,
        0x00000001, 0x00000068, 0x00000002, 0x0000000d, 0x00000003, 0x00000002, 0x00000001, 0x00000076,
        0x00000000, 0x0000000e, 0x00000003, 0x00000003, 0x00000001, 0x545f5653, 0x46737365, 0x6f746361,
        0x56530072, 0x736e495f, 0x54656469, 0x46737365, 0x6f746361, 0xabab0072, 0x4e47534f, 0x0000002c,
        0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f,
        0x505f5653, 0x7469736f, 0x006e6f69, 0x58454853, 0x000000e0, 0x00040050, 0x00000038, 0x01002093,
        0x01001095, 0x0100086a, 0x0200005f, 0x0001c072, 0x0400005f, 0x002190f2, 0x00000004, 0x00000000,
        0x04000067, 0x001020f2, 0x00000000, 0x00000001, 0x02000068, 0x00000001, 0x0a000032, 0x001000f2,
        0x00000000, 0x00219e46, 0x00000000, 0x00000000, 0x0001c006, 0x00219e46, 0x00000003, 0x00000000,
        0x09000032, 0x001000f2, 0x00000000, 0x00219e46, 0x00000001, 0x00000000, 0x0001c556, 0x00100e46,
        0x00000000, 0x09000032, 0x001000f2, 0x00000000, 0x00219e46, 0x00000002, 0x00000000, 0x0001caa6,
        0x00100e46, 0x00000000, 0x0a000038, 0x001020f2, 0x00000000, 0x00100e46, 0x00000000, 0x00004002,
        0x3f000000, 0x3f000000, 0x3f000000, 0x3f000000, 0x0100003e,
    };
    static const D3D12_INPUT_ELEMENT_DESC layout_desc[] =
    {
        {"P", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0,  0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
        {"P", 1, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 16, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
    };
    static const D3D12_SO_DECLARATION_ENTRY so_declaration[] =
    {
        {0, "SV_POSITION",  0, 0, 4, 0},
    };
    static const struct
    {
        struct vec4 texcoord[2];
    }
    vertices[] =
    {
        {{{1.0f, 1.0f, 1.0f, 1.0f}, { 8.0f,  8.0f,  8.0f, 1.0f}}},
        {{{2.0f, 2.0f, 2.0f, 1.0f}, {16.0f, 16.0f, 16.0f, 1.0f}}},
        {{{4.0f, 4.0f, 4.0f, 1.0f}, {32.0f, 32.0f, 32.0f, 1.0f}}},
    };
    static const struct uvec4 indices[] =
    {
        {0, 0},
        {1, 1},
        {2, 1},
        {2, 0},
    };
    static const struct triangle expected_triangle =
    {{
        {10.0f, 10.0f, 10.0f, 1.0f},
        {18.0f, 18.0f, 18.0f, 1.0f},
        { 2.5f,  2.5f,  2.5f, 1.0f},
    }};
    unsigned int strides[] = {sizeof(struct vec4)};

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

    root_parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
    root_parameters[0].Constants.ShaderRegister = 0;
    root_parameters[0].Constants.RegisterSpace = 0;
    root_parameters[0].Constants.Num32BitValues = 16;
    root_parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_HULL;
    memset(&root_signature_desc, 0, sizeof(root_signature_desc));
    root_signature_desc.NumParameters = ARRAY_SIZE(root_parameters);
    root_signature_desc.pParameters = root_parameters;
    root_signature_desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT
            | D3D12_ROOT_SIGNATURE_FLAG_ALLOW_STREAM_OUTPUT;
    hr = create_root_signature(context.device, &root_signature_desc, &context.root_signature);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);

    input_layout.pInputElementDescs = layout_desc;
    input_layout.NumElements = ARRAY_SIZE(layout_desc);
    init_pipeline_state_desc(&pso_desc, context.root_signature, DXGI_FORMAT_UNKNOWN, NULL, NULL, &input_layout);
    pso_desc.VS = shader_bytecode(vs_code, sizeof(vs_code));
    pso_desc.HS = shader_bytecode(hs_code, sizeof(hs_code));
    pso_desc.DS = shader_bytecode(ds_code, sizeof(ds_code));
    memset(&pso_desc.PS, 0, sizeof(pso_desc.PS));
    pso_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_PATCH;
    pso_desc.StreamOutput.NumEntries = ARRAY_SIZE(so_declaration);
    pso_desc.StreamOutput.pSODeclaration = so_declaration;
    pso_desc.StreamOutput.pBufferStrides = strides;
    pso_desc.StreamOutput.NumStrides = ARRAY_SIZE(strides);
    pso_desc.StreamOutput.RasterizedStream = D3D12_SO_NO_RASTERIZED_STREAM;
    hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
    bug_if(is_mvk_device(context.device))
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);

    vb = create_upload_buffer(context.device, sizeof(vertices), vertices);
    vbv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vb);
    vbv.StrideInBytes = sizeof(*vertices);
    vbv.SizeInBytes = sizeof(vertices);

    so_buffer = create_default_buffer(context.device, 4096,
            D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_STREAM_OUT);
    sobv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(so_buffer);
    sobv.SizeInBytes = 1024;
    sobv.BufferFilledSizeLocation = sobv.BufferLocation + sobv.SizeInBytes;

    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 0, 16, indices, 0);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST);
    ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, 1, &vbv);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_SOSetTargets(command_list, 0, 1, &sobv);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, so_buffer,
            D3D12_RESOURCE_STATE_STREAM_OUT, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_buffer_readback_with_command_list(so_buffer, DXGI_FORMAT_UNKNOWN, &rb, queue, command_list);
    bug_if(is_radv_device(context.device) || is_mvk_device(context.device))
    check_triangles(&rb.rb, &expected_triangle, 1);
    release_resource_readback(&rb);

    ID3D12Resource_Release(so_buffer);
    ID3D12Resource_Release(vb);
    destroy_test_context(&context);
}

static void test_hull_shader_patch_constant_inputs(void)
{
    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
    ID3D12GraphicsCommandList *command_list;
    D3D12_STREAM_OUTPUT_BUFFER_VIEW sobv;
    D3D12_INPUT_LAYOUT_DESC input_layout;
    struct d3d12_resource_readback rb;
    ID3D12Resource *vb, *so_buffer;
    struct test_context_desc desc;
    D3D12_VERTEX_BUFFER_VIEW vbv;
    struct test_context context;
    ID3D12CommandQueue *queue;
    HRESULT hr;

#if 0
    struct patch_constant_data
    {
        float2 p[3] : P;
        float2 c : C;
        float factor[3] : SV_TessFactor;
        float inside : SV_InsideTessFactor;
    };

    struct point_data
    {
        float2 p[2] : P;
    };

    struct patch_constant_data patch_constant(InputPatch<struct point_data, 3> input)
    {
        struct patch_constant_data output;

        output.factor[0] = 1.0f;
        output.factor[1] = 1.0f;
        output.factor[2] = 1.0f;
        output.inside = 1.0f;

        output.p[0] = input[0].p[0] + input[0].p[1];
        output.p[1] = input[1].p[0] + input[1].p[1];
        output.p[2] = input[2].p[0] + input[2].p[1];
        output.c = (output.p[0] + output.p[1] + output.p[2]) / 3;

        return output;
    }

    struct point_data vs_main(struct point_data input)
    {
        return input;
    }

    [domain("tri")]
    [outputcontrolpoints(3)]
    [outputtopology("triangle_cw")]
    [partitioning("fractional_odd")]
    [patchconstantfunc("patch_constant")]
    void hs_main(InputPatch<struct point_data, 3> input)
    {
    }

    [domain("tri")]
    float4 ds_main(struct patch_constant_data input, float3 tess_coord : SV_DomainLocation,
            OutputPatch<struct point_data, 3> patch) : SV_POSITION
    {
        return float4(input.c, input.p[0] * tess_coord.x + input.p[1] * tess_coord.y + input.p[2] * tess_coord.z);
    }
#endif
    static const DWORD vs_code[] =
    {
        0x43425844, 0x2b21ed94, 0x44ae97f2, 0x654a3d25, 0x437c7e3e, 0x00000001, 0x00000124, 0x00000003,
        0x0000002c, 0x00000070, 0x000000b4, 0x4e475349, 0x0000003c, 0x00000002, 0x00000008, 0x00000038,
        0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000303, 0x00000038, 0x00000001, 0x00000000,
        0x00000003, 0x00000001, 0x00000303, 0xabab0050, 0x4e47534f, 0x0000003c, 0x00000002, 0x00000008,
        0x00000038, 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000c03, 0x00000038, 0x00000001,
        0x00000000, 0x00000003, 0x00000001, 0x00000c03, 0xabab0050, 0x58454853, 0x00000068, 0x00010050,
        0x0000001a, 0x0100086a, 0x0300005f, 0x00101032, 0x00000000, 0x0300005f, 0x00101032, 0x00000001,
        0x03000065, 0x00102032, 0x00000000, 0x03000065, 0x00102032, 0x00000001, 0x05000036, 0x00102032,
        0x00000000, 0x00101046, 0x00000000, 0x05000036, 0x00102032, 0x00000001, 0x00101046, 0x00000001,
        0x0100003e,
    };
    static const DWORD hs_code[] =
    {
        0x43425844, 0xaad1f130, 0xc203f4bd, 0x61d030b1, 0x0444e4b3, 0x00000001, 0x0000054c, 0x00000004,
        0x00000030, 0x00000074, 0x000000b8, 0x000001b0, 0x4e475349, 0x0000003c, 0x00000002, 0x00000008,
        0x00000038, 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000303, 0x00000038, 0x00000001,
        0x00000000, 0x00000003, 0x00000001, 0x00000303, 0xabab0050, 0x4e47534f, 0x0000003c, 0x00000002,
        0x00000008, 0x00000038, 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000c03, 0x00000038,
        0x00000001, 0x00000000, 0x00000003, 0x00000001, 0x00000c03, 0xabab0050, 0x47534350, 0x000000f0,
        0x00000008, 0x00000008, 0x000000c8, 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000c03,
        0x000000ca, 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x0000030c, 0x000000c8, 0x00000001,
        0x00000000, 0x00000003, 0x00000001, 0x00000c03, 0x000000cc, 0x00000000, 0x0000000e, 0x00000003,
        0x00000001, 0x00000b04, 0x000000c8, 0x00000002, 0x00000000, 0x00000003, 0x00000002, 0x00000c03,
        0x000000e0, 0x00000000, 0x0000000d, 0x00000003, 0x00000003, 0x00000e01, 0x000000e0, 0x00000001,
        0x0000000d, 0x00000003, 0x00000004, 0x00000e01, 0x000000e0, 0x00000002, 0x0000000d, 0x00000003,
        0x00000005, 0x00000e01, 0x00430050, 0x495f5653, 0x6469736e, 0x73655465, 0x63614673, 0x00726f74,
        0x545f5653, 0x46737365, 0x6f746361, 0xabab0072, 0x58454853, 0x00000394, 0x00030050, 0x000000e5,
        0x01000071, 0x01001893, 0x01001894, 0x01001095, 0x01001896, 0x01001897, 0x0100086a, 0x01000073,
        0x02000099, 0x00000003, 0x0200005f, 0x00017000, 0x0400005f, 0x00219012, 0x00000003, 0x00000000,
        0x0400005f, 0x00219012, 0x00000003, 0x00000001, 0x03000065, 0x00102012, 0x00000000, 0x03000065,
        0x00102012, 0x00000001, 0x03000065, 0x00102012, 0x00000002, 0x02000068, 0x00000001, 0x0400005b,
        0x00102012, 0x00000000, 0x00000003, 0x04000036, 0x00100012, 0x00000000, 0x0001700a, 0x0b000000,
        0x00100022, 0x00000000, 0x00a1900a, 0x0010000a, 0x00000000, 0x00000000, 0x00a1900a, 0x0010000a,
        0x00000000, 0x00000001, 0x06000036, 0x00902012, 0x0010000a, 0x00000000, 0x0010001a, 0x00000000,
        0x0100003e, 0x01000073, 0x02000099, 0x00000003, 0x0200005f, 0x00017000, 0x0400005f, 0x00219022,
        0x00000003, 0x00000000, 0x0400005f, 0x00219022, 0x00000003, 0x00000001, 0x03000065, 0x00102022,
        0x00000000, 0x03000065, 0x00102022, 0x00000001, 0x03000065, 0x00102022, 0x00000002, 0x02000068,
        0x00000001, 0x0400005b, 0x00102022, 0x00000000, 0x00000003, 0x04000036, 0x00100012, 0x00000000,
        0x0001700a, 0x0b000000, 0x00100022, 0x00000000, 0x00a1901a, 0x0010000a, 0x00000000, 0x00000000,
        0x00a1901a, 0x0010000a, 0x00000000, 0x00000001, 0x06000036, 0x00902022, 0x0010000a, 0x00000000,
        0x0010001a, 0x00000000, 0x0100003e, 0x01000073, 0x02000099, 0x00000003, 0x0200005f, 0x00017000,
        0x04000067, 0x00102012, 0x00000003, 0x00000011, 0x04000067, 0x00102012, 0x00000004, 0x00000012,
        0x04000067, 0x00102012, 0x00000005, 0x00000013, 0x02000068, 0x00000001, 0x0400005b, 0x00102012,
        0x00000003, 0x00000003, 0x04000036, 0x00100012, 0x00000000, 0x0001700a, 0x07000036, 0x00d02012,
        0x00000003, 0x0010000a, 0x00000000, 0x00004001, 0x3f800000, 0x0100003e, 0x01000073, 0x04000067,
        0x00102042, 0x00000001, 0x00000014, 0x05000036, 0x00102042, 0x00000001, 0x00004001, 0x3f800000,
        0x0100003e, 0x01000074, 0x0300005f, 0x0011b012, 0x00000000, 0x0300005f, 0x0011b012, 0x00000001,
        0x0300005f, 0x0011b012, 0x00000002, 0x03000065, 0x00102042, 0x00000000, 0x02000068, 0x00000001,
        0x07000000, 0x00100012, 0x00000000, 0x0011b00a, 0x00000000, 0x0011b00a, 0x00000001, 0x07000000,
        0x00100012, 0x00000000, 0x0010000a, 0x00000000, 0x0011b00a, 0x00000002, 0x07000038, 0x00102042,
        0x00000000, 0x0010000a, 0x00000000, 0x00004001, 0x3eaaaaab, 0x0100003e, 0x01000074, 0x0300005f,
        0x0011b022, 0x00000000, 0x0300005f, 0x0011b022, 0x00000001, 0x0300005f, 0x0011b022, 0x00000002,
        0x03000065, 0x00102082, 0x00000000, 0x02000068, 0x00000001, 0x07000000, 0x00100012, 0x00000000,
        0x0011b01a, 0x00000000, 0x0011b01a, 0x00000001, 0x07000000, 0x00100012, 0x00000000, 0x0010000a,
        0x00000000, 0x0011b01a, 0x00000002, 0x07000038, 0x00102082, 0x00000000, 0x0010000a, 0x00000000,
        0x00004001, 0x3eaaaaab, 0x0100003e,
    };
    static const DWORD ds_code[] =
    {
        0x43425844, 0xd5cd44e8, 0x94050d92, 0xd93d3a7c, 0x5c86220a, 0x00000001, 0x0000027c, 0x00000004,
        0x00000030, 0x00000074, 0x0000016c, 0x000001a0, 0x4e475349, 0x0000003c, 0x00000002, 0x00000008,
        0x00000038, 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000003, 0x00000038, 0x00000001,
        0x00000000, 0x00000003, 0x00000001, 0x00000003, 0xabab0050, 0x47534350, 0x000000f0, 0x00000008,
        0x00000008, 0x000000c8, 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000303, 0x000000ca,
        0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000c0c, 0x000000c8, 0x00000001, 0x00000000,
        0x00000003, 0x00000001, 0x00000303, 0x000000cc, 0x00000000, 0x0000000e, 0x00000003, 0x00000001,
        0x00000004, 0x000000c8, 0x00000002, 0x00000000, 0x00000003, 0x00000002, 0x00000303, 0x000000e0,
        0x00000000, 0x0000000d, 0x00000003, 0x00000003, 0x00000001, 0x000000e0, 0x00000001, 0x0000000d,
        0x00000003, 0x00000004, 0x00000001, 0x000000e0, 0x00000002, 0x0000000d, 0x00000003, 0x00000005,
        0x00000001, 0x00430050, 0x495f5653, 0x6469736e, 0x73655465, 0x63614673, 0x00726f74, 0x545f5653,
        0x46737365, 0x6f746361, 0xabab0072, 0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x505f5653, 0x5449534f, 0x004e4f49,
        0x58454853, 0x000000d4, 0x00040050, 0x00000035, 0x01001893, 0x01001095, 0x0100086a, 0x0300005f,
        0x0011b032, 0x00000000, 0x0300005f, 0x0011b0c2, 0x00000000, 0x0300005f, 0x0011b032, 0x00000001,
        0x0300005f, 0x0011b032, 0x00000002, 0x0200005f, 0x0001c072, 0x04000067, 0x001020f2, 0x00000000,
        0x00000001, 0x02000068, 0x00000001, 0x06000038, 0x00100032, 0x00000000, 0x0011b046, 0x00000001,
        0x0001c556, 0x08000032, 0x00100032, 0x00000000, 0x0011b046, 0x00000000, 0x0001c006, 0x00100046,
        0x00000000, 0x08000032, 0x001020c2, 0x00000000, 0x0011b406, 0x00000002, 0x0001caa6, 0x00100406,
        0x00000000, 0x05000036, 0x00102032, 0x00000000, 0x0011bae6, 0x00000000, 0x0100003e,
    };
    static const D3D12_INPUT_ELEMENT_DESC layout_desc[] =
    {
        {"P", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
        {"P", 1, DXGI_FORMAT_R32G32_FLOAT, 0, 8, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
    };
    static const D3D12_SO_DECLARATION_ENTRY so_declaration[] =
    {
        {0, "SV_POSITION",  0, 0, 4, 0},
    };
    static const struct
    {
        struct vec2 p[2];
    }
    vertices[] =
    {
        {{{ 1.0f,  2.0f}, {  64.0f,  128.0f}}},
        {{{ 4.0f,  8.0f}, { 256.0f,  512.0f}}},
        {{{16.0f, 32.0f}, {1024.0f, 2048.0f}}},
    };
    static const struct triangle expected_triangle =
    {{
        {455.0f, 910.0f,  260.0f,  520.0f},
        {455.0f, 910.0f, 1040.0f, 2080.0f},
        {455.0f, 910.0f,   65.0f,  130.0f},
    }};
    unsigned int strides[] = {sizeof(struct vec4)};

    memset(&desc, 0, sizeof(desc));
    desc.root_signature_flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT
            | D3D12_ROOT_SIGNATURE_FLAG_ALLOW_STREAM_OUTPUT;
    desc.no_pipeline = true;
    if (!init_test_context(&context, &desc))
        return;
    command_list = context.list;
    queue = context.queue;

    input_layout.pInputElementDescs = layout_desc;
    input_layout.NumElements = ARRAY_SIZE(layout_desc);
    init_pipeline_state_desc(&pso_desc, context.root_signature, DXGI_FORMAT_UNKNOWN, NULL, NULL, &input_layout);
    pso_desc.VS = shader_bytecode(vs_code, sizeof(vs_code));
    pso_desc.HS = shader_bytecode(hs_code, sizeof(hs_code));
    pso_desc.DS = shader_bytecode(ds_code, sizeof(ds_code));
    memset(&pso_desc.PS, 0, sizeof(pso_desc.PS));
    pso_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_PATCH;
    pso_desc.StreamOutput.NumEntries = ARRAY_SIZE(so_declaration);
    pso_desc.StreamOutput.pSODeclaration = so_declaration;
    pso_desc.StreamOutput.pBufferStrides = strides;
    pso_desc.StreamOutput.NumStrides = ARRAY_SIZE(strides);
    pso_desc.StreamOutput.RasterizedStream = D3D12_SO_NO_RASTERIZED_STREAM;
    hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
    bug_if(is_mvk_device(context.device))
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);

    vb = create_upload_buffer(context.device, sizeof(vertices), vertices);
    vbv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(vb);
    vbv.StrideInBytes = sizeof(*vertices);
    vbv.SizeInBytes = sizeof(vertices);

    so_buffer = create_default_buffer(context.device, 4096,
            D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_STREAM_OUT);
    sobv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(so_buffer);
    sobv.SizeInBytes = 1024;
    sobv.BufferFilledSizeLocation = sobv.BufferLocation + sobv.SizeInBytes;

    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST);
    ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, 1, &vbv);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_SOSetTargets(command_list, 0, 1, &sobv);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, so_buffer,
            D3D12_RESOURCE_STATE_STREAM_OUT, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_buffer_readback_with_command_list(so_buffer, DXGI_FORMAT_UNKNOWN, &rb, queue, command_list);
    bug_if(is_mvk_device(context.device))
    check_triangles(&rb.rb, &expected_triangle, 1);
    release_resource_readback(&rb);

    ID3D12Resource_Release(so_buffer);
    ID3D12Resource_Release(vb);
    destroy_test_context(&context);
}

static void test_resource_arrays(void)
{
    ID3D12Resource *input_buffers[8], *output_buffers[6];
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    D3D12_UNORDERED_ACCESS_VIEW_DESC uav_desc;
    D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc;
    ID3D12GraphicsCommandList *command_list;
    struct d3d12_resource_readback rb;
    unsigned int descriptor_count;
    struct test_context context;
    ID3D12DescriptorHeap *heap;
    ID3D12CommandQueue *queue;
    ID3D12Device *device;
    unsigned int i;
    HRESULT hr;

    static const D3D12_DESCRIPTOR_RANGE descriptor_ranges[] =
    {
        {D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 8, 2, 2, 0},
        {D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 6, 1, 3, D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND},
    };

    static const struct uvec4 cb_data[] =
    {
        {0, 0},
        {1, 1},
        {0, 5},
        {1, 4},
        {2, 0},
        {3, 1},
    };

    static const D3D12_ROOT_PARAMETER root_parameters[] =
    {
        {D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE,
                    .DescriptorTable = {ARRAY_SIZE(descriptor_ranges), descriptor_ranges}},
        {D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS, .Constants = {2, 1, ARRAY_SIZE(cb_data) * 4}},
    };

    static const DWORD cs_code[] =
    {
#if 0
        cbuffer cb : register(b2, space1)
        {
            uint2 c[6];
        }

        Buffer<uint> t1[2] : register(t2, space2);
        Buffer<uint> t2[] : register(t4, space2);

        RWBuffer<uint> u1[2] : register(u1, space3);
        RWBuffer<uint> u2[] : register(u3, space3);

        [numthreads(1, 1, 1)]
        void main()
        {
            u1[c[0].x][0] = t1[c[0].y][0];
            u1[c[1].x][0] = t1[c[1].y][0];
            u2[c[2].x][0] = t2[c[2].y][0];
            u2[c[3].x][0] = t2[c[3].y][0];
            u2[c[4].x][0] = t2[c[4].y][0];
            u2[c[5].x][0] = t2[c[5].y][0];
        }
#endif
        0x43425844, 0xef615fd3, 0x708c1d93, 0xdb9908b4, 0xb1853d57, 0x00000001, 0x000004c8, 0x00000003,
        0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x00000474, 0x00050051, 0x0000011d, 0x0100086a,
        0x07000059, 0x00308e46, 0x00000000, 0x00000002, 0x00000002, 0x00000006, 0x00000001, 0x07000858,
        0x00307e46, 0x00000000, 0x00000002, 0x00000003, 0x00004444, 0x00000002, 0x07000858, 0x00307e46,
        0x00000001, 0x00000004, 0xffffffff, 0x00004444, 0x00000002, 0x0700089c, 0x0031ee46, 0x00000000,
        0x00000001, 0x00000002, 0x00004444, 0x00000003, 0x0700089c, 0x0031ee46, 0x00000001, 0x00000003,
        0xffffffff, 0x00004444, 0x00000003, 0x02000068, 0x00000001, 0x0400009b, 0x00000001, 0x00000001,
        0x00000001, 0x07000036, 0x00100012, 0x00000000, 0x0030801a, 0x00000000, 0x00000002, 0x00000000,
        0x0d00002d, 0x00100012, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
        0x06207e46, 0x00000000, 0x00000002, 0x0010000a, 0x00000000, 0x07000036, 0x00100022, 0x00000000,
        0x0030800a, 0x00000000, 0x00000002, 0x00000000, 0x0d0000a4, 0x0621e0f2, 0x00000000, 0x00000001,
        0x0010001a, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00100006,
        0x00000000, 0x07000036, 0x00100012, 0x00000000, 0x0030801a, 0x00000000, 0x00000002, 0x00000001,
        0x0d00002d, 0x00100012, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
        0x06207e46, 0x00000000, 0x00000002, 0x0010000a, 0x00000000, 0x07000036, 0x00100022, 0x00000000,
        0x0030800a, 0x00000000, 0x00000002, 0x00000001, 0x0d0000a4, 0x0621e0f2, 0x00000000, 0x00000001,
        0x0010001a, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00100006,
        0x00000000, 0x07000036, 0x00100012, 0x00000000, 0x0030801a, 0x00000000, 0x00000002, 0x00000002,
        0x0d00002d, 0x00100012, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
        0x06207e46, 0x00000001, 0x00000004, 0x0010000a, 0x00000000, 0x07000036, 0x00100022, 0x00000000,
        0x0030800a, 0x00000000, 0x00000002, 0x00000002, 0x0d0000a4, 0x0621e0f2, 0x00000001, 0x00000003,
        0x0010001a, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00100006,
        0x00000000, 0x07000036, 0x00100012, 0x00000000, 0x0030801a, 0x00000000, 0x00000002, 0x00000003,
        0x0d00002d, 0x00100012, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
        0x06207e46, 0x00000001, 0x00000004, 0x0010000a, 0x00000000, 0x07000036, 0x00100022, 0x00000000,
        0x0030800a, 0x00000000, 0x00000002, 0x00000003, 0x0d0000a4, 0x0621e0f2, 0x00000001, 0x00000003,
        0x0010001a, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00100006,
        0x00000000, 0x07000036, 0x00100012, 0x00000000, 0x0030801a, 0x00000000, 0x00000002, 0x00000004,
        0x0d00002d, 0x00100012, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
        0x06207e46, 0x00000001, 0x00000004, 0x0010000a, 0x00000000, 0x07000036, 0x00100022, 0x00000000,
        0x0030800a, 0x00000000, 0x00000002, 0x00000004, 0x0d0000a4, 0x0621e0f2, 0x00000001, 0x00000003,
        0x0010001a, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00100006,
        0x00000000, 0x07000036, 0x00100012, 0x00000000, 0x0030801a, 0x00000000, 0x00000002, 0x00000005,
        0x0d00002d, 0x00100012, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
        0x06207e46, 0x00000001, 0x00000004, 0x0010000a, 0x00000000, 0x07000036, 0x00100022, 0x00000000,
        0x0030800a, 0x00000000, 0x00000002, 0x00000005, 0x0d0000a4, 0x0621e0f2, 0x00000001, 0x00000003,
        0x0010001a, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00100006,
        0x00000000, 0x0100003e,
    };

    static const uint32_t srv_data[] = {0x1, 0x4, 0x9, 0x10, 0x19, 0x24, 0x31, 0x40};
    static const uint32_t uav_data[] = {0x1, 0x4, 0x40, 0x31, 0x9, 0x10};

    if (!init_compute_test_context(&context))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    memset(&root_signature_desc, 0, sizeof(root_signature_desc));
    root_signature_desc.NumParameters = ARRAY_SIZE(root_parameters);
    root_signature_desc.pParameters = root_parameters;
    hr = create_root_signature(device, &root_signature_desc, &context.root_signature);
    ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr);

    for (i = 0, descriptor_count = 0; i < ARRAY_SIZE(descriptor_ranges); ++i)
    {
        descriptor_count += descriptor_ranges[i].NumDescriptors;
    }
    heap = create_gpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, descriptor_count);

    for (i = 0; i < ARRAY_SIZE(input_buffers); ++i)
    {
        input_buffers[i] = create_default_buffer(device, sizeof(uint32_t),
                D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST);
        upload_buffer_data(input_buffers[i], 0, sizeof(srv_data[i]), &srv_data[i], queue, command_list);
        reset_command_list(command_list, context.allocator);
        transition_resource_state(command_list, input_buffers[i],
                D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);

        memset(&srv_desc, 0, sizeof(srv_desc));
        srv_desc.Format = DXGI_FORMAT_R32_UINT;
        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 = 1;
        ID3D12Device_CreateShaderResourceView(device, input_buffers[i], &srv_desc,
                get_cpu_descriptor_handle(&context, heap, i));
    }

    for (i = 0; i < ARRAY_SIZE(output_buffers); ++i)
    {
        output_buffers[i] = create_default_buffer(device, sizeof(uint32_t),
                D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

        memset(&uav_desc, 0, sizeof(uav_desc));
        uav_desc.Format = DXGI_FORMAT_R32_UINT;
        uav_desc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
        uav_desc.Buffer.FirstElement = 0;
        uav_desc.Buffer.NumElements = 1;
        ID3D12Device_CreateUnorderedAccessView(device, output_buffers[i], NULL, &uav_desc,
                get_cpu_descriptor_handle(&context, heap, ARRAY_SIZE(input_buffers) + i));
    }

    bug_if(is_mvk_device(device))
    context.pipeline_state = create_compute_pipeline_state(device, context.root_signature,
            shader_bytecode(cs_code, sizeof(cs_code)));
    if (!context.pipeline_state)
    {
        skip("Shader descriptor arrays are not supported.\n");
        goto done;
    }

    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_SetComputeRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &heap);
    ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(command_list,
            0, get_gpu_descriptor_handle(&context, heap, 0));
    ID3D12GraphicsCommandList_SetComputeRoot32BitConstants(command_list, 1, ARRAY_SIZE(cb_data) * 4, cb_data, 0);

    ID3D12GraphicsCommandList_Dispatch(command_list, 1, 1, 1);

    for (i = 0; i < ARRAY_SIZE(output_buffers); ++i)
    {
        vkd3d_test_push_context("buffer %u", i);
        transition_sub_resource_state(command_list, output_buffers[i], 0,
                D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
        get_buffer_readback_with_command_list(output_buffers[i], DXGI_FORMAT_R32_UINT, &rb, queue, command_list);
        check_readback_data_uint(&rb.rb, NULL, uav_data[i], 0);
        release_resource_readback(&rb);
        reset_command_list(command_list, context.allocator);
        vkd3d_test_pop_context();
    }

done:
    for (i = 0; i < ARRAY_SIZE(output_buffers); ++i)
        ID3D12Resource_Release(output_buffers[i]);
    for (i = 0; i < ARRAY_SIZE(input_buffers); ++i)
        ID3D12Resource_Release(input_buffers[i]);
    ID3D12DescriptorHeap_Release(heap);
    destroy_test_context(&context);
}

static void test_unbounded_resource_arrays(void)
{
    ID3D12Resource *constant_buffers[64], *input_buffers[32], *output_buffers[128];
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    D3D12_UNORDERED_ACCESS_VIEW_DESC uav_desc;
    D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc;
    D3D12_CONSTANT_BUFFER_VIEW_DESC cbv_desc;
    ID3D12GraphicsCommandList *command_list;
    ID3D12Resource *input_textures[32];
    struct d3d12_resource_readback rb;
    unsigned int i, heap_offset;
    struct test_context context;
    ID3D12DescriptorHeap *heap;
    ID3D12CommandQueue *queue;
    ID3D12Device *device;
    HRESULT hr;

    static const D3D12_DESCRIPTOR_RANGE descriptor_ranges[] =
    {
        {D3D12_DESCRIPTOR_RANGE_TYPE_SRV, UINT_MAX, 2, 1, 0},
        /* Test a buffer/texture descriptor type with a shader which accesses both. */
        {D3D12_DESCRIPTOR_RANGE_TYPE_SRV, UINT_MAX, 2, 2, 0},
        {D3D12_DESCRIPTOR_RANGE_TYPE_CBV, UINT_MAX, 2, 1, 64},
        {D3D12_DESCRIPTOR_RANGE_TYPE_UAV,        1, 1, 4, 128},
        {D3D12_DESCRIPTOR_RANGE_TYPE_UAV, UINT_MAX, 1, 1, 128},
        {D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 65, 1, 2, 127},
        {D3D12_DESCRIPTOR_RANGE_TYPE_UAV, UINT_MAX, 1, 3, 192},
    };

    static const D3D12_ROOT_PARAMETER root_parameters[] =
    {
        {D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE,
                    .DescriptorTable = {ARRAY_SIZE(descriptor_ranges), descriptor_ranges}},
    };

    static const DWORD cs_code[] =
    {
        /* Compiled with /res_may_alias (but it has no effect on the output from fxc 10.1). */
#if 0
        struct cb
        {
            uint value;
        };
        ConstantBuffer<cb> c1[] : register(b2, space1);

        Buffer<uint> t1[] : register(t2, space1);
        Texture2D<uint> t2[] : register(t2, space2);

        RWBuffer<uint> u1[] : register(u1, space1);
        RWBuffer<uint> u1_4 : register(u1, space4);
        RWBuffer<uint> u2[] : register(u2, space2);
        RWBuffer<uint> u3[] : register(u1, space3);

        [numthreads(64, 1, 1)]
        void main(uint id : SV_DispatchThreadID)
        {
            uint i = c1[NonUniformResourceIndex(id)].value;
            /* fxc emits a race condition error on the below statements apparently because it fails to account
             * for id being the thread id. The error check is skipped if the statements are conditional. */
            if (id < 64)
            {
                uint u;

                if (i < 32)
                    u = t1[NonUniformResourceIndex(i)][0];
                else
                    u = t2[NonUniformResourceIndex(i)][uint2(0, 0)];

                if (id > 0)
                    u1[NonUniformResourceIndex(id)][0] = u;
                else
                    u1_4[0] = u;
                /* If u2 is an alias of u1, this should copy u1. */
                u3[NonUniformResourceIndex(id)][0] = u2[NonUniformResourceIndex(id)][0];
            }
        }
#endif
        0x43425844, 0x8a5b49a5, 0xe0747f9a, 0xda1361d4, 0x060961bb, 0x00000001, 0x00000368, 0x00000003,
        0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x00000314, 0x00050051, 0x000000c5, 0x0100086a,
        0x07000859, 0x00308e46, 0x00000000, 0x00000002, 0xffffffff, 0x00000001, 0x00000001, 0x07000858,
        0x00307e46, 0x00000000, 0x00000002, 0xffffffff, 0x00004444, 0x00000001, 0x07001858, 0x00307e46,
        0x00000001, 0x00000002, 0xffffffff, 0x00004444, 0x00000002, 0x0700089c, 0x0031ee46, 0x00000000,
        0x00000001, 0xffffffff, 0x00004444, 0x00000001, 0x0700089c, 0x0031ee46, 0x00000001, 0x00000002,
        0xffffffff, 0x00004444, 0x00000002, 0x0700089c, 0x0031ee46, 0x00000002, 0x00000001, 0xffffffff,
        0x00004444, 0x00000003, 0x0700089c, 0x0031ee46, 0x00000003, 0x00000001, 0x00000001, 0x00004444,
        0x00000004, 0x0200005f, 0x00020012, 0x02000068, 0x00000001, 0x0400009b, 0x00000040, 0x00000001,
        0x00000001, 0x0600004f, 0x00100012, 0x00000000, 0x0002000a, 0x00004001, 0x00000040, 0x0304001f,
        0x0010000a, 0x00000000, 0x04000036, 0x00100012, 0x00000000, 0x0002000a, 0x0c00004f, 0x00100022,
        0x00000000, 0x8630800a, 0x00020001, 0x00000000, 0x00000002, 0x0010000a, 0x00000000, 0x00000000,
        0x00004001, 0x00000020, 0x0304001f, 0x0010001a, 0x00000000, 0x0a000036, 0x00100022, 0x00000000,
        0x8630800a, 0x00020001, 0x00000000, 0x00000002, 0x0010000a, 0x00000000, 0x00000000, 0x0e00002d,
        0x00100022, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x86207e16,
        0x00020001, 0x00000000, 0x00000002, 0x0010001a, 0x00000000, 0x01000012, 0x0a000036, 0x00100042,
        0x00000000, 0x8630800a, 0x00020001, 0x00000000, 0x00000002, 0x0010000a, 0x00000000, 0x00000000,
        0x0e00002d, 0x00100022, 0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
        0x86207e16, 0x00020001, 0x00000001, 0x00000002, 0x0010002a, 0x00000000, 0x01000015, 0x0204001f,
        0x0002000a, 0x0e0000a4, 0x8621e0f2, 0x00020001, 0x00000000, 0x00000001, 0x0010000a, 0x00000000,
        0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00100556, 0x00000000, 0x01000012,
        0x0b0000a4, 0x0021e0f2, 0x00000003, 0x00000001, 0x00004002, 0x00000000, 0x00000000, 0x00000000,
        0x00000000, 0x00100556, 0x00000000, 0x01000015, 0x0e0000a3, 0x00100022, 0x00000000, 0x00004002,
        0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x8621ee16, 0x00020001, 0x00000001, 0x00000002,
        0x0010000a, 0x00000000, 0x0e0000a4, 0x8621e0f2, 0x00020001, 0x00000002, 0x00000001, 0x0010000a,
        0x00000000, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00100556, 0x00000000,
        0x01000015, 0x0100003e,
    };

    if (test_options.use_warp_device)
    {
        skip("Broken on WARP.\n");
        return;
    }

    if (!init_compute_test_context(&context))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    if (get_resource_binding_tier(device) < D3D12_RESOURCE_BINDING_TIER_2)
    {
        skip("Device doesn't support resource binding tier 2.\n");
        goto done;
    }

    memset(&root_signature_desc, 0, sizeof(root_signature_desc));
    root_signature_desc.NumParameters = ARRAY_SIZE(root_parameters);
    root_signature_desc.pParameters = root_parameters;
    hr = create_root_signature(device, &root_signature_desc, &context.root_signature);
    ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr);
    if (FAILED(hr))
        goto done;

    heap = create_gpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 256);

    for (i = 0; i < ARRAY_SIZE(input_buffers); ++i)
    {
        uint32_t srv_data = i ^ 0x35;
        input_buffers[i] = create_default_buffer(device, sizeof(uint32_t),
                D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST);
        upload_buffer_data(input_buffers[i], 0, sizeof(srv_data), &srv_data, queue, command_list);
        reset_command_list(command_list, context.allocator);
        transition_resource_state(command_list, input_buffers[i],
                D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);

        memset(&srv_desc, 0, sizeof(srv_desc));
        srv_desc.Format = DXGI_FORMAT_R32_UINT;
        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 = 1;
        ID3D12Device_CreateShaderResourceView(device, input_buffers[i], &srv_desc,
                get_cpu_descriptor_handle(&context, heap, i));
    }
    heap_offset = ARRAY_SIZE(input_buffers);

    for (i = 0; i < ARRAY_SIZE(input_textures); ++i)
    {
        uint32_t srv_data = (i + heap_offset) ^ 0x35;
        D3D12_SUBRESOURCE_DATA data;

        input_textures[i] = create_default_texture2d(device, 1, 1, 1, 1, DXGI_FORMAT_R32_UINT, D3D12_RESOURCE_FLAG_NONE,
            D3D12_RESOURCE_STATE_COPY_DEST);
        data.pData = &srv_data;
        data.RowPitch = sizeof(uint32_t);
        data.SlicePitch = data.RowPitch;
        upload_texture_data(input_textures[i], &data, 1, queue, command_list);
        reset_command_list(command_list, context.allocator);
        transition_resource_state(command_list, input_textures[i],
                D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);

        memset(&srv_desc, 0, sizeof(srv_desc));
        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.MipLevels = 1;
        srv_desc.Texture2D.MostDetailedMip = 0;
        srv_desc.Texture2D.PlaneSlice = 0;
        srv_desc.Texture2D.ResourceMinLODClamp = 0;
        ID3D12Device_CreateShaderResourceView(device, input_textures[i], &srv_desc,
                get_cpu_descriptor_handle(&context, heap, heap_offset + i));
    }
    heap_offset += ARRAY_SIZE(input_textures);

    for (i = 0; i < ARRAY_SIZE(constant_buffers); ++i)
    {
        uint32_t cb_data = 63 - i;
        constant_buffers[i] = create_default_buffer(device, D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT,
                D3D12_RESOURCE_FLAG_NONE, D3D12_RESOURCE_STATE_COPY_DEST);
        upload_buffer_data(constant_buffers[i], 0, sizeof(cb_data), &cb_data, queue, command_list);
        reset_command_list(command_list, context.allocator);
        transition_resource_state(command_list, constant_buffers[i],
                D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER);

        cbv_desc.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(constant_buffers[i]);
        cbv_desc.SizeInBytes = align(sizeof(cb_data), D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT);
        ID3D12Device_CreateConstantBufferView(context.device, &cbv_desc,
                get_cpu_descriptor_handle(&context, heap, heap_offset + i));
    }
    heap_offset += ARRAY_SIZE(constant_buffers);

    for (i = 0; i < ARRAY_SIZE(output_buffers); ++i)
    {
        output_buffers[i] = create_default_buffer(device, sizeof(uint32_t),
                D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

        memset(&uav_desc, 0, sizeof(uav_desc));
        uav_desc.Format = DXGI_FORMAT_R32_UINT;
        uav_desc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
        uav_desc.Buffer.FirstElement = 0;
        uav_desc.Buffer.NumElements = 1;
        ID3D12Device_CreateUnorderedAccessView(device, output_buffers[i], NULL,
                &uav_desc, get_cpu_descriptor_handle(&context, heap, heap_offset + i));
    }

    context.pipeline_state = create_compute_pipeline_state(device, context.root_signature,
            shader_bytecode(cs_code, sizeof(cs_code)));

    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_SetComputeRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 1, &heap);
    ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(command_list,
            0, get_gpu_descriptor_handle(&context, heap, 0));

    ID3D12GraphicsCommandList_Dispatch(command_list, 1, 1, 1);

    for (i = 0; i < ARRAY_SIZE(output_buffers); ++i)
    {
        vkd3d_test_push_context("buffer %u", i);
        transition_sub_resource_state(command_list, output_buffers[i], 0,
                D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
        get_buffer_readback_with_command_list(output_buffers[i], DXGI_FORMAT_R32_UINT, &rb, queue, command_list);
        /* Buffers at index >= 64 are aliased. */
        check_readback_data_uint(&rb.rb, NULL, (i < 64 ? 63 - i : 127 - i) ^ 0x35, 0);
        release_resource_readback(&rb);
        reset_command_list(command_list, context.allocator);
        vkd3d_test_pop_context();
    }

    for (i = 0; i < ARRAY_SIZE(output_buffers); ++i)
        ID3D12Resource_Release(output_buffers[i]);
    for (i = 0; i < ARRAY_SIZE(input_buffers); ++i)
        ID3D12Resource_Release(input_buffers[i]);
    for (i = 0; i < ARRAY_SIZE(input_textures); ++i)
        ID3D12Resource_Release(input_textures[i]);
    for (i = 0; i < ARRAY_SIZE(constant_buffers); ++i)
        ID3D12Resource_Release(constant_buffers[i]);
    ID3D12DescriptorHeap_Release(heap);
done:
    destroy_test_context(&context);
}

static void test_unbounded_samplers(void)
{
    ID3D12DescriptorHeap *heap, *sampler_heap, *heaps[2];
    ID3D12Resource *input_texture, *output_buffer;
    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
    D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc;
    ID3D12GraphicsCommandList *command_list;
    struct d3d12_resource_readback rb;
    D3D12_SAMPLER_DESC sampler_desc;
    D3D12_SUBRESOURCE_DATA data;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Device *device;
    unsigned int i;
    HRESULT hr;

    static const D3D12_DESCRIPTOR_RANGE descriptor_ranges[] =
    {
        {D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 1, 1, D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND},
        {D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, UINT_MAX, 1, 1, D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND},
    };

    static const D3D12_ROOT_PARAMETER root_parameters[] =
    {
        {D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE, .DescriptorTable = {1, &descriptor_ranges[0]}},
        {D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE, .DescriptorTable = {1, &descriptor_ranges[1]}},
        {D3D12_ROOT_PARAMETER_TYPE_UAV, .Descriptor = {1, 1}},
    };

    static const DWORD cs_code[] =
    {
#if 0
        Texture2D<float> t1 : register(t1, space1);
        SamplerState s1[][2] : register(s1, space1);
        RWByteAddressBuffer u1 : register(u1, space1);

        [numthreads(64, 1, 1)]
        void main(uint id : SV_DispatchThreadID)
        {
            /* wrap yields address 0.1; clamp yields address 1.0.
             * Samplers alternate (wrap, wrap), (clamp, wrap), (wrap, clamp), (clamp, clamp). */
            uint value = t1.SampleLevel(s1[NonUniformResourceIndex(id / 2)][NonUniformResourceIndex(id % 2)], float2(1.1, 1.1), 0.0);
            u1.Store(4 * id, value);
        }
#endif
        0x43425844, 0x19feacce, 0xef7000f7, 0xd6411d98, 0x890a6fa4, 0x00000001, 0x00000178, 0x00000003,
        0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
        0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x00000124, 0x00050051, 0x00000049, 0x0100086a,
        0x0600005a, 0x00306e46, 0x00000000, 0x00000001, 0xffffffff, 0x00000001, 0x07001858, 0x00307e46,
        0x00000000, 0x00000001, 0x00000001, 0x00005555, 0x00000001, 0x0600009d, 0x0031ee46, 0x00000000,
        0x00000001, 0x00000001, 0x00000001, 0x0200005f, 0x00020012, 0x02000068, 0x00000001, 0x0400009b,
        0x00000040, 0x00000001, 0x00000001, 0x04000036, 0x00100012, 0x00000000, 0x0002000a, 0x13000048,
        0x00100012, 0x00000000, 0x00004002, 0x3f8ccccd, 0x3f8ccccd, 0x00000000, 0x00000000, 0x00207e46,
        0x00000000, 0x00000001, 0x86206000, 0x00020001, 0x00000000, 0x00000001, 0x0010000a, 0x00000000,
        0x00004001, 0x00000000, 0x0500001c, 0x00100012, 0x00000000, 0x0010000a, 0x00000000, 0x06000029,
        0x00100022, 0x00000000, 0x0002000a, 0x00004001, 0x00000002, 0x080000a6, 0x0021e012, 0x00000000,
        0x00000001, 0x0010001a, 0x00000000, 0x0010000a, 0x00000000, 0x0100003e,
    };

    static const float texture_data[] = {10.0f, 11.0f, 12.0f, 13.0f};

    if (!init_compute_test_context(&context))
        return;
    device = context.device;
    command_list = context.list;
    queue = context.queue;

    if (get_resource_binding_tier(device) < D3D12_RESOURCE_BINDING_TIER_2)
    {
        skip("Device doesn't support resource binding tier 2.\n");
        goto done;
    }

    root_signature_desc.NumParameters = ARRAY_SIZE(root_parameters);
    root_signature_desc.Flags = 0;
    root_signature_desc.NumStaticSamplers = 0;
    root_signature_desc.pStaticSamplers = NULL;
    root_signature_desc.pParameters = root_parameters;

    hr = create_root_signature(device, &root_signature_desc, &context.root_signature);
    ok(SUCCEEDED(hr), "Failed to create root signature, hr %#x.\n", hr);
    if (FAILED(hr))
        goto done;

    input_texture = create_default_texture2d(device, 2, 2, 1, 1, DXGI_FORMAT_R32_FLOAT, D3D12_RESOURCE_FLAG_NONE,
            D3D12_RESOURCE_STATE_COPY_DEST);
    data.pData = texture_data;
    data.RowPitch = 2 * sizeof(uint32_t);
    data.SlicePitch = 2 * data.RowPitch;
    upload_texture_data(input_texture, &data, 1, queue, command_list);
    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, input_texture, D3D12_RESOURCE_STATE_COPY_DEST,
            D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);

    output_buffer = create_default_buffer(device, 64 * sizeof(uint32_t), D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS,
            D3D12_RESOURCE_STATE_UNORDERED_ACCESS);

    context.pipeline_state = create_compute_pipeline_state(device,
            context.root_signature, shader_bytecode(cs_code, sizeof(cs_code)));

    heap = create_gpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1);
    sampler_heap = create_gpu_descriptor_heap(device, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, 64);

    srv_desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
    srv_desc.Format = DXGI_FORMAT_R32_FLOAT;
    srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
    srv_desc.Texture2D.MipLevels = 1;
    srv_desc.Texture2D.MostDetailedMip = 0;
    srv_desc.Texture2D.PlaneSlice = 0;
    srv_desc.Texture2D.ResourceMinLODClamp = 0;
    ID3D12Device_CreateShaderResourceView(device, input_texture, &srv_desc,
            ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(heap));

    for (i = 0; i < 64; ++i)
    {
        memset(&sampler_desc, 0, sizeof(sampler_desc));
        sampler_desc.Filter = D3D12_FILTER_MIN_MAG_MIP_POINT;
        sampler_desc.AddressU = (i & 1) ? D3D12_TEXTURE_ADDRESS_MODE_CLAMP : D3D12_TEXTURE_ADDRESS_MODE_WRAP;
        sampler_desc.AddressV = (i & 2) ? D3D12_TEXTURE_ADDRESS_MODE_CLAMP : D3D12_TEXTURE_ADDRESS_MODE_WRAP;
        sampler_desc.AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
        ID3D12Device_CreateSampler(device, &sampler_desc, get_cpu_sampler_handle(&context, sampler_heap, i));
    }

    ID3D12GraphicsCommandList_SetComputeRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    heaps[0] = heap; heaps[1] = sampler_heap;
    ID3D12GraphicsCommandList_SetDescriptorHeaps(command_list, 2, heaps);
    ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(command_list, 0, ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(heap));
    ID3D12GraphicsCommandList_SetComputeRootDescriptorTable(command_list, 1, ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(sampler_heap));
    ID3D12GraphicsCommandList_SetComputeRootUnorderedAccessView(command_list, 2, ID3D12Resource_GetGPUVirtualAddress(output_buffer));
    ID3D12GraphicsCommandList_Dispatch(command_list, 1, 1, 1);

    transition_resource_state(command_list, output_buffer, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
    get_buffer_readback_with_command_list(output_buffer, DXGI_FORMAT_UNKNOWN, &rb, queue, command_list);
    for (i = 0; i < 64; ++i)
    {
        unsigned int value = get_readback_uint(&rb.rb, i, 0, 0);
        unsigned int expected = 10 + (i % 4);
        ok(value == expected, "Got %u, expected %u at %u.\n", value, expected, i);
    }
    release_resource_readback(&rb);

    ID3D12Resource_Release(input_texture);
    ID3D12Resource_Release(output_buffer);
    ID3D12DescriptorHeap_Release(heap);
    ID3D12DescriptorHeap_Release(sampler_heap);
done:
    destroy_test_context(&context);
}

static void test_clock_calibration(void)
{
    uint64_t cpu_times[2] = {0}, gpu_times[2] = {0};
    struct test_context context;
    HRESULT hr;

    if (!init_test_context(&context, NULL))
        return;

    hr = ID3D12CommandQueue_GetClockCalibration(context.queue, &gpu_times[0], &cpu_times[0]);
    ok(hr == S_OK, "Failed to retrieve calibrated timestamps, hr %#x.\n", hr);

    vkd3d_sleep(100);

    hr = ID3D12CommandQueue_GetClockCalibration(context.queue, &gpu_times[1], &cpu_times[1]);
    ok(hr == S_OK, "Failed to retrieve calibrated timestamps, hr %#x.\n", hr);

    bug_if(is_macos_lt(15, 0, 0))
    ok(gpu_times[1] > gpu_times[0], "Inconsistent GPU timestamps %"PRIu64" and %"PRIu64".\n",
            gpu_times[0], gpu_times[1]);
    ok(cpu_times[1] > cpu_times[0], "Inconsistent CPU timestamps %"PRIu64" and %"PRIu64".\n",
            cpu_times[0], cpu_times[1]);

    destroy_test_context(&context);
}

static void test_readback_map_stability(void)
{
    D3D12_TEXTURE_COPY_LOCATION dst_location, src_location;
    ID3D12GraphicsCommandList *command_list;
    unsigned int width, height, row_pitch;
    D3D12_RESOURCE_DESC resource_desc;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Resource *buffer;
    D3D12_RANGE read_range;
    ID3D12Device *device;
    void *data, *data2;
    uint32_t colour;
    HRESULT hr;

    static const float green[] = {0.0f, 1.0f, 0.0f, 1.0f};
    static const float blue[] = {0.0f, 0.0f, 1.0f, 1.0f};

    if (!init_test_context(&context, NULL))
        return;
    device = context.device;
    queue = context.queue;
    command_list = context.list;

    resource_desc = ID3D12Resource_GetDesc(context.render_target);
    width = align(resource_desc.Width, format_block_width(resource_desc.Format));
    height = align(resource_desc.Height, format_block_height(resource_desc.Format));
    row_pitch = align(width * format_size(resource_desc.Format), D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
    buffer = create_readback_buffer(device, row_pitch * height);

    dst_location.pResource = buffer;
    dst_location.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
    dst_location.PlacedFootprint.Offset = 0;
    dst_location.PlacedFootprint.Footprint.Format = resource_desc.Format;
    dst_location.PlacedFootprint.Footprint.Width = width;
    dst_location.PlacedFootprint.Footprint.Height = height;
    dst_location.PlacedFootprint.Footprint.Depth = 1;
    dst_location.PlacedFootprint.Footprint.RowPitch = row_pitch;

    src_location.pResource = context.render_target;
    src_location.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
    src_location.SubresourceIndex = 0;

    read_range.Begin = 0;
    read_range.End = row_pitch * height;

    ID3D12GraphicsCommandList_ClearRenderTargetView(context.list, context.rtv, green, 0, NULL);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);

    ID3D12GraphicsCommandList_CopyTextureRegion(command_list, &dst_location, 0, 0, 0, &src_location, NULL);

    hr = ID3D12GraphicsCommandList_Close(command_list);
    assert_that(hr == S_OK, "Failed to close command list, hr %#x.\n", hr);
    exec_command_list(queue, command_list);
    wait_queue_idle(device, queue);

    hr = ID3D12Resource_Map(buffer, 0, &read_range, &data);
    assert_that(hr == S_OK, "Failed to map readback buffer, hr %#x.\n", hr);

    colour = *(uint32_t *)data;
    ok(colour == 0xff00ff00, "Got colour %08x.\n", colour);

    ID3D12Resource_Unmap(buffer, 0, NULL);

    colour = *(uint32_t *)data;
    ok(colour == 0xff00ff00, "Got colour %08x.\n", colour);

    reset_command_list(command_list, context.allocator);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET);

    ID3D12GraphicsCommandList_ClearRenderTargetView(context.list, context.rtv, blue, 0, NULL);
    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);

    ID3D12GraphicsCommandList_CopyTextureRegion(command_list, &dst_location, 0, 0, 0, &src_location, NULL);

    hr = ID3D12GraphicsCommandList_Close(command_list);
    assert_that(hr == S_OK, "Failed to close command list, hr %#x.\n", hr);
    exec_command_list(queue, command_list);
    wait_queue_idle(device, queue);

    colour = *(uint32_t *)data;
    ok(colour == 0xffff0000, "Got colour %08x.\n", colour);

    hr = ID3D12Resource_Map(buffer, 0, &read_range, &data2);
    assert_that(hr == S_OK, "Failed to map readback buffer, hr %#x.\n", hr);

    ok(data2 == data, "Expected map pointer to be stable.\n");
    colour = *(uint32_t *)data2;
    ok(colour == 0xffff0000, "Got colour %08x.\n", colour);

    ID3D12Resource_Unmap(buffer, 0, NULL);

    ID3D12Resource_Release(buffer);

    destroy_test_context(&context);
}

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 - 1) / tile_size;
}

static void test_get_resource_tiling(void)
{
    UINT resource_tile_count, tilings_count;
    D3D12_TILED_RESOURCES_TIER tiled_tier;
    D3D12_PACKED_MIP_INFO packed_mip_info;
    D3D12_SUBRESOURCE_TILING tilings[64];
    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,  0,  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 },
    };
    static const D3D12_SUBRESOURCE_TILING uninit_tiling = {0xdeadbeef, 0xdead, 0xbeef, 0xdeadbeef};

    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;

    if ((tiled_tier = get_tiled_resources_tier(context.device)) < D3D12_TILED_RESOURCES_TIER_1)
    {
        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 tilings_count is NULL, tilings is ignored. */
    tilings[0] = uninit_tiling;
    ID3D12Device_GetResourceTiling(context.device, resource, NULL, NULL, NULL, NULL, 0, tilings);
    ok(!memcmp(&tilings[0], &uninit_tiling, sizeof(uninit_tiling)), "Mismatch.\n");

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

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

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

    tilings_count = 1;
    memset(&tilings, 0xaa, sizeof(tilings));
    ID3D12Device_GetResourceTiling(context.device, resource, NULL, NULL, NULL, &tilings_count, 2, tilings);
    ok(tilings_count == 1, "Unexpected tiling count %u.\n", tilings_count);
    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++, vkd3d_test_pop_context())
    {
        unsigned int tile_index = 0;
        vkd3d_test_push_context("test %u", i);

        if (tests[i].min_tier > tiled_tier)
        {
            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));

        resource_tile_count = 0xdeadbeef;
        tilings_count = 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, &resource_tile_count, &packed_mip_info, &tile_shape, &tilings_count, 0, tilings);
        assert(tilings_count <= ARRAY_SIZE(tilings));

        ok(resource_tile_count != 0xdeadbeef && resource_tile_count >= tests[i].expected_tile_count,
                "Unexpected resource tile count %u.\n", resource_tile_count);
        ok(tilings_count == tests[i].expected_tiling_count, "Unexpected subresource tiling count %u.\n", tilings_count);

        ok(packed_mip_info.NumStandardMips != 0xaa && 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.NumPackedMips != 0xaa
                && (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(resource_tile_count == tile_index + packed_mip_info.NumTilesForPackedMips,
                "Unexpected resource tile count %u.\n", resource_tile_count);
        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);
    }

    destroy_test_context(&context);
}

static void test_hull_shader_punned_array(void)
{
    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
    ID3D12GraphicsCommandList *command_list;
    struct test_context_desc desc;
    struct test_context context;
    ID3D12CommandQueue *queue;
    HRESULT hr;

    static const DWORD hs_code[] =
    {
#if 0
        float4 tess_factor;
        uint u;

        struct patch_in_data
        {
            float3 f0 : FOO0;
            float4 f1 : FOO1;
            float3 f2 : FOO2;
            float4 position : P;
        };

        struct patch_out_data
        {
            float4 position : SV_Position;
            float3 f0 : FOO0;
            float4 f1 : FOO1;
            float3 f2 : FOO2;
        };

        struct patch_constant_data
        {
            float edges[3] : SV_TessFactor;
            float inside : SV_InsideTessFactor;
            float3 f : FOO;
        };

        void patch_constant(InputPatch<patch_in_data, 3> input, out patch_constant_data output)
        {
            output.edges[0] = tess_factor.x;
            output.edges[1] = tess_factor.y;
            output.edges[2] = tess_factor.z;
            output.inside = tess_factor.w;
            /* Compiles into punned array access to f0-f3 using dcl_indexrange. */
            float3 f[3] = {input[0].f0, input[0].f1.xyz, input[0].f2};
            output.f = f[u];
        }

        [domain("tri")]
        [outputcontrolpoints(3)]
        [partitioning("integer")]
        [outputtopology("triangle_cw")]
        [patchconstantfunc("patch_constant")]
        patch_out_data hs_main(InputPatch<patch_in_data, 3> input, uint i : SV_OutputControlPointID)
        {
            patch_out_data result;
            result.position = input[i].position;
            result.f0 = input[i].f0;
            result.f1 = input[i].f1;
            /* Read a value with more components than the first element in dcl_indexrange. Without
             * special handling, this would fail because the dcl_indexrange instruction uses only
             * the mask from element 0. */
            result.f2 = input[0].f1.yzw;
            return result;
        }

        [domain("tri")]
        void ds_main(patch_constant_data input,
                float3 tess_coord : SV_DomainLocation,
                const OutputPatch<patch_out_data, 3> patch,
                out patch_out_data output)
        {
            output.position = tess_coord.x * patch[0].position
                    + tess_coord.y * patch[1].position
                    + tess_coord.z * patch[2].position;
            output.f0.x = tess_coord.x * patch[0].f0.x + tess_coord.y * patch[1].f0.x + tess_coord.z * patch[2].f0.x;
            output.f0.y = tess_coord.x * patch[0].f0.y + tess_coord.y * patch[1].f0.y + tess_coord.z * patch[2].f0.y;
            output.f0.z = tess_coord.x * patch[0].f0.z + tess_coord.y * patch[1].f0.z + tess_coord.z * patch[2].f0.z;
            output.f1 = float4(input.f, 0.0);
            output.f2 = patch[0].f2;
        }

        void vs_main(uint id : SV_VertexID, out patch_in_data output)
        {
            float2 coords = float2((id << 1) & 2, id & 2);
            output.position = float4(coords * float2(2, -2) + float2(-1, 1), 0, 1);
            output.f0 = float3(0.2, 0.4, 0.1);
            output.f1 = float4(0.6, 0.8, 0.3, 0.5);
            output.f2 = float3(0.7, 0.9, 1.0);
        }

        float4 ps_main(patch_out_data input) : sv_target
        {
            return float4(input.f0.xy, input.f1.x, input.f2.x);
        }
#endif
        0x43425844, 0xca86855e, 0xb4676233, 0xb42762ad, 0x825dfc5f, 0x00000001, 0x000005b4, 0x00000004,
        0x00000030, 0x000000a8, 0x00000128, 0x000001d8, 0x4e475349, 0x00000070, 0x00000004, 0x00000008,
        0x00000068, 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000707, 0x00000068, 0x00000001,
        0x00000000, 0x00000003, 0x00000001, 0x00000f0f, 0x00000068, 0x00000002, 0x00000000, 0x00000003,
        0x00000002, 0x00000707, 0x0000006c, 0x00000000, 0x00000000, 0x00000003, 0x00000003, 0x00000f0f,
        0x004f4f46, 0xabab0050, 0x4e47534f, 0x00000078, 0x00000004, 0x00000008, 0x00000068, 0x00000000,
        0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x00000074, 0x00000000, 0x00000000, 0x00000003,
        0x00000001, 0x00000807, 0x00000074, 0x00000001, 0x00000000, 0x00000003, 0x00000002, 0x0000000f,
        0x00000074, 0x00000002, 0x00000000, 0x00000003, 0x00000003, 0x00000807, 0x505f5653, 0x7469736f,
        0x006e6f69, 0x004f4f46, 0x47534350, 0x000000a8, 0x00000005, 0x00000008, 0x00000080, 0x00000000,
        0x0000000d, 0x00000003, 0x00000000, 0x00000e01, 0x0000008e, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x0000010e, 0x00000080, 0x00000001, 0x0000000d, 0x00000003, 0x00000001, 0x00000e01,
        0x00000080, 0x00000002, 0x0000000d, 0x00000003, 0x00000002, 0x00000e01, 0x00000092, 0x00000000,
        0x0000000e, 0x00000003, 0x00000003, 0x00000e01, 0x545f5653, 0x46737365, 0x6f746361, 0x4f460072,
        0x5653004f, 0x736e495f, 0x54656469, 0x46737365, 0x6f746361, 0xabab0072, 0x58454853, 0x000003d4,
        0x00030050, 0x000000f5, 0x01000071, 0x01001893, 0x01001894, 0x01001095, 0x01000896, 0x01001897,
        0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000002, 0x01000072, 0x0200005f, 0x00016000,
        0x0400005f, 0x00201072, 0x00000003, 0x00000000, 0x0400005f, 0x002010f2, 0x00000003, 0x00000001,
        0x0400005f, 0x002010f2, 0x00000003, 0x00000003, 0x03000065, 0x001020f2, 0x00000000, 0x03000065,
        0x00102072, 0x00000001, 0x03000065, 0x001020f2, 0x00000002, 0x03000065, 0x00102072, 0x00000003,
        0x02000068, 0x00000001, 0x04000036, 0x00100012, 0x00000000, 0x00016001, 0x07000036, 0x001020f2,
        0x00000000, 0x00a01e46, 0x0010000a, 0x00000000, 0x00000003, 0x07000036, 0x00102072, 0x00000001,
        0x00a01246, 0x0010000a, 0x00000000, 0x00000000, 0x07000036, 0x001020f2, 0x00000002, 0x00a01e46,
        0x0010000a, 0x00000000, 0x00000001, 0x06000036, 0x00102072, 0x00000003, 0x00201796, 0x00000000,
        0x00000001, 0x0100003e, 0x01000073, 0x04000067, 0x00102012, 0x00000000, 0x00000011, 0x06000036,
        0x00102012, 0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x0100003e, 0x01000073, 0x04000067,
        0x00102012, 0x00000001, 0x00000012, 0x06000036, 0x00102012, 0x00000001, 0x0020801a, 0x00000000,
        0x00000000, 0x0100003e, 0x01000073, 0x04000067, 0x00102012, 0x00000002, 0x00000013, 0x06000036,
        0x00102012, 0x00000002, 0x0020802a, 0x00000000, 0x00000000, 0x0100003e, 0x01000073, 0x04000067,
        0x00102012, 0x00000003, 0x00000014, 0x06000036, 0x00102012, 0x00000003, 0x0020803a, 0x00000000,
        0x00000000, 0x0100003e, 0x01000073, 0x0400005f, 0x00219012, 0x00000003, 0x00000000, 0x0400005f,
        0x00219012, 0x00000003, 0x00000001, 0x0400005f, 0x00219012, 0x00000003, 0x00000002, 0x0400005f,
        0x00219012, 0x00000003, 0x00000003, 0x03000065, 0x00102022, 0x00000000, 0x02000068, 0x00000001,
        0x0500005b, 0x00219012, 0x00000003, 0x00000000, 0x00000004, 0x06000036, 0x00100012, 0x00000000,
        0x0020800a, 0x00000000, 0x00000001, 0x07000036, 0x00102022, 0x00000000, 0x0421900a, 0x00000000,
        0x0010000a, 0x00000000, 0x0100003e, 0x01000073, 0x0400005f, 0x00219022, 0x00000003, 0x00000000,
        0x0400005f, 0x00219022, 0x00000003, 0x00000001, 0x0400005f, 0x00219022, 0x00000003, 0x00000002,
        0x0400005f, 0x00219022, 0x00000003, 0x00000003, 0x03000065, 0x00102042, 0x00000000, 0x02000068,
        0x00000001, 0x0500005b, 0x00219022, 0x00000003, 0x00000000, 0x00000004, 0x06000036, 0x00100012,
        0x00000000, 0x0020800a, 0x00000000, 0x00000001, 0x07000036, 0x00102042, 0x00000000, 0x0421901a,
        0x00000000, 0x0010000a, 0x00000000, 0x0100003e, 0x01000073, 0x0400005f, 0x00219042, 0x00000003,
        0x00000000, 0x0400005f, 0x00219042, 0x00000003, 0x00000001, 0x0400005f, 0x00219042, 0x00000003,
        0x00000002, 0x0400005f, 0x00219042, 0x00000003, 0x00000003, 0x03000065, 0x00102082, 0x00000000,
        0x02000068, 0x00000001, 0x0500005b, 0x00219042, 0x00000003, 0x00000000, 0x00000004, 0x06000036,
        0x00100012, 0x00000000, 0x0020800a, 0x00000000, 0x00000001, 0x07000036, 0x00102082, 0x00000000,
        0x0421902a, 0x00000000, 0x0010000a, 0x00000000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE hs = {hs_code, sizeof(hs_code)};
    static const DWORD ds_code[] =
    {
        0x43425844, 0x25772b56, 0xf9f25be8, 0xf1e02cc3, 0xb95e5380, 0x00000001, 0x00000388, 0x00000004,
        0x00000030, 0x000000b0, 0x00000160, 0x000001e0, 0x4e475349, 0x00000078, 0x00000004, 0x00000008,
        0x00000068, 0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x00000f0f, 0x00000074, 0x00000000,
        0x00000000, 0x00000003, 0x00000001, 0x00000707, 0x00000074, 0x00000001, 0x00000000, 0x00000003,
        0x00000002, 0x0000000f, 0x00000074, 0x00000002, 0x00000000, 0x00000003, 0x00000003, 0x00000707,
        0x505f5653, 0x7469736f, 0x006e6f69, 0x004f4f46, 0x47534350, 0x000000a8, 0x00000005, 0x00000008,
        0x00000080, 0x00000000, 0x0000000d, 0x00000003, 0x00000000, 0x00000001, 0x0000008e, 0x00000000,
        0x00000000, 0x00000003, 0x00000000, 0x00000e0e, 0x00000080, 0x00000001, 0x0000000d, 0x00000003,
        0x00000001, 0x00000001, 0x00000080, 0x00000002, 0x0000000d, 0x00000003, 0x00000002, 0x00000001,
        0x00000092, 0x00000000, 0x0000000e, 0x00000003, 0x00000003, 0x00000001, 0x545f5653, 0x46737365,
        0x6f746361, 0x4f460072, 0x5653004f, 0x736e495f, 0x54656469, 0x46737365, 0x6f746361, 0xabab0072,
        0x4e47534f, 0x00000078, 0x00000004, 0x00000008, 0x00000068, 0x00000000, 0x00000001, 0x00000003,
        0x00000000, 0x0000000f, 0x00000074, 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x00000807,
        0x00000074, 0x00000001, 0x00000000, 0x00000003, 0x00000002, 0x0000000f, 0x00000074, 0x00000002,
        0x00000000, 0x00000003, 0x00000003, 0x00000807, 0x505f5653, 0x7469736f, 0x006e6f69, 0x004f4f46,
        0x58454853, 0x000001a0, 0x00040050, 0x00000068, 0x01001893, 0x01001095, 0x0100086a, 0x0300005f,
        0x0011b0e2, 0x00000000, 0x0200005f, 0x0001c072, 0x0400005f, 0x002190f2, 0x00000003, 0x00000000,
        0x0400005f, 0x00219072, 0x00000003, 0x00000001, 0x0400005f, 0x00219072, 0x00000003, 0x00000003,
        0x04000067, 0x001020f2, 0x00000000, 0x00000001, 0x03000065, 0x00102072, 0x00000001, 0x03000065,
        0x001020f2, 0x00000002, 0x03000065, 0x00102072, 0x00000003, 0x02000068, 0x00000001, 0x07000038,
        0x001000f2, 0x00000000, 0x0001c556, 0x00219e46, 0x00000001, 0x00000000, 0x09000032, 0x001000f2,
        0x00000000, 0x0001c006, 0x00219e46, 0x00000000, 0x00000000, 0x00100e46, 0x00000000, 0x09000032,
        0x001020f2, 0x00000000, 0x0001caa6, 0x00219e46, 0x00000002, 0x00000000, 0x00100e46, 0x00000000,
        0x07000038, 0x00100072, 0x00000000, 0x0001c556, 0x00219246, 0x00000001, 0x00000001, 0x09000032,
        0x00100072, 0x00000000, 0x0001c006, 0x00219246, 0x00000000, 0x00000001, 0x00100246, 0x00000000,
        0x09000032, 0x00102072, 0x00000001, 0x0001caa6, 0x00219246, 0x00000002, 0x00000001, 0x00100246,
        0x00000000, 0x05000036, 0x00102072, 0x00000002, 0x0011b796, 0x00000000, 0x05000036, 0x00102082,
        0x00000002, 0x00004001, 0x00000000, 0x06000036, 0x00102072, 0x00000003, 0x00219246, 0x00000000,
        0x00000003, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ds = {ds_code, sizeof(ds_code)};
    static const DWORD vs_code[] =
    {
        0x43425844, 0xf6c8872b, 0x5f2d2170, 0xd8f05ee8, 0x1efbc879, 0x00000001, 0x00000250, 0x00000003,
        0x0000002c, 0x00000060, 0x000000d8, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000006, 0x00000001, 0x00000000, 0x00000101, 0x565f5653, 0x65747265, 0x00444978,
        0x4e47534f, 0x00000070, 0x00000004, 0x00000008, 0x00000068, 0x00000000, 0x00000000, 0x00000003,
        0x00000000, 0x00000807, 0x00000068, 0x00000001, 0x00000000, 0x00000003, 0x00000001, 0x0000000f,
        0x00000068, 0x00000002, 0x00000000, 0x00000003, 0x00000002, 0x00000807, 0x0000006c, 0x00000000,
        0x00000000, 0x00000003, 0x00000003, 0x0000000f, 0x004f4f46, 0xabab0050, 0x58454853, 0x00000170,
        0x00010050, 0x0000005c, 0x0100086a, 0x04000060, 0x00101012, 0x00000000, 0x00000006, 0x03000065,
        0x00102072, 0x00000000, 0x03000065, 0x001020f2, 0x00000001, 0x03000065, 0x00102072, 0x00000002,
        0x03000065, 0x001020f2, 0x00000003, 0x02000068, 0x00000001, 0x08000036, 0x00102072, 0x00000000,
        0x00004002, 0x3e4ccccd, 0x3ecccccd, 0x3dcccccd, 0x00000000, 0x08000036, 0x001020f2, 0x00000001,
        0x00004002, 0x3f19999a, 0x3f4ccccd, 0x3e99999a, 0x3f000000, 0x08000036, 0x00102072, 0x00000002,
        0x00004002, 0x3f333333, 0x3f666666, 0x3f800000, 0x00000000, 0x0b00008c, 0x00100012, 0x00000000,
        0x00004001, 0x00000001, 0x00004001, 0x00000001, 0x0010100a, 0x00000000, 0x00004001, 0x00000000,
        0x07000001, 0x00100042, 0x00000000, 0x0010100a, 0x00000000, 0x00004001, 0x00000002, 0x05000056,
        0x00100032, 0x00000000, 0x00100086, 0x00000000, 0x0f000032, 0x00102032, 0x00000003, 0x00100046,
        0x00000000, 0x00004002, 0x40000000, 0xc0000000, 0x00000000, 0x00000000, 0x00004002, 0xbf800000,
        0x3f800000, 0x00000000, 0x00000000, 0x08000036, 0x001020c2, 0x00000003, 0x00004002, 0x00000000,
        0x00000000, 0x00000000, 0x3f800000, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE vs = {vs_code, sizeof(vs_code)};
    static const DWORD ps_code[] =
    {
        0x43425844, 0x5f0bb13f, 0x692a4798, 0x649648dc, 0xb8d68169, 0x00000001, 0x00000164, 0x00000003,
        0x0000002c, 0x000000ac, 0x000000e0, 0x4e475349, 0x00000078, 0x00000004, 0x00000008, 0x00000068,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x00000074, 0x00000000, 0x00000000,
        0x00000003, 0x00000001, 0x00000307, 0x00000074, 0x00000001, 0x00000000, 0x00000003, 0x00000002,
        0x0000010f, 0x00000074, 0x00000002, 0x00000000, 0x00000003, 0x00000003, 0x00000107, 0x505f5653,
        0x7469736f, 0x006e6f69, 0x004f4f46, 0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
        0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x0000000f, 0x745f7673, 0x65677261, 0xabab0074,
        0x58454853, 0x0000007c, 0x00000050, 0x0000001f, 0x0100086a, 0x03001062, 0x00101032, 0x00000001,
        0x03001062, 0x00101012, 0x00000002, 0x03001062, 0x00101012, 0x00000003, 0x03000065, 0x001020f2,
        0x00000000, 0x05000036, 0x00102032, 0x00000000, 0x00101046, 0x00000001, 0x05000036, 0x00102042,
        0x00000000, 0x0010100a, 0x00000002, 0x05000036, 0x00102082, 0x00000000, 0x0010100a, 0x00000003,
        0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};
    static const struct vec4 tess_factors = {1.0f, 1.0f, 1.0f, 1.0f};
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    static const uint32_t u = 1;

    memset(&desc, 0, sizeof(desc));
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    command_list = context.list;
    queue = context.queue;

    context.root_signature = create_32bit_constants_root_signature(context.device,
            0, 5, D3D12_SHADER_VISIBILITY_HULL);

    init_pipeline_state_desc(&pso_desc, context.root_signature,
            context.render_target_desc.Format, &vs, &ps, NULL);
    pso_desc.HS = hs;
    pso_desc.DS = ds;
    pso_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_PATCH;
    hr = ID3D12Device_CreateGraphicsPipelineState(context.device, &pso_desc,
            &IID_ID3D12PipelineState, (void **)&context.pipeline_state);
    ok(hr == S_OK, "Failed to create state, hr %#x.\n", hr);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 0, 4, &tess_factors.x, 0);
    ID3D12GraphicsCommandList_SetGraphicsRoot32BitConstants(command_list, 0, 1, &u, 4);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 3, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);

    bug_if(is_mvk_device(context.device))
    check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xcc996633, 0);

    destroy_test_context(&context);
}

static void test_unused_interpolated_input(void)
{
    ID3D12GraphicsCommandList *command_list;
    D3D12_INPUT_LAYOUT_DESC input_layout;
    struct d3d12_resource_readback rb;
    struct test_context_desc desc;
    D3D12_VERTEX_BUFFER_VIEW vbv;
    struct test_context context;
    ID3D12CommandQueue *queue;
    ID3D12Resource *buffer;

    static const DWORD vs_code[] =
    {
#if 0
        void main(float4 in_position : POSITION, float4 in_color : COLOR,
                out float4 out_position : SV_POSITION, out float2 out_color_xy : COLOR0,
                out float2 out_color_zw : COLOR1)
        {
            out_position = in_position;
            out_color_xy = in_color.xy;
            out_color_zw = in_color.zw;
        }
#endif
        0x43425844, 0x300ab133, 0x7ab911f3, 0x18c8bc61, 0x7a2eb5e9, 0x00000001, 0x00000168, 0x00000003,
        0x0000002c, 0x0000007c, 0x000000e8, 0x4e475349, 0x00000048, 0x00000002, 0x00000008, 0x00000038,
        0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000f0f, 0x00000041, 0x00000000, 0x00000000,
        0x00000003, 0x00000001, 0x00000f0f, 0x49534f50, 0x4e4f4954, 0x4c4f4300, 0xab00524f, 0x4e47534f,
        0x00000064, 0x00000003, 0x00000008, 0x00000050, 0x00000000, 0x00000001, 0x00000003, 0x00000000,
        0x0000000f, 0x0000005c, 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x00000c03, 0x0000005c,
        0x00000001, 0x00000000, 0x00000003, 0x00000001, 0x0000030c, 0x505f5653, 0x5449534f, 0x004e4f49,
        0x4f4c4f43, 0xabab0052, 0x58454853, 0x00000078, 0x00010050, 0x0000001e, 0x0100086a, 0x0300005f,
        0x001010f2, 0x00000000, 0x0300005f, 0x001010f2, 0x00000001, 0x04000067, 0x001020f2, 0x00000000,
        0x00000001, 0x03000065, 0x00102032, 0x00000001, 0x03000065, 0x001020c2, 0x00000001, 0x05000036,
        0x001020f2, 0x00000000, 0x00101e46, 0x00000000, 0x05000036, 0x001020f2, 0x00000001, 0x00101e46,
        0x00000001, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE vs = {vs_code, sizeof(vs_code)};
    static const DWORD ps_code[] =
    {
        /* Compiled with fxc, this results in a single input register split into two signature
         * elements, xy and zw, where xy is unused and has default interpolation. */
#if 0
        void main(float4 in_position : SV_POSITION, nointerpolation float2 in_color_xy : COLOR0,
                nointerpolation float2 in_color_zw : COLOR1, out float4 out_color : SV_TARGET)
        {
            /* in_color_xy is unused */
            out_color.xy = in_color_zw;
            out_color.zw = in_color_zw;
        }
#endif
        0x43425844, 0xeb0d015d, 0x5dc78d0f, 0x0e0a87d1, 0x93ea3988, 0x00000001, 0x00000110, 0x00000003,
        0x0000002c, 0x00000098, 0x000000cc, 0x4e475349, 0x00000064, 0x00000003, 0x00000008, 0x00000050,
        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x0000005c, 0x00000000, 0x00000000,
        0x00000003, 0x00000001, 0x00000003, 0x0000005c, 0x00000001, 0x00000000, 0x00000003, 0x00000001,
        0x00000c0c, 0x505f5653, 0x5449534f, 0x004e4f49, 0x4f4c4f43, 0xabab0052, 0x4e47534f, 0x0000002c,
        0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x0000000f,
        0x545f5653, 0x45475241, 0xabab0054, 0x58454853, 0x0000003c, 0x00000050, 0x0000000f, 0x0100086a,
        0x03000862, 0x001010c2, 0x00000001, 0x03000065, 0x001020f2, 0x00000000, 0x05000036, 0x001020f2,
        0x00000000, 0x00101ee6, 0x00000001, 0x0100003e,
    };
    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};
    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
    static const D3D12_BOX box = {8, 8, 0, 9, 9, 1};
    static const D3D12_INPUT_ELEMENT_DESC layout_desc[] =
    {
        {"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT,       0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
        {"COLOR",    0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 8, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
    };
    static const struct
    {
        struct vec2 position;
        struct vec4 color;
    }
    quad[] =
    {
        {{-1.0f, -1.0f}, {0.0f, 1.0f, 0.0f, 1.0f}},
        {{-1.0f,  1.0f}, {0.0f, 1.0f, 0.0f, 1.0f}},
        {{ 1.0f, -1.0f}, {1.0f, 0.0f, 1.0f, 0.0f}},
        {{ 1.0f,  1.0f}, {0.0f, 1.0f, 0.0f, 1.0f}},
    };

    memset(&desc, 0, sizeof(desc));
    desc.no_root_signature = true;
    if (!init_test_context(&context, &desc))
        return;
    command_list = context.list;
    queue = context.queue;

    context.root_signature = create_empty_root_signature(context.device,
            D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
    input_layout.pInputElementDescs = layout_desc;
    input_layout.NumElements = ARRAY_SIZE(layout_desc);
    context.pipeline_state = create_pipeline_state(context.device,
            context.root_signature, context.render_target_desc.Format, &vs, &ps, &input_layout);

    buffer = create_upload_buffer(context.device, sizeof(quad), quad);

    vbv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(buffer);
    vbv.StrideInBytes = sizeof(*quad);
    vbv.SizeInBytes = sizeof(quad);

    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);

    ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &context.rtv, false, NULL);
    ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, context.root_signature);
    ID3D12GraphicsCommandList_SetPipelineState(command_list, context.pipeline_state);
    ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
    ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, 1, &vbv);
    ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &context.viewport);
    ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &context.scissor_rect);
    ID3D12GraphicsCommandList_DrawInstanced(command_list, 4, 1, 0, 0);

    transition_resource_state(command_list, context.render_target,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);

    get_resource_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
    check_readback_data_uint(&rb.rb, &box, 0xff00ff00, 0);
    release_resource_readback(&rb);

    ID3D12Resource_Release(buffer);
    destroy_test_context(&context);
}

static void test_shader_cache(void)
{
    unsigned int refcount, base_refcount, value_size;
    ID3D12ShaderCacheSession *session, *session2;
    D3D12_SHADER_CACHE_SESSION_DESC desc = {0};
    struct test_context context;
    ID3D12Device9 *device;
    IUnknown *unk, *unk2;
    HRESULT hr;

    static const GUID test_guid
            = {0xfdb37466, 0x428f, 0x4edf, {0xa3, 0x7f, 0x9b, 0x1d, 0xf4, 0x88, 0xc5, 0xfc}};

    static const char key1[] = "1234567890";
    static const char key2[] = "ABCDEFGHIJ";
    char blob1[20 * 1024];
    char blob2[20 * 1024];
    char blob3[30 * 1024];
    char blob4[40 * 1024];

    if (!init_test_context(&context, NULL))
        return;

    hr = ID3D12Device_QueryInterface(context.device, &IID_ID3D12Device9, (void **)&device);
    ok(hr == S_OK || broken(hr == E_NOINTERFACE), "Got unexpected hr %#x.\n", hr);
    if (FAILED(hr))
    {
        skip("ID3D12Device9 not available, skipping shader cache tests.\n");
        destroy_test_context(&context);
        return;
    }

    base_refcount = get_refcount(device);

    /* The description needs to be non-NULL and have at least the identifier set. */
    unk = (IUnknown *)0xdeadbeef;
    hr = ID3D12Device9_CreateShaderCacheSession(device, NULL, &IID_IUnknown, (void **)&unk);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    hr = ID3D12Device9_CreateShaderCacheSession(device, NULL, &IID_IUnknown, NULL);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    hr = ID3D12Device9_CreateShaderCacheSession(device, &desc, &IID_IUnknown, (void **)&unk);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    hr = ID3D12Device9_CreateShaderCacheSession(device, &desc, &IID_IUnknown, NULL);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    hr = ID3D12Device9_CreateShaderCacheSession(device, &desc, NULL, (void **)&unk);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    ok(unk == (IUnknown *)0xdeadbeef, "Got unexpected pointer %p.\n", unk);

    desc.Identifier = test_guid;
    desc.Mode = D3D12_SHADER_CACHE_MODE_MEMORY;
    if (0) /* This crashes on Windows. */
    {
        hr = ID3D12Device9_CreateShaderCacheSession(device, &desc, NULL, (void **)&unk);
        ok(0, "Expected to crash, hr %#x.\n", hr);
    }
    hr = ID3D12Device9_CreateShaderCacheSession(device, &desc, &IID_IUnknown, NULL);
    ok(hr == S_FALSE, "Got unexpected hr %#x.\n", hr);
    hr = ID3D12Device9_CreateShaderCacheSession(device, &desc, NULL, NULL);
    ok(hr == S_FALSE, "Got unexpected hr %#x.\n", hr);

    /* Default values for sizes and number of entries. */
    hr = ID3D12Device9_CreateShaderCacheSession(device, &desc, &IID_ID3D12ShaderCacheSession,
            (void **)&session);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    desc = ID3D12ShaderCacheSession_GetDesc(session);
    ID3D12ShaderCacheSession_Release(session);
    ok(desc.MaximumInMemoryCacheEntries == 128, "Got MaximumInMemoryCacheEntries %u.\n",
            desc.MaximumInMemoryCacheEntries);
    ok(!desc.Flags, "Got Flags %#x.\n", desc.Flags);
    ok(!desc.Version, "Got Version %"PRId64".\n", desc.Version);
    ok(desc.MaximumInMemoryCacheSizeBytes == 1024 * 1024, "Got MaximumInMemoryCacheSizeBytes %u.\n",
            desc.MaximumInMemoryCacheSizeBytes);
    ok(desc.MaximumValueFileSizeBytes == 128 * 1024 * 1024, "Got MaximumValueFileSizeBytes %u.\n",
            desc.MaximumValueFileSizeBytes);

    /* A memory size larger than disk size is valid. UINT_MAX is OK for memory size. */
    memset(&desc, 0, sizeof(desc));
    desc.Identifier = test_guid;
    desc.MaximumInMemoryCacheSizeBytes = UINT_MAX;
    hr = ID3D12Device9_CreateShaderCacheSession(device, &desc, &IID_ID3D12ShaderCacheSession,
            (void **)&session);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    desc = ID3D12ShaderCacheSession_GetDesc(session);
    ID3D12ShaderCacheSession_Release(session);
    ok(desc.MaximumInMemoryCacheSizeBytes == UINT_MAX, "Got MaximumInMemoryCacheSizeBytes %u.\n",
            desc.MaximumInMemoryCacheSizeBytes);
    ok(desc.MaximumValueFileSizeBytes == 128 * 1024 * 1024, "Got MaximumValueFileSizeBytes %u.\n",
            desc.MaximumValueFileSizeBytes);

    /* MaximumValueFileSizeBytes is limited to 1 GiB regardless of cache type. */
    desc.MaximumValueFileSizeBytes = 1024 * 1024 * 1024;
    hr = ID3D12Device9_CreateShaderCacheSession(device, &desc, &IID_ID3D12ShaderCacheSession,
            (void **)&session);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    ID3D12ShaderCacheSession_Release(session);

    session = (ID3D12ShaderCacheSession *)0xdeadbeef;
    desc.MaximumValueFileSizeBytes = 1024 * 1024 * 1024 + 1;
    hr = ID3D12Device9_CreateShaderCacheSession(device, &desc, &IID_ID3D12ShaderCacheSession,
            (void **)&session);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    ok(session == (ID3D12ShaderCacheSession *)0xdeadbeef, "Got unexpected pointer %p.\n", unk);

    memset(&desc, 0, sizeof(desc));
    desc.Identifier = test_guid;
    desc.MaximumValueFileSizeBytes = 1;
    desc.MaximumInMemoryCacheSizeBytes = 1;
    hr = ID3D12Device9_CreateShaderCacheSession(device, &desc,
            &IID_ID3D12ShaderCacheSession, (void **)&session);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    desc = ID3D12ShaderCacheSession_GetDesc(session);
    ID3D12ShaderCacheSession_Release(session);
    ok(desc.MaximumInMemoryCacheSizeBytes == 1, "Got MaximumInMemoryCacheSizeBytes %u.\n",
            desc.MaximumInMemoryCacheSizeBytes);
    ok(desc.MaximumValueFileSizeBytes == 1, "Got MaximumValueFileSizeBytes %u.\n",
            desc.MaximumValueFileSizeBytes);

    /* Invalid flags and mode are rejected. */
    memset(&desc, 0, sizeof(desc));
    desc.Identifier = test_guid;
    desc.Mode = D3D12_SHADER_CACHE_MODE_MEMORY;
    desc.Flags = 0x1245670;
    session = (ID3D12ShaderCacheSession *)0xdeadbeef;
    hr = ID3D12Device9_CreateShaderCacheSession(device, &desc,
            &IID_ID3D12ShaderCacheSession, (void **)&session);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    desc.Flags = 0;
    desc.Mode = 9876; /* Same for Mode=2, but 2 is more likely to mean something in the future. */
    hr = ID3D12Device9_CreateShaderCacheSession(device, &desc,
            &IID_ID3D12ShaderCacheSession, (void **)&session);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    ok(session == (ID3D12ShaderCacheSession *)0xdeadbeef, "Got unexpected pointer %p.\n", unk);

    memset(&desc, 0, sizeof(desc));
    desc.Identifier = test_guid;
    desc.Mode = D3D12_SHADER_CACHE_MODE_MEMORY;
    desc.Version = 12344;
    desc.MaximumInMemoryCacheSizeBytes = 32 * 1024;
    hr = ID3D12Device9_CreateShaderCacheSession(device, &desc, &IID_IUnknown, (void **)&unk);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);

    refcount = get_refcount(device);
    ok(refcount == base_refcount + 1, "Got unexpected refcount %u.\n", refcount);

    refcount = get_refcount(unk);
    ok(refcount == 1, "Got unexpected refcount %u.\n", refcount);

    hr = IUnknown_QueryInterface(unk, &IID_ID3D12ShaderCacheSession, (void **)&session);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    todo ok((IUnknown *)session != unk, "Expected different interface pointers, got %p for both.\n",
            session);
    hr = ID3D12ShaderCacheSession_QueryInterface(session, &IID_IUnknown, (void **)&unk2);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    ok(unk2 == unk, "Got different interface pointers %p and %p.\n", session, unk);

    /* The S_FALSE for CreateSession(session = NULL) does not come from QI. */
    hr = ID3D12ShaderCacheSession_QueryInterface(session, &IID_IUnknown, NULL);
    ok(hr == E_POINTER, "Got unexpected hr %#x.\n", hr);

    refcount = get_refcount(unk);
    ok(refcount == 3, "Got unexpected refcount %u.\n", refcount);
    refcount = get_refcount(session);
    ok(refcount == 3, "Got unexpected refcount %u.\n", refcount);

    IUnknown_Release(unk);
    IUnknown_Release(unk2);

    /* Ad-hoc testing shows that in-memory sessions are per-process. They are not magically
     * shared between processes. If a disk cache is accessed by two processes, it will fail
     * with E_INVALIDARG, regardless of version compatibility. */
    desc.Version = 12345;
    session2 = (void *)0xdeadbeef;
    hr = ID3D12Device9_CreateShaderCacheSession(device, &desc,
            &IID_ID3D12ShaderCacheSession, (void **)&session2);
    ok(hr == DXGI_ERROR_ALREADY_EXISTS, "Got unexpected hr %#x.\n", hr);
    ok(!session2, "Got unexpected pointer %p.\n", session2);
    hr = ID3D12Device9_CreateShaderCacheSession(device, &desc, &IID_IUnknown, NULL);
    ok(hr == S_FALSE, "NULL outptr: Got hr %#x.\n", hr);

    ID3D12ShaderCacheSession_Release(session);

    refcount = get_refcount(device);
    ok(refcount == base_refcount, "Got unexpected refcount %u.\n", refcount);

    /* Create two sessions with the same cache GUID. */
    hr = ID3D12Device9_CreateShaderCacheSession(device, &desc,
            &IID_ID3D12ShaderCacheSession, (void **)&session);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    hr = ID3D12Device9_CreateShaderCacheSession(device, &desc,
            &IID_ID3D12ShaderCacheSession, (void **)&session2);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    ok(session2 != session, "Expected different interface pointers, got %p for both.\n",
            session);
    ID3D12ShaderCacheSession_Release(session2);

    memset(blob1, '1', sizeof(blob1));
    memset(blob2, '2', sizeof(blob2));
    memset(blob3, '3', sizeof(blob3));
    memset(blob4, '4', sizeof(blob4));

    /* Basic store and retrieval.
     *
     * Adding a key a second time is not allowed (unless the entry has been evicted), but there
     * seems to be a bug in native: Despite returning an error and not changing the cache contents,
     * the "added" entry counts towards the cache size, filling up the quota. Eventually native
     * returns DXGI_ERROR_CACHE_FULL.
     *
     * To avoid relying on this bug or being hit by it in the eviction tests below, destroy the
     * cache and recreate it after verifying the value. */
    hr = ID3D12ShaderCacheSession_StoreValue(session, key1, sizeof(key1), blob1, sizeof(blob1));
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    hr = ID3D12ShaderCacheSession_StoreValue(session, key1, sizeof(key1), blob1, sizeof(blob1));
    ok(hr == DXGI_ERROR_ALREADY_EXISTS, "Got unexpected hr %#x.\n", hr);
    hr = ID3D12ShaderCacheSession_StoreValue(session, key1, sizeof(key1), blob2, sizeof(blob2));
    ok(hr == DXGI_ERROR_ALREADY_EXISTS, "Got unexpected hr %#x.\n", hr);

    value_size = sizeof(blob3);
    hr = ID3D12ShaderCacheSession_FindValue(session, key1, sizeof(key1), blob3, &value_size);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    ok(value_size == sizeof(blob1), "Got unexpected size %#x.\n", value_size);
    ok(!memcmp(blob3, blob1, sizeof(blob1)), "Unexpected value retrieved.\n");
    ok(blob3[sizeof(blob1)] == '3', "Output buffer was modified beyond the stored value.\n");
    memset(blob3, '3', sizeof(blob3));

    ID3D12ShaderCacheSession_Release(session);
    hr = ID3D12Device9_CreateShaderCacheSession(device, &desc,
            &IID_ID3D12ShaderCacheSession, (void **)&session);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    hr = ID3D12ShaderCacheSession_StoreValue(session, key1, sizeof(key1), blob1, sizeof(blob1));
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);

    /* Try to retrieve the value from another session sharing the same name. */
    hr = ID3D12Device9_CreateShaderCacheSession(device, &desc,
            &IID_ID3D12ShaderCacheSession, (void **)&session2);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);

    hr = ID3D12ShaderCacheSession_FindValue(session2, key1, sizeof(key1), blob3, &value_size);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    ok(value_size == sizeof(blob1), "Got unexpected size %#x.\n", value_size);
    ok(!memcmp(blob3, blob1, sizeof(blob1)), "Unexpected value retrieved.\n");
    ok(blob3[sizeof(blob1)] == '3', "Output buffer was modified beyond the stored value.\n");
    hr = ID3D12ShaderCacheSession_FindValue(session2, key1, sizeof(key1), blob3, &value_size);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);

    ID3D12ShaderCacheSession_Release(session2);

    /* value_size must be set. */
    hr = ID3D12ShaderCacheSession_FindValue(session, key1, sizeof(key1), NULL, NULL);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    hr = ID3D12ShaderCacheSession_FindValue(session, key1, sizeof(key1), blob3, NULL);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    hr = ID3D12ShaderCacheSession_FindValue(session, key2, sizeof(key2), NULL, NULL);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    hr = ID3D12ShaderCacheSession_FindValue(session, key2, sizeof(key2), blob3, NULL);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    /* Test how the output size is handled. - A NULL data ptr returns S_OK and sets
     * *value_size to the required size. */
    value_size = 0;
    hr = ID3D12ShaderCacheSession_FindValue(session, key1, sizeof(key1), NULL, &value_size);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    ok(value_size == sizeof(blob1), "Got unexpected size %#x.\n", value_size);
    value_size = sizeof(blob1) + 1;
    hr = ID3D12ShaderCacheSession_FindValue(session, key1, sizeof(key1), NULL, &value_size);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    ok(value_size == sizeof(blob1), "Got unexpected size %#x.\n", value_size);
    /* It remains untouched if the item was not found. */
    value_size = 0xdeadbeef;
    hr = ID3D12ShaderCacheSession_FindValue(session, key2, sizeof(key2), NULL, &value_size);
    ok(hr == DXGI_ERROR_NOT_FOUND, "Got unexpected hr %#x.\n", hr);
    ok(value_size == 0xdeadbeef, "Got unexpected size %#x.\n", value_size);
    /* If a too small data pointer is passed, *value_size is set, but no data is written. */
    value_size = sizeof(blob1) - 1;
    memset(blob3, 'C', sizeof(blob3));
    hr = ID3D12ShaderCacheSession_FindValue(session, key1, sizeof(key1), blob3, &value_size);
    ok(hr == DXGI_ERROR_MORE_DATA, "Got unexpected hr %#x.\n", hr);
    ok(value_size == sizeof(blob1), "Got unexpected size %#x.\n", value_size);
    ok(blob3[0] == 'C', "Output buffer was modified.\n");

    /* Test evicition due to lack of room in the cache. */
    hr = ID3D12ShaderCacheSession_StoreValue(session, key2, sizeof(key2), blob2, sizeof(blob2));
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    value_size = sizeof(blob3);
    hr = ID3D12ShaderCacheSession_FindValue(session, key2, sizeof(key2), blob3, &value_size);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    ok(value_size == sizeof(blob2), "Got unexpected size %#x.\n", value_size);
    value_size = sizeof(blob3);
    hr = ID3D12ShaderCacheSession_FindValue(session, key1, sizeof(key1), blob3, &value_size);
    todo ok(hr == DXGI_ERROR_NOT_FOUND, "Got unexpected hr %#x.\n", hr);

    /* The full key is stored as well. Use a huge key for a small value. It counts towards the
     * size and will evict existing data. */
    hr = ID3D12ShaderCacheSession_StoreValue(session, blob1, sizeof(blob1), &desc, sizeof(desc));
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    value_size = 0xdeadbeef;
    hr = ID3D12ShaderCacheSession_FindValue(session, blob1, sizeof(blob1), NULL, &value_size);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    ok(value_size == sizeof(desc), "Got unexpected size %#x.\n", value_size);
    hr = ID3D12ShaderCacheSession_FindValue(session, key2, sizeof(key2), NULL, &value_size);
    todo ok(hr == DXGI_ERROR_NOT_FOUND, "Got unexpected hr %#x.\n", hr);

    /* Keys are not truncated. */
    blob1[sizeof(blob1) - 1] = 'X';
    hr = ID3D12ShaderCacheSession_FindValue(session, blob1, sizeof(blob1), NULL, &value_size);
    ok(hr == DXGI_ERROR_NOT_FOUND, "Got unexpected hr %#x.\n", hr);
    /* Make sure the huge key was not accidentally evicted. */
    blob1[sizeof(blob1) - 1] = '1';
    hr = ID3D12ShaderCacheSession_FindValue(session, blob1, sizeof(blob1), NULL, &value_size);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);

    /* Reset the cache so we don't get collisions on 'key1' if eviction failed. */
    ID3D12ShaderCacheSession_Release(session);
    desc.Mode = D3D12_SHADER_CACHE_MODE_MEMORY;
    hr = ID3D12Device9_CreateShaderCacheSession(device, &desc,
            &IID_ID3D12ShaderCacheSession, (void **)&session);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);

    /* Store a blob that is too big. It goes in at first but gets evicted next time.
     *
     * Why are we not getting DXGI_ERROR_CACHE_FULL here? I don't know. */
    hr = ID3D12ShaderCacheSession_StoreValue(session, key1, sizeof(key1), blob4, sizeof(blob4));
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    hr = ID3D12ShaderCacheSession_FindValue(session, key1, sizeof(key1), NULL, &value_size);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    hr = ID3D12ShaderCacheSession_StoreValue(session, key2, sizeof(key2), &desc, sizeof(desc));
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
    hr = ID3D12ShaderCacheSession_FindValue(session, key1, sizeof(key1), NULL, &value_size);
    todo ok(hr == DXGI_ERROR_NOT_FOUND, "Got unexpected hr %#x.\n", hr);
    hr = ID3D12ShaderCacheSession_FindValue(session, key2, sizeof(key2), NULL, &value_size);
    ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);

    /* Can we delete specific entries? It doesn't look like it. In fact, anything with a zero length
     * or pointer is invalid. There is device::ShaderCacheControl with CONTROL_FLAG_CLEAR and
     * SetDeleteOnDestroy, but those nuke the entire cache, not single entries. */
    hr = ID3D12ShaderCacheSession_StoreValue(session, key2, sizeof(key2), NULL, 0);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    hr = ID3D12ShaderCacheSession_StoreValue(session, key2, sizeof(key2), &desc, 0);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    hr = ID3D12ShaderCacheSession_StoreValue(session, key2, sizeof(key2), NULL, sizeof(desc));
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    hr = ID3D12ShaderCacheSession_StoreValue(session, NULL, sizeof(key2), &desc, sizeof(desc));
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    hr = ID3D12ShaderCacheSession_StoreValue(session, key2, 0, &desc, 1);
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
    hr = ID3D12ShaderCacheSession_StoreValue(session, NULL, sizeof(key2), &desc, sizeof(desc));
    ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);

    ID3D12ShaderCacheSession_Release(session);

    ID3D12Device9_Release(device);
    destroy_test_context(&context);
}

struct multi_fence_event_wait_data
{
    ID3D12Device1 *device;
    ID3D12Fence **fences;
    uint64_t *values;
    unsigned int fence_count;
    D3D12_MULTIPLE_FENCE_WAIT_FLAGS flags;
    HANDLE started, completed;
};

static void multi_fence_event_wait_main(void *ctx)
{
    struct multi_fence_event_wait_data *data = ctx;
    HRESULT hr;

    signal_event(data->started);

    hr = ID3D12Device1_SetEventOnMultipleFenceCompletion(data->device,
            data->fences, data->values, data->fence_count, data->flags, NULL);
    ok(hr == S_OK, "Got hr %#x.\n", hr);

    signal_event(data->completed);
}

static void test_multi_fence_event(void)
{
    struct multi_fence_event_wait_data thread_data;
    uint64_t fence_values[3];
    ID3D12Device1 *device1;
    ID3D12Fence *fences[3];
    unsigned int refcount;
    ID3D12Device *device;
    HANDLE event, thread;
    unsigned int i, ret;
    HRESULT hr;

    if (!(device = create_device()))
    {
        skip("Failed to create device.\n");
        return;
    }

    if (FAILED(ID3D12Device_QueryInterface(device, &IID_ID3D12Device1, (void **)&device1)))
    {
        skip("ID3D12Device1 not available; skipping tests.\n");
        ID3D12Device_Release(device);
        return;
    }

    for (i = 0; i < ARRAY_SIZE(fences); ++i)
    {
        hr = ID3D12Device_CreateFence(device, 0, D3D12_FENCE_FLAG_NONE, &IID_ID3D12Fence, (void **)&fences[i]);
        ok(hr == S_OK, "Got hr %#x.\n", hr);
    }
    event = create_event();
    ok(event, "Failed to create event.\n");
    ret = wait_event(event, 0);
    ok(ret == WAIT_TIMEOUT, "Got ret %#x.\n", ret);

    thread_data.device = device1;
    thread_data.fences = fences;
    thread_data.values = fence_values;
    thread_data.fence_count = 0;
    thread_data.flags = 0;
    thread_data.started = create_event();
    ok(thread_data.started, "Failed to create event.\n");
    thread_data.completed = create_event();
    ok(thread_data.completed, "Failed to create event.\n");

    /* 0 fence count. */

    hr = ID3D12Device1_SetEventOnMultipleFenceCompletion(device1, NULL, NULL, 0, 0, NULL);
    ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr);
    hr = ID3D12Device1_SetEventOnMultipleFenceCompletion(device1,
            NULL, NULL, 0, D3D12_MULTIPLE_FENCE_WAIT_FLAG_ANY, NULL);
    ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr);
    hr = ID3D12Device1_SetEventOnMultipleFenceCompletion(device1, fences, fence_values, 0, 0, event);
    ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr);
    ret = wait_event(event, 0);
    ok(ret == WAIT_TIMEOUT, "Got ret %#x.\n", ret);

    /* Single fences. */

    fence_values[0] = 1;
    hr = ID3D12Device1_SetEventOnMultipleFenceCompletion(device1, fences,
            fence_values, 1, D3D12_MULTIPLE_FENCE_WAIT_FLAG_ALL, event);
    ok(hr == S_OK, "Got hr %#x.\n", hr);
    ret = wait_event(event, 0);
    ok(ret == WAIT_TIMEOUT, "Got ret %#x.\n", ret);
    hr = ID3D12Fence_Signal(fences[0], 1);
    ok(hr == S_OK, "Got hr %#x.\n", hr);
    ret = wait_event(event, 0);
    ok(ret == WAIT_OBJECT_0, "Got ret %#x.\n", ret);
    hr = ID3D12Device1_SetEventOnMultipleFenceCompletion(device1, fences,
            fence_values, 1, D3D12_MULTIPLE_FENCE_WAIT_FLAG_ALL, NULL);
    ok(hr == S_OK, "Got hr %#x.\n", hr);

    fence_values[0] = 2;
    thread_data.fence_count = 1;
    thread = create_thread(multi_fence_event_wait_main, &thread_data);
    ok(thread, "Failed to create thread.\n");
    ret = wait_event(thread_data.started, INFINITE);
    ok(ret == WAIT_OBJECT_0, "Got ret %#x.\n", ret);
    ret = wait_event(thread_data.completed, 0);
    ok(ret == WAIT_TIMEOUT, "Got ret %#x.\n", ret);
    hr = ID3D12Fence_Signal(fences[0], 2);
    ok(hr == S_OK, "Got hr %#x.\n", hr);
    ret = wait_event(thread_data.completed, INFINITE);
    ok(ret == WAIT_OBJECT_0, "Got ret %#x.\n", ret);
    ok(join_thread(thread), "Failed to join thread.\n");

    fence_values[0] = 4;
    hr = ID3D12Device1_SetEventOnMultipleFenceCompletion(device1, fences,
            fence_values, 1, D3D12_MULTIPLE_FENCE_WAIT_FLAG_ANY, event);
    ok(hr == S_OK, "Got hr %#x.\n", hr);
    ret = wait_event(event, 0);
    ok(ret == WAIT_TIMEOUT, "Got ret %#x.\n", ret);
    hr = ID3D12Fence_Signal(fences[0], 4);
    ok(hr == S_OK, "Got hr %#x.\n", hr);
    ret = wait_event(event, 0);
    ok(ret == WAIT_OBJECT_0, "Got ret %#x.\n", ret);
    hr = ID3D12Device1_SetEventOnMultipleFenceCompletion(device1, fences,
            fence_values, 1, D3D12_MULTIPLE_FENCE_WAIT_FLAG_ANY, NULL);
    ok(hr == S_OK, "Got hr %#x.\n", hr);

    fence_values[0] = 8;
    thread_data.flags = D3D12_MULTIPLE_FENCE_WAIT_FLAG_ANY;
    thread = create_thread(multi_fence_event_wait_main, &thread_data);
    ok(thread, "Failed to create thread.\n");
    ret = wait_event(thread_data.started, INFINITE);
    ok(ret == WAIT_OBJECT_0, "Got ret %#x.\n", ret);
    ret = wait_event(thread_data.completed, 0);
    ok(ret == WAIT_TIMEOUT, "Got ret %#x.\n", ret);
    hr = ID3D12Fence_Signal(fences[0], 8);
    ok(hr == S_OK, "Got hr %#x.\n", hr);
    ret = wait_event(thread_data.completed, INFINITE);
    ok(ret == WAIT_OBJECT_0, "Got ret %#x.\n", ret);
    ok(join_thread(thread), "Failed to join thread.\n");

    /* Multiple fences. */

    /* All fences already signalled. */
    fence_values[0] = 16;
    fence_values[1] = 17;
    fence_values[2] = 18;
    hr = ID3D12Fence_Signal(fences[0], 16);
    ok(hr == S_OK, "Got hr %#x.\n", hr);
    hr = ID3D12Fence_Signal(fences[1], 17);
    ok(hr == S_OK, "Got hr %#x.\n", hr);
    hr = ID3D12Fence_Signal(fences[2], 18);
    ok(hr == S_OK, "Got hr %#x.\n", hr);
    hr = ID3D12Device1_SetEventOnMultipleFenceCompletion(device1, fences,
            fence_values, 3, D3D12_MULTIPLE_FENCE_WAIT_FLAG_ALL, NULL);
    ok(hr == S_OK, "Got hr %#x.\n", hr);
    ret = wait_event(event, 0);
    ok(ret == WAIT_TIMEOUT, "Got ret %#x.\n", ret);
    hr = ID3D12Device1_SetEventOnMultipleFenceCompletion(device1, fences,
            fence_values, 3, D3D12_MULTIPLE_FENCE_WAIT_FLAG_ALL, event);
    ok(hr == S_OK, "Got hr %#x.\n", hr);
    ret = wait_event(event, 0);
    ok(ret == WAIT_OBJECT_0, "Got ret %#x.\n", ret);
    hr = ID3D12Device1_SetEventOnMultipleFenceCompletion(device1, fences,
            fence_values, 3, D3D12_MULTIPLE_FENCE_WAIT_FLAG_ANY, NULL);
    ok(hr == S_OK, "Got hr %#x.\n", hr);
    ret = wait_event(event, 0);
    ok(ret == WAIT_TIMEOUT, "Got ret %#x.\n", ret);
    hr = ID3D12Device1_SetEventOnMultipleFenceCompletion(device1, fences,
            fence_values, 3, D3D12_MULTIPLE_FENCE_WAIT_FLAG_ANY, event);
    ok(hr == S_OK, "Got hr %#x.\n", hr);
    ret = wait_event(event, 0);
    ok(ret == WAIT_OBJECT_0, "Got ret %#x.\n", ret);

    /* No fences signalled yet. */
    fence_values[0] = 32;
    fence_values[1] = 33;
    fence_values[2] = 34;
    hr = ID3D12Device1_SetEventOnMultipleFenceCompletion(device1, fences,
            fence_values, 3, D3D12_MULTIPLE_FENCE_WAIT_FLAG_ALL, event);
    ok(hr == S_OK, "Got hr %#x.\n", hr);
    ret = wait_event(event, 0);
    ok(ret == WAIT_TIMEOUT, "Got ret %#x.\n", ret);
    hr = ID3D12Fence_Signal(fences[2], 34);
    ok(hr == S_OK, "Got hr %#x.\n", hr);
    ret = wait_event(event, 0);
    ok(ret == WAIT_TIMEOUT, "Got ret %#x.\n", ret);
    hr = ID3D12Fence_Signal(fences[0], 32);
    ok(hr == S_OK, "Got hr %#x.\n", hr);
    ret = wait_event(event, 0);
    ok(ret == WAIT_TIMEOUT, "Got ret %#x.\n", ret);
    hr = ID3D12Fence_Signal(fences[1], 33);
    ok(hr == S_OK, "Got hr %#x.\n", hr);
    ret = wait_event(event, 0);
    ok(ret == WAIT_OBJECT_0, "Got ret %#x.\n", ret);

    fence_values[0] = 40;
    fence_values[1] = 41;
    fence_values[2] = 42;
    thread_data.fence_count = 3;
    thread_data.flags = D3D12_MULTIPLE_FENCE_WAIT_FLAG_ALL;
    thread = create_thread(multi_fence_event_wait_main, &thread_data);
    ok(thread, "Failed to create thread.\n");
    ret = wait_event(thread_data.started, INFINITE);
    ok(ret == WAIT_OBJECT_0, "Got ret %#x.\n", ret);
    ret = wait_event(thread_data.completed, 0);
    ok(ret == WAIT_TIMEOUT, "Got ret %#x.\n", ret);
    hr = ID3D12Fence_Signal(fences[1], 41);
    ok(hr == S_OK, "Got hr %#x.\n", hr);
    ret = wait_event(thread_data.completed, 0);
    ok(ret == WAIT_TIMEOUT, "Got ret %#x.\n", ret);
    hr = ID3D12Fence_Signal(fences[0], 40);
    ok(hr == S_OK, "Got hr %#x.\n", hr);
    ret = wait_event(thread_data.completed, 0);
    ok(ret == WAIT_TIMEOUT, "Got ret %#x.\n", ret);
    hr = ID3D12Fence_Signal(fences[2], 42);
    ok(hr == S_OK, "Got hr %#x.\n", hr);
    ret = wait_event(thread_data.completed, INFINITE);
    ok(ret == WAIT_OBJECT_0, "Got ret %#x.\n", ret);
    ok(join_thread(thread), "Failed to join thread.\n");

    fence_values[0] = 48;
    fence_values[1] = 49;
    fence_values[2] = 50;
    hr = ID3D12Device1_SetEventOnMultipleFenceCompletion(device1, fences,
            fence_values, 3, D3D12_MULTIPLE_FENCE_WAIT_FLAG_ANY, event);
    ok(hr == S_OK, "Got hr %#x.\n", hr);
    ret = wait_event(event, 0);
    ok(ret == WAIT_TIMEOUT, "Got ret %#x.\n", ret);
    hr = ID3D12Fence_Signal(fences[2], 50);
    ok(hr == S_OK, "Got hr %#x.\n", hr);
    ret = wait_event(event, 0);
    ok(ret == WAIT_OBJECT_0, "Got ret %#x.\n", ret);
    hr = ID3D12Fence_Signal(fences[0], 48);
    ok(hr == S_OK, "Got hr %#x.\n", hr);
    ret = wait_event(event, 0);
    ok(ret == WAIT_TIMEOUT, "Got ret %#x.\n", ret);
    hr = ID3D12Fence_Signal(fences[1], 49);
    ok(hr == S_OK, "Got hr %#x.\n", hr);
    ret = wait_event(event, 0);
    ok(ret == WAIT_TIMEOUT, "Got ret %#x.\n", ret);

    fence_values[0] = 56;
    fence_values[1] = 57;
    fence_values[2] = 58;
    thread_data.flags = D3D12_MULTIPLE_FENCE_WAIT_FLAG_ANY;
    thread = create_thread(multi_fence_event_wait_main, &thread_data);
    ok(thread, "Failed to create thread.\n");
    ret = wait_event(thread_data.started, INFINITE);
    ok(ret == WAIT_OBJECT_0, "Got ret %#x.\n", ret);
    ret = wait_event(thread_data.completed, 0);
    ok(ret == WAIT_TIMEOUT, "Got ret %#x.\n", ret);
    hr = ID3D12Fence_Signal(fences[1], 57);
    ok(hr == S_OK, "Got hr %#x.\n", hr);
    ret = wait_event(thread_data.completed, INFINITE);
    ok(ret == WAIT_OBJECT_0, "Got ret %#x.\n", ret);
    ok(join_thread(thread), "Failed to join thread.\n");

    /* Some fences already signalled. */
    fence_values[0] = 64;
    fence_values[1] = 65;
    fence_values[2] = 66;
    hr = ID3D12Fence_Signal(fences[0], 64);
    ok(hr == S_OK, "Got hr %#x.\n", hr);
    hr = ID3D12Fence_Signal(fences[2], 66);
    ok(hr == S_OK, "Got hr %#x.\n", hr);
    hr = ID3D12Device1_SetEventOnMultipleFenceCompletion(device1, fences,
            fence_values, 3, D3D12_MULTIPLE_FENCE_WAIT_FLAG_ALL, event);
    ok(hr == S_OK, "Got hr %#x.\n", hr);
    ret = wait_event(event, 0);
    ok(ret == WAIT_TIMEOUT, "Got ret %#x.\n", ret);
    hr = ID3D12Fence_Signal(fences[1], 65);
    ok(hr == S_OK, "Got hr %#x.\n", hr);
    ret = wait_event(event, 0);
    ok(ret == WAIT_OBJECT_0, "Got ret %#x.\n", ret);

    fence_values[1] = 67;
    thread_data.flags = D3D12_MULTIPLE_FENCE_WAIT_FLAG_ALL;
    thread = create_thread(multi_fence_event_wait_main, &thread_data);
    ok(thread, "Failed to create thread.\n");
    ret = wait_event(thread_data.started, INFINITE);
    ok(ret == WAIT_OBJECT_0, "Got ret %#x.\n", ret);
    ret = wait_event(thread_data.completed, 0);
    ok(ret == WAIT_TIMEOUT, "Got ret %#x.\n", ret);
    hr = ID3D12Fence_Signal(fences[1], 67);
    ok(hr == S_OK, "Got hr %#x.\n", hr);
    ret = wait_event(thread_data.completed, INFINITE);
    ok(ret == WAIT_OBJECT_0, "Got ret %#x.\n", ret);
    ok(join_thread(thread), "Failed to join thread.\n");

    fence_values[1] = 68;
    ret = wait_event(event, 0);
    ok(ret == WAIT_TIMEOUT, "Got ret %#x.\n", ret);
    hr = ID3D12Device1_SetEventOnMultipleFenceCompletion(device1, fences,
            fence_values, 3, D3D12_MULTIPLE_FENCE_WAIT_FLAG_ANY, event);
    ok(hr == S_OK, "Got hr %#x.\n", hr);
    ret = wait_event(event, 0);
    ok(ret == WAIT_OBJECT_0, "Got ret %#x.\n", ret);

    hr = ID3D12Device1_SetEventOnMultipleFenceCompletion(device1, fences,
            fence_values, 3, D3D12_MULTIPLE_FENCE_WAIT_FLAG_ANY, NULL);
    ok(hr == S_OK, "Got hr %#x.\n", hr);

    destroy_event(thread_data.completed);
    destroy_event(thread_data.started);
    destroy_event(event);
    for (i = 0; i < ARRAY_SIZE(fences); ++i)
    {
        ID3D12Fence_Release(fences[i]);
    }

    ID3D12Device1_Release(device1);
    refcount = ID3D12Device_Release(device);
    ok(!refcount, "ID3D12Device has %u references left.\n", refcount);
}

static void test_enumerate_meta_commands(void)
{
    ID3D12Device5 *device5;
    unsigned int refcount;
    ID3D12Device *device;
    UINT count;
    HRESULT hr;

    if (!(device = create_device()))
    {
        skip("Failed to create device.\n");
        return;
    }

    if (FAILED(ID3D12Device_QueryInterface(device, &IID_ID3D12Device5, (void **)&device5)))
    {
        skip("ID3D12Device5 not available; skipping tests.\n");
        ID3D12Device_Release(device);
        return;
    }

    hr = ID3D12Device5_EnumerateMetaCommands(device5, NULL, NULL);
    ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr);

    count = 0;
    hr = ID3D12Device5_EnumerateMetaCommands(device5, &count, NULL);
    ok(hr == S_OK, "Got hr %#x.\n", hr);

    ID3D12Device5_Release(device5);
    refcount = ID3D12Device_Release(device);
    ok(!refcount, "ID3D12Device has %u references left.\n", refcount);
}

START_TEST(d3d12)
{
    parse_args(argc, argv);
    enable_d3d12_debug_layer();
    init_adapter_info();

    pfn_D3D12CreateVersionedRootSignatureDeserializer = get_d3d12_pfn(D3D12CreateVersionedRootSignatureDeserializer);
    pfn_D3D12SerializeVersionedRootSignature = get_d3d12_pfn(D3D12SerializeVersionedRootSignature);

    run_test(test_create_device);
    run_test(test_node_count);
    run_test(test_check_feature_support);
    run_test(test_format_support);
    run_test(test_multisample_quality_levels);
    run_test(test_create_command_allocator);
    run_test(test_create_command_list);
    run_test(test_create_command_queue);
    run_test(test_create_command_signature);
    run_test(test_create_committed_resource);
    run_test(test_create_heap);
    run_test(test_create_placed_resource);
    run_test(test_create_reserved_resource);
    run_test(test_create_descriptor_heap);
    run_test(test_create_sampler);
    run_test(test_create_unordered_access_view);
    run_test(test_create_root_signature);
    run_test(test_root_signature_limits);
    run_test(test_create_compute_pipeline_state);
    run_test(test_create_graphics_pipeline_state);
    run_test(test_create_pipeline_state);
    run_test(test_create_fence);
    run_test(test_object_interface);
    run_test(test_multithread_private_data);
    run_test(test_reset_command_allocator);
    run_test(test_cpu_signal_fence);
    run_test(test_gpu_signal_fence);
    run_test(test_multithread_fence_wait);
    run_test(test_fence_values);
    run_test(test_clear_depth_stencil_view);
    run_test(test_clear_render_target_view);
    run_test(test_clear_unordered_access_view_buffer);
    run_test(test_clear_unordered_access_view_large_buffer);
    run_test(test_clear_unordered_access_view_image);
    run_test(test_set_render_targets);
    run_test(test_draw_instanced);
    run_test(test_draw_indexed_instanced);
    run_test(test_draw_no_descriptor_bindings);
    run_test(test_multiple_render_targets);
    run_test(test_unknown_rtv_format);
    run_test(test_unknown_dsv_format);
    run_test(test_append_aligned_element);
    run_test(test_gpu_virtual_address);
    run_test(test_fragment_coords);
    run_test(test_fractional_viewports);
    run_test(test_scissor);
    run_test(test_draw_depth_no_ps);
    run_test(test_draw_depth_only);
    run_test(test_draw_uav_only);
    run_test(test_texture_resource_barriers);
    run_test(test_device_removed_reason);
    run_test(test_map_resource);
    run_test(test_map_placed_resources);
    run_test(test_bundle_state_inheritance);
    run_test(test_shader_instructions);
    run_test(test_compute_shader_instructions);
    run_test(test_discard_instruction);
    run_test(test_shader_input_output_components);
    run_test(test_root_signature_byte_code);
    run_test(test_cs_constant_buffer);
    run_test(test_constant_buffer_relative_addressing);
    run_test(test_immediate_constant_buffer);
    run_test(test_root_constants);
    run_test(test_sample_instructions);
    run_test(test_texture_ld);
    run_test(test_gather);
    run_test(test_gather_c);
    run_test(test_sample_c_lz);
    run_test(test_cube_maps);
    run_test(test_multisample_array_texture);
    run_test(test_resinfo);
    run_test(test_srv_component_mapping);
    run_test(test_descriptor_tables);
    run_test(test_descriptor_tables_overlapping_bindings);
    run_test(test_update_root_descriptors);
    run_test(test_update_descriptor_tables);
    run_test(test_update_descriptor_heap_after_closing_command_list);
    run_test(test_update_compute_descriptor_tables);
    run_test(test_update_descriptor_tables_after_root_signature_change);
    run_test(test_copy_descriptors);
    run_test(test_copy_descriptors_range_sizes);
    run_test(test_descriptors_visibility);
    run_test(test_create_null_descriptors);
    run_test(test_null_cbv);
    run_test(test_null_srv);
    run_test(test_null_uav);
    run_test(test_null_vbv);
    run_test(test_get_copyable_footprints);
    run_test(test_depth_clip);
    run_test(test_depth_stencil_sampling);
    run_test(test_depth_load);
    run_test(test_depth_read_only_view);
    run_test(test_stencil_load);
    run_test(test_stencil_export);
    run_test(test_typed_buffer_uav);
    run_test(test_typed_uav_store);
    run_test(test_compute_shader_registers);
    run_test(test_tgsm);
    run_test(test_uav_load);
    run_test(test_cs_uav_store);
    run_test(test_uav_counters);
    run_test(test_decrement_uav_counter);
    run_test(test_graphics_uav_counters);
    run_test(test_atomic_instructions);
    run_test(test_buffer_srv);
    run_test(test_create_query_heap);
    run_test(test_query_timestamp);
    run_test(test_query_pipeline_statistics);
    run_test(test_query_occlusion);
    run_test(test_resolve_non_issued_query_data);
    run_test(test_resolve_query_data_in_different_command_list);
    run_test(test_resolve_query_data_in_reordered_command_list);
    run_test(test_execute_indirect);
    run_test(test_dispatch_zero_thread_groups);
    run_test(test_zero_vertex_stride);
    run_test(test_instance_id);
    run_test(test_vertex_id);
    run_test(test_copy_texture);
    run_test(test_copy_texture_buffer);
    run_test(test_copy_buffer_texture);
    run_test(test_copy_block_compressed_texture);
    run_test(test_separate_bindings);
    run_test(test_face_culling);
    run_test(test_multithread_command_queue_exec);
    run_test(test_geometry_shader);
    run_test(test_layered_rendering);
    run_test(test_ps_layer);
    run_test(test_nop_tessellation_shaders);
    run_test(test_quad_tessellation);
    run_test(test_tessellation_dcl_index_range);
    run_test(test_hull_shader_control_point_phase);
    run_test(test_hull_shader_fork_phase);
    run_test(test_line_tessellation);
    run_test(test_tessellation_primitive_id);
    run_test(test_domain_shader_inputs);
    run_test(test_domain_shader_one_patch_constant_input);
    run_test(test_render_a8);
    run_test(test_cpu_descriptors_lifetime);
    run_test(test_clip_distance);
    run_test(test_combined_clip_and_cull_distances);
    run_test(test_resource_allocation_info);
    run_test(test_64kb_texture_alignment);
    run_test(test_suballocate_small_textures);
    run_test(test_command_list_initial_pipeline_state);
    run_test(test_blend_factor);
    run_test(test_dual_source_blending);
    run_test(test_output_merger_logic_op);
    run_test(test_multisample_rendering);
    run_test(test_sample_mask);
    run_test(test_coverage);
    run_test(test_shader_get_render_target_sample_count);
    run_test(test_shader_sample_position);
    run_test(test_shader_eval_attribute);
    run_test(test_primitive_restart);
    run_test(test_vertex_shader_stream_output);
    run_test(test_read_write_subresource);
    run_test(test_queue_wait);
    run_test(test_graphics_compute_queue_synchronization);
    run_test(test_early_depth_stencil_tests);
    run_test(test_conditional_rendering);
    run_test(test_bufinfo_instruction);
    run_test(test_write_buffer_immediate);
    run_test(test_register_space);
    run_test(test_sampler_register_space);
    run_test(test_hull_shader_relative_addressing);
    run_test(test_hull_shader_patch_constant_inputs);
    run_test(test_resource_arrays);
    run_test(test_unbounded_resource_arrays);
    run_test(test_unbounded_samplers);
    run_test(test_clock_calibration);
    run_test(test_readback_map_stability);
    run_test(test_get_resource_tiling);
    run_test(test_hull_shader_punned_array);
    run_test(test_unused_interpolated_input);
    run_test(test_shader_cache);
    run_test(test_multi_fence_event);
    run_test(test_enumerate_meta_commands);
}
