[PATCH vkd3d v2] Support RS 1.0 VOLATILE descriptors

cybermax at dexter.no cybermax at dexter.no
Wed Nov 20 16:02:12 CST 2019


From: Sveinar Søpler <cybermax at dexter.no>

Rework of this patch to fix bug https://www.winehq.org/pipermail/wine-devel/2019-November/153658.html

Original commit message:

From: post at arntzen-software.no
Use EXT_descriptor_indexing's UPDATE_AFTER_BIND feature to support
semantics required by RS 1.0 VOLATILE descriptors. We implement this by
deferring all updates of desciptor sets until Submit time.

This is fine, as command buffers cannot be executed simultaneously on
D3D12, so at Submit time, we know that the command buffer is not being
executed on the GPU, and updating descriptors for multiple submissions
is correct.

If EXT_descriptor_indexing is not available, the fallback is the older
method, which matches RS 1.1 STATIC descriptor model.

Signed-off-by: Sveinar Søpler <cybermax at dexter.no>
---
 libs/vkd3d/command.c            |   359 +-
 libs/vkd3d/command.c.orig       |  6249 ++++++
 libs/vkd3d/device.c             |    20 +-
 libs/vkd3d/state.c              |    22 +
 libs/vkd3d/vkd3d_private.h      |    19 +-
 libs/vkd3d/vkd3d_private.h.orig |  1299 ++
 tests/d3d12.c                   |     2 +-
 tests/d3d12.c.orig              | 32850 ++++++++++++++++++++++++++++++
 8 files changed, 40737 insertions(+), 83 deletions(-)
 create mode 100644 libs/vkd3d/command.c.orig
 create mode 100644 libs/vkd3d/vkd3d_private.h.orig
 create mode 100644 tests/d3d12.c.orig

diff --git a/libs/vkd3d/command.c b/libs/vkd3d/command.c
index 297054b..8d8ba1d 100644
--- a/libs/vkd3d/command.c
+++ b/libs/vkd3d/command.c
@@ -1341,7 +1341,7 @@ static VkDescriptorPool d3d12_command_allocator_allocate_descriptor_pool(
     {
         pool_desc.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
         pool_desc.pNext = NULL;
-        pool_desc.flags = 0;
+        pool_desc.flags = device->vk_info.EXT_descriptor_indexing ? VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT_EXT : 0;
         pool_desc.maxSets = 512;
         pool_desc.poolSizeCount = ARRAY_SIZE(pool_sizes);
         pool_desc.pPoolSizes = pool_sizes;
@@ -1865,6 +1865,10 @@ static void d3d12_command_list_invalidate_bindings(struct d3d12_command_list *li
     if (!state)
         return;
 
+    /* Each pipeline state has its own set layout for UAV counters
+     * based on their implicit usage in the shader.
+     * Binding a different pipeline state means having to re-remit
+     * UAV counters in a new descriptor set (and layout). */
     if (state->uav_counter_mask)
     {
         struct vkd3d_pipeline_bindings *bindings = &list->pipeline_bindings[state->vk_bind_point];
@@ -2209,6 +2213,7 @@ static ULONG STDMETHODCALLTYPE d3d12_command_list_Release(ID3D12GraphicsCommandL
         if (list->allocator)
             d3d12_command_allocator_free_command_buffer(list->allocator, list);
 
+        vkd3d_free(list->descriptor_updates);
         vkd3d_free(list);
 
         d3d12_device_release(device);
@@ -2347,6 +2352,9 @@ static void d3d12_command_list_reset_state(struct d3d12_command_list *list,
 
     list->state = NULL;
 
+    /* Recycle deferred descriptor update memory if possible. */
+    list->descriptor_updates_count = 0;
+
     memset(list->so_counter_buffers, 0, sizeof(list->so_counter_buffers));
     memset(list->so_counter_buffer_offsets, 0, sizeof(list->so_counter_buffer_offsets));
 
@@ -2566,15 +2574,54 @@ static void d3d12_command_list_prepare_descriptors(struct d3d12_command_list *li
      *   time in between. Thus, the contents must not be altered (overwritten
      *   by an update command, or freed) between when the command is recorded
      *   and when the command completes executing on the queue."
+     *
+     * Even if we have descriptor indexing and UPDATE_AFTER_BIND,
+     * we need at the very least a new descriptor set.
      */
     bindings->descriptor_set = d3d12_command_allocator_allocate_descriptor_set(list->allocator,
             root_signature->vk_set_layout);
+
     bindings->in_use = false;
 
     bindings->descriptor_table_dirty_mask |= bindings->descriptor_table_active_mask & root_signature->descriptor_table_mask;
     bindings->push_descriptor_dirty_mask |= bindings->push_descriptor_active_mask & root_signature->push_descriptor_mask;
 }
 
+static void d3d12_command_list_prepare_uav_counter_descriptors(struct d3d12_command_list *list,
+        VkPipelineBindPoint bind_point)
+{
+    struct vkd3d_pipeline_bindings *bindings = &list->pipeline_bindings[bind_point];
+
+    if (bindings->uav_counter_descriptor_set && !bindings->uav_counter_in_use)
+        return;
+
+    /* We cannot modify bound descriptor sets. We need a new descriptor set if
+     * we are about to update resource bindings.
+     *
+     * The Vulkan spec says:
+     *
+     *   "The descriptor set contents bound by a call to
+     *   vkCmdBindDescriptorSets may be consumed during host execution of the
+     *   command, or during shader execution of the resulting draws, or any
+     *   time in between. Thus, the contents must not be altered (overwritten
+     *   by an update command, or freed) between when the command is recorded
+     *   and when the command completes executing on the queue."
+     *
+     * Even if we have descriptor indexing and UPDATE_AFTER_BIND,
+     * we need at the very least a new descriptor set.
+     */
+
+    if (list->state->uav_counter_mask)
+    {
+        bindings->uav_counter_descriptor_set = d3d12_command_allocator_allocate_descriptor_set(list->allocator,
+                list->state->vk_set_layout);
+    }
+    else
+        bindings->uav_counter_descriptor_set = VK_NULL_HANDLE;
+
+    bindings->uav_counter_in_use = false;
+}
+
 static bool vk_write_descriptor_set_from_d3d12_desc(VkWriteDescriptorSet *vk_descriptor_write,
         VkDescriptorImageInfo *vk_image_info, const struct d3d12_desc *descriptor,
         uint32_t descriptor_range_magic, VkDescriptorSet vk_descriptor_set,
@@ -2643,26 +2690,61 @@ static bool vk_write_descriptor_set_from_d3d12_desc(VkWriteDescriptorSet *vk_des
     return true;
 }
 
-static void d3d12_command_list_update_descriptor_table(struct d3d12_command_list *list,
-        VkPipelineBindPoint bind_point, unsigned int index, struct d3d12_desc *base_descriptor)
+static void d3d12_command_list_defer_update_descriptor_table(struct d3d12_command_list *list,
+        VkPipelineBindPoint bind_point, uint64_t table_mask, bool uav)
 {
+    const struct d3d12_desc *base_descriptor;
+    unsigned i;
+    struct d3d12_deferred_descriptor_set_update *update;
     struct vkd3d_pipeline_bindings *bindings = &list->pipeline_bindings[bind_point];
-    struct VkWriteDescriptorSet descriptor_writes[24], *current_descriptor_write;
-    const struct d3d12_root_signature *root_signature = bindings->root_signature;
+
+    for (i = 0; i < ARRAY_SIZE(bindings->descriptor_tables); ++i)
+    {
+        if (table_mask & ((uint64_t)1 << i))
+        {
+            if ((base_descriptor = d3d12_desc_from_gpu_handle(bindings->descriptor_tables[i])))
+            {
+                vkd3d_array_reserve((void **)&list->descriptor_updates, &list->descriptor_updates_size,
+                                    list->descriptor_updates_count + 1, sizeof(*list->descriptor_updates));
+                update = &list->descriptor_updates[list->descriptor_updates_count];
+
+                update->base_descriptor = base_descriptor;
+                update->index = i;
+                update->root_signature = bindings->root_signature;
+                update->descriptor_set = uav ? bindings->uav_counter_descriptor_set : bindings->descriptor_set;
+                update->uav = uav;
+                list->descriptor_updates_count++;
+            }
+            else
+                WARN("Descriptor table %u is not set.\n", i);
+        }
+    }
+}
+
+static void d3d12_command_list_resolve_descriptor_table_uav(struct d3d12_command_list *list,
+                                                            const struct d3d12_deferred_descriptor_set_update *update)
+{
+    const struct d3d12_root_signature *root_signature = update->root_signature;
     const struct vkd3d_vk_device_procs *vk_procs = &list->device->vk_procs;
-    struct VkDescriptorImageInfo image_infos[24], *current_image_info;
     const struct d3d12_root_descriptor_table *descriptor_table;
     const struct d3d12_root_descriptor_table_range *range;
     VkDevice vk_device = list->device->vk_device;
-    unsigned int i, j, descriptor_count;
-    struct d3d12_desc *descriptor;
-
-    descriptor_table = root_signature_get_descriptor_table(root_signature, index);
+    unsigned int i, j;
+    unsigned int uav_counter_count;
+    const struct d3d12_desc *descriptor;
+    VkWriteDescriptorSet vk_descriptor_writes[VKD3D_SHADER_MAX_UNORDERED_ACCESS_VIEWS];
+    VkBufferView vk_uav_counter_views[VKD3D_SHADER_MAX_UNORDERED_ACCESS_VIEWS];
+    VkDescriptorSet vk_descriptor_set;
+    const struct d3d12_desc *base_descriptor = update->base_descriptor;
 
+    descriptor_table = root_signature_get_descriptor_table(root_signature, update->index);
     descriptor = base_descriptor;
-    descriptor_count = 0;
-    current_descriptor_write = descriptor_writes;
-    current_image_info = image_infos;
+
+    vk_descriptor_set = update->descriptor_set;
+    if (!vk_descriptor_set)
+        return;
+
+    /* FIXME: There should be a smarter way than scanning through all the descriptor table ranges for this. */
     for (i = 0; i < descriptor_table->range_count; ++i)
     {
         range = &descriptor_table->ranges[i];
@@ -2676,20 +2758,74 @@ static void d3d12_command_list_update_descriptor_table(struct d3d12_command_list
         {
             unsigned int register_idx = range->base_register_idx + j;
 
-            /* Track UAV counters. */
+            /* Fish out UAV counters. */
             if (range->descriptor_magic == VKD3D_DESCRIPTOR_MAGIC_UAV
-                    && register_idx < ARRAY_SIZE(bindings->vk_uav_counter_views))
+                && register_idx < ARRAY_SIZE(vk_uav_counter_views))
             {
                 VkBufferView vk_counter_view = descriptor->magic == VKD3D_DESCRIPTOR_MAGIC_UAV
-                        ? descriptor->u.view->vk_counter_view : VK_NULL_HANDLE;
-                if (bindings->vk_uav_counter_views[register_idx] != vk_counter_view)
-                    bindings->uav_counter_dirty_mask |= 1u << register_idx;
-                bindings->vk_uav_counter_views[register_idx] = vk_counter_view;
+                                               ? descriptor->u.view->vk_counter_view : VK_NULL_HANDLE;
+                vk_uav_counter_views[register_idx] = vk_counter_view;
             }
+        }
+    }
+
+    uav_counter_count = vkd3d_popcount(list->state->uav_counter_mask);
+    assert(uav_counter_count <= ARRAY_SIZE(vk_descriptor_writes));
 
+    for (i = 0; i < uav_counter_count; ++i)
+    {
+        const struct vkd3d_shader_uav_counter_binding *uav_counter = &list->state->uav_counters[i];
+        assert(vk_uav_counter_views[uav_counter->register_index]);
+
+        vk_descriptor_writes[i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+        vk_descriptor_writes[i].pNext = NULL;
+        vk_descriptor_writes[i].dstSet = vk_descriptor_set;
+        vk_descriptor_writes[i].dstBinding = uav_counter->binding.binding;
+        vk_descriptor_writes[i].dstArrayElement = 0;
+        vk_descriptor_writes[i].descriptorCount = 1;
+        vk_descriptor_writes[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;
+        vk_descriptor_writes[i].pImageInfo = NULL;
+        vk_descriptor_writes[i].pBufferInfo = NULL;
+        vk_descriptor_writes[i].pTexelBufferView = &vk_uav_counter_views[uav_counter->register_index];
+    }
+
+    VK_CALL(vkUpdateDescriptorSets(vk_device, uav_counter_count, vk_descriptor_writes, 0, NULL));
+}
+
+static void d3d12_command_list_resolve_descriptor_table_normal(struct d3d12_command_list *list,
+        const struct d3d12_deferred_descriptor_set_update *update)
+{
+    const struct d3d12_root_descriptor_table *descriptor_table;
+    struct VkWriteDescriptorSet descriptor_writes[24], *current_descriptor_write;
+    struct VkDescriptorImageInfo image_infos[24], *current_image_info;
+    const struct d3d12_root_signature *root_signature = update->root_signature;
+    const struct vkd3d_vk_device_procs *vk_procs = &list->device->vk_procs;
+    const struct d3d12_root_descriptor_table_range *range;
+    VkDevice vk_device = list->device->vk_device;
+    unsigned int i, j, descriptor_count;
+    const struct d3d12_desc *descriptor;
+    const struct d3d12_desc *base_descriptor = update->base_descriptor;
+
+    descriptor_table = root_signature_get_descriptor_table(root_signature, update->index);
+
+    descriptor = update->base_descriptor;
+    descriptor_count = 0;
+    current_descriptor_write = descriptor_writes;
+    current_image_info = image_infos;
+    for (i = 0; i < descriptor_table->range_count; ++i)
+    {
+        range = &descriptor_table->ranges[i];
+
+        if (range->offset != D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND)
+        {
+            descriptor = base_descriptor + range->offset;
+        }
+
+        for (j = 0; j < range->descriptor_count; ++j, ++descriptor)
+        {
             if (!vk_write_descriptor_set_from_d3d12_desc(current_descriptor_write,
-                    current_image_info, descriptor, range->descriptor_magic,
-                    bindings->descriptor_set, range->binding, j))
+                                                         current_image_info, descriptor, range->descriptor_magic,
+                                                         update->descriptor_set, range->binding, j))
                 continue;
 
             ++descriptor_count;
@@ -2709,6 +2845,52 @@ static void d3d12_command_list_update_descriptor_table(struct d3d12_command_list
     VK_CALL(vkUpdateDescriptorSets(vk_device, descriptor_count, descriptor_writes, 0, NULL));
 }
 
+static void d3d12_command_list_resolve_descriptor_table(struct d3d12_command_list *list,
+                                                        const struct d3d12_deferred_descriptor_set_update *update)
+{
+    if (update->uav)
+        d3d12_command_list_resolve_descriptor_table_uav(list, update);
+    else
+        d3d12_command_list_resolve_descriptor_table_normal(list, update);
+}
+
+static void d3d12_command_list_resolve_descriptor_tables(struct d3d12_command_list *list)
+{
+    unsigned i;
+    for (i = 0; i < list->descriptor_updates_count; i++)
+        d3d12_command_list_resolve_descriptor_table(list, &list->descriptor_updates[i]);
+}
+
+static void d3d12_command_list_update_descriptor_table(struct d3d12_command_list *list,
+        VkPipelineBindPoint bind_point, unsigned int index, struct d3d12_desc *base_descriptor)
+{
+    struct d3d12_deferred_descriptor_set_update update;
+    struct vkd3d_pipeline_bindings *bindings = &list->pipeline_bindings[bind_point];
+    const struct d3d12_root_signature *root_signature = bindings->root_signature;
+
+    update.descriptor_set = bindings->descriptor_set;
+    update.index = index;
+    update.root_signature = root_signature;
+    update.base_descriptor = base_descriptor;
+    update.uav = false;
+    d3d12_command_list_resolve_descriptor_table_normal(list, &update);
+}
+
+static void d3d12_command_list_update_uav_counter_descriptor_table(struct d3d12_command_list *list,
+        VkPipelineBindPoint bind_point, unsigned int index, struct d3d12_desc *base_descriptor)
+{
+    struct d3d12_deferred_descriptor_set_update update;
+    struct vkd3d_pipeline_bindings *bindings = &list->pipeline_bindings[bind_point];
+    const struct d3d12_root_signature *root_signature = bindings->root_signature;
+
+    update.descriptor_set = bindings->uav_counter_descriptor_set;
+    update.index = index;
+    update.root_signature = root_signature;
+    update.base_descriptor = base_descriptor;
+    update.uav = true;
+    d3d12_command_list_resolve_descriptor_table_uav(list, &update);
+}
+
 static bool vk_write_descriptor_set_from_root_descriptor(VkWriteDescriptorSet *vk_descriptor_write,
         const struct d3d12_root_parameter *root_parameter, VkDescriptorSet vk_descriptor_set,
         VkBufferView *vk_buffer_view, const VkDescriptorBufferInfo *vk_buffer_info)
@@ -2813,55 +2995,6 @@ done:
     vkd3d_free(buffer_infos);
 }
 
-static void d3d12_command_list_update_uav_counter_descriptors(struct d3d12_command_list *list,
-        VkPipelineBindPoint bind_point)
-{
-    VkWriteDescriptorSet vk_descriptor_writes[VKD3D_SHADER_MAX_UNORDERED_ACCESS_VIEWS];
-    struct vkd3d_pipeline_bindings *bindings = &list->pipeline_bindings[bind_point];
-    const struct vkd3d_vk_device_procs *vk_procs = &list->device->vk_procs;
-    const struct d3d12_pipeline_state *state = list->state;
-    VkDevice vk_device = list->device->vk_device;
-    VkDescriptorSet vk_descriptor_set;
-    unsigned int uav_counter_count;
-    unsigned int i;
-
-    if (!state || !(state->uav_counter_mask & bindings->uav_counter_dirty_mask))
-        return;
-
-    uav_counter_count = vkd3d_popcount(state->uav_counter_mask);
-    assert(uav_counter_count <= ARRAY_SIZE(vk_descriptor_writes));
-
-    vk_descriptor_set = d3d12_command_allocator_allocate_descriptor_set(list->allocator, state->vk_set_layout);
-    if (!vk_descriptor_set)
-        return;
-
-    for (i = 0; i < uav_counter_count; ++i)
-    {
-        const struct vkd3d_shader_uav_counter_binding *uav_counter = &state->uav_counters[i];
-        const VkBufferView *vk_uav_counter_views = bindings->vk_uav_counter_views;
-
-        assert(vk_uav_counter_views[uav_counter->register_index]);
-
-        vk_descriptor_writes[i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
-        vk_descriptor_writes[i].pNext = NULL;
-        vk_descriptor_writes[i].dstSet = vk_descriptor_set;
-        vk_descriptor_writes[i].dstBinding = uav_counter->binding.binding;
-        vk_descriptor_writes[i].dstArrayElement = 0;
-        vk_descriptor_writes[i].descriptorCount = 1;
-        vk_descriptor_writes[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;
-        vk_descriptor_writes[i].pImageInfo = NULL;
-        vk_descriptor_writes[i].pBufferInfo = NULL;
-        vk_descriptor_writes[i].pTexelBufferView = &vk_uav_counter_views[uav_counter->register_index];
-    }
-
-    VK_CALL(vkUpdateDescriptorSets(vk_device, uav_counter_count, vk_descriptor_writes, 0, NULL));
-
-    VK_CALL(vkCmdBindDescriptorSets(list->vk_command_buffer, bind_point,
-            state->vk_pipeline_layout, state->set_index, 1, &vk_descriptor_set, 0, NULL));
-
-    bindings->uav_counter_dirty_mask = 0;
-}
-
 static void d3d12_command_list_update_descriptors(struct d3d12_command_list *list,
         VkPipelineBindPoint bind_point)
 {
@@ -2874,31 +3007,86 @@ static void d3d12_command_list_update_descriptors(struct d3d12_command_list *lis
     if (!rs || !rs->vk_set_layout)
         return;
 
+    if ((bindings->descriptor_table_active_mask | bindings->push_descriptor_dirty_mask |
+         (list->state->uav_counter_mask & bindings->uav_counter_dirty_mask)) == 0)
+    {
+        /* Nothing is dirty, so just return early. */
+        return;
+    }
+
     if (bindings->descriptor_table_dirty_mask || bindings->push_descriptor_dirty_mask)
         d3d12_command_list_prepare_descriptors(list, bind_point);
+    if (list->state->uav_counter_mask & bindings->uav_counter_dirty_mask)
+        d3d12_command_list_prepare_uav_counter_descriptors(list, bind_point);
 
-    for (i = 0; i < ARRAY_SIZE(bindings->descriptor_tables); ++i)
+    if (list->device->vk_info.EXT_descriptor_indexing)
+    {
+        d3d12_command_list_defer_update_descriptor_table(list, bind_point, bindings->descriptor_table_dirty_mask,
+                                                         false);
+    }
+    else
     {
-        if (bindings->descriptor_table_dirty_mask & ((uint64_t)1 << i))
+        /* FIXME: FOR_EACH_BIT */
+        for (i = 0; i < ARRAY_SIZE(bindings->descriptor_tables); ++i)
         {
-            if ((base_descriptor = d3d12_desc_from_gpu_handle(bindings->descriptor_tables[i])))
-                d3d12_command_list_update_descriptor_table(list, bind_point, i, base_descriptor);
-            else
-                WARN("Descriptor table %u is not set.\n", i);
+            if (bindings->descriptor_table_dirty_mask & ((uint64_t) 1 << i))
+            {
+                if ((base_descriptor = d3d12_desc_from_gpu_handle(bindings->descriptor_tables[i])))
+                    d3d12_command_list_update_descriptor_table(list, bind_point, i, base_descriptor);
+                else
+                    WARN("Descriptor table %u is not set.\n", i);
+            }
         }
     }
     bindings->descriptor_table_dirty_mask = 0;
 
+    /* Need to go through all descriptor tables here in the root signature,
+     * not just descriptor_table_dirty_mask. Binding a different shader may not invalidate descriptor tables,
+     * but it may invalidate the UAV counter set. */
+    if (bindings->uav_counter_dirty_mask)
+    {
+        if (list->device->vk_info.EXT_descriptor_indexing)
+        {
+            d3d12_command_list_defer_update_descriptor_table(list, bind_point, bindings->descriptor_table_active_mask,
+                                                             true);
+        }
+        else
+        {
+            /* FIXME: FOR_EACH_BIT */
+            for (i = 0; i < ARRAY_SIZE(bindings->descriptor_tables); i++)
+            {
+                if (bindings->descriptor_table_active_mask & ((uint64_t) 1 << i))
+                {
+                    if ((base_descriptor = d3d12_desc_from_gpu_handle(bindings->descriptor_tables[i])))
+                        d3d12_command_list_update_uav_counter_descriptor_table(list, bind_point, i, base_descriptor);
+                    else
+                        WARN("Descriptor table %u is not set.\n", i);
+                }
+            }
+        }
+    }
+    bindings->uav_counter_dirty_mask = 0;
+
     d3d12_command_list_update_push_descriptors(list, bind_point);
 
-    if (bindings->descriptor_set)
+    /* Don't rebind the same descriptor set as long as we're in same pipeline layout. */
+    if (bindings->descriptor_set && bindings->descriptor_set != bindings->descriptor_set_bound)
     {
         VK_CALL(vkCmdBindDescriptorSets(list->vk_command_buffer, bind_point,
                 rs->vk_pipeline_layout, rs->main_set, 1, &bindings->descriptor_set, 0, NULL));
         bindings->in_use = true;
+        bindings->descriptor_set_bound = bindings->descriptor_set;
     }
 
-    d3d12_command_list_update_uav_counter_descriptors(list, bind_point);
+    /* Don't rebind the same descriptor set as long as we're in same pipeline layout. */
+    if (bindings->uav_counter_descriptor_set &&
+        bindings->uav_counter_descriptor_set != bindings->uav_counter_descriptor_set_bound)
+    {
+        VK_CALL(vkCmdBindDescriptorSets(list->vk_command_buffer, bind_point,
+                list->state->vk_pipeline_layout, list->state->set_index, 1, &bindings->uav_counter_descriptor_set, 0, NULL));
+        bindings->uav_counter_in_use = true;
+        bindings->uav_counter_descriptor_set_bound = bindings->uav_counter_descriptor_set;
+    }
 }
 
 static bool d3d12_command_list_update_compute_state(struct d3d12_command_list *list)
@@ -4054,6 +4242,11 @@ static void d3d12_command_list_set_root_signature(struct d3d12_command_list *lis
     bindings->root_signature = root_signature;
 
     d3d12_command_list_invalidate_root_parameters(list, bind_point);
+
+    bindings->uav_counter_descriptor_set = VK_NULL_HANDLE;
+    bindings->descriptor_set_bound = VK_NULL_HANDLE;
+    bindings->uav_counter_descriptor_set_bound = VK_NULL_HANDLE;
+
 }
 
 static void STDMETHODCALLTYPE d3d12_command_list_SetComputeRootSignature(ID3D12GraphicsCommandList1 *iface,
@@ -5422,6 +5615,9 @@ static HRESULT d3d12_command_list_init(struct d3d12_command_list *list, struct d
     d3d12_device_add_ref(list->device = device);
 
     list->allocator = allocator;
+    list->descriptor_updates = NULL;
+    list->descriptor_updates_count = 0;
+    list->descriptor_updates_size = 0;
 
     if (SUCCEEDED(hr = d3d12_command_allocator_allocate_command_buffer(allocator, list)))
     {
@@ -5659,6 +5855,13 @@ static void STDMETHODCALLTYPE d3d12_command_queue_ExecuteCommandLists(ID3D12Comm
             return;
         }
 
+        /* Descriptors in root signature in 1.0 are VOLATILE by default, so
+         * the descriptor heap only need to be valid right before we submit them to the GPU.
+         * If we have EXT_descriptor_indexing enabled with UpdateAfterBind, we update
+         * descriptor sets here rather than while we're recording the command buffer.
+         * For each submission of the command buffer, we can modify the descriptor heap as we please. */
+        d3d12_command_list_resolve_descriptor_tables(cmd_list);
+
         buffers[i] = cmd_list->vk_command_buffer;
     }
 
diff --git a/libs/vkd3d/command.c.orig b/libs/vkd3d/command.c.orig
new file mode 100644
index 0000000..297054b
--- /dev/null
+++ b/libs/vkd3d/command.c.orig
@@ -0,0 +1,6249 @@
+/*
+ * Copyright 2016 Józef Kucia for CodeWeavers
+ * Copyright 2016 Henri Verbeet 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
+ */
+
+#include "vkd3d_private.h"
+
+static HRESULT d3d12_fence_signal(struct d3d12_fence *fence, uint64_t value, VkFence vk_fence);
+
+HRESULT vkd3d_queue_create(struct d3d12_device *device,
+        uint32_t family_index, const VkQueueFamilyProperties *properties, struct vkd3d_queue **queue)
+{
+    const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
+    struct vkd3d_queue *object;
+    int rc;
+
+    if (!(object = vkd3d_malloc(sizeof(*object))))
+        return E_OUTOFMEMORY;
+
+    if ((rc = pthread_mutex_init(&object->mutex, NULL)))
+    {
+        ERR("Failed to initialize mutex, error %d.\n", rc);
+        vkd3d_free(object);
+        return hresult_from_errno(rc);
+    }
+
+    object->completed_sequence_number = 0;
+    object->submitted_sequence_number = 0;
+
+    object->vk_family_index = family_index;
+    object->vk_queue_flags = properties->queueFlags;
+    object->timestamp_bits = properties->timestampValidBits;
+
+    object->semaphores = NULL;
+    object->semaphores_size = 0;
+    object->semaphore_count = 0;
+
+    memset(object->old_vk_semaphores, 0, sizeof(object->old_vk_semaphores));
+
+    VK_CALL(vkGetDeviceQueue(device->vk_device, family_index, 0, &object->vk_queue));
+
+    TRACE("Created queue %p for queue family index %u.\n", object, family_index);
+
+    *queue = object;
+
+    return S_OK;
+}
+
+void vkd3d_queue_destroy(struct vkd3d_queue *queue, struct d3d12_device *device)
+{
+    const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
+    unsigned int i;
+    int rc;
+
+    if ((rc = pthread_mutex_lock(&queue->mutex)))
+        ERR("Failed to lock mutex, error %d.\n", rc);
+
+    for (i = 0; i < queue->semaphore_count; ++i)
+        VK_CALL(vkDestroySemaphore(device->vk_device, queue->semaphores[i].vk_semaphore, NULL));
+
+    vkd3d_free(queue->semaphores);
+
+    for (i = 0; i < ARRAY_SIZE(queue->old_vk_semaphores); ++i)
+    {
+        if (queue->old_vk_semaphores[i])
+            VK_CALL(vkDestroySemaphore(device->vk_device, queue->old_vk_semaphores[i], NULL));
+    }
+
+    if (!rc)
+        pthread_mutex_unlock(&queue->mutex);
+
+    pthread_mutex_destroy(&queue->mutex);
+    vkd3d_free(queue);
+}
+
+VkQueue vkd3d_queue_acquire(struct vkd3d_queue *queue)
+{
+    int rc;
+
+    TRACE("queue %p.\n", queue);
+
+    if ((rc = pthread_mutex_lock(&queue->mutex)))
+    {
+        ERR("Failed to lock mutex, error %d.\n", rc);
+        return VK_NULL_HANDLE;
+    }
+
+    assert(queue->vk_queue);
+    return queue->vk_queue;
+}
+
+void vkd3d_queue_release(struct vkd3d_queue *queue)
+{
+    TRACE("queue %p.\n", queue);
+
+    pthread_mutex_unlock(&queue->mutex);
+}
+
+static VkResult vkd3d_queue_wait_idle(struct vkd3d_queue *queue,
+        const struct vkd3d_vk_device_procs *vk_procs)
+{
+    VkQueue vk_queue;
+    VkResult vr;
+
+    if ((vk_queue = vkd3d_queue_acquire(queue)))
+    {
+        vr = VK_CALL(vkQueueWaitIdle(vk_queue));
+        vkd3d_queue_release(queue);
+
+        if (vr < 0)
+            WARN("Failed to wait for queue, vr %d.\n", vr);
+    }
+    else
+    {
+        ERR("Failed to acquire queue %p.\n", queue);
+        vr = VK_ERROR_OUT_OF_HOST_MEMORY;
+    }
+
+    return vr;
+}
+
+static void vkd3d_queue_update_sequence_number(struct vkd3d_queue *queue,
+        uint64_t sequence_number, struct d3d12_device *device)
+{
+    const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
+    unsigned int destroyed_semaphore_count = 0;
+    uint64_t completed_sequence_number;
+    VkSemaphore vk_semaphore;
+    unsigned int i, j;
+    int rc;
+
+    if ((rc = pthread_mutex_lock(&queue->mutex)))
+    {
+        ERR("Failed to lock mutex, error %d.\n", rc);
+        return;
+    }
+
+    completed_sequence_number = queue->completed_sequence_number;
+    queue->completed_sequence_number = max(sequence_number, queue->completed_sequence_number);
+
+    TRACE("Queue %p sequence number %"PRIu64" -> %"PRIu64".\n",
+            queue, completed_sequence_number, queue->completed_sequence_number);
+
+    for (i = 0; i < queue->semaphore_count; ++i)
+    {
+        if (queue->semaphores[i].sequence_number > queue->completed_sequence_number)
+            break;
+
+        vk_semaphore = queue->semaphores[i].vk_semaphore;
+
+        /* Try to store the Vulkan semaphore for reuse. */
+        for (j = 0; j < ARRAY_SIZE(queue->old_vk_semaphores); ++j)
+        {
+            if (queue->old_vk_semaphores[j] == VK_NULL_HANDLE)
+            {
+                queue->old_vk_semaphores[j] = vk_semaphore;
+                vk_semaphore = VK_NULL_HANDLE;
+                break;
+            }
+        }
+
+        if (!vk_semaphore)
+            continue;
+
+        VK_CALL(vkDestroySemaphore(device->vk_device, vk_semaphore, NULL));
+        ++destroyed_semaphore_count;
+    }
+    if (i > 0)
+    {
+        queue->semaphore_count -= i;
+        memmove(queue->semaphores, &queue->semaphores[i], queue->semaphore_count * sizeof(*queue->semaphores));
+    }
+
+    if (destroyed_semaphore_count)
+        TRACE("Destroyed %u Vulkan semaphores.\n", destroyed_semaphore_count);
+
+    pthread_mutex_unlock(&queue->mutex);
+}
+
+static uint64_t vkd3d_queue_reset_sequence_number_locked(struct vkd3d_queue *queue)
+{
+    unsigned int i;
+
+    WARN("Ressetting sequence number for queue %p.\n", queue);
+
+    queue->completed_sequence_number = 0;
+    queue->submitted_sequence_number = 1;
+
+    for (i = 0; i < queue->semaphore_count; ++i)
+        queue->semaphores[i].sequence_number = queue->submitted_sequence_number;
+
+    return queue->submitted_sequence_number;
+}
+
+static VkResult vkd3d_queue_create_vk_semaphore_locked(struct vkd3d_queue *queue,
+        struct d3d12_device *device, VkSemaphore *vk_semaphore)
+{
+    const struct vkd3d_vk_device_procs *vk_procs;
+    VkSemaphoreCreateInfo semaphore_info;
+    unsigned int i;
+    VkResult vr;
+
+    *vk_semaphore = VK_NULL_HANDLE;
+
+    for (i = 0; i < ARRAY_SIZE(queue->old_vk_semaphores); ++i)
+    {
+        if ((*vk_semaphore = queue->old_vk_semaphores[i]))
+        {
+            queue->old_vk_semaphores[i] = VK_NULL_HANDLE;
+            break;
+        }
+    }
+
+    if (*vk_semaphore)
+        return VK_SUCCESS;
+
+    vk_procs = &device->vk_procs;
+
+    semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
+    semaphore_info.pNext = NULL;
+    semaphore_info.flags = 0;
+    if ((vr = VK_CALL(vkCreateSemaphore(device->vk_device, &semaphore_info, NULL, vk_semaphore))) < 0)
+    {
+        WARN("Failed to create Vulkan semaphore, vr %d.\n", vr);
+        *vk_semaphore = VK_NULL_HANDLE;
+    }
+
+    return vr;
+}
+
+/* Fence worker thread */
+static HRESULT vkd3d_enqueue_gpu_fence(struct vkd3d_fence_worker *worker,
+        VkFence vk_fence, struct d3d12_fence *fence, uint64_t value,
+        struct vkd3d_queue *queue, uint64_t queue_sequence_number)
+{
+    struct vkd3d_waiting_fence *waiting_fence;
+    int rc;
+
+    TRACE("worker %p, fence %p, value %#"PRIx64".\n", worker, fence, value);
+
+    if ((rc = pthread_mutex_lock(&worker->mutex)))
+    {
+        ERR("Failed to lock mutex, error %d.\n", rc);
+        return hresult_from_errno(rc);
+    }
+
+    if (!vkd3d_array_reserve((void **)&worker->enqueued_fences, &worker->enqueued_fences_size,
+            worker->enqueued_fence_count + 1, sizeof(*worker->enqueued_fences)))
+    {
+        ERR("Failed to add GPU fence.\n");
+        pthread_mutex_unlock(&worker->mutex);
+        return E_OUTOFMEMORY;
+    }
+
+    worker->enqueued_fences[worker->enqueued_fence_count].vk_fence = vk_fence;
+    waiting_fence = &worker->enqueued_fences[worker->enqueued_fence_count].waiting_fence;
+    waiting_fence->fence = fence;
+    waiting_fence->value = value;
+    waiting_fence->queue = queue;
+    waiting_fence->queue_sequence_number = queue_sequence_number;
+    ++worker->enqueued_fence_count;
+
+    InterlockedIncrement(&fence->pending_worker_operation_count);
+
+    pthread_cond_signal(&worker->cond);
+    pthread_mutex_unlock(&worker->mutex);
+
+    return S_OK;
+}
+
+static void vkd3d_fence_worker_remove_fence(struct vkd3d_fence_worker *worker, struct d3d12_fence *fence)
+{
+    LONG count;
+    int rc;
+
+    if (!(count = atomic_add_fetch(&fence->pending_worker_operation_count, 0)))
+        return;
+
+    WARN("Waiting for %u pending fence operations (fence %p).\n", count, fence);
+
+    if ((rc = pthread_mutex_lock(&worker->mutex)))
+    {
+        ERR("Failed to lock mutex, error %d.\n", rc);
+        return;
+    }
+
+    while ((count = atomic_add_fetch(&fence->pending_worker_operation_count, 0)))
+    {
+        TRACE("Still waiting for %u pending fence operations (fence %p).\n", count, fence);
+
+        worker->pending_fence_destruction = true;
+        pthread_cond_signal(&worker->cond);
+
+        pthread_cond_wait(&worker->fence_destruction_cond, &worker->mutex);
+    }
+
+    TRACE("Removed fence %p.\n", fence);
+
+    pthread_mutex_unlock(&worker->mutex);
+}
+
+static void vkd3d_fence_worker_move_enqueued_fences_locked(struct vkd3d_fence_worker *worker)
+{
+    unsigned int i;
+    size_t count;
+    bool ret;
+
+    if (!worker->enqueued_fence_count)
+        return;
+
+    count = worker->fence_count + worker->enqueued_fence_count;
+
+    ret = vkd3d_array_reserve((void **)&worker->vk_fences, &worker->vk_fences_size,
+            count, sizeof(*worker->vk_fences));
+    ret &= vkd3d_array_reserve((void **)&worker->fences, &worker->fences_size,
+            count, sizeof(*worker->fences));
+    if (!ret)
+    {
+        ERR("Failed to reserve memory.\n");
+        return;
+    }
+
+    for (i = 0; i < worker->enqueued_fence_count; ++i)
+    {
+        struct vkd3d_enqueued_fence *current = &worker->enqueued_fences[i];
+
+        worker->vk_fences[worker->fence_count] = current->vk_fence;
+        worker->fences[worker->fence_count] = current->waiting_fence;
+        ++worker->fence_count;
+    }
+    assert(worker->fence_count == count);
+    worker->enqueued_fence_count = 0;
+}
+
+static void vkd3d_wait_for_gpu_fences(struct vkd3d_fence_worker *worker)
+{
+    struct d3d12_device *device = worker->device;
+    const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
+    unsigned int i, j;
+    VkFence vk_fence;
+    HRESULT hr;
+    int vr;
+
+    if (!worker->fence_count)
+        return;
+
+    vr = VK_CALL(vkWaitForFences(device->vk_device,
+            worker->fence_count, worker->vk_fences, VK_FALSE, ~(uint64_t)0));
+    if (vr == VK_TIMEOUT)
+        return;
+    if (vr != VK_SUCCESS)
+    {
+        ERR("Failed to wait for Vulkan fences, vr %d.\n", vr);
+        return;
+    }
+
+    for (i = 0, j = 0; i < worker->fence_count; ++i)
+    {
+        vk_fence = worker->vk_fences[i];
+        if (!(vr = VK_CALL(vkGetFenceStatus(device->vk_device, vk_fence))))
+        {
+            struct vkd3d_waiting_fence *current = &worker->fences[i];
+
+            TRACE("Signaling fence %p value %#"PRIx64".\n", current->fence, current->value);
+            if (FAILED(hr = d3d12_fence_signal(current->fence, current->value, vk_fence)))
+                ERR("Failed to signal D3D12 fence, hr %#x.\n", hr);
+
+            InterlockedDecrement(&current->fence->pending_worker_operation_count);
+
+            vkd3d_queue_update_sequence_number(current->queue, current->queue_sequence_number, device);
+            continue;
+        }
+
+        if (vr != VK_NOT_READY)
+            ERR("Failed to get Vulkan fence status, vr %d.\n", vr);
+
+        if (i != j)
+        {
+            worker->vk_fences[j] = worker->vk_fences[i];
+            worker->fences[j] = worker->fences[i];
+        }
+        ++j;
+    }
+    worker->fence_count = j;
+}
+
+static void *vkd3d_fence_worker_main(void *arg)
+{
+    struct vkd3d_fence_worker *worker = arg;
+    int rc;
+
+    vkd3d_set_thread_name("vkd3d_fence");
+
+    for (;;)
+    {
+        vkd3d_wait_for_gpu_fences(worker);
+
+        if (!worker->fence_count || atomic_add_fetch(&worker->enqueued_fence_count, 0))
+        {
+            if ((rc = pthread_mutex_lock(&worker->mutex)))
+            {
+                ERR("Failed to lock mutex, error %d.\n", rc);
+                break;
+            }
+
+            if (worker->pending_fence_destruction)
+            {
+                pthread_cond_broadcast(&worker->fence_destruction_cond);
+                worker->pending_fence_destruction = false;
+            }
+
+            if (worker->enqueued_fence_count)
+            {
+                vkd3d_fence_worker_move_enqueued_fences_locked(worker);
+            }
+            else
+            {
+                if (worker->should_exit)
+                {
+                    pthread_mutex_unlock(&worker->mutex);
+                    break;
+                }
+
+                if ((rc = pthread_cond_wait(&worker->cond, &worker->mutex)))
+                {
+                    ERR("Failed to wait on condition variable, error %d.\n", rc);
+                    pthread_mutex_unlock(&worker->mutex);
+                    break;
+                }
+            }
+
+            pthread_mutex_unlock(&worker->mutex);
+        }
+    }
+
+    return NULL;
+}
+
+HRESULT vkd3d_fence_worker_start(struct vkd3d_fence_worker *worker,
+        struct d3d12_device *device)
+{
+    HRESULT hr;
+    int rc;
+
+    TRACE("worker %p.\n", worker);
+
+    worker->should_exit = false;
+    worker->pending_fence_destruction = false;
+    worker->device = device;
+
+    worker->enqueued_fence_count = 0;
+    worker->enqueued_fences = NULL;
+    worker->enqueued_fences_size = 0;
+
+    worker->fence_count = 0;
+
+    worker->vk_fences = NULL;
+    worker->vk_fences_size = 0;
+    worker->fences = NULL;
+    worker->fences_size = 0;
+
+    if ((rc = pthread_mutex_init(&worker->mutex, NULL)))
+    {
+        ERR("Failed to initialize mutex, error %d.\n", rc);
+        return hresult_from_errno(rc);
+    }
+
+    if ((rc = pthread_cond_init(&worker->cond, NULL)))
+    {
+        ERR("Failed to initialize condition variable, error %d.\n", rc);
+        pthread_mutex_destroy(&worker->mutex);
+        return hresult_from_errno(rc);
+    }
+
+    if ((rc = pthread_cond_init(&worker->fence_destruction_cond, NULL)))
+    {
+        ERR("Failed to initialize condition variable, error %d.\n", rc);
+        pthread_mutex_destroy(&worker->mutex);
+        pthread_cond_destroy(&worker->cond);
+        return hresult_from_errno(rc);
+    }
+
+    if (FAILED(hr = vkd3d_create_thread(device->vkd3d_instance,
+            vkd3d_fence_worker_main, worker, &worker->thread)))
+    {
+        pthread_mutex_destroy(&worker->mutex);
+        pthread_cond_destroy(&worker->cond);
+        pthread_cond_destroy(&worker->fence_destruction_cond);
+    }
+
+    return hr;
+}
+
+HRESULT vkd3d_fence_worker_stop(struct vkd3d_fence_worker *worker,
+        struct d3d12_device *device)
+{
+    HRESULT hr;
+    int rc;
+
+    TRACE("worker %p.\n", worker);
+
+    if ((rc = pthread_mutex_lock(&worker->mutex)))
+    {
+        ERR("Failed to lock mutex, error %d.\n", rc);
+        return hresult_from_errno(rc);
+    }
+
+    worker->should_exit = true;
+    pthread_cond_signal(&worker->cond);
+
+    pthread_mutex_unlock(&worker->mutex);
+
+    if (FAILED(hr = vkd3d_join_thread(device->vkd3d_instance, &worker->thread)))
+        return hr;
+
+    pthread_mutex_destroy(&worker->mutex);
+    pthread_cond_destroy(&worker->cond);
+    pthread_cond_destroy(&worker->fence_destruction_cond);
+
+    vkd3d_free(worker->enqueued_fences);
+    vkd3d_free(worker->vk_fences);
+    vkd3d_free(worker->fences);
+
+    return S_OK;
+}
+
+static const struct d3d12_root_parameter *root_signature_get_parameter(
+        const struct d3d12_root_signature *root_signature, unsigned int index)
+{
+    assert(index < root_signature->parameter_count);
+    return &root_signature->parameters[index];
+}
+
+static const struct d3d12_root_descriptor_table *root_signature_get_descriptor_table(
+        const struct d3d12_root_signature *root_signature, unsigned int index)
+{
+    const struct d3d12_root_parameter *p = root_signature_get_parameter(root_signature, index);
+    assert(p->parameter_type == D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE);
+    return &p->u.descriptor_table;
+}
+
+static const struct d3d12_root_constant *root_signature_get_32bit_constants(
+        const struct d3d12_root_signature *root_signature, unsigned int index)
+{
+    const struct d3d12_root_parameter *p = root_signature_get_parameter(root_signature, index);
+    assert(p->parameter_type == D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS);
+    return &p->u.constant;
+}
+
+static const struct d3d12_root_parameter *root_signature_get_root_descriptor(
+        const struct d3d12_root_signature *root_signature, unsigned int index)
+{
+    const struct d3d12_root_parameter *p = root_signature_get_parameter(root_signature, index);
+    assert(p->parameter_type == D3D12_ROOT_PARAMETER_TYPE_CBV
+        || p->parameter_type == D3D12_ROOT_PARAMETER_TYPE_SRV
+        || p->parameter_type == D3D12_ROOT_PARAMETER_TYPE_UAV);
+    return p;
+}
+
+/* ID3D12Fence */
+static struct d3d12_fence *impl_from_ID3D12Fence(ID3D12Fence *iface)
+{
+    return CONTAINING_RECORD(iface, struct d3d12_fence, ID3D12Fence_iface);
+}
+
+static VkResult d3d12_fence_create_vk_fence(struct d3d12_fence *fence, VkFence *vk_fence)
+{
+    const struct vkd3d_vk_device_procs *vk_procs;
+    struct d3d12_device *device = fence->device;
+    VkFenceCreateInfo fence_info;
+    unsigned int i;
+    VkResult vr;
+    int rc;
+
+    *vk_fence = VK_NULL_HANDLE;
+
+    if ((rc = pthread_mutex_lock(&fence->mutex)))
+    {
+        ERR("Failed to lock mutex, error %d.\n", rc);
+        goto create_fence;
+    }
+
+    for (i = 0; i < ARRAY_SIZE(fence->old_vk_fences); ++i)
+    {
+        if ((*vk_fence = fence->old_vk_fences[i]))
+        {
+            fence->old_vk_fences[i] = VK_NULL_HANDLE;
+            break;
+        }
+    }
+
+    pthread_mutex_unlock(&fence->mutex);
+
+    if (*vk_fence)
+        return VK_SUCCESS;
+
+create_fence:
+    vk_procs = &device->vk_procs;
+
+    fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
+    fence_info.pNext = NULL;
+    fence_info.flags = 0;
+    if ((vr = VK_CALL(vkCreateFence(device->vk_device, &fence_info, NULL, vk_fence))) < 0)
+    {
+        WARN("Failed to create Vulkan fence, vr %d.\n", vr);
+        *vk_fence = VK_NULL_HANDLE;
+    }
+
+    return vr;
+}
+
+static void d3d12_fence_garbage_collect_vk_semaphores_locked(struct d3d12_fence *fence,
+        bool destroy_all)
+{
+    struct d3d12_device *device = fence->device;
+    const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
+    struct vkd3d_signaled_semaphore *current, *p;
+    unsigned int semaphore_count;
+
+    semaphore_count = fence->semaphore_count;
+    if (!destroy_all && semaphore_count < VKD3D_MAX_VK_SYNC_OBJECTS)
+        return;
+
+    LIST_FOR_EACH_ENTRY_SAFE(current, p, &fence->semaphores, struct vkd3d_signaled_semaphore, entry)
+    {
+        if (!destroy_all && fence->semaphore_count < VKD3D_MAX_VK_SYNC_OBJECTS)
+            break;
+
+        /* The semaphore doesn't have a pending signal operation if the fence
+         * was signaled. */
+        if ((current->vk_fence || current->is_acquired) && !destroy_all)
+            continue;
+
+        if (current->vk_fence)
+            WARN("Destroying potentially pending semaphore.\n");
+        assert(!current->is_acquired);
+
+        VK_CALL(vkDestroySemaphore(device->vk_device, current->vk_semaphore, NULL));
+        list_remove(&current->entry);
+        vkd3d_free(current);
+
+        --fence->semaphore_count;
+    }
+
+    if (semaphore_count != fence->semaphore_count)
+        TRACE("Destroyed %u Vulkan semaphores.\n", semaphore_count - fence->semaphore_count);
+}
+
+static void d3d12_fence_destroy_vk_objects(struct d3d12_fence *fence)
+{
+    const struct vkd3d_vk_device_procs *vk_procs;
+    struct d3d12_device *device = fence->device;
+    unsigned int i;
+    int rc;
+
+    if ((rc = pthread_mutex_lock(&fence->mutex)))
+    {
+        ERR("Failed to lock mutex, error %d.\n", rc);
+        return;
+    }
+
+    vk_procs = &device->vk_procs;
+
+    for (i = 0; i < ARRAY_SIZE(fence->old_vk_fences); ++i)
+    {
+        if (fence->old_vk_fences[i])
+            VK_CALL(vkDestroyFence(device->vk_device, fence->old_vk_fences[i], NULL));
+        fence->old_vk_fences[i] = VK_NULL_HANDLE;
+    }
+
+    d3d12_fence_garbage_collect_vk_semaphores_locked(fence, true);
+
+    pthread_mutex_unlock(&fence->mutex);
+}
+
+static struct vkd3d_signaled_semaphore *d3d12_fence_acquire_vk_semaphore(struct d3d12_fence *fence,
+        uint64_t value, uint64_t *completed_value)
+{
+    struct vkd3d_signaled_semaphore *semaphore;
+    struct vkd3d_signaled_semaphore *current;
+    uint64_t semaphore_value;
+    int rc;
+
+    TRACE("fence %p, value %#"PRIx64".\n", fence, value);
+
+    if ((rc = pthread_mutex_lock(&fence->mutex)))
+    {
+        ERR("Failed to lock mutex, error %d.\n", rc);
+        return VK_NULL_HANDLE;
+    }
+
+    semaphore = NULL;
+    semaphore_value = ~(uint64_t)0;
+
+    LIST_FOR_EACH_ENTRY(current, &fence->semaphores, struct vkd3d_signaled_semaphore, entry)
+    {
+        /* Prefer a semaphore with the smallest value. */
+        if (!current->is_acquired && current->value >= value && semaphore_value >= current->value)
+        {
+            semaphore = current;
+            semaphore_value = current->value;
+        }
+        if (semaphore_value == value)
+            break;
+    }
+
+    if (semaphore)
+        semaphore->is_acquired = true;
+
+    *completed_value = fence->value;
+
+    pthread_mutex_unlock(&fence->mutex);
+
+    return semaphore;
+}
+
+static void d3d12_fence_remove_vk_semaphore(struct d3d12_fence *fence, struct vkd3d_signaled_semaphore *semaphore)
+{
+    int rc;
+
+    if ((rc = pthread_mutex_lock(&fence->mutex)))
+    {
+        ERR("Failed to lock mutex, error %d.\n", rc);
+        return;
+    }
+
+    assert(semaphore->is_acquired);
+
+    list_remove(&semaphore->entry);
+    vkd3d_free(semaphore);
+
+    --fence->semaphore_count;
+
+    pthread_mutex_unlock(&fence->mutex);
+}
+
+static void d3d12_fence_release_vk_semaphore(struct d3d12_fence *fence, struct vkd3d_signaled_semaphore *semaphore)
+{
+    int rc;
+
+    if ((rc = pthread_mutex_lock(&fence->mutex)))
+    {
+        ERR("Failed to lock mutex, error %d.\n", rc);
+        return;
+    }
+
+    assert(semaphore->is_acquired);
+    semaphore->is_acquired = false;
+
+    pthread_mutex_unlock(&fence->mutex);
+}
+
+static HRESULT d3d12_fence_add_vk_semaphore(struct d3d12_fence *fence,
+        VkSemaphore vk_semaphore, VkFence vk_fence, uint64_t value)
+{
+    struct vkd3d_signaled_semaphore *semaphore;
+    HRESULT hr = S_OK;
+    int rc;
+
+    TRACE("fence %p, value %#"PRIx64".\n", fence, value);
+
+    if (!(semaphore = vkd3d_malloc(sizeof(*semaphore))))
+    {
+        ERR("Failed to add semaphore.\n");
+        return E_OUTOFMEMORY;
+    }
+
+    if ((rc = pthread_mutex_lock(&fence->mutex)))
+    {
+        ERR("Failed to lock mutex, error %d.\n", rc);
+        vkd3d_free(semaphore);
+        return E_FAIL;
+    }
+
+    d3d12_fence_garbage_collect_vk_semaphores_locked(fence, false);
+
+    semaphore->value = value;
+    semaphore->vk_semaphore = vk_semaphore;
+    semaphore->vk_fence = vk_fence;
+    semaphore->is_acquired = false;
+
+    list_add_tail(&fence->semaphores, &semaphore->entry);
+    ++fence->semaphore_count;
+
+    pthread_mutex_unlock(&fence->mutex);
+
+    return hr;
+}
+
+static HRESULT d3d12_fence_signal(struct d3d12_fence *fence, uint64_t value, VkFence vk_fence)
+{
+    struct d3d12_device *device = fence->device;
+    struct vkd3d_signaled_semaphore *current;
+    unsigned int i, j;
+    int rc;
+
+    if ((rc = pthread_mutex_lock(&fence->mutex)))
+    {
+        ERR("Failed to lock mutex, error %d.\n", rc);
+        return hresult_from_errno(rc);
+    }
+
+    fence->value = value;
+
+    for (i = 0, j = 0; i < fence->event_count; ++i)
+    {
+        struct vkd3d_waiting_event *current = &fence->events[i];
+
+        if (current->value <= value)
+        {
+            fence->device->signal_event(current->event);
+        }
+        else
+        {
+            if (i != j)
+                fence->events[j] = *current;
+            ++j;
+        }
+    }
+    fence->event_count = j;
+
+    if (vk_fence)
+    {
+        const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
+
+        LIST_FOR_EACH_ENTRY(current, &fence->semaphores, struct vkd3d_signaled_semaphore, entry)
+        {
+            if (current->vk_fence == vk_fence)
+                current->vk_fence = VK_NULL_HANDLE;
+        }
+
+        for (i = 0; i < ARRAY_SIZE(fence->old_vk_fences); ++i)
+        {
+            if (fence->old_vk_fences[i] == VK_NULL_HANDLE)
+            {
+                fence->old_vk_fences[i] = vk_fence;
+                VK_CALL(vkResetFences(device->vk_device, 1, &vk_fence));
+                vk_fence = VK_NULL_HANDLE;
+                break;
+            }
+        }
+        if (vk_fence)
+            VK_CALL(vkDestroyFence(device->vk_device, vk_fence, NULL));
+    }
+
+    pthread_mutex_unlock(&fence->mutex);
+
+    return S_OK;
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_fence_QueryInterface(ID3D12Fence *iface,
+        REFIID riid, void **object)
+{
+    TRACE("iface %p, riid %s, object %p.\n", iface, debugstr_guid(riid), object);
+
+    if (IsEqualGUID(riid, &IID_ID3D12Fence)
+            || IsEqualGUID(riid, &IID_ID3D12Pageable)
+            || IsEqualGUID(riid, &IID_ID3D12DeviceChild)
+            || IsEqualGUID(riid, &IID_ID3D12Object)
+            || IsEqualGUID(riid, &IID_IUnknown))
+    {
+        ID3D12Fence_AddRef(iface);
+        *object = iface;
+        return S_OK;
+    }
+
+    WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(riid));
+
+    *object = NULL;
+    return E_NOINTERFACE;
+}
+
+static ULONG STDMETHODCALLTYPE d3d12_fence_AddRef(ID3D12Fence *iface)
+{
+    struct d3d12_fence *fence = impl_from_ID3D12Fence(iface);
+    ULONG refcount = InterlockedIncrement(&fence->refcount);
+
+    TRACE("%p increasing refcount to %u.\n", fence, refcount);
+
+    return refcount;
+}
+
+static ULONG STDMETHODCALLTYPE d3d12_fence_Release(ID3D12Fence *iface)
+{
+    struct d3d12_fence *fence = impl_from_ID3D12Fence(iface);
+    ULONG refcount = InterlockedDecrement(&fence->refcount);
+    int rc;
+
+    TRACE("%p decreasing refcount to %u.\n", fence, refcount);
+
+    if (!refcount)
+    {
+        struct d3d12_device *device = fence->device;
+
+        vkd3d_private_store_destroy(&fence->private_store);
+
+        vkd3d_fence_worker_remove_fence(&device->fence_worker, fence);
+
+        d3d12_fence_destroy_vk_objects(fence);
+
+        vkd3d_free(fence->events);
+        if ((rc = pthread_mutex_destroy(&fence->mutex)))
+            ERR("Failed to destroy mutex, error %d.\n", rc);
+        vkd3d_free(fence);
+
+        d3d12_device_release(device);
+    }
+
+    return refcount;
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_fence_GetPrivateData(ID3D12Fence *iface,
+        REFGUID guid, UINT *data_size, void *data)
+{
+    struct d3d12_fence *fence = impl_from_ID3D12Fence(iface);
+
+    TRACE("iface %p, guid %s, data_size %p, data %p.\n",
+            iface, debugstr_guid(guid), data_size, data);
+
+    return vkd3d_get_private_data(&fence->private_store, guid, data_size, data);
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_fence_SetPrivateData(ID3D12Fence *iface,
+        REFGUID guid, UINT data_size, const void *data)
+{
+    struct d3d12_fence *fence = impl_from_ID3D12Fence(iface);
+
+    TRACE("iface %p, guid %s, data_size %u, data %p.\n",
+            iface, debugstr_guid(guid), data_size, data);
+
+    return vkd3d_set_private_data(&fence->private_store, guid, data_size, data);
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_fence_SetPrivateDataInterface(ID3D12Fence *iface,
+        REFGUID guid, const IUnknown *data)
+{
+    struct d3d12_fence *fence = impl_from_ID3D12Fence(iface);
+
+    TRACE("iface %p, guid %s, data %p.\n", iface, debugstr_guid(guid), data);
+
+    return vkd3d_set_private_data_interface(&fence->private_store, guid, data);
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_fence_SetName(ID3D12Fence *iface, const WCHAR *name)
+{
+    struct d3d12_fence *fence = impl_from_ID3D12Fence(iface);
+
+    TRACE("iface %p, name %s.\n", iface, debugstr_w(name, fence->device->wchar_size));
+
+    return name ? S_OK : E_INVALIDARG;
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_fence_GetDevice(ID3D12Fence *iface, REFIID iid, void **device)
+{
+    struct d3d12_fence *fence = impl_from_ID3D12Fence(iface);
+
+    TRACE("iface %p, iid %s, device %p.\n", iface, debugstr_guid(iid), device);
+
+    return d3d12_device_query_interface(fence->device, iid, device);
+}
+
+static UINT64 STDMETHODCALLTYPE d3d12_fence_GetCompletedValue(ID3D12Fence *iface)
+{
+    struct d3d12_fence *fence = impl_from_ID3D12Fence(iface);
+    uint64_t completed_value;
+    int rc;
+
+    TRACE("iface %p.\n", iface);
+
+    if ((rc = pthread_mutex_lock(&fence->mutex)))
+    {
+        ERR("Failed to lock mutex, error %d.\n", rc);
+        return 0;
+    }
+    completed_value = fence->value;
+    pthread_mutex_unlock(&fence->mutex);
+    return completed_value;
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_fence_SetEventOnCompletion(ID3D12Fence *iface,
+        UINT64 value, HANDLE event)
+{
+    struct d3d12_fence *fence = impl_from_ID3D12Fence(iface);
+    unsigned int i;
+    int rc;
+
+    TRACE("iface %p, value %#"PRIx64", event %p.\n", iface, value, event);
+
+    if ((rc = pthread_mutex_lock(&fence->mutex)))
+    {
+        ERR("Failed to lock mutex, error %d.\n", rc);
+        return hresult_from_errno(rc);
+    }
+
+    if (value <= fence->value)
+    {
+        fence->device->signal_event(event);
+        pthread_mutex_unlock(&fence->mutex);
+        return S_OK;
+    }
+
+    for (i = 0; i < fence->event_count; ++i)
+    {
+        struct vkd3d_waiting_event *current = &fence->events[i];
+        if (current->value == value && current->event == event)
+        {
+            WARN("Event completion for (%p, %#"PRIx64") is already in the list.\n",
+                    event, value);
+            pthread_mutex_unlock(&fence->mutex);
+            return S_OK;
+        }
+    }
+
+    if (!vkd3d_array_reserve((void **)&fence->events, &fence->events_size,
+            fence->event_count + 1, sizeof(*fence->events)))
+    {
+        WARN("Failed to add event.\n");
+        pthread_mutex_unlock(&fence->mutex);
+        return E_OUTOFMEMORY;
+    }
+
+    fence->events[fence->event_count].value = value;
+    fence->events[fence->event_count].event = event;
+    ++fence->event_count;
+
+    pthread_mutex_unlock(&fence->mutex);
+    return S_OK;
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_fence_Signal(ID3D12Fence *iface, UINT64 value)
+{
+    struct d3d12_fence *fence = impl_from_ID3D12Fence(iface);
+
+    TRACE("iface %p, value %#"PRIx64".\n", iface, value);
+
+    return d3d12_fence_signal(fence, value, VK_NULL_HANDLE);
+}
+
+static const struct ID3D12FenceVtbl d3d12_fence_vtbl =
+{
+    /* IUnknown methods */
+    d3d12_fence_QueryInterface,
+    d3d12_fence_AddRef,
+    d3d12_fence_Release,
+    /* ID3D12Object methods */
+    d3d12_fence_GetPrivateData,
+    d3d12_fence_SetPrivateData,
+    d3d12_fence_SetPrivateDataInterface,
+    d3d12_fence_SetName,
+    /* ID3D12DeviceChild methods */
+    d3d12_fence_GetDevice,
+    /* ID3D12Fence methods */
+    d3d12_fence_GetCompletedValue,
+    d3d12_fence_SetEventOnCompletion,
+    d3d12_fence_Signal,
+};
+
+static struct d3d12_fence *unsafe_impl_from_ID3D12Fence(ID3D12Fence *iface)
+{
+    if (!iface)
+        return NULL;
+    assert(iface->lpVtbl == &d3d12_fence_vtbl);
+    return impl_from_ID3D12Fence(iface);
+}
+
+static HRESULT d3d12_fence_init(struct d3d12_fence *fence, struct d3d12_device *device,
+        UINT64 initial_value, D3D12_FENCE_FLAGS flags)
+{
+    HRESULT hr;
+    int rc;
+
+    fence->ID3D12Fence_iface.lpVtbl = &d3d12_fence_vtbl;
+    fence->refcount = 1;
+
+    fence->value = initial_value;
+
+    if ((rc = pthread_mutex_init(&fence->mutex, NULL)))
+    {
+        ERR("Failed to initialize mutex, error %d.\n", rc);
+        return hresult_from_errno(rc);
+    }
+
+    if (flags)
+        FIXME("Ignoring flags %#x.\n", flags);
+
+    fence->events = NULL;
+    fence->events_size = 0;
+    fence->event_count = 0;
+
+    list_init(&fence->semaphores);
+    fence->semaphore_count = 0;
+
+    memset(fence->old_vk_fences, 0, sizeof(fence->old_vk_fences));
+
+    fence->pending_worker_operation_count = 0;
+
+    if (FAILED(hr = vkd3d_private_store_init(&fence->private_store)))
+    {
+        pthread_mutex_destroy(&fence->mutex);
+        return hr;
+    }
+
+    d3d12_device_add_ref(fence->device = device);
+
+    return S_OK;
+}
+
+HRESULT d3d12_fence_create(struct d3d12_device *device,
+        uint64_t initial_value, D3D12_FENCE_FLAGS flags, struct d3d12_fence **fence)
+{
+    struct d3d12_fence *object;
+
+    if (!(object = vkd3d_malloc(sizeof(*object))))
+        return E_OUTOFMEMORY;
+
+    d3d12_fence_init(object, device, initial_value, flags);
+
+    TRACE("Created fence %p.\n", object);
+
+    *fence = object;
+
+    return S_OK;
+}
+
+/* Command buffers */
+static void d3d12_command_list_mark_as_invalid(struct d3d12_command_list *list,
+        const char *message, ...)
+{
+    va_list args;
+
+    va_start(args, message);
+    WARN("Command list %p is invalid: \"%s\".\n", list, vkd3d_dbg_vsprintf(message, args));
+    va_end(args);
+
+    list->is_valid = false;
+}
+
+static HRESULT d3d12_command_list_begin_command_buffer(struct d3d12_command_list *list)
+{
+    struct d3d12_device *device = list->device;
+    const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
+    VkCommandBufferBeginInfo begin_info;
+    VkResult vr;
+
+    begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+    begin_info.pNext = NULL;
+    begin_info.flags = 0;
+    begin_info.pInheritanceInfo = NULL;
+
+    if ((vr = VK_CALL(vkBeginCommandBuffer(list->vk_command_buffer, &begin_info))) < 0)
+    {
+        WARN("Failed to begin command buffer, vr %d.\n", vr);
+        return hresult_from_vk_result(vr);
+    }
+
+    list->is_recording = true;
+    list->is_valid = true;
+
+    return S_OK;
+}
+
+static HRESULT d3d12_command_allocator_allocate_command_buffer(struct d3d12_command_allocator *allocator,
+        struct d3d12_command_list *list)
+{
+    struct d3d12_device *device = allocator->device;
+    const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
+    VkCommandBufferAllocateInfo command_buffer_info;
+    VkResult vr;
+    HRESULT hr;
+
+    TRACE("allocator %p, list %p.\n", allocator, list);
+
+    if (allocator->current_command_list)
+    {
+        WARN("Command allocator is already in use.\n");
+        return E_INVALIDARG;
+    }
+
+    command_buffer_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
+    command_buffer_info.pNext = NULL;
+    command_buffer_info.commandPool = allocator->vk_command_pool;
+    command_buffer_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
+    command_buffer_info.commandBufferCount = 1;
+
+    if ((vr = VK_CALL(vkAllocateCommandBuffers(device->vk_device, &command_buffer_info,
+            &list->vk_command_buffer))) < 0)
+    {
+        WARN("Failed to allocate Vulkan command buffer, vr %d.\n", vr);
+        return hresult_from_vk_result(vr);
+    }
+
+    list->vk_queue_flags = allocator->vk_queue_flags;
+
+    if (FAILED(hr = d3d12_command_list_begin_command_buffer(list)))
+    {
+        VK_CALL(vkFreeCommandBuffers(device->vk_device, allocator->vk_command_pool,
+                1, &list->vk_command_buffer));
+        return hr;
+    }
+
+    allocator->current_command_list = list;
+
+    return S_OK;
+}
+
+static void d3d12_command_allocator_free_command_buffer(struct d3d12_command_allocator *allocator,
+        struct d3d12_command_list *list)
+{
+    struct d3d12_device *device = allocator->device;
+    const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
+
+    TRACE("allocator %p, list %p.\n", allocator, list);
+
+    if (allocator->current_command_list == list)
+        allocator->current_command_list = NULL;
+
+    if (!vkd3d_array_reserve((void **)&allocator->command_buffers, &allocator->command_buffers_size,
+            allocator->command_buffer_count + 1, sizeof(*allocator->command_buffers)))
+    {
+        WARN("Failed to add command buffer.\n");
+        VK_CALL(vkFreeCommandBuffers(device->vk_device, allocator->vk_command_pool,
+                1, &list->vk_command_buffer));
+        return;
+    }
+
+    allocator->command_buffers[allocator->command_buffer_count++] = list->vk_command_buffer;
+}
+
+static bool d3d12_command_allocator_add_render_pass(struct d3d12_command_allocator *allocator, VkRenderPass pass)
+{
+    if (!vkd3d_array_reserve((void **)&allocator->passes, &allocator->passes_size,
+            allocator->pass_count + 1, sizeof(*allocator->passes)))
+        return false;
+
+    allocator->passes[allocator->pass_count++] = pass;
+
+    return true;
+}
+
+static bool d3d12_command_allocator_add_framebuffer(struct d3d12_command_allocator *allocator,
+        VkFramebuffer framebuffer)
+{
+    if (!vkd3d_array_reserve((void **)&allocator->framebuffers, &allocator->framebuffers_size,
+            allocator->framebuffer_count + 1, sizeof(*allocator->framebuffers)))
+        return false;
+
+    allocator->framebuffers[allocator->framebuffer_count++] = framebuffer;
+
+    return true;
+}
+
+static bool d3d12_command_allocator_add_descriptor_pool(struct d3d12_command_allocator *allocator,
+        VkDescriptorPool pool)
+{
+    if (!vkd3d_array_reserve((void **)&allocator->descriptor_pools, &allocator->descriptor_pools_size,
+            allocator->descriptor_pool_count + 1, sizeof(*allocator->descriptor_pools)))
+        return false;
+
+    allocator->descriptor_pools[allocator->descriptor_pool_count++] = pool;
+
+    return true;
+}
+
+static bool d3d12_command_allocator_add_view(struct d3d12_command_allocator *allocator,
+        struct vkd3d_view *view)
+{
+    if (!vkd3d_array_reserve((void **)&allocator->views, &allocator->views_size,
+            allocator->view_count + 1, sizeof(*allocator->views)))
+        return false;
+
+    vkd3d_view_incref(view);
+    allocator->views[allocator->view_count++] = view;
+
+    return true;
+}
+
+static bool d3d12_command_allocator_add_buffer_view(struct d3d12_command_allocator *allocator,
+        VkBufferView view)
+{
+    if (!vkd3d_array_reserve((void **)&allocator->buffer_views, &allocator->buffer_views_size,
+            allocator->buffer_view_count + 1, sizeof(*allocator->buffer_views)))
+        return false;
+
+    allocator->buffer_views[allocator->buffer_view_count++] = view;
+
+    return true;
+}
+
+static bool d3d12_command_allocator_add_transfer_buffer(struct d3d12_command_allocator *allocator,
+        const struct vkd3d_buffer *buffer)
+{
+    if (!vkd3d_array_reserve((void **)&allocator->transfer_buffers, &allocator->transfer_buffers_size,
+            allocator->transfer_buffer_count + 1, sizeof(*allocator->transfer_buffers)))
+        return false;
+
+    allocator->transfer_buffers[allocator->transfer_buffer_count++] = *buffer;
+
+    return true;
+}
+
+static VkDescriptorPool d3d12_command_allocator_allocate_descriptor_pool(
+        struct d3d12_command_allocator *allocator)
+{
+    static const VkDescriptorPoolSize pool_sizes[] =
+    {
+        {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1024},
+        {VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1024},
+        {VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1024},
+        {VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1024},
+        {VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1024},
+        {VK_DESCRIPTOR_TYPE_SAMPLER, 1024},
+    };
+    struct d3d12_device *device = allocator->device;
+    const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
+    struct VkDescriptorPoolCreateInfo pool_desc;
+    VkDevice vk_device = device->vk_device;
+    VkDescriptorPool vk_pool;
+    VkResult vr;
+
+    if (allocator->free_descriptor_pool_count > 0)
+    {
+        vk_pool = allocator->free_descriptor_pools[allocator->free_descriptor_pool_count - 1];
+        allocator->free_descriptor_pools[allocator->free_descriptor_pool_count - 1] = VK_NULL_HANDLE;
+        --allocator->free_descriptor_pool_count;
+    }
+    else
+    {
+        pool_desc.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
+        pool_desc.pNext = NULL;
+        pool_desc.flags = 0;
+        pool_desc.maxSets = 512;
+        pool_desc.poolSizeCount = ARRAY_SIZE(pool_sizes);
+        pool_desc.pPoolSizes = pool_sizes;
+        if ((vr = VK_CALL(vkCreateDescriptorPool(vk_device, &pool_desc, NULL, &vk_pool))) < 0)
+        {
+            ERR("Failed to create descriptor pool, vr %d.\n", vr);
+            return VK_NULL_HANDLE;
+        }
+    }
+
+    if (!(d3d12_command_allocator_add_descriptor_pool(allocator, vk_pool)))
+    {
+        ERR("Failed to add descriptor pool.\n");
+        VK_CALL(vkDestroyDescriptorPool(vk_device, vk_pool, NULL));
+        return VK_NULL_HANDLE;
+    }
+
+    return vk_pool;
+}
+
+static VkDescriptorSet d3d12_command_allocator_allocate_descriptor_set(
+        struct d3d12_command_allocator *allocator, VkDescriptorSetLayout vk_set_layout)
+{
+    struct d3d12_device *device = allocator->device;
+    const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
+    struct VkDescriptorSetAllocateInfo set_desc;
+    VkDevice vk_device = device->vk_device;
+    VkDescriptorSet vk_descriptor_set;
+    VkResult vr;
+
+    if (!allocator->vk_descriptor_pool)
+        allocator->vk_descriptor_pool = d3d12_command_allocator_allocate_descriptor_pool(allocator);
+    if (!allocator->vk_descriptor_pool)
+        return VK_NULL_HANDLE;
+
+    set_desc.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
+    set_desc.pNext = NULL;
+    set_desc.descriptorPool = allocator->vk_descriptor_pool;
+    set_desc.descriptorSetCount = 1;
+    set_desc.pSetLayouts = &vk_set_layout;
+    if ((vr = VK_CALL(vkAllocateDescriptorSets(vk_device, &set_desc, &vk_descriptor_set))) >= 0)
+        return vk_descriptor_set;
+
+    allocator->vk_descriptor_pool = VK_NULL_HANDLE;
+    if (vr == VK_ERROR_FRAGMENTED_POOL || vr == VK_ERROR_OUT_OF_POOL_MEMORY_KHR)
+        allocator->vk_descriptor_pool = d3d12_command_allocator_allocate_descriptor_pool(allocator);
+    if (!allocator->vk_descriptor_pool)
+    {
+        ERR("Failed to allocate descriptor set, vr %d.\n", vr);
+        return VK_NULL_HANDLE;
+    }
+
+    set_desc.descriptorPool = allocator->vk_descriptor_pool;
+    if ((vr = VK_CALL(vkAllocateDescriptorSets(vk_device, &set_desc, &vk_descriptor_set))) < 0)
+    {
+        FIXME("Failed to allocate descriptor set from a new pool, vr %d.\n", vr);
+        return VK_NULL_HANDLE;
+    }
+
+    return vk_descriptor_set;
+}
+
+static void d3d12_command_list_allocator_destroyed(struct d3d12_command_list *list)
+{
+    TRACE("list %p.\n", list);
+
+    list->allocator = NULL;
+    list->vk_command_buffer = VK_NULL_HANDLE;
+}
+
+static void vkd3d_buffer_destroy(struct vkd3d_buffer *buffer, struct d3d12_device *device)
+{
+    const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
+
+    VK_CALL(vkFreeMemory(device->vk_device, buffer->vk_memory, NULL));
+    VK_CALL(vkDestroyBuffer(device->vk_device, buffer->vk_buffer, NULL));
+}
+
+static void d3d12_command_allocator_free_resources(struct d3d12_command_allocator *allocator,
+        bool keep_reusable_resources)
+{
+    struct d3d12_device *device = allocator->device;
+    const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
+    unsigned int i, j;
+
+    allocator->vk_descriptor_pool = VK_NULL_HANDLE;
+
+    if (keep_reusable_resources)
+    {
+        if (vkd3d_array_reserve((void **)&allocator->free_descriptor_pools,
+                &allocator->free_descriptor_pools_size,
+                allocator->free_descriptor_pool_count + allocator->descriptor_pool_count,
+                sizeof(*allocator->free_descriptor_pools)))
+        {
+            for (i = 0, j = allocator->free_descriptor_pool_count; i < allocator->descriptor_pool_count; ++i, ++j)
+            {
+                VK_CALL(vkResetDescriptorPool(device->vk_device, allocator->descriptor_pools[i], 0));
+                allocator->free_descriptor_pools[j] = allocator->descriptor_pools[i];
+            }
+            allocator->free_descriptor_pool_count += allocator->descriptor_pool_count;
+            allocator->descriptor_pool_count = 0;
+        }
+    }
+    else
+    {
+        for (i = 0; i < allocator->free_descriptor_pool_count; ++i)
+        {
+            VK_CALL(vkDestroyDescriptorPool(device->vk_device, allocator->free_descriptor_pools[i], NULL));
+        }
+        allocator->free_descriptor_pool_count = 0;
+    }
+
+    for (i = 0; i < allocator->transfer_buffer_count; ++i)
+    {
+        vkd3d_buffer_destroy(&allocator->transfer_buffers[i], device);
+    }
+    allocator->transfer_buffer_count = 0;
+
+    for (i = 0; i < allocator->buffer_view_count; ++i)
+    {
+        VK_CALL(vkDestroyBufferView(device->vk_device, allocator->buffer_views[i], NULL));
+    }
+    allocator->buffer_view_count = 0;
+
+    for (i = 0; i < allocator->view_count; ++i)
+    {
+        vkd3d_view_decref(allocator->views[i], device);
+    }
+    allocator->view_count = 0;
+
+    for (i = 0; i < allocator->descriptor_pool_count; ++i)
+    {
+        VK_CALL(vkDestroyDescriptorPool(device->vk_device, allocator->descriptor_pools[i], NULL));
+    }
+    allocator->descriptor_pool_count = 0;
+
+    for (i = 0; i < allocator->framebuffer_count; ++i)
+    {
+        VK_CALL(vkDestroyFramebuffer(device->vk_device, allocator->framebuffers[i], NULL));
+    }
+    allocator->framebuffer_count = 0;
+
+    for (i = 0; i < allocator->pass_count; ++i)
+    {
+        VK_CALL(vkDestroyRenderPass(device->vk_device, allocator->passes[i], NULL));
+    }
+    allocator->pass_count = 0;
+}
+
+/* ID3D12CommandAllocator */
+static inline struct d3d12_command_allocator *impl_from_ID3D12CommandAllocator(ID3D12CommandAllocator *iface)
+{
+    return CONTAINING_RECORD(iface, struct d3d12_command_allocator, ID3D12CommandAllocator_iface);
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_command_allocator_QueryInterface(ID3D12CommandAllocator *iface,
+        REFIID riid, void **object)
+{
+    TRACE("iface %p, riid %s, object %p.\n", iface, debugstr_guid(riid), object);
+
+    if (IsEqualGUID(riid, &IID_ID3D12CommandAllocator)
+            || IsEqualGUID(riid, &IID_ID3D12Pageable)
+            || IsEqualGUID(riid, &IID_ID3D12DeviceChild)
+            || IsEqualGUID(riid, &IID_ID3D12Object)
+            || IsEqualGUID(riid, &IID_IUnknown))
+    {
+        ID3D12CommandAllocator_AddRef(iface);
+        *object = iface;
+        return S_OK;
+    }
+
+    WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(riid));
+
+    *object = NULL;
+    return E_NOINTERFACE;
+}
+
+static ULONG STDMETHODCALLTYPE d3d12_command_allocator_AddRef(ID3D12CommandAllocator *iface)
+{
+    struct d3d12_command_allocator *allocator = impl_from_ID3D12CommandAllocator(iface);
+    ULONG refcount = InterlockedIncrement(&allocator->refcount);
+
+    TRACE("%p increasing refcount to %u.\n", allocator, refcount);
+
+    return refcount;
+}
+
+static ULONG STDMETHODCALLTYPE d3d12_command_allocator_Release(ID3D12CommandAllocator *iface)
+{
+    struct d3d12_command_allocator *allocator = impl_from_ID3D12CommandAllocator(iface);
+    ULONG refcount = InterlockedDecrement(&allocator->refcount);
+
+    TRACE("%p decreasing refcount to %u.\n", allocator, refcount);
+
+    if (!refcount)
+    {
+        struct d3d12_device *device = allocator->device;
+        const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
+
+        vkd3d_private_store_destroy(&allocator->private_store);
+
+        if (allocator->current_command_list)
+            d3d12_command_list_allocator_destroyed(allocator->current_command_list);
+
+        d3d12_command_allocator_free_resources(allocator, false);
+        vkd3d_free(allocator->transfer_buffers);
+        vkd3d_free(allocator->buffer_views);
+        vkd3d_free(allocator->views);
+        vkd3d_free(allocator->descriptor_pools);
+        vkd3d_free(allocator->free_descriptor_pools);
+        vkd3d_free(allocator->framebuffers);
+        vkd3d_free(allocator->passes);
+
+        /* All command buffers are implicitly freed when a pool is destroyed. */
+        vkd3d_free(allocator->command_buffers);
+        VK_CALL(vkDestroyCommandPool(device->vk_device, allocator->vk_command_pool, NULL));
+
+        vkd3d_free(allocator);
+
+        d3d12_device_release(device);
+    }
+
+    return refcount;
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_command_allocator_GetPrivateData(ID3D12CommandAllocator *iface,
+        REFGUID guid, UINT *data_size, void *data)
+{
+    struct d3d12_command_allocator *allocator = impl_from_ID3D12CommandAllocator(iface);
+
+    TRACE("iface %p, guid %s, data_size %p, data %p.\n", iface, debugstr_guid(guid), data_size, data);
+
+    return vkd3d_get_private_data(&allocator->private_store, guid, data_size, data);
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_command_allocator_SetPrivateData(ID3D12CommandAllocator *iface,
+        REFGUID guid, UINT data_size, const void *data)
+{
+    struct d3d12_command_allocator *allocator = impl_from_ID3D12CommandAllocator(iface);
+
+    TRACE("iface %p, guid %s, data_size %u, data %p.\n", iface, debugstr_guid(guid), data_size, data);
+
+    return vkd3d_set_private_data(&allocator->private_store, guid, data_size, data);
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_command_allocator_SetPrivateDataInterface(ID3D12CommandAllocator *iface,
+        REFGUID guid, const IUnknown *data)
+{
+    struct d3d12_command_allocator *allocator = impl_from_ID3D12CommandAllocator(iface);
+
+    TRACE("iface %p, guid %s, data %p.\n", iface, debugstr_guid(guid), data);
+
+    return vkd3d_set_private_data_interface(&allocator->private_store, guid, data);
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_command_allocator_SetName(ID3D12CommandAllocator *iface, const WCHAR *name)
+{
+    struct d3d12_command_allocator *allocator = impl_from_ID3D12CommandAllocator(iface);
+
+    TRACE("iface %p, name %s.\n", iface, debugstr_w(name, allocator->device->wchar_size));
+
+    return vkd3d_set_vk_object_name(allocator->device, (uint64_t)allocator->vk_command_pool,
+            VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_POOL_EXT, name);
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_command_allocator_GetDevice(ID3D12CommandAllocator *iface, REFIID iid, void **device)
+{
+    struct d3d12_command_allocator *allocator = impl_from_ID3D12CommandAllocator(iface);
+
+    TRACE("iface %p, iid %s, device %p.\n", iface, debugstr_guid(iid), device);
+
+    return d3d12_device_query_interface(allocator->device, iid, device);
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_command_allocator_Reset(ID3D12CommandAllocator *iface)
+{
+    struct d3d12_command_allocator *allocator = impl_from_ID3D12CommandAllocator(iface);
+    const struct vkd3d_vk_device_procs *vk_procs;
+    struct d3d12_command_list *list;
+    struct d3d12_device *device;
+    VkResult vr;
+
+    TRACE("iface %p.\n", iface);
+
+    if ((list = allocator->current_command_list))
+    {
+        if (list->is_recording)
+        {
+            WARN("A command list using this allocator is in the recording state.\n");
+            return E_FAIL;
+        }
+
+        TRACE("Resetting command list %p.\n", list);
+    }
+
+    device = allocator->device;
+    vk_procs = &device->vk_procs;
+
+    d3d12_command_allocator_free_resources(allocator, true);
+    if (allocator->command_buffer_count)
+    {
+        VK_CALL(vkFreeCommandBuffers(device->vk_device, allocator->vk_command_pool,
+                allocator->command_buffer_count, allocator->command_buffers));
+        allocator->command_buffer_count = 0;
+    }
+
+    /* The intent here is to recycle memory, so do not use RELEASE_RESOURCES_BIT here. */
+    if ((vr = VK_CALL(vkResetCommandPool(device->vk_device, allocator->vk_command_pool, 0))))
+    {
+        WARN("Resetting command pool failed, vr %d.\n", vr);
+        return hresult_from_vk_result(vr);
+    }
+
+    return S_OK;
+}
+
+static const struct ID3D12CommandAllocatorVtbl d3d12_command_allocator_vtbl =
+{
+    /* IUnknown methods */
+    d3d12_command_allocator_QueryInterface,
+    d3d12_command_allocator_AddRef,
+    d3d12_command_allocator_Release,
+    /* ID3D12Object methods */
+    d3d12_command_allocator_GetPrivateData,
+    d3d12_command_allocator_SetPrivateData,
+    d3d12_command_allocator_SetPrivateDataInterface,
+    d3d12_command_allocator_SetName,
+    /* ID3D12DeviceChild methods */
+    d3d12_command_allocator_GetDevice,
+    /* ID3D12CommandAllocator methods */
+    d3d12_command_allocator_Reset,
+};
+
+static struct d3d12_command_allocator *unsafe_impl_from_ID3D12CommandAllocator(ID3D12CommandAllocator *iface)
+{
+    if (!iface)
+        return NULL;
+    assert(iface->lpVtbl == &d3d12_command_allocator_vtbl);
+    return impl_from_ID3D12CommandAllocator(iface);
+}
+
+struct vkd3d_queue *d3d12_device_get_vkd3d_queue(struct d3d12_device *device,
+        D3D12_COMMAND_LIST_TYPE type)
+{
+    switch (type)
+    {
+        case D3D12_COMMAND_LIST_TYPE_DIRECT:
+            return device->direct_queue;
+        case D3D12_COMMAND_LIST_TYPE_COMPUTE:
+            return device->compute_queue;
+        case D3D12_COMMAND_LIST_TYPE_COPY:
+            return device->copy_queue;
+        default:
+            FIXME("Unhandled command list type %#x.\n", type);
+            return NULL;
+    }
+}
+
+static HRESULT d3d12_command_allocator_init(struct d3d12_command_allocator *allocator,
+        struct d3d12_device *device, D3D12_COMMAND_LIST_TYPE type)
+{
+    const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
+    VkCommandPoolCreateInfo command_pool_info;
+    struct vkd3d_queue *queue;
+    VkResult vr;
+    HRESULT hr;
+
+    if (FAILED(hr = vkd3d_private_store_init(&allocator->private_store)))
+        return hr;
+
+    if (!(queue = d3d12_device_get_vkd3d_queue(device, type)))
+        queue = device->direct_queue;
+
+    allocator->ID3D12CommandAllocator_iface.lpVtbl = &d3d12_command_allocator_vtbl;
+    allocator->refcount = 1;
+
+    allocator->type = type;
+    allocator->vk_queue_flags = queue->vk_queue_flags;
+
+    command_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
+    command_pool_info.pNext = NULL;
+    /* Do not use RESET_COMMAND_BUFFER_BIT. This allows the CommandPool to be a D3D12-style command pool.
+     * Memory is owned by the pool and CommandBuffers become lightweight handles,
+     * assuming a half-decent driver implementation. */
+    command_pool_info.flags = 0;
+    command_pool_info.queueFamilyIndex = queue->vk_family_index;
+
+    if ((vr = VK_CALL(vkCreateCommandPool(device->vk_device, &command_pool_info, NULL,
+            &allocator->vk_command_pool))) < 0)
+    {
+        WARN("Failed to create Vulkan command pool, vr %d.\n", vr);
+        vkd3d_private_store_destroy(&allocator->private_store);
+        return hresult_from_vk_result(vr);
+    }
+
+    allocator->vk_descriptor_pool = VK_NULL_HANDLE;
+
+    allocator->free_descriptor_pools = NULL;
+    allocator->free_descriptor_pools_size = 0;
+    allocator->free_descriptor_pool_count = 0;
+
+    allocator->passes = NULL;
+    allocator->passes_size = 0;
+    allocator->pass_count = 0;
+
+    allocator->framebuffers = NULL;
+    allocator->framebuffers_size = 0;
+    allocator->framebuffer_count = 0;
+
+    allocator->descriptor_pools = NULL;
+    allocator->descriptor_pools_size = 0;
+    allocator->descriptor_pool_count = 0;
+
+    allocator->views = NULL;
+    allocator->views_size = 0;
+    allocator->view_count = 0;
+
+    allocator->buffer_views = NULL;
+    allocator->buffer_views_size = 0;
+    allocator->buffer_view_count = 0;
+
+    allocator->transfer_buffers = NULL;
+    allocator->transfer_buffers_size = 0;
+    allocator->transfer_buffer_count = 0;
+
+    allocator->command_buffers = NULL;
+    allocator->command_buffers_size = 0;
+    allocator->command_buffer_count = 0;
+
+    allocator->current_command_list = NULL;
+
+    d3d12_device_add_ref(allocator->device = device);
+
+    return S_OK;
+}
+
+HRESULT d3d12_command_allocator_create(struct d3d12_device *device,
+        D3D12_COMMAND_LIST_TYPE type, struct d3d12_command_allocator **allocator)
+{
+    struct d3d12_command_allocator *object;
+    HRESULT hr;
+
+    if (!(D3D12_COMMAND_LIST_TYPE_DIRECT <= type && type <= D3D12_COMMAND_LIST_TYPE_COPY))
+    {
+        WARN("Invalid type %#x.\n", type);
+        return E_INVALIDARG;
+    }
+
+    if (!(object = vkd3d_malloc(sizeof(*object))))
+        return E_OUTOFMEMORY;
+
+    if (FAILED(hr = d3d12_command_allocator_init(object, device, type)))
+    {
+        vkd3d_free(object);
+        return hr;
+    }
+
+    TRACE("Created command allocator %p.\n", object);
+
+    *allocator = object;
+
+    return S_OK;
+}
+
+/* ID3D12CommandList */
+static inline struct d3d12_command_list *impl_from_ID3D12GraphicsCommandList1(ID3D12GraphicsCommandList1 *iface)
+{
+    return CONTAINING_RECORD(iface, struct d3d12_command_list, ID3D12GraphicsCommandList1_iface);
+}
+
+static void d3d12_command_list_invalidate_current_framebuffer(struct d3d12_command_list *list)
+{
+    list->current_framebuffer = VK_NULL_HANDLE;
+}
+
+static void d3d12_command_list_invalidate_current_pipeline(struct d3d12_command_list *list)
+{
+    list->current_pipeline = VK_NULL_HANDLE;
+}
+
+static void d3d12_command_list_end_current_render_pass(struct d3d12_command_list *list)
+{
+    const struct vkd3d_vk_device_procs *vk_procs = &list->device->vk_procs;
+
+    if (list->xfb_enabled)
+    {
+        VK_CALL(vkCmdEndTransformFeedbackEXT(list->vk_command_buffer, 0, ARRAY_SIZE(list->so_counter_buffers),
+                list->so_counter_buffers, list->so_counter_buffer_offsets));
+    }
+
+    if (list->current_render_pass)
+        VK_CALL(vkCmdEndRenderPass(list->vk_command_buffer));
+
+    list->current_render_pass = VK_NULL_HANDLE;
+
+    if (list->xfb_enabled)
+    {
+        VkMemoryBarrier vk_barrier;
+
+        /* We need a barrier between pause and resume. */
+        vk_barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
+        vk_barrier.pNext = NULL;
+        vk_barrier.srcAccessMask = VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT;
+        vk_barrier.dstAccessMask = VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT;
+        VK_CALL(vkCmdPipelineBarrier(list->vk_command_buffer,
+                VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT, VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT, 0,
+                1, &vk_barrier, 0, NULL, 0, NULL));
+
+        list->xfb_enabled = false;
+    }
+}
+
+static void d3d12_command_list_invalidate_current_render_pass(struct d3d12_command_list *list)
+{
+    d3d12_command_list_end_current_render_pass(list);
+}
+
+static void d3d12_command_list_invalidate_bindings(struct d3d12_command_list *list,
+        struct d3d12_pipeline_state *state)
+{
+    if (!state)
+        return;
+
+    if (state->uav_counter_mask)
+    {
+        struct vkd3d_pipeline_bindings *bindings = &list->pipeline_bindings[state->vk_bind_point];
+        bindings->uav_counter_dirty_mask = ~(uint8_t)0;
+    }
+}
+
+static void d3d12_command_list_invalidate_root_parameters(struct d3d12_command_list *list,
+        VkPipelineBindPoint bind_point)
+{
+    struct vkd3d_pipeline_bindings *bindings = &list->pipeline_bindings[bind_point];
+
+    if (!bindings->root_signature)
+        return;
+
+    bindings->descriptor_set = VK_NULL_HANDLE;
+    bindings->descriptor_table_dirty_mask = bindings->descriptor_table_active_mask & bindings->root_signature->descriptor_table_mask;
+    bindings->push_descriptor_dirty_mask = bindings->push_descriptor_active_mask & bindings->root_signature->push_descriptor_mask;
+}
+
+static bool vk_barrier_parameters_from_d3d12_resource_state(unsigned int state, unsigned int stencil_state,
+        const struct d3d12_resource *resource, VkQueueFlags vk_queue_flags, const struct vkd3d_vulkan_info *vk_info,
+        VkAccessFlags *access_mask, VkPipelineStageFlags *stage_flags, VkImageLayout *image_layout)
+{
+    bool is_swapchain_image = resource && (resource->flags & VKD3D_RESOURCE_PRESENT_STATE_TRANSITION);
+    VkPipelineStageFlags queue_shader_stages = 0;
+
+    if (vk_queue_flags & VK_QUEUE_GRAPHICS_BIT)
+    {
+        queue_shader_stages |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT
+                | VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT
+                | VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT
+                | VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT
+                | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
+    }
+    if (vk_queue_flags & VK_QUEUE_COMPUTE_BIT)
+        queue_shader_stages |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
+
+    switch (state)
+    {
+        case D3D12_RESOURCE_STATE_COMMON: /* D3D12_RESOURCE_STATE_PRESENT */
+            /* The COMMON state is used for ownership transfer between
+             * DIRECT/COMPUTE and COPY queues. Additionally, a texture has to
+             * be in the COMMON state to be accessed by CPU. Moreover,
+             * resources can be implicitly promoted to other states out of the
+             * COMMON state, and the resource state can decay to the COMMON
+             * state when GPU finishes execution of a command list. */
+            if (is_swapchain_image)
+            {
+                if (resource->present_state == D3D12_RESOURCE_STATE_PRESENT)
+                {
+                    *access_mask = VK_ACCESS_MEMORY_READ_BIT;
+                    *stage_flags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+                    if (image_layout)
+                        *image_layout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
+                    return true;
+                }
+                else if (resource->present_state != D3D12_RESOURCE_STATE_COMMON)
+                {
+                    vk_barrier_parameters_from_d3d12_resource_state(resource->present_state, 0,
+                            resource, vk_queue_flags, vk_info, access_mask, stage_flags, image_layout);
+                    return true;
+                }
+            }
+
+            *access_mask = VK_ACCESS_HOST_READ_BIT | VK_ACCESS_HOST_WRITE_BIT;
+            *stage_flags = VK_PIPELINE_STAGE_HOST_BIT;
+            if (image_layout)
+                *image_layout = VK_IMAGE_LAYOUT_GENERAL;
+            return true;
+
+        /* Handle write states. */
+        case D3D12_RESOURCE_STATE_RENDER_TARGET:
+            *access_mask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT
+                    | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+            *stage_flags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+            if (image_layout)
+                *image_layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+            return true;
+
+        case D3D12_RESOURCE_STATE_UNORDERED_ACCESS:
+            *access_mask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
+            *stage_flags = queue_shader_stages;
+            if (image_layout)
+                *image_layout = VK_IMAGE_LAYOUT_GENERAL;
+            return true;
+
+        case D3D12_RESOURCE_STATE_DEPTH_WRITE:
+            *access_mask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT
+                    | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
+            *stage_flags = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT
+                    | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
+            if (image_layout)
+            {
+                if (!stencil_state || (stencil_state & D3D12_RESOURCE_STATE_DEPTH_WRITE))
+                    *image_layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+                else
+                    *image_layout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL;
+            }
+            return true;
+
+        case D3D12_RESOURCE_STATE_COPY_DEST:
+        case D3D12_RESOURCE_STATE_RESOLVE_DEST:
+            *access_mask = VK_ACCESS_TRANSFER_WRITE_BIT;
+            *stage_flags = VK_PIPELINE_STAGE_TRANSFER_BIT;
+            if (image_layout)
+                *image_layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
+            return true;
+
+        case D3D12_RESOURCE_STATE_STREAM_OUT:
+            *access_mask = VK_ACCESS_TRANSFORM_FEEDBACK_WRITE_BIT_EXT
+                    | VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT
+                    | VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT;
+            *stage_flags = VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT
+                    | VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT;
+            if (image_layout)
+                *image_layout = VK_IMAGE_LAYOUT_UNDEFINED;
+            return true;
+
+        /* Set the Vulkan image layout for read-only states. */
+        case D3D12_RESOURCE_STATE_DEPTH_READ:
+        case D3D12_RESOURCE_STATE_DEPTH_READ | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE:
+        case D3D12_RESOURCE_STATE_DEPTH_READ | D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE:
+        case D3D12_RESOURCE_STATE_DEPTH_READ | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE
+                | D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE:
+            *access_mask = 0;
+            *stage_flags = 0;
+            if (image_layout)
+            {
+                if (stencil_state & D3D12_RESOURCE_STATE_DEPTH_WRITE)
+                {
+                    *image_layout = VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL;
+                    *access_mask |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
+                }
+                else
+                {
+                    *image_layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
+                }
+            }
+            break;
+
+        case D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE:
+        case D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE:
+        case D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE:
+            *access_mask = 0;
+            *stage_flags = 0;
+            if (image_layout)
+                *image_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+            break;
+
+        case D3D12_RESOURCE_STATE_COPY_SOURCE:
+        case D3D12_RESOURCE_STATE_RESOLVE_SOURCE:
+            *access_mask = 0;
+            *stage_flags = 0;
+            if (image_layout)
+                *image_layout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
+            break;
+
+        default:
+            *access_mask = 0;
+            *stage_flags = 0;
+            if (image_layout)
+                *image_layout = VK_IMAGE_LAYOUT_GENERAL;
+            break;
+    }
+
+    /* Handle read-only states. */
+    assert(!is_write_resource_state(state));
+
+    if (state & D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER)
+    {
+        *access_mask |= VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT
+                | VK_ACCESS_UNIFORM_READ_BIT;
+        *stage_flags |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT
+                | queue_shader_stages;
+        state &= ~D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER;
+    }
+
+    if (state & D3D12_RESOURCE_STATE_INDEX_BUFFER)
+    {
+        *access_mask |= VK_ACCESS_INDEX_READ_BIT;
+        *stage_flags |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
+        state &= ~D3D12_RESOURCE_STATE_INDEX_BUFFER;
+    }
+
+    if (state & D3D12_RESOURCE_STATE_DEPTH_READ)
+    {
+        *access_mask |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
+        *stage_flags |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT
+                | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
+        state &= ~D3D12_RESOURCE_STATE_DEPTH_READ;
+    }
+
+    if (state & D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE)
+    {
+        *access_mask |= VK_ACCESS_SHADER_READ_BIT;
+        *stage_flags |= (queue_shader_stages & ~VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
+        state &= ~D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE;
+    }
+    if (state & D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE)
+    {
+        *access_mask |= VK_ACCESS_SHADER_READ_BIT;
+        *stage_flags |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
+        state &= ~D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
+    }
+
+    if (state & D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT) /* D3D12_RESOURCE_STATE_PREDICATION */
+    {
+        *access_mask |= VK_ACCESS_INDIRECT_COMMAND_READ_BIT;
+        *stage_flags |= VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT;
+        if (vk_info->EXT_conditional_rendering)
+        {
+            *access_mask |= VK_ACCESS_CONDITIONAL_RENDERING_READ_BIT_EXT;
+            *stage_flags |= VK_PIPELINE_STAGE_CONDITIONAL_RENDERING_BIT_EXT;
+        }
+        state &= ~D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT;
+    }
+
+    if (state & (D3D12_RESOURCE_STATE_COPY_SOURCE | D3D12_RESOURCE_STATE_RESOLVE_SOURCE))
+    {
+        *access_mask |= VK_ACCESS_TRANSFER_READ_BIT;
+        *stage_flags |= VK_PIPELINE_STAGE_TRANSFER_BIT;
+        state &= ~(D3D12_RESOURCE_STATE_COPY_SOURCE | D3D12_RESOURCE_STATE_RESOLVE_SOURCE);
+    }
+
+    if (state)
+    {
+        WARN("Invalid resource state %#x.\n", state);
+        return false;
+    }
+    return true;
+}
+
+static void d3d12_command_list_transition_resource_to_initial_state(struct d3d12_command_list *list,
+        struct d3d12_resource *resource)
+{
+    const struct vkd3d_vk_device_procs *vk_procs = &list->device->vk_procs;
+    const struct vkd3d_vulkan_info *vk_info = &list->device->vk_info;
+    VkPipelineStageFlags src_stage_mask, dst_stage_mask;
+    const struct vkd3d_format *format;
+    VkImageMemoryBarrier barrier;
+
+    assert(d3d12_resource_is_texture(resource));
+
+    if (!(format = vkd3d_format_from_d3d12_resource_desc(list->device, &resource->desc, 0)))
+    {
+        ERR("Resource %p has invalid format %#x.\n", resource, resource->desc.Format);
+        return;
+    }
+
+    barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+    barrier.pNext = NULL;
+
+    /* vkQueueSubmit() defines a memory dependency with prior host writes. */
+    src_stage_mask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+    barrier.srcAccessMask = 0;
+    barrier.oldLayout = d3d12_resource_is_cpu_accessible(resource) ?
+            VK_IMAGE_LAYOUT_PREINITIALIZED : VK_IMAGE_LAYOUT_UNDEFINED;
+
+    if (!vk_barrier_parameters_from_d3d12_resource_state(resource->initial_state, 0,
+            resource, list->vk_queue_flags, vk_info, &barrier.dstAccessMask, &dst_stage_mask, &barrier.newLayout))
+    {
+        FIXME("Unhandled state %#x.\n", resource->initial_state);
+        return;
+    }
+
+    barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+    barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+    barrier.image = resource->u.vk_image;
+    barrier.subresourceRange.aspectMask = format->vk_aspect_mask;
+    barrier.subresourceRange.baseMipLevel = 0;
+    barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
+    barrier.subresourceRange.baseArrayLayer = 0;
+    barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
+
+    TRACE("Initial state %#x transition for resource %p (old layout %#x, new layout %#x).\n",
+            resource->initial_state, resource, barrier.oldLayout, barrier.newLayout);
+
+    VK_CALL(vkCmdPipelineBarrier(list->vk_command_buffer, src_stage_mask, dst_stage_mask, 0,
+            0, NULL, 0, NULL, 1, &barrier));
+}
+
+static void d3d12_command_list_track_resource_usage(struct d3d12_command_list *list,
+        struct d3d12_resource *resource)
+{
+    if (resource->flags & VKD3D_RESOURCE_INITIAL_STATE_TRANSITION)
+    {
+        d3d12_command_list_end_current_render_pass(list);
+
+        d3d12_command_list_transition_resource_to_initial_state(list, resource);
+        resource->flags &= ~VKD3D_RESOURCE_INITIAL_STATE_TRANSITION;
+    }
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_command_list_QueryInterface(ID3D12GraphicsCommandList1 *iface,
+        REFIID iid, void **object)
+{
+    TRACE("iface %p, iid %s, object %p.\n", iface, debugstr_guid(iid), object);
+
+    if (IsEqualGUID(iid, &IID_ID3D12GraphicsCommandList1)
+            || IsEqualGUID(iid, &IID_ID3D12GraphicsCommandList)
+            || IsEqualGUID(iid, &IID_ID3D12CommandList)
+            || IsEqualGUID(iid, &IID_ID3D12DeviceChild)
+            || IsEqualGUID(iid, &IID_ID3D12Object)
+            || IsEqualGUID(iid, &IID_IUnknown))
+    {
+        ID3D12GraphicsCommandList1_AddRef(iface);
+        *object = iface;
+        return S_OK;
+    }
+
+    WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid));
+
+    *object = NULL;
+    return E_NOINTERFACE;
+}
+
+static ULONG STDMETHODCALLTYPE d3d12_command_list_AddRef(ID3D12GraphicsCommandList1 *iface)
+{
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+    ULONG refcount = InterlockedIncrement(&list->refcount);
+
+    TRACE("%p increasing refcount to %u.\n", list, refcount);
+
+    return refcount;
+}
+
+static ULONG STDMETHODCALLTYPE d3d12_command_list_Release(ID3D12GraphicsCommandList1 *iface)
+{
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+    ULONG refcount = InterlockedDecrement(&list->refcount);
+
+    TRACE("%p decreasing refcount to %u.\n", list, refcount);
+
+    if (!refcount)
+    {
+        struct d3d12_device *device = list->device;
+
+        vkd3d_private_store_destroy(&list->private_store);
+
+        /* When command pool is destroyed, all command buffers are implicitly freed. */
+        if (list->allocator)
+            d3d12_command_allocator_free_command_buffer(list->allocator, list);
+
+        vkd3d_free(list);
+
+        d3d12_device_release(device);
+    }
+
+    return refcount;
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_command_list_GetPrivateData(ID3D12GraphicsCommandList1 *iface,
+        REFGUID guid, UINT *data_size, void *data)
+{
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+
+    TRACE("iface %p, guid %s, data_size %p, data %p.\n", iface, debugstr_guid(guid), data_size, data);
+
+    return vkd3d_get_private_data(&list->private_store, guid, data_size, data);
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_command_list_SetPrivateData(ID3D12GraphicsCommandList1 *iface,
+        REFGUID guid, UINT data_size, const void *data)
+{
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+
+    TRACE("iface %p, guid %s, data_size %u, data %p.\n", iface, debugstr_guid(guid), data_size, data);
+
+    return vkd3d_set_private_data(&list->private_store, guid, data_size, data);
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_command_list_SetPrivateDataInterface(ID3D12GraphicsCommandList1 *iface,
+        REFGUID guid, const IUnknown *data)
+{
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+
+    TRACE("iface %p, guid %s, data %p.\n", iface, debugstr_guid(guid), data);
+
+    return vkd3d_set_private_data_interface(&list->private_store, guid, data);
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_command_list_SetName(ID3D12GraphicsCommandList1 *iface, const WCHAR *name)
+{
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+
+    TRACE("iface %p, name %s.\n", iface, debugstr_w(name, list->device->wchar_size));
+
+    return name ? S_OK : E_INVALIDARG;
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_command_list_GetDevice(ID3D12GraphicsCommandList1 *iface, REFIID iid, void **device)
+{
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+
+    TRACE("iface %p, iid %s, device %p.\n", iface, debugstr_guid(iid), device);
+
+    return d3d12_device_query_interface(list->device, iid, device);
+}
+
+static D3D12_COMMAND_LIST_TYPE STDMETHODCALLTYPE d3d12_command_list_GetType(ID3D12GraphicsCommandList1 *iface)
+{
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+
+    TRACE("iface %p.\n", iface);
+
+    return list->type;
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_command_list_Close(ID3D12GraphicsCommandList1 *iface)
+{
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+    const struct vkd3d_vk_device_procs *vk_procs;
+    VkResult vr;
+
+    TRACE("iface %p.\n", iface);
+
+    if (!list->is_recording)
+    {
+        WARN("Command list is not in the recording state.\n");
+        return E_FAIL;
+    }
+
+    vk_procs = &list->device->vk_procs;
+
+    d3d12_command_list_end_current_render_pass(list);
+    if (list->is_predicated)
+        VK_CALL(vkCmdEndConditionalRenderingEXT(list->vk_command_buffer));
+
+    if ((vr = VK_CALL(vkEndCommandBuffer(list->vk_command_buffer))) < 0)
+    {
+        WARN("Failed to end command buffer, vr %d.\n", vr);
+        return hresult_from_vk_result(vr);
+    }
+
+    if (list->allocator)
+    {
+        d3d12_command_allocator_free_command_buffer(list->allocator, list);
+        list->allocator = NULL;
+    }
+
+    list->is_recording = false;
+
+    if (!list->is_valid)
+    {
+        WARN("Error occurred during command list recording.\n");
+        return E_INVALIDARG;
+    }
+
+    return S_OK;
+}
+
+static void d3d12_command_list_reset_state(struct d3d12_command_list *list,
+        ID3D12PipelineState *initial_pipeline_state)
+{
+    ID3D12GraphicsCommandList1 *iface = &list->ID3D12GraphicsCommandList1_iface;
+
+    memset(list->strides, 0, sizeof(list->strides));
+    list->primitive_topology = D3D_PRIMITIVE_TOPOLOGY_POINTLIST;
+
+    list->index_buffer_format = DXGI_FORMAT_UNKNOWN;
+
+    memset(list->rtvs, 0, sizeof(list->rtvs));
+    list->dsv = VK_NULL_HANDLE;
+    list->dsv_format = VK_FORMAT_UNDEFINED;
+    list->fb_width = 0;
+    list->fb_height = 0;
+    list->fb_layer_count = 0;
+
+    list->xfb_enabled = false;
+
+    list->is_predicated = false;
+
+    list->current_framebuffer = VK_NULL_HANDLE;
+    list->current_pipeline = VK_NULL_HANDLE;
+    list->pso_render_pass = VK_NULL_HANDLE;
+    list->current_render_pass = VK_NULL_HANDLE;
+
+    memset(list->pipeline_bindings, 0, sizeof(list->pipeline_bindings));
+
+    list->state = NULL;
+
+    memset(list->so_counter_buffers, 0, sizeof(list->so_counter_buffers));
+    memset(list->so_counter_buffer_offsets, 0, sizeof(list->so_counter_buffer_offsets));
+
+    ID3D12GraphicsCommandList1_SetPipelineState(iface, initial_pipeline_state);
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_command_list_Reset(ID3D12GraphicsCommandList1 *iface,
+        ID3D12CommandAllocator *allocator, ID3D12PipelineState *initial_pipeline_state)
+{
+    struct d3d12_command_allocator *allocator_impl = unsafe_impl_from_ID3D12CommandAllocator(allocator);
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+    HRESULT hr;
+
+    TRACE("iface %p, allocator %p, initial_pipeline_state %p.\n",
+            iface, allocator, initial_pipeline_state);
+
+    if (!allocator_impl)
+    {
+        WARN("Command allocator is NULL.\n");
+        return E_INVALIDARG;
+    }
+
+    if (list->is_recording)
+    {
+        WARN("Command list is in the recording state.\n");
+        return E_FAIL;
+    }
+
+    if (SUCCEEDED(hr = d3d12_command_allocator_allocate_command_buffer(allocator_impl, list)))
+    {
+        list->allocator = allocator_impl;
+        d3d12_command_list_reset_state(list, initial_pipeline_state);
+    }
+
+    return hr;
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_command_list_ClearState(ID3D12GraphicsCommandList1 *iface,
+        ID3D12PipelineState *pipeline_state)
+{
+    FIXME("iface %p, pipline_state %p stub!\n", iface, pipeline_state);
+
+    return E_NOTIMPL;
+}
+
+static bool d3d12_command_list_has_depth_stencil_view(struct d3d12_command_list *list)
+{
+    struct d3d12_graphics_pipeline_state *graphics;
+
+    assert(d3d12_pipeline_state_is_graphics(list->state));
+    graphics = &list->state->u.graphics;
+
+    return graphics->dsv_format || (d3d12_pipeline_state_has_unknown_dsv_format(list->state) && list->dsv_format);
+}
+
+static void d3d12_command_list_get_fb_extent(struct d3d12_command_list *list,
+        uint32_t *width, uint32_t *height, uint32_t *layer_count)
+{
+    struct d3d12_graphics_pipeline_state *graphics = &list->state->u.graphics;
+    struct d3d12_device *device = list->device;
+
+    if (graphics->rt_count || d3d12_command_list_has_depth_stencil_view(list))
+    {
+        *width = list->fb_width;
+        *height = list->fb_height;
+        if (layer_count)
+            *layer_count = list->fb_layer_count;
+    }
+    else
+    {
+        *width = device->vk_info.device_limits.maxFramebufferWidth;
+        *height = device->vk_info.device_limits.maxFramebufferHeight;
+        if (layer_count)
+            *layer_count = 1;
+    }
+}
+
+static bool d3d12_command_list_update_current_framebuffer(struct d3d12_command_list *list)
+{
+    struct d3d12_device *device = list->device;
+    const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
+    VkImageView views[D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT + 1];
+    struct d3d12_graphics_pipeline_state *graphics;
+    struct VkFramebufferCreateInfo fb_desc;
+    VkFramebuffer vk_framebuffer;
+    unsigned int view_count;
+    unsigned int i;
+    VkResult vr;
+
+    if (list->current_framebuffer != VK_NULL_HANDLE)
+        return true;
+
+    graphics = &list->state->u.graphics;
+
+    for (i = 0, view_count = 0; i < graphics->rt_count; ++i)
+    {
+        if (graphics->null_attachment_mask & (1u << i))
+        {
+            if (list->rtvs[i])
+                WARN("Expected NULL RTV for attachment %u.\n", i);
+            continue;
+        }
+
+        if (!list->rtvs[i])
+        {
+            FIXME("Invalid RTV for attachment %u.\n", i);
+            return false;
+        }
+
+        views[view_count++] = list->rtvs[i];
+    }
+
+    if (d3d12_command_list_has_depth_stencil_view(list))
+    {
+        if (!(views[view_count++] = list->dsv))
+        {
+            FIXME("Invalid DSV.\n");
+            return false;
+        }
+    }
+
+    fb_desc.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
+    fb_desc.pNext = NULL;
+    fb_desc.flags = 0;
+    fb_desc.renderPass = list->pso_render_pass;
+    fb_desc.attachmentCount = view_count;
+    fb_desc.pAttachments = views;
+    d3d12_command_list_get_fb_extent(list, &fb_desc.width, &fb_desc.height, &fb_desc.layers);
+    if ((vr = VK_CALL(vkCreateFramebuffer(device->vk_device, &fb_desc, NULL, &vk_framebuffer))) < 0)
+    {
+        WARN("Failed to create Vulkan framebuffer, vr %d.\n", vr);
+        return false;
+    }
+
+    if (!d3d12_command_allocator_add_framebuffer(list->allocator, vk_framebuffer))
+    {
+        WARN("Failed to add framebuffer.\n");
+        VK_CALL(vkDestroyFramebuffer(device->vk_device, vk_framebuffer, NULL));
+        return false;
+    }
+
+    list->current_framebuffer = vk_framebuffer;
+
+    return true;
+}
+
+static bool d3d12_command_list_update_compute_pipeline(struct d3d12_command_list *list)
+{
+    const struct vkd3d_vk_device_procs *vk_procs = &list->device->vk_procs;
+
+    if (list->current_pipeline != VK_NULL_HANDLE)
+        return true;
+
+    if (!d3d12_pipeline_state_is_compute(list->state))
+    {
+        WARN("Pipeline state %p is not a compute pipeline.\n", list->state);
+        return false;
+    }
+
+    VK_CALL(vkCmdBindPipeline(list->vk_command_buffer, list->state->vk_bind_point, list->state->u.compute.vk_pipeline));
+    list->current_pipeline = list->state->u.compute.vk_pipeline;
+
+    return true;
+}
+
+static bool d3d12_command_list_update_graphics_pipeline(struct d3d12_command_list *list)
+{
+    const struct vkd3d_vk_device_procs *vk_procs = &list->device->vk_procs;
+    VkRenderPass vk_render_pass;
+    VkPipeline vk_pipeline;
+
+    if (list->current_pipeline != VK_NULL_HANDLE)
+        return true;
+
+    if (!d3d12_pipeline_state_is_graphics(list->state))
+    {
+        WARN("Pipeline state %p is not a graphics pipeline.\n", list->state);
+        return false;
+    }
+
+    if (!(vk_pipeline = d3d12_pipeline_state_get_or_create_pipeline(list->state,
+            list->primitive_topology, list->strides, list->dsv_format, &vk_render_pass)))
+        return false;
+
+    /* The render pass cache ensures that we use the same Vulkan render pass
+     * object for compatible render passes. */
+    if (list->pso_render_pass != vk_render_pass)
+    {
+        list->pso_render_pass = vk_render_pass;
+        d3d12_command_list_invalidate_current_framebuffer(list);
+        d3d12_command_list_invalidate_current_render_pass(list);
+    }
+
+    VK_CALL(vkCmdBindPipeline(list->vk_command_buffer, list->state->vk_bind_point, vk_pipeline));
+    list->current_pipeline = vk_pipeline;
+
+    return true;
+}
+
+static void d3d12_command_list_prepare_descriptors(struct d3d12_command_list *list,
+        VkPipelineBindPoint bind_point)
+{
+    struct vkd3d_pipeline_bindings *bindings = &list->pipeline_bindings[bind_point];
+    const struct d3d12_root_signature *root_signature = bindings->root_signature;
+
+    if (bindings->descriptor_set && !bindings->in_use)
+        return;
+
+    /* We cannot modify bound descriptor sets. We need a new descriptor set if
+     * we are about to update resource bindings.
+     *
+     * The Vulkan spec says:
+     *
+     *   "The descriptor set contents bound by a call to
+     *   vkCmdBindDescriptorSets may be consumed during host execution of the
+     *   command, or during shader execution of the resulting draws, or any
+     *   time in between. Thus, the contents must not be altered (overwritten
+     *   by an update command, or freed) between when the command is recorded
+     *   and when the command completes executing on the queue."
+     */
+    bindings->descriptor_set = d3d12_command_allocator_allocate_descriptor_set(list->allocator,
+            root_signature->vk_set_layout);
+    bindings->in_use = false;
+
+    bindings->descriptor_table_dirty_mask |= bindings->descriptor_table_active_mask & root_signature->descriptor_table_mask;
+    bindings->push_descriptor_dirty_mask |= bindings->push_descriptor_active_mask & root_signature->push_descriptor_mask;
+}
+
+static bool vk_write_descriptor_set_from_d3d12_desc(VkWriteDescriptorSet *vk_descriptor_write,
+        VkDescriptorImageInfo *vk_image_info, const struct d3d12_desc *descriptor,
+        uint32_t descriptor_range_magic, VkDescriptorSet vk_descriptor_set,
+        uint32_t vk_binding, unsigned int index)
+{
+    const struct vkd3d_view *view = descriptor->u.view;
+
+    if (descriptor->magic != descriptor_range_magic)
+        return false;
+
+    vk_descriptor_write->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+    vk_descriptor_write->pNext = NULL;
+    vk_descriptor_write->dstSet = vk_descriptor_set;
+    vk_descriptor_write->dstBinding = vk_binding + index;
+    vk_descriptor_write->dstArrayElement = 0;
+    vk_descriptor_write->descriptorCount = 1;
+    vk_descriptor_write->descriptorType = descriptor->vk_descriptor_type;
+    vk_descriptor_write->pImageInfo = NULL;
+    vk_descriptor_write->pBufferInfo = NULL;
+    vk_descriptor_write->pTexelBufferView = NULL;
+
+    switch (descriptor->magic)
+    {
+        case VKD3D_DESCRIPTOR_MAGIC_CBV:
+            vk_descriptor_write->pBufferInfo = &descriptor->u.vk_cbv_info;
+            break;
+
+        case VKD3D_DESCRIPTOR_MAGIC_SRV:
+        case VKD3D_DESCRIPTOR_MAGIC_UAV:
+            /* We use separate bindings for buffer and texture SRVs/UAVs.
+             * See d3d12_root_signature_init(). */
+            vk_descriptor_write->dstBinding = vk_binding + 2 * index;
+            if (descriptor->vk_descriptor_type != VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER
+                    && descriptor->vk_descriptor_type != VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER)
+                ++vk_descriptor_write->dstBinding;
+
+            if (descriptor->vk_descriptor_type == VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER
+                    || descriptor->vk_descriptor_type == VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER)
+            {
+                vk_descriptor_write->pTexelBufferView = &view->u.vk_buffer_view;
+            }
+            else
+            {
+                vk_image_info->sampler = VK_NULL_HANDLE;
+                vk_image_info->imageView = view->u.vk_image_view;
+                vk_image_info->imageLayout = descriptor->magic == VKD3D_DESCRIPTOR_MAGIC_SRV
+                        ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : VK_IMAGE_LAYOUT_GENERAL;
+
+                vk_descriptor_write->pImageInfo = vk_image_info;
+            }
+            break;
+
+        case VKD3D_DESCRIPTOR_MAGIC_SAMPLER:
+            vk_image_info->sampler = view->u.vk_sampler;
+            vk_image_info->imageView = VK_NULL_HANDLE;
+            vk_image_info->imageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+
+            vk_descriptor_write->pImageInfo = vk_image_info;
+            break;
+
+        default:
+            ERR("Invalid descriptor %#x.\n", descriptor->magic);
+            return false;
+    }
+
+    return true;
+}
+
+static void d3d12_command_list_update_descriptor_table(struct d3d12_command_list *list,
+        VkPipelineBindPoint bind_point, unsigned int index, struct d3d12_desc *base_descriptor)
+{
+    struct vkd3d_pipeline_bindings *bindings = &list->pipeline_bindings[bind_point];
+    struct VkWriteDescriptorSet descriptor_writes[24], *current_descriptor_write;
+    const struct d3d12_root_signature *root_signature = bindings->root_signature;
+    const struct vkd3d_vk_device_procs *vk_procs = &list->device->vk_procs;
+    struct VkDescriptorImageInfo image_infos[24], *current_image_info;
+    const struct d3d12_root_descriptor_table *descriptor_table;
+    const struct d3d12_root_descriptor_table_range *range;
+    VkDevice vk_device = list->device->vk_device;
+    unsigned int i, j, descriptor_count;
+    struct d3d12_desc *descriptor;
+
+    descriptor_table = root_signature_get_descriptor_table(root_signature, index);
+
+    descriptor = base_descriptor;
+    descriptor_count = 0;
+    current_descriptor_write = descriptor_writes;
+    current_image_info = image_infos;
+    for (i = 0; i < descriptor_table->range_count; ++i)
+    {
+        range = &descriptor_table->ranges[i];
+
+        if (range->offset != D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND)
+        {
+            descriptor = base_descriptor + range->offset;
+        }
+
+        for (j = 0; j < range->descriptor_count; ++j, ++descriptor)
+        {
+            unsigned int register_idx = range->base_register_idx + j;
+
+            /* Track UAV counters. */
+            if (range->descriptor_magic == VKD3D_DESCRIPTOR_MAGIC_UAV
+                    && register_idx < ARRAY_SIZE(bindings->vk_uav_counter_views))
+            {
+                VkBufferView vk_counter_view = descriptor->magic == VKD3D_DESCRIPTOR_MAGIC_UAV
+                        ? descriptor->u.view->vk_counter_view : VK_NULL_HANDLE;
+                if (bindings->vk_uav_counter_views[register_idx] != vk_counter_view)
+                    bindings->uav_counter_dirty_mask |= 1u << register_idx;
+                bindings->vk_uav_counter_views[register_idx] = vk_counter_view;
+            }
+
+            if (!vk_write_descriptor_set_from_d3d12_desc(current_descriptor_write,
+                    current_image_info, descriptor, range->descriptor_magic,
+                    bindings->descriptor_set, range->binding, j))
+                continue;
+
+            ++descriptor_count;
+            ++current_descriptor_write;
+            ++current_image_info;
+
+            if (descriptor_count == ARRAY_SIZE(descriptor_writes))
+            {
+                VK_CALL(vkUpdateDescriptorSets(vk_device, descriptor_count, descriptor_writes, 0, NULL));
+                descriptor_count = 0;
+                current_descriptor_write = descriptor_writes;
+                current_image_info = image_infos;
+            }
+        }
+    }
+
+    VK_CALL(vkUpdateDescriptorSets(vk_device, descriptor_count, descriptor_writes, 0, NULL));
+}
+
+static bool vk_write_descriptor_set_from_root_descriptor(VkWriteDescriptorSet *vk_descriptor_write,
+        const struct d3d12_root_parameter *root_parameter, VkDescriptorSet vk_descriptor_set,
+        VkBufferView *vk_buffer_view, const VkDescriptorBufferInfo *vk_buffer_info)
+{
+    const struct d3d12_root_descriptor *root_descriptor;
+
+    switch (root_parameter->parameter_type)
+    {
+        case D3D12_ROOT_PARAMETER_TYPE_CBV:
+            vk_descriptor_write->descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+            break;
+        case D3D12_ROOT_PARAMETER_TYPE_SRV:
+            vk_descriptor_write->descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
+            break;
+        case D3D12_ROOT_PARAMETER_TYPE_UAV:
+            vk_descriptor_write->descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;
+            break;
+        default:
+            ERR("Invalid root descriptor %#x.\n", root_parameter->parameter_type);
+            return false;
+    }
+
+    root_descriptor = &root_parameter->u.descriptor;
+
+    vk_descriptor_write->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+    vk_descriptor_write->pNext = NULL;
+    vk_descriptor_write->dstSet = vk_descriptor_set;
+    vk_descriptor_write->dstBinding = root_descriptor->binding;
+    vk_descriptor_write->dstArrayElement = 0;
+    vk_descriptor_write->descriptorCount = 1;
+    vk_descriptor_write->pImageInfo = NULL;
+    vk_descriptor_write->pBufferInfo = vk_buffer_info;
+    vk_descriptor_write->pTexelBufferView = vk_buffer_view;
+
+    return true;
+}
+
+static void d3d12_command_list_update_push_descriptors(struct d3d12_command_list *list,
+        VkPipelineBindPoint bind_point)
+{
+    struct vkd3d_pipeline_bindings *bindings = &list->pipeline_bindings[bind_point];
+    const struct d3d12_root_signature *root_signature = bindings->root_signature;
+    VkWriteDescriptorSet *descriptor_writes = NULL, *current_descriptor_write;
+    const struct vkd3d_vk_device_procs *vk_procs = &list->device->vk_procs;
+    VkDescriptorBufferInfo *buffer_infos = NULL, *current_buffer_info;
+    const struct d3d12_root_parameter *root_parameter;
+    struct vkd3d_push_descriptor *push_descriptor;
+    struct d3d12_device *device = list->device;
+    VkDescriptorBufferInfo *vk_buffer_info;
+    unsigned int i, descriptor_count;
+    VkBufferView *vk_buffer_view;
+
+    if (!bindings->push_descriptor_dirty_mask)
+        return;
+
+    descriptor_count = vkd3d_popcount(bindings->push_descriptor_dirty_mask);
+
+    if (!(descriptor_writes = vkd3d_calloc(descriptor_count, sizeof(*descriptor_writes))))
+        return;
+    if (!(buffer_infos = vkd3d_calloc(descriptor_count, sizeof(*buffer_infos))))
+        goto done;
+
+    descriptor_count = 0;
+    current_buffer_info = buffer_infos;
+    current_descriptor_write = descriptor_writes;
+    for (i = 0; i < ARRAY_SIZE(bindings->push_descriptors); ++i)
+    {
+        if (!(bindings->push_descriptor_dirty_mask & (1u << i)))
+            continue;
+
+        root_parameter = root_signature_get_root_descriptor(root_signature, i);
+        push_descriptor = &bindings->push_descriptors[i];
+
+        if (root_parameter->parameter_type == D3D12_ROOT_PARAMETER_TYPE_CBV)
+        {
+            vk_buffer_view = NULL;
+            vk_buffer_info = current_buffer_info;
+            vk_buffer_info->buffer = push_descriptor->u.cbv.vk_buffer;
+            vk_buffer_info->offset = push_descriptor->u.cbv.offset;
+            vk_buffer_info->range = VK_WHOLE_SIZE;
+        }
+        else
+        {
+            vk_buffer_view = &push_descriptor->u.vk_buffer_view;
+            vk_buffer_info = NULL;
+        }
+
+        if (!vk_write_descriptor_set_from_root_descriptor(current_descriptor_write,
+                root_parameter, bindings->descriptor_set, vk_buffer_view, vk_buffer_info))
+            continue;
+
+        ++descriptor_count;
+        ++current_descriptor_write;
+        ++current_buffer_info;
+    }
+
+    VK_CALL(vkUpdateDescriptorSets(device->vk_device, descriptor_count, descriptor_writes, 0, NULL));
+    bindings->push_descriptor_dirty_mask = 0;
+
+done:
+    vkd3d_free(descriptor_writes);
+    vkd3d_free(buffer_infos);
+}
+
+static void d3d12_command_list_update_uav_counter_descriptors(struct d3d12_command_list *list,
+        VkPipelineBindPoint bind_point)
+{
+    VkWriteDescriptorSet vk_descriptor_writes[VKD3D_SHADER_MAX_UNORDERED_ACCESS_VIEWS];
+    struct vkd3d_pipeline_bindings *bindings = &list->pipeline_bindings[bind_point];
+    const struct vkd3d_vk_device_procs *vk_procs = &list->device->vk_procs;
+    const struct d3d12_pipeline_state *state = list->state;
+    VkDevice vk_device = list->device->vk_device;
+    VkDescriptorSet vk_descriptor_set;
+    unsigned int uav_counter_count;
+    unsigned int i;
+
+    if (!state || !(state->uav_counter_mask & bindings->uav_counter_dirty_mask))
+        return;
+
+    uav_counter_count = vkd3d_popcount(state->uav_counter_mask);
+    assert(uav_counter_count <= ARRAY_SIZE(vk_descriptor_writes));
+
+    vk_descriptor_set = d3d12_command_allocator_allocate_descriptor_set(list->allocator, state->vk_set_layout);
+    if (!vk_descriptor_set)
+        return;
+
+    for (i = 0; i < uav_counter_count; ++i)
+    {
+        const struct vkd3d_shader_uav_counter_binding *uav_counter = &state->uav_counters[i];
+        const VkBufferView *vk_uav_counter_views = bindings->vk_uav_counter_views;
+
+        assert(vk_uav_counter_views[uav_counter->register_index]);
+
+        vk_descriptor_writes[i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+        vk_descriptor_writes[i].pNext = NULL;
+        vk_descriptor_writes[i].dstSet = vk_descriptor_set;
+        vk_descriptor_writes[i].dstBinding = uav_counter->binding.binding;
+        vk_descriptor_writes[i].dstArrayElement = 0;
+        vk_descriptor_writes[i].descriptorCount = 1;
+        vk_descriptor_writes[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;
+        vk_descriptor_writes[i].pImageInfo = NULL;
+        vk_descriptor_writes[i].pBufferInfo = NULL;
+        vk_descriptor_writes[i].pTexelBufferView = &vk_uav_counter_views[uav_counter->register_index];
+    }
+
+    VK_CALL(vkUpdateDescriptorSets(vk_device, uav_counter_count, vk_descriptor_writes, 0, NULL));
+
+    VK_CALL(vkCmdBindDescriptorSets(list->vk_command_buffer, bind_point,
+            state->vk_pipeline_layout, state->set_index, 1, &vk_descriptor_set, 0, NULL));
+
+    bindings->uav_counter_dirty_mask = 0;
+}
+
+static void d3d12_command_list_update_descriptors(struct d3d12_command_list *list,
+        VkPipelineBindPoint bind_point)
+{
+    struct vkd3d_pipeline_bindings *bindings = &list->pipeline_bindings[bind_point];
+    const struct vkd3d_vk_device_procs *vk_procs = &list->device->vk_procs;
+    const struct d3d12_root_signature *rs = bindings->root_signature;
+    struct d3d12_desc *base_descriptor;
+    unsigned int i;
+
+    if (!rs || !rs->vk_set_layout)
+        return;
+
+    if (bindings->descriptor_table_dirty_mask || bindings->push_descriptor_dirty_mask)
+        d3d12_command_list_prepare_descriptors(list, bind_point);
+
+    for (i = 0; i < ARRAY_SIZE(bindings->descriptor_tables); ++i)
+    {
+        if (bindings->descriptor_table_dirty_mask & ((uint64_t)1 << i))
+        {
+            if ((base_descriptor = d3d12_desc_from_gpu_handle(bindings->descriptor_tables[i])))
+                d3d12_command_list_update_descriptor_table(list, bind_point, i, base_descriptor);
+            else
+                WARN("Descriptor table %u is not set.\n", i);
+        }
+    }
+    bindings->descriptor_table_dirty_mask = 0;
+
+    d3d12_command_list_update_push_descriptors(list, bind_point);
+
+    if (bindings->descriptor_set)
+    {
+        VK_CALL(vkCmdBindDescriptorSets(list->vk_command_buffer, bind_point,
+                rs->vk_pipeline_layout, rs->main_set, 1, &bindings->descriptor_set, 0, NULL));
+        bindings->in_use = true;
+    }
+
+    d3d12_command_list_update_uav_counter_descriptors(list, bind_point);
+}
+
+static bool d3d12_command_list_update_compute_state(struct d3d12_command_list *list)
+{
+    d3d12_command_list_end_current_render_pass(list);
+
+    if (!d3d12_command_list_update_compute_pipeline(list))
+        return false;
+
+    d3d12_command_list_update_descriptors(list, VK_PIPELINE_BIND_POINT_COMPUTE);
+
+    return true;
+}
+
+static bool d3d12_command_list_begin_render_pass(struct d3d12_command_list *list)
+{
+    const struct vkd3d_vk_device_procs *vk_procs = &list->device->vk_procs;
+    struct d3d12_graphics_pipeline_state *graphics;
+    struct VkRenderPassBeginInfo begin_desc;
+    VkRenderPass vk_render_pass;
+
+    if (!d3d12_command_list_update_graphics_pipeline(list))
+        return false;
+    if (!d3d12_command_list_update_current_framebuffer(list))
+        return false;
+
+    d3d12_command_list_update_descriptors(list, VK_PIPELINE_BIND_POINT_GRAPHICS);
+
+    if (list->current_render_pass != VK_NULL_HANDLE)
+        return true;
+
+    vk_render_pass = list->pso_render_pass;
+    assert(vk_render_pass);
+
+    begin_desc.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
+    begin_desc.pNext = NULL;
+    begin_desc.renderPass = vk_render_pass;
+    begin_desc.framebuffer = list->current_framebuffer;
+    begin_desc.renderArea.offset.x = 0;
+    begin_desc.renderArea.offset.y = 0;
+    d3d12_command_list_get_fb_extent(list,
+            &begin_desc.renderArea.extent.width, &begin_desc.renderArea.extent.height, NULL);
+    begin_desc.clearValueCount = 0;
+    begin_desc.pClearValues = NULL;
+    VK_CALL(vkCmdBeginRenderPass(list->vk_command_buffer, &begin_desc, VK_SUBPASS_CONTENTS_INLINE));
+
+    list->current_render_pass = vk_render_pass;
+
+    graphics = &list->state->u.graphics;
+    if (graphics->xfb_enabled)
+    {
+        VK_CALL(vkCmdBeginTransformFeedbackEXT(list->vk_command_buffer, 0, ARRAY_SIZE(list->so_counter_buffers),
+                list->so_counter_buffers, list->so_counter_buffer_offsets));
+
+        list->xfb_enabled = true;
+    }
+
+    return true;
+}
+
+static void d3d12_command_list_check_index_buffer_strip_cut_value(struct d3d12_command_list *list)
+{
+    struct d3d12_graphics_pipeline_state *graphics = &list->state->u.graphics;
+
+    /* In Vulkan, the strip cut value is derived from the index buffer format. */
+    switch (graphics->index_buffer_strip_cut_value)
+    {
+        case D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_0xFFFF:
+            if (list->index_buffer_format != DXGI_FORMAT_R16_UINT)
+            {
+                FIXME("Strip cut value 0xffff is not supported with index buffer format %#x.\n",
+                        list->index_buffer_format);
+            }
+            break;
+
+        case D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_0xFFFFFFFF:
+            if (list->index_buffer_format != DXGI_FORMAT_R32_UINT)
+            {
+                FIXME("Strip cut value 0xffffffff is not supported with index buffer format %#x.\n",
+                        list->index_buffer_format);
+            }
+            break;
+
+        default:
+            break;
+    }
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_DrawInstanced(ID3D12GraphicsCommandList1 *iface,
+        UINT vertex_count_per_instance, UINT instance_count, UINT start_vertex_location,
+        UINT start_instance_location)
+{
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+    const struct vkd3d_vk_device_procs *vk_procs;
+
+    TRACE("iface %p, vertex_count_per_instance %u, instance_count %u, "
+            "start_vertex_location %u, start_instance_location %u.\n",
+            iface, vertex_count_per_instance, instance_count,
+            start_vertex_location, start_instance_location);
+
+    vk_procs = &list->device->vk_procs;
+
+    if (!d3d12_command_list_begin_render_pass(list))
+    {
+        WARN("Failed to begin render pass, ignoring draw call.\n");
+        return;
+    }
+
+    VK_CALL(vkCmdDraw(list->vk_command_buffer, vertex_count_per_instance,
+            instance_count, start_vertex_location, start_instance_location));
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_DrawIndexedInstanced(ID3D12GraphicsCommandList1 *iface,
+        UINT index_count_per_instance, UINT instance_count, UINT start_vertex_location,
+        INT base_vertex_location, UINT start_instance_location)
+{
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+    const struct vkd3d_vk_device_procs *vk_procs;
+
+    TRACE("iface %p, index_count_per_instance %u, instance_count %u, start_vertex_location %u, "
+            "base_vertex_location %d, start_instance_location %u.\n",
+            iface, index_count_per_instance, instance_count, start_vertex_location,
+            base_vertex_location, start_instance_location);
+
+    if (!d3d12_command_list_begin_render_pass(list))
+    {
+        WARN("Failed to begin render pass, ignoring draw call.\n");
+        return;
+    }
+
+    vk_procs = &list->device->vk_procs;
+
+    d3d12_command_list_check_index_buffer_strip_cut_value(list);
+
+    VK_CALL(vkCmdDrawIndexed(list->vk_command_buffer, index_count_per_instance,
+            instance_count, start_vertex_location, base_vertex_location, start_instance_location));
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_Dispatch(ID3D12GraphicsCommandList1 *iface,
+        UINT x, UINT y, UINT z)
+{
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+    const struct vkd3d_vk_device_procs *vk_procs;
+
+    TRACE("iface %p, x %u, y %u, z %u.\n", iface, x, y, z);
+
+    if (!d3d12_command_list_update_compute_state(list))
+    {
+        WARN("Failed to update compute state, ignoring dispatch.\n");
+        return;
+    }
+
+    vk_procs = &list->device->vk_procs;
+
+    VK_CALL(vkCmdDispatch(list->vk_command_buffer, x, y, z));
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_CopyBufferRegion(ID3D12GraphicsCommandList1 *iface,
+        ID3D12Resource *dst, UINT64 dst_offset, ID3D12Resource *src, UINT64 src_offset, UINT64 byte_count)
+{
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+    struct d3d12_resource *dst_resource, *src_resource;
+    const struct vkd3d_vk_device_procs *vk_procs;
+    VkBufferCopy buffer_copy;
+
+    TRACE("iface %p, dst_resource %p, dst_offset %#"PRIx64", src_resource %p, "
+            "src_offset %#"PRIx64", byte_count %#"PRIx64".\n",
+            iface, dst, dst_offset, src, src_offset, byte_count);
+
+    vk_procs = &list->device->vk_procs;
+
+    dst_resource = unsafe_impl_from_ID3D12Resource(dst);
+    assert(d3d12_resource_is_buffer(dst_resource));
+    src_resource = unsafe_impl_from_ID3D12Resource(src);
+    assert(d3d12_resource_is_buffer(src_resource));
+
+    d3d12_command_list_track_resource_usage(list, dst_resource);
+    d3d12_command_list_track_resource_usage(list, src_resource);
+
+    d3d12_command_list_end_current_render_pass(list);
+
+    buffer_copy.srcOffset = src_offset;
+    buffer_copy.dstOffset = dst_offset;
+    buffer_copy.size = byte_count;
+
+    VK_CALL(vkCmdCopyBuffer(list->vk_command_buffer,
+            src_resource->u.vk_buffer, dst_resource->u.vk_buffer, 1, &buffer_copy));
+}
+
+static void vk_image_subresource_layers_from_d3d12(VkImageSubresourceLayers *subresource,
+        const struct vkd3d_format *format, unsigned int sub_resource_idx, unsigned int miplevel_count)
+{
+    subresource->aspectMask = format->vk_aspect_mask;
+    subresource->mipLevel = sub_resource_idx % miplevel_count;
+    subresource->baseArrayLayer = sub_resource_idx / miplevel_count;
+    subresource->layerCount = 1;
+}
+
+static void vk_extent_3d_from_d3d12_miplevel(VkExtent3D *extent,
+        const D3D12_RESOURCE_DESC *resource_desc, unsigned int miplevel_idx)
+{
+    extent->width = d3d12_resource_desc_get_width(resource_desc, miplevel_idx);
+    extent->height = d3d12_resource_desc_get_height(resource_desc, miplevel_idx);
+    extent->depth = d3d12_resource_desc_get_depth(resource_desc, miplevel_idx);
+}
+
+static void vk_buffer_image_copy_from_d3d12(VkBufferImageCopy *copy,
+        const D3D12_PLACED_SUBRESOURCE_FOOTPRINT *footprint, unsigned int sub_resource_idx,
+        const D3D12_RESOURCE_DESC *image_desc, const struct vkd3d_format *format,
+        const D3D12_BOX *src_box, unsigned int dst_x, unsigned int dst_y, unsigned int dst_z)
+{
+    copy->bufferOffset = footprint->Offset;
+    if (src_box)
+    {
+        VkDeviceSize row_count = footprint->Footprint.Height / format->block_height;
+        copy->bufferOffset += vkd3d_format_get_data_offset(format, footprint->Footprint.RowPitch,
+                row_count * footprint->Footprint.RowPitch, src_box->left, src_box->top, src_box->front);
+    }
+    copy->bufferRowLength = footprint->Footprint.RowPitch /
+            (format->byte_count * format->block_byte_count) * format->block_width;
+    copy->bufferImageHeight = footprint->Footprint.Height;
+    vk_image_subresource_layers_from_d3d12(&copy->imageSubresource,
+            format, sub_resource_idx, image_desc->MipLevels);
+    copy->imageOffset.x = dst_x;
+    copy->imageOffset.y = dst_y;
+    copy->imageOffset.z = dst_z;
+
+    vk_extent_3d_from_d3d12_miplevel(&copy->imageExtent, image_desc,
+            copy->imageSubresource.mipLevel);
+    copy->imageExtent.width -= copy->imageOffset.x;
+    copy->imageExtent.height -= copy->imageOffset.y;
+    copy->imageExtent.depth -= copy->imageOffset.z;
+
+    if (src_box)
+    {
+        copy->imageExtent.width = min(copy->imageExtent.width, src_box->right - src_box->left);
+        copy->imageExtent.height = min(copy->imageExtent.height, src_box->bottom - src_box->top);
+        copy->imageExtent.depth = min(copy->imageExtent.depth, src_box->back - src_box->front);
+    }
+    else
+    {
+        copy->imageExtent.width = min(copy->imageExtent.width, footprint->Footprint.Width);
+        copy->imageExtent.height = min(copy->imageExtent.height, footprint->Footprint.Height);
+        copy->imageExtent.depth = min(copy->imageExtent.depth, footprint->Footprint.Depth);
+    }
+}
+
+static void vk_image_buffer_copy_from_d3d12(VkBufferImageCopy *copy,
+        const D3D12_PLACED_SUBRESOURCE_FOOTPRINT *footprint, unsigned int sub_resource_idx,
+        const D3D12_RESOURCE_DESC *image_desc, const struct vkd3d_format *format,
+        const D3D12_BOX *src_box, unsigned int dst_x, unsigned int dst_y, unsigned int dst_z)
+{
+    VkDeviceSize row_count = footprint->Footprint.Height / format->block_height;
+
+    copy->bufferOffset = footprint->Offset + vkd3d_format_get_data_offset(format,
+            footprint->Footprint.RowPitch, row_count * footprint->Footprint.RowPitch, dst_x, dst_y, dst_z);
+    copy->bufferRowLength = footprint->Footprint.RowPitch /
+            (format->byte_count * format->block_byte_count) * format->block_width;
+    copy->bufferImageHeight = footprint->Footprint.Height;
+    vk_image_subresource_layers_from_d3d12(&copy->imageSubresource,
+            format, sub_resource_idx, image_desc->MipLevels);
+    copy->imageOffset.x = src_box ? src_box->left : 0;
+    copy->imageOffset.y = src_box ? src_box->top : 0;
+    copy->imageOffset.z = src_box ? src_box->front : 0;
+    if (src_box)
+    {
+        copy->imageExtent.width = src_box->right - src_box->left;
+        copy->imageExtent.height = src_box->bottom - src_box->top;
+        copy->imageExtent.depth = src_box->back - src_box->front;
+    }
+    else
+    {
+        unsigned int miplevel = copy->imageSubresource.mipLevel;
+        vk_extent_3d_from_d3d12_miplevel(&copy->imageExtent, image_desc, miplevel);
+    }
+}
+
+static void vk_image_copy_from_d3d12(VkImageCopy *image_copy,
+        unsigned int src_sub_resource_idx, unsigned int dst_sub_resource_idx,
+        const D3D12_RESOURCE_DESC *src_desc, const D3D12_RESOURCE_DESC *dst_desc,
+        const struct vkd3d_format *src_format, const struct vkd3d_format *dst_format,
+        const D3D12_BOX *src_box, unsigned int dst_x, unsigned int dst_y, unsigned int dst_z)
+{
+    vk_image_subresource_layers_from_d3d12(&image_copy->srcSubresource,
+            src_format, src_sub_resource_idx, src_desc->MipLevels);
+    image_copy->srcOffset.x = src_box ? src_box->left : 0;
+    image_copy->srcOffset.y = src_box ? src_box->top : 0;
+    image_copy->srcOffset.z = src_box ? src_box->front : 0;
+    vk_image_subresource_layers_from_d3d12(&image_copy->dstSubresource,
+            dst_format, dst_sub_resource_idx, dst_desc->MipLevels);
+    image_copy->dstOffset.x = dst_x;
+    image_copy->dstOffset.y = dst_y;
+    image_copy->dstOffset.z = dst_z;
+    if (src_box)
+    {
+        image_copy->extent.width = src_box->right - src_box->left;
+        image_copy->extent.height = src_box->bottom - src_box->top;
+        image_copy->extent.depth = src_box->back - src_box->front;
+    }
+    else
+    {
+        unsigned int miplevel = image_copy->srcSubresource.mipLevel;
+        vk_extent_3d_from_d3d12_miplevel(&image_copy->extent, src_desc, miplevel);
+    }
+}
+
+static HRESULT d3d12_command_list_allocate_transfer_buffer(struct d3d12_command_list *list,
+        VkDeviceSize size, struct vkd3d_buffer *buffer)
+{
+    const struct vkd3d_vk_device_procs *vk_procs = &list->device->vk_procs;
+    struct d3d12_device *device = list->device;
+    D3D12_HEAP_PROPERTIES heap_properties;
+    D3D12_RESOURCE_DESC buffer_desc;
+    HRESULT hr;
+
+    memset(&heap_properties, 0, sizeof(heap_properties));
+    heap_properties.Type = D3D12_HEAP_TYPE_DEFAULT;
+
+    buffer_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
+    buffer_desc.Alignment = 0;
+    buffer_desc.Width = size;
+    buffer_desc.Height = 1;
+    buffer_desc.DepthOrArraySize = 1;
+    buffer_desc.MipLevels = 1;
+    buffer_desc.Format = DXGI_FORMAT_UNKNOWN;
+    buffer_desc.SampleDesc.Count = 1;
+    buffer_desc.SampleDesc.Quality = 0;
+    buffer_desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
+    buffer_desc.Flags = D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE;
+
+    if (FAILED(hr = vkd3d_create_buffer(device, &heap_properties, D3D12_HEAP_FLAG_NONE,
+            &buffer_desc, &buffer->vk_buffer)))
+        return hr;
+    if (FAILED(hr = vkd3d_allocate_buffer_memory(device, buffer->vk_buffer,
+            &heap_properties, D3D12_HEAP_FLAG_NONE, &buffer->vk_memory, NULL, NULL)))
+    {
+        VK_CALL(vkDestroyBuffer(device->vk_device, buffer->vk_buffer, NULL));
+        return hr;
+    }
+
+    if (!d3d12_command_allocator_add_transfer_buffer(list->allocator, buffer))
+    {
+        ERR("Failed to add transfer buffer.\n");
+        vkd3d_buffer_destroy(buffer, device);
+        return E_OUTOFMEMORY;
+    }
+
+    return S_OK;
+}
+
+/* In Vulkan, each depth/stencil format is only compatible with itself.
+ * This means that we are not allowed to copy texture regions directly between
+ * depth/stencil and color formats.
+ *
+ * FIXME: Implement color <-> depth/stencil blits in shaders.
+ */
+static void d3d12_command_list_copy_incompatible_texture_region(struct d3d12_command_list *list,
+        struct d3d12_resource *dst_resource, unsigned int dst_sub_resource_idx,
+        const struct vkd3d_format *dst_format, struct d3d12_resource *src_resource,
+        unsigned int src_sub_resource_idx, const struct vkd3d_format *src_format)
+{
+    const struct vkd3d_vk_device_procs *vk_procs = &list->device->vk_procs;
+    const D3D12_RESOURCE_DESC *dst_desc = &dst_resource->desc;
+    const D3D12_RESOURCE_DESC *src_desc = &src_resource->desc;
+    unsigned int dst_miplevel_idx, src_miplevel_idx;
+    struct vkd3d_buffer transfer_buffer;
+    VkBufferImageCopy buffer_image_copy;
+    VkBufferMemoryBarrier vk_barrier;
+    VkDeviceSize buffer_size;
+    HRESULT hr;
+
+    WARN("Copying incompatible texture formats %#x, %#x -> %#x, %#x.\n",
+            src_format->dxgi_format, src_format->vk_format,
+            dst_format->dxgi_format, dst_format->vk_format);
+
+    assert(d3d12_resource_is_texture(dst_resource));
+    assert(d3d12_resource_is_texture(src_resource));
+    assert(!vkd3d_format_is_compressed(dst_format));
+    assert(!vkd3d_format_is_compressed(src_format));
+    assert(dst_format->byte_count == src_format->byte_count);
+
+    buffer_image_copy.bufferOffset = 0;
+    buffer_image_copy.bufferRowLength = 0;
+    buffer_image_copy.bufferImageHeight = 0;
+    vk_image_subresource_layers_from_d3d12(&buffer_image_copy.imageSubresource,
+            src_format, src_sub_resource_idx, src_desc->MipLevels);
+    src_miplevel_idx = buffer_image_copy.imageSubresource.mipLevel;
+    buffer_image_copy.imageOffset.x = 0;
+    buffer_image_copy.imageOffset.y = 0;
+    buffer_image_copy.imageOffset.z = 0;
+    vk_extent_3d_from_d3d12_miplevel(&buffer_image_copy.imageExtent, src_desc, src_miplevel_idx);
+
+    buffer_size = src_format->byte_count * buffer_image_copy.imageExtent.width *
+            buffer_image_copy.imageExtent.height * buffer_image_copy.imageExtent.depth;
+    if (FAILED(hr = d3d12_command_list_allocate_transfer_buffer(list, buffer_size, &transfer_buffer)))
+    {
+        ERR("Failed to allocate transfer buffer, hr %#x.\n", hr);
+        return;
+    }
+
+    VK_CALL(vkCmdCopyImageToBuffer(list->vk_command_buffer,
+            src_resource->u.vk_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+            transfer_buffer.vk_buffer, 1, &buffer_image_copy));
+
+    vk_barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
+    vk_barrier.pNext = NULL;
+    vk_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+    vk_barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
+    vk_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+    vk_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+    vk_barrier.buffer = transfer_buffer.vk_buffer;
+    vk_barrier.offset = 0;
+    vk_barrier.size = VK_WHOLE_SIZE;
+    VK_CALL(vkCmdPipelineBarrier(list->vk_command_buffer,
+            VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
+            0, NULL, 1, &vk_barrier, 0, NULL));
+
+    vk_image_subresource_layers_from_d3d12(&buffer_image_copy.imageSubresource,
+            dst_format, dst_sub_resource_idx, dst_desc->MipLevels);
+    dst_miplevel_idx = buffer_image_copy.imageSubresource.mipLevel;
+
+    assert(d3d12_resource_desc_get_width(src_desc, src_miplevel_idx) ==
+            d3d12_resource_desc_get_width(dst_desc, dst_miplevel_idx));
+    assert(d3d12_resource_desc_get_height(src_desc, src_miplevel_idx) ==
+            d3d12_resource_desc_get_height(dst_desc, dst_miplevel_idx));
+    assert(d3d12_resource_desc_get_depth(src_desc, src_miplevel_idx) ==
+            d3d12_resource_desc_get_depth(dst_desc, dst_miplevel_idx));
+
+    VK_CALL(vkCmdCopyBufferToImage(list->vk_command_buffer,
+            transfer_buffer.vk_buffer, dst_resource->u.vk_image,
+            VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &buffer_image_copy));
+}
+
+static bool validate_d3d12_box(const D3D12_BOX *box)
+{
+    return box->right > box->left
+            && box->bottom > box->top
+            && box->back > box->front;
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_CopyTextureRegion(ID3D12GraphicsCommandList1 *iface,
+        const D3D12_TEXTURE_COPY_LOCATION *dst, UINT dst_x, UINT dst_y, UINT dst_z,
+        const D3D12_TEXTURE_COPY_LOCATION *src, const D3D12_BOX *src_box)
+{
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+    struct d3d12_resource *dst_resource, *src_resource;
+    const struct vkd3d_format *src_format, *dst_format;
+    const struct vkd3d_vk_device_procs *vk_procs;
+    VkBufferImageCopy buffer_image_copy;
+    VkImageCopy image_copy;
+
+    TRACE("iface %p, dst %p, dst_x %u, dst_y %u, dst_z %u, src %p, src_box %p.\n",
+            iface, dst, dst_x, dst_y, dst_z, src, src_box);
+
+    if (src_box && !validate_d3d12_box(src_box))
+    {
+        WARN("Empty box %s.\n", debug_d3d12_box(src_box));
+        return;
+    }
+
+    vk_procs = &list->device->vk_procs;
+
+    dst_resource = unsafe_impl_from_ID3D12Resource(dst->pResource);
+    src_resource = unsafe_impl_from_ID3D12Resource(src->pResource);
+
+    d3d12_command_list_track_resource_usage(list, dst_resource);
+    d3d12_command_list_track_resource_usage(list, src_resource);
+
+    d3d12_command_list_end_current_render_pass(list);
+
+    if (src->Type == D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX
+            && dst->Type == D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT)
+    {
+        assert(d3d12_resource_is_buffer(dst_resource));
+        assert(d3d12_resource_is_texture(src_resource));
+
+        if (!(dst_format = vkd3d_format_from_d3d12_resource_desc(list->device,
+                &src_resource->desc, dst->u.PlacedFootprint.Footprint.Format)))
+        {
+            WARN("Invalid format %#x.\n", dst->u.PlacedFootprint.Footprint.Format);
+            return;
+        }
+
+        if (dst_format->is_emulated)
+        {
+            FIXME("Format %#x is not supported yet.\n", dst_format->dxgi_format);
+            return;
+        }
+
+        if ((dst_format->vk_aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT)
+                && (dst_format->vk_aspect_mask & VK_IMAGE_ASPECT_STENCIL_BIT))
+            FIXME("Depth-stencil format %#x not fully supported yet.\n", dst_format->dxgi_format);
+
+        vk_image_buffer_copy_from_d3d12(&buffer_image_copy, &dst->u.PlacedFootprint,
+                src->u.SubresourceIndex, &src_resource->desc, dst_format, src_box, dst_x, dst_y, dst_z);
+        VK_CALL(vkCmdCopyImageToBuffer(list->vk_command_buffer,
+                src_resource->u.vk_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+                dst_resource->u.vk_buffer, 1, &buffer_image_copy));
+    }
+    else if (src->Type == D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT
+            && dst->Type == D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX)
+    {
+        assert(d3d12_resource_is_texture(dst_resource));
+        assert(d3d12_resource_is_buffer(src_resource));
+
+        if (!(src_format = vkd3d_format_from_d3d12_resource_desc(list->device,
+                &dst_resource->desc, src->u.PlacedFootprint.Footprint.Format)))
+        {
+            WARN("Invalid format %#x.\n", src->u.PlacedFootprint.Footprint.Format);
+            return;
+        }
+
+        if (src_format->is_emulated)
+        {
+            FIXME("Format %#x is not supported yet.\n", src_format->dxgi_format);
+            return;
+        }
+
+        if ((src_format->vk_aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT)
+                && (src_format->vk_aspect_mask & VK_IMAGE_ASPECT_STENCIL_BIT))
+            FIXME("Depth-stencil format %#x not fully supported yet.\n", src_format->dxgi_format);
+
+        vk_buffer_image_copy_from_d3d12(&buffer_image_copy, &src->u.PlacedFootprint,
+                dst->u.SubresourceIndex, &dst_resource->desc, src_format, src_box, dst_x, dst_y, dst_z);
+        VK_CALL(vkCmdCopyBufferToImage(list->vk_command_buffer,
+                src_resource->u.vk_buffer, dst_resource->u.vk_image,
+                VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &buffer_image_copy));
+    }
+    else if (src->Type == D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX
+            && dst->Type == D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX)
+    {
+        assert(d3d12_resource_is_texture(dst_resource));
+        assert(d3d12_resource_is_texture(src_resource));
+
+        if (!(dst_format = vkd3d_format_from_d3d12_resource_desc(list->device,
+                &dst_resource->desc, DXGI_FORMAT_UNKNOWN)))
+        {
+            WARN("Invalid format %#x.\n", dst_resource->desc.Format);
+            return;
+        }
+        if (!(src_format = vkd3d_format_from_d3d12_resource_desc(list->device,
+                &src_resource->desc, DXGI_FORMAT_UNKNOWN)))
+        {
+            WARN("Invalid format %#x.\n", src_resource->desc.Format);
+            return;
+        }
+
+        if ((dst_format->vk_aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT)
+                && (dst_format->vk_aspect_mask & VK_IMAGE_ASPECT_STENCIL_BIT))
+            FIXME("Depth-stencil format %#x not fully supported yet.\n", dst_format->dxgi_format);
+        if ((src_format->vk_aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT)
+                && (src_format->vk_aspect_mask & VK_IMAGE_ASPECT_STENCIL_BIT))
+            FIXME("Depth-stencil format %#x not fully supported yet.\n", src_format->dxgi_format);
+
+        if (dst_format->vk_aspect_mask != src_format->vk_aspect_mask)
+        {
+            d3d12_command_list_copy_incompatible_texture_region(list,
+                    dst_resource, dst->u.SubresourceIndex, dst_format,
+                    src_resource, src->u.SubresourceIndex, src_format);
+            return;
+        }
+
+        vk_image_copy_from_d3d12(&image_copy, src->u.SubresourceIndex, dst->u.SubresourceIndex,
+                 &src_resource->desc, &dst_resource->desc, src_format, dst_format,
+                 src_box, dst_x, dst_y, dst_z);
+        VK_CALL(vkCmdCopyImage(list->vk_command_buffer, src_resource->u.vk_image,
+                VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst_resource->u.vk_image,
+                VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy));
+    }
+    else
+    {
+        FIXME("Copy type %#x -> %#x not implemented.\n", src->Type, dst->Type);
+    }
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_CopyResource(ID3D12GraphicsCommandList1 *iface,
+        ID3D12Resource *dst, ID3D12Resource *src)
+{
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+    struct d3d12_resource *dst_resource, *src_resource;
+    const struct vkd3d_format *src_format, *dst_format;
+    const struct vkd3d_vk_device_procs *vk_procs;
+    VkBufferCopy vk_buffer_copy;
+    VkImageCopy vk_image_copy;
+    unsigned int layer_count;
+    unsigned int i;
+
+    TRACE("iface %p, dst_resource %p, src_resource %p.\n", iface, dst, src);
+
+    vk_procs = &list->device->vk_procs;
+
+    dst_resource = unsafe_impl_from_ID3D12Resource(dst);
+    src_resource = unsafe_impl_from_ID3D12Resource(src);
+
+    d3d12_command_list_track_resource_usage(list, dst_resource);
+    d3d12_command_list_track_resource_usage(list, src_resource);
+
+    d3d12_command_list_end_current_render_pass(list);
+
+    if (d3d12_resource_is_buffer(dst_resource))
+    {
+        assert(d3d12_resource_is_buffer(src_resource));
+        assert(src_resource->desc.Width == dst_resource->desc.Width);
+
+        vk_buffer_copy.srcOffset = 0;
+        vk_buffer_copy.dstOffset = 0;
+        vk_buffer_copy.size = dst_resource->desc.Width;
+        VK_CALL(vkCmdCopyBuffer(list->vk_command_buffer,
+                src_resource->u.vk_buffer, dst_resource->u.vk_buffer, 1, &vk_buffer_copy));
+    }
+    else
+    {
+        if (!(dst_format = vkd3d_format_from_d3d12_resource_desc(list->device,
+                &dst_resource->desc, DXGI_FORMAT_UNKNOWN)))
+        {
+            WARN("Invalid format %#x.\n", dst_resource->desc.Format);
+            return;
+        }
+        if (!(src_format = vkd3d_format_from_d3d12_resource_desc(list->device,
+                &src_resource->desc, DXGI_FORMAT_UNKNOWN)))
+        {
+            WARN("Invalid format %#x.\n", src_resource->desc.Format);
+            return;
+        }
+
+        layer_count = d3d12_resource_desc_get_layer_count(&dst_resource->desc);
+
+        assert(d3d12_resource_is_texture(dst_resource));
+        assert(d3d12_resource_is_texture(src_resource));
+        assert(dst_resource->desc.MipLevels == src_resource->desc.MipLevels);
+        assert(layer_count == d3d12_resource_desc_get_layer_count(&src_resource->desc));
+
+        for (i = 0; i < dst_resource->desc.MipLevels; ++i)
+        {
+            vk_image_copy_from_d3d12(&vk_image_copy, i, i,
+                    &src_resource->desc, &dst_resource->desc, src_format, dst_format, NULL, 0, 0, 0);
+            vk_image_copy.dstSubresource.layerCount = layer_count;
+            vk_image_copy.srcSubresource.layerCount = layer_count;
+            VK_CALL(vkCmdCopyImage(list->vk_command_buffer, src_resource->u.vk_image,
+                    VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst_resource->u.vk_image,
+                    VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &vk_image_copy));
+        }
+    }
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_CopyTiles(ID3D12GraphicsCommandList1 *iface,
+        ID3D12Resource *tiled_resource, const D3D12_TILED_RESOURCE_COORDINATE *tile_region_start_coordinate,
+        const D3D12_TILE_REGION_SIZE *tile_region_size, ID3D12Resource *buffer, UINT64 buffer_offset,
+        D3D12_TILE_COPY_FLAGS flags)
+{
+    FIXME("iface %p, tiled_resource %p, tile_region_start_coordinate %p, tile_region_size %p, "
+            "buffer %p, buffer_offset %#"PRIx64", flags %#x stub!\n",
+            iface, tiled_resource, tile_region_start_coordinate, tile_region_size,
+            buffer, buffer_offset, flags);
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_ResolveSubresource(ID3D12GraphicsCommandList1 *iface,
+        ID3D12Resource *dst, UINT dst_sub_resource_idx,
+        ID3D12Resource *src, UINT src_sub_resource_idx, DXGI_FORMAT format)
+{
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+    const struct vkd3d_format *src_format, *dst_format, *vk_format;
+    struct d3d12_resource *dst_resource, *src_resource;
+    const struct vkd3d_vk_device_procs *vk_procs;
+    const struct d3d12_device *device;
+    VkImageResolve vk_image_resolve;
+
+    TRACE("iface %p, dst_resource %p, dst_sub_resource_idx %u, src_resource %p, src_sub_resource_idx %u, "
+            "format %#x.\n", iface, dst, dst_sub_resource_idx, src, src_sub_resource_idx, format);
+
+    device = list->device;
+    vk_procs = &device->vk_procs;
+
+    dst_resource = unsafe_impl_from_ID3D12Resource(dst);
+    src_resource = unsafe_impl_from_ID3D12Resource(src);
+
+    assert(d3d12_resource_is_texture(dst_resource));
+    assert(d3d12_resource_is_texture(src_resource));
+
+    d3d12_command_list_track_resource_usage(list, dst_resource);
+    d3d12_command_list_track_resource_usage(list, src_resource);
+
+    d3d12_command_list_end_current_render_pass(list);
+
+    if (!(dst_format = vkd3d_format_from_d3d12_resource_desc(device, &dst_resource->desc, DXGI_FORMAT_UNKNOWN)))
+    {
+        WARN("Invalid format %#x.\n", dst_resource->desc.Format);
+        return;
+    }
+    if (!(src_format = vkd3d_format_from_d3d12_resource_desc(device, &src_resource->desc, DXGI_FORMAT_UNKNOWN)))
+    {
+        WARN("Invalid format %#x.\n", src_resource->desc.Format);
+        return;
+    }
+
+    if (dst_format->type == VKD3D_FORMAT_TYPE_TYPELESS || src_format->type == VKD3D_FORMAT_TYPE_TYPELESS)
+    {
+        if (!(vk_format = vkd3d_format_from_d3d12_resource_desc(device, &dst_resource->desc, format)))
+        {
+            WARN("Invalid format %#x.\n", format);
+            return;
+        }
+        if (dst_format->vk_format != src_format->vk_format || dst_format->vk_format != vk_format->vk_format)
+        {
+            FIXME("Not implemented for typeless resources.\n");
+            return;
+        }
+    }
+
+    /* Resolve of depth/stencil images is not supported in Vulkan. */
+    if ((dst_format->vk_aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT))
+            || (src_format->vk_aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)))
+    {
+        FIXME("Resolve of depth/stencil images is not implemented yet.\n");
+        return;
+    }
+
+    vk_image_subresource_layers_from_d3d12(&vk_image_resolve.srcSubresource,
+            src_format, src_sub_resource_idx, src_resource->desc.MipLevels);
+    memset(&vk_image_resolve.srcOffset, 0, sizeof(vk_image_resolve.srcOffset));
+    vk_image_subresource_layers_from_d3d12(&vk_image_resolve.dstSubresource,
+            dst_format, dst_sub_resource_idx, dst_resource->desc.MipLevels);
+    memset(&vk_image_resolve.dstOffset, 0, sizeof(vk_image_resolve.dstOffset));
+    vk_extent_3d_from_d3d12_miplevel(&vk_image_resolve.extent,
+            &dst_resource->desc, vk_image_resolve.dstSubresource.mipLevel);
+
+    VK_CALL(vkCmdResolveImage(list->vk_command_buffer, src_resource->u.vk_image,
+            VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst_resource->u.vk_image,
+            VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &vk_image_resolve));
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_IASetPrimitiveTopology(ID3D12GraphicsCommandList1 *iface,
+        D3D12_PRIMITIVE_TOPOLOGY topology)
+{
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+
+    TRACE("iface %p, topology %#x.\n", iface, topology);
+
+    if (topology == D3D_PRIMITIVE_TOPOLOGY_UNDEFINED)
+    {
+        WARN("Ignoring D3D_PRIMITIVE_TOPOLOGY_UNDEFINED.\n");
+        return;
+    }
+
+    if (list->primitive_topology == topology)
+        return;
+
+    list->primitive_topology = topology;
+    d3d12_command_list_invalidate_current_pipeline(list);
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_RSSetViewports(ID3D12GraphicsCommandList1 *iface,
+        UINT viewport_count, const D3D12_VIEWPORT *viewports)
+{
+    VkViewport vk_viewports[D3D12_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE];
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+    const struct vkd3d_vk_device_procs *vk_procs;
+    unsigned int i;
+
+    TRACE("iface %p, viewport_count %u, viewports %p.\n", iface, viewport_count, viewports);
+
+    if (viewport_count > ARRAY_SIZE(vk_viewports))
+    {
+        FIXME("Viewport count %u > D3D12_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE.\n", viewport_count);
+        viewport_count = ARRAY_SIZE(vk_viewports);
+    }
+
+    for (i = 0; i < viewport_count; ++i)
+    {
+        vk_viewports[i].x = viewports[i].TopLeftX;
+        vk_viewports[i].y = viewports[i].TopLeftY + viewports[i].Height;
+        vk_viewports[i].width = viewports[i].Width;
+        vk_viewports[i].height = -viewports[i].Height;
+        vk_viewports[i].minDepth = viewports[i].MinDepth;
+        vk_viewports[i].maxDepth = viewports[i].MaxDepth;
+
+        if (!vk_viewports[i].width || !vk_viewports[i].height)
+        {
+            FIXME_ONCE("Invalid viewport %u, ignoring RSSetViewports().\n", i);
+            return;
+        }
+    }
+
+    vk_procs = &list->device->vk_procs;
+    VK_CALL(vkCmdSetViewport(list->vk_command_buffer, 0, viewport_count, vk_viewports));
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_RSSetScissorRects(ID3D12GraphicsCommandList1 *iface,
+        UINT rect_count, const D3D12_RECT *rects)
+{
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+    VkRect2D vk_rects[D3D12_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE];
+    const struct vkd3d_vk_device_procs *vk_procs;
+    unsigned int i;
+
+    TRACE("iface %p, rect_count %u, rects %p.\n", iface, rect_count, rects);
+
+    if (rect_count > ARRAY_SIZE(vk_rects))
+    {
+        FIXME("Rect count %u > D3D12_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE.\n", rect_count);
+        rect_count = ARRAY_SIZE(vk_rects);
+    }
+
+    for (i = 0; i < rect_count; ++i)
+    {
+        vk_rects[i].offset.x = rects[i].left;
+        vk_rects[i].offset.y = rects[i].top;
+        vk_rects[i].extent.width = rects[i].right - rects[i].left;
+        vk_rects[i].extent.height = rects[i].bottom - rects[i].top;
+    }
+
+    vk_procs = &list->device->vk_procs;
+    VK_CALL(vkCmdSetScissor(list->vk_command_buffer, 0, rect_count, vk_rects));
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_OMSetBlendFactor(ID3D12GraphicsCommandList1 *iface,
+        const FLOAT blend_factor[4])
+{
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+    const struct vkd3d_vk_device_procs *vk_procs;
+
+    TRACE("iface %p, blend_factor %p.\n", iface, blend_factor);
+
+    vk_procs = &list->device->vk_procs;
+    VK_CALL(vkCmdSetBlendConstants(list->vk_command_buffer, blend_factor));
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_OMSetStencilRef(ID3D12GraphicsCommandList1 *iface,
+        UINT stencil_ref)
+{
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+    const struct vkd3d_vk_device_procs *vk_procs;
+
+    TRACE("iface %p, stencil_ref %u.\n", iface, stencil_ref);
+
+    vk_procs = &list->device->vk_procs;
+    VK_CALL(vkCmdSetStencilReference(list->vk_command_buffer, VK_STENCIL_FRONT_AND_BACK, stencil_ref));
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_SetPipelineState(ID3D12GraphicsCommandList1 *iface,
+        ID3D12PipelineState *pipeline_state)
+{
+    struct d3d12_pipeline_state *state = unsafe_impl_from_ID3D12PipelineState(pipeline_state);
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+
+    TRACE("iface %p, pipeline_state %p.\n", iface, pipeline_state);
+
+    if (list->state == state)
+        return;
+
+    d3d12_command_list_invalidate_bindings(list, state);
+    d3d12_command_list_invalidate_current_pipeline(list);
+
+    list->state = state;
+}
+
+static bool is_ds_multiplanar_resolvable(unsigned int first_state, unsigned int second_state)
+{
+    /* Only combinations of depth/stencil read/write are supported. */
+    return first_state == second_state
+            || ((first_state & (D3D12_RESOURCE_STATE_DEPTH_READ | D3D12_RESOURCE_STATE_DEPTH_WRITE))
+            && (second_state & (D3D12_RESOURCE_STATE_DEPTH_READ | D3D12_RESOURCE_STATE_DEPTH_WRITE)));
+}
+
+static unsigned int d3d12_find_ds_multiplanar_transition(const D3D12_RESOURCE_BARRIER *barriers,
+        unsigned int i, unsigned int barrier_count, unsigned int sub_resource_count)
+{
+    unsigned int sub_resource_idx = barriers[i].u.Transition.Subresource;
+    unsigned int j;
+
+    for (j = i + 1; j < barrier_count; ++j)
+    {
+        if (barriers[j].Type == D3D12_RESOURCE_BARRIER_TYPE_TRANSITION
+                && barriers[j].u.Transition.pResource == barriers[i].u.Transition.pResource
+                && sub_resource_idx % sub_resource_count == barriers[j].u.Transition.Subresource % sub_resource_count)
+        {
+            /* Second barrier must be for a different plane. */
+            if (barriers[j].u.Transition.Subresource == sub_resource_idx)
+                return 0;
+
+            /* Validate the second barrier and check if the combination of two states is supported. */
+            if (!is_valid_resource_state(barriers[j].u.Transition.StateBefore)
+                    || !is_ds_multiplanar_resolvable(barriers[i].u.Transition.StateBefore, barriers[j].u.Transition.StateBefore)
+                    || !is_valid_resource_state(barriers[j].u.Transition.StateAfter)
+                    || !is_ds_multiplanar_resolvable(barriers[i].u.Transition.StateAfter, barriers[j].u.Transition.StateAfter)
+                    || barriers[j].u.Transition.Subresource >= sub_resource_count * 2u)
+                return 0;
+
+            return j;
+        }
+    }
+    return 0;
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_ResourceBarrier(ID3D12GraphicsCommandList1 *iface,
+        UINT barrier_count, const D3D12_RESOURCE_BARRIER *barriers)
+{
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+    bool have_aliasing_barriers = false, have_split_barriers = false;
+    const struct vkd3d_vk_device_procs *vk_procs;
+    const struct vkd3d_vulkan_info *vk_info;
+    bool *multiplanar_handled = NULL;
+    unsigned int i;
+
+    TRACE("iface %p, barrier_count %u, barriers %p.\n", iface, barrier_count, barriers);
+
+    vk_procs = &list->device->vk_procs;
+    vk_info = &list->device->vk_info;
+
+    d3d12_command_list_end_current_render_pass(list);
+
+    for (i = 0; i < barrier_count; ++i)
+    {
+        unsigned int sub_resource_idx = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
+        VkPipelineStageFlags src_stage_mask = 0, dst_stage_mask = 0;
+        VkAccessFlags src_access_mask = 0, dst_access_mask = 0;
+        const D3D12_RESOURCE_BARRIER *current = &barriers[i];
+        VkImageLayout layout_before, layout_after;
+        struct d3d12_resource *resource;
+
+        have_split_barriers = have_split_barriers
+                || (current->Flags & D3D12_RESOURCE_BARRIER_FLAG_BEGIN_ONLY)
+                || (current->Flags & D3D12_RESOURCE_BARRIER_FLAG_END_ONLY);
+
+        if (current->Flags & D3D12_RESOURCE_BARRIER_FLAG_BEGIN_ONLY)
+            continue;
+
+        switch (current->Type)
+        {
+            case D3D12_RESOURCE_BARRIER_TYPE_TRANSITION:
+            {
+                unsigned int state_before, state_after, stencil_state_before = 0, stencil_state_after = 0;
+                const D3D12_RESOURCE_TRANSITION_BARRIER *transition = &current->u.Transition;
+
+                if (!is_valid_resource_state(transition->StateBefore))
+                {
+                    d3d12_command_list_mark_as_invalid(list,
+                            "Invalid StateBefore %#x (barrier %u).", transition->StateBefore, i);
+                    continue;
+                }
+                if (!is_valid_resource_state(transition->StateAfter))
+                {
+                    d3d12_command_list_mark_as_invalid(list,
+                            "Invalid StateAfter %#x (barrier %u).", transition->StateAfter, i);
+                    continue;
+                }
+
+                if (!(resource = unsafe_impl_from_ID3D12Resource(transition->pResource)))
+                {
+                    d3d12_command_list_mark_as_invalid(list, "A resource pointer is NULL.");
+                    continue;
+                }
+
+                if (multiplanar_handled && multiplanar_handled[i])
+                    continue;
+
+                state_before = transition->StateBefore;
+                state_after = transition->StateAfter;
+
+                sub_resource_idx = transition->Subresource;
+
+                if (sub_resource_idx != D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES
+                        && (resource->desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL))
+                {
+                    unsigned int sub_resource_count = d3d12_resource_desc_get_sub_resource_count(&resource->desc);
+                    unsigned int j = d3d12_find_ds_multiplanar_transition(barriers, i, barrier_count, sub_resource_count);
+                    if (j && (multiplanar_handled || (multiplanar_handled = vkd3d_calloc(barrier_count, sizeof(*multiplanar_handled)))))
+                    {
+                        multiplanar_handled[j] = true;
+                        if (sub_resource_idx >= sub_resource_count)
+                        {
+                            sub_resource_idx -= sub_resource_count;
+                            /* The stencil barrier is at i, depth at j. */
+                            state_before = barriers[j].u.Transition.StateBefore;
+                            state_after = barriers[j].u.Transition.StateAfter;
+                            stencil_state_before = transition->StateBefore;
+                            stencil_state_after = transition->StateAfter;
+                        }
+                        else
+                        {
+                            /* Depth at i, stencil at j. */
+                            stencil_state_before = barriers[j].u.Transition.StateBefore;
+                            stencil_state_after = barriers[j].u.Transition.StateAfter;
+                        }
+                    }
+                    else if (sub_resource_idx >= sub_resource_count)
+                    {
+                        FIXME_ONCE("Unhandled sub-resource idx %u.\n", sub_resource_idx);
+                        continue;
+                    }
+                }
+
+                if (!vk_barrier_parameters_from_d3d12_resource_state(state_before, stencil_state_before,
+                        resource, list->vk_queue_flags, vk_info, &src_access_mask, &src_stage_mask, &layout_before))
+                {
+                    FIXME("Unhandled state %#x.\n", state_before);
+                    continue;
+                }
+                if (!vk_barrier_parameters_from_d3d12_resource_state(state_after, stencil_state_after,
+                        resource, list->vk_queue_flags, vk_info, &dst_access_mask, &dst_stage_mask, &layout_after))
+                {
+                    FIXME("Unhandled state %#x.\n", state_after);
+                    continue;
+                }
+
+                TRACE("Transition barrier (resource %p, subresource %#x, before %#x, after %#x).\n",
+                        resource, transition->Subresource, transition->StateBefore, transition->StateAfter);
+                break;
+            }
+
+            case D3D12_RESOURCE_BARRIER_TYPE_UAV:
+            {
+                const D3D12_RESOURCE_UAV_BARRIER *uav = &current->u.UAV;
+                VkPipelineStageFlags stage_mask;
+                VkImageLayout image_layout;
+                VkAccessFlags access_mask;
+
+                resource = unsafe_impl_from_ID3D12Resource(uav->pResource);
+                vk_barrier_parameters_from_d3d12_resource_state(D3D12_RESOURCE_STATE_UNORDERED_ACCESS, 0,
+                        resource, list->vk_queue_flags, vk_info, &access_mask, &stage_mask, &image_layout);
+                src_access_mask = dst_access_mask = access_mask;
+                src_stage_mask = dst_stage_mask = stage_mask;
+                layout_before = layout_after = image_layout;
+
+                TRACE("UAV barrier (resource %p).\n", resource);
+                break;
+            }
+
+            case D3D12_RESOURCE_BARRIER_TYPE_ALIASING:
+                have_aliasing_barriers = true;
+                continue;
+            default:
+                WARN("Invalid barrier type %#x.\n", current->Type);
+                continue;
+        }
+
+        if (resource)
+            d3d12_command_list_track_resource_usage(list, resource);
+
+        if (!resource)
+        {
+            VkMemoryBarrier vk_barrier;
+
+            vk_barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
+            vk_barrier.pNext = NULL;
+            vk_barrier.srcAccessMask = src_access_mask;
+            vk_barrier.dstAccessMask = dst_access_mask;
+
+            VK_CALL(vkCmdPipelineBarrier(list->vk_command_buffer, src_stage_mask, dst_stage_mask, 0,
+                    1, &vk_barrier, 0, NULL, 0, NULL));
+        }
+        else if (d3d12_resource_is_buffer(resource))
+        {
+            VkBufferMemoryBarrier vk_barrier;
+
+            vk_barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
+            vk_barrier.pNext = NULL;
+            vk_barrier.srcAccessMask = src_access_mask;
+            vk_barrier.dstAccessMask = dst_access_mask;
+            vk_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+            vk_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+            vk_barrier.buffer = resource->u.vk_buffer;
+            vk_barrier.offset = 0;
+            vk_barrier.size = VK_WHOLE_SIZE;
+
+            VK_CALL(vkCmdPipelineBarrier(list->vk_command_buffer, src_stage_mask, dst_stage_mask, 0,
+                    0, NULL, 1, &vk_barrier, 0, NULL));
+        }
+        else
+        {
+            const struct vkd3d_format *format;
+            VkImageMemoryBarrier vk_barrier;
+
+            if (!(format = vkd3d_format_from_d3d12_resource_desc(list->device, &resource->desc, 0)))
+            {
+                ERR("Resource %p has invalid format %#x.\n", resource, resource->desc.Format);
+                continue;
+            }
+
+            vk_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+            vk_barrier.pNext = NULL;
+            vk_barrier.srcAccessMask = src_access_mask;
+            vk_barrier.dstAccessMask = dst_access_mask;
+            vk_barrier.oldLayout = layout_before;
+            vk_barrier.newLayout = layout_after;
+            vk_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+            vk_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+            vk_barrier.image = resource->u.vk_image;
+
+            vk_barrier.subresourceRange.aspectMask = format->vk_aspect_mask;
+            if (sub_resource_idx == D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES)
+            {
+                vk_barrier.subresourceRange.baseMipLevel = 0;
+                vk_barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
+                vk_barrier.subresourceRange.baseArrayLayer = 0;
+                vk_barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
+            }
+            else
+            {
+                /* FIXME: Some formats in D3D12 are planar. Each plane is a separate sub-resource. */
+                if (sub_resource_idx >= d3d12_resource_desc_get_sub_resource_count(&resource->desc))
+                {
+                    FIXME_ONCE("Unhandled sub-resource idx %u.\n", sub_resource_idx);
+                    continue;
+                }
+
+                vk_barrier.subresourceRange.baseMipLevel = sub_resource_idx % resource->desc.MipLevels;
+                vk_barrier.subresourceRange.levelCount = 1;
+                vk_barrier.subresourceRange.baseArrayLayer = sub_resource_idx / resource->desc.MipLevels;
+                vk_barrier.subresourceRange.layerCount = 1;
+            }
+
+            VK_CALL(vkCmdPipelineBarrier(list->vk_command_buffer, src_stage_mask, dst_stage_mask, 0,
+                    0, NULL, 0, NULL, 1, &vk_barrier));
+        }
+    }
+
+    vkd3d_free(multiplanar_handled);
+
+    if (have_aliasing_barriers)
+        FIXME_ONCE("Aliasing barriers not implemented yet.\n");
+
+    /* Vulkan doesn't support split barriers. */
+    if (have_split_barriers)
+        WARN("Issuing split barrier(s) on D3D12_RESOURCE_BARRIER_FLAG_END_ONLY.\n");
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_ExecuteBundle(ID3D12GraphicsCommandList1 *iface,
+        ID3D12GraphicsCommandList *command_list)
+{
+    FIXME("iface %p, command_list %p stub!\n", iface, command_list);
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_SetDescriptorHeaps(ID3D12GraphicsCommandList1 *iface,
+        UINT heap_count, ID3D12DescriptorHeap *const *heaps)
+{
+    TRACE("iface %p, heap_count %u, heaps %p.\n", iface, heap_count, heaps);
+
+    /* Our current implementation does not need this method.
+     *
+     * It could be used to validate descriptor tables but we do not have an
+     * equivalent of the D3D12 Debug Layer. */
+}
+
+static void d3d12_command_list_set_root_signature(struct d3d12_command_list *list,
+        VkPipelineBindPoint bind_point, const struct d3d12_root_signature *root_signature)
+{
+    struct vkd3d_pipeline_bindings *bindings = &list->pipeline_bindings[bind_point];
+
+    if (bindings->root_signature == root_signature)
+        return;
+
+    bindings->root_signature = root_signature;
+
+    d3d12_command_list_invalidate_root_parameters(list, bind_point);
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_SetComputeRootSignature(ID3D12GraphicsCommandList1 *iface,
+        ID3D12RootSignature *root_signature)
+{
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+
+    TRACE("iface %p, root_signature %p.\n", iface, root_signature);
+
+    d3d12_command_list_set_root_signature(list, VK_PIPELINE_BIND_POINT_COMPUTE,
+            unsafe_impl_from_ID3D12RootSignature(root_signature));
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_SetGraphicsRootSignature(ID3D12GraphicsCommandList1 *iface,
+        ID3D12RootSignature *root_signature)
+{
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+
+    TRACE("iface %p, root_signature %p.\n", iface, root_signature);
+
+    d3d12_command_list_set_root_signature(list, VK_PIPELINE_BIND_POINT_GRAPHICS,
+            unsafe_impl_from_ID3D12RootSignature(root_signature));
+}
+
+static void d3d12_command_list_set_descriptor_table(struct d3d12_command_list *list,
+        VkPipelineBindPoint bind_point, unsigned int index, D3D12_GPU_DESCRIPTOR_HANDLE base_descriptor)
+{
+    struct vkd3d_pipeline_bindings *bindings = &list->pipeline_bindings[bind_point];
+    const struct d3d12_root_signature *root_signature = bindings->root_signature;
+
+    assert(root_signature_get_descriptor_table(root_signature, index));
+
+    assert(index < ARRAY_SIZE(bindings->descriptor_tables));
+    bindings->descriptor_tables[index] = base_descriptor;
+    bindings->descriptor_table_dirty_mask |= (uint64_t)1 << index;
+    bindings->descriptor_table_active_mask |= (uint64_t)1 << index;
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_SetComputeRootDescriptorTable(ID3D12GraphicsCommandList1 *iface,
+        UINT root_parameter_index, D3D12_GPU_DESCRIPTOR_HANDLE base_descriptor)
+{
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+
+    TRACE("iface %p, root_parameter_index %u, base_descriptor %#"PRIx64".\n",
+            iface, root_parameter_index, base_descriptor.ptr);
+
+    d3d12_command_list_set_descriptor_table(list, VK_PIPELINE_BIND_POINT_COMPUTE,
+            root_parameter_index, base_descriptor);
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_SetGraphicsRootDescriptorTable(ID3D12GraphicsCommandList1 *iface,
+        UINT root_parameter_index, D3D12_GPU_DESCRIPTOR_HANDLE base_descriptor)
+{
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+
+    TRACE("iface %p, root_parameter_index %u, base_descriptor %#"PRIx64".\n",
+            iface, root_parameter_index, base_descriptor.ptr);
+
+    d3d12_command_list_set_descriptor_table(list, VK_PIPELINE_BIND_POINT_GRAPHICS,
+            root_parameter_index, base_descriptor);
+}
+
+static void d3d12_command_list_set_root_constants(struct d3d12_command_list *list,
+        VkPipelineBindPoint bind_point, unsigned int index, unsigned int offset,
+        unsigned int count, const void *data)
+{
+    const struct d3d12_root_signature *root_signature = list->pipeline_bindings[bind_point].root_signature;
+    const struct vkd3d_vk_device_procs *vk_procs = &list->device->vk_procs;
+    const struct d3d12_root_constant *c;
+
+    c = root_signature_get_32bit_constants(root_signature, index);
+    VK_CALL(vkCmdPushConstants(list->vk_command_buffer, root_signature->vk_pipeline_layout,
+            c->stage_flags, c->offset + offset * sizeof(uint32_t), count * sizeof(uint32_t), data));
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_SetComputeRoot32BitConstant(ID3D12GraphicsCommandList1 *iface,
+        UINT root_parameter_index, UINT data, UINT dst_offset)
+{
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+
+    TRACE("iface %p, root_parameter_index %u, data 0x%08x, dst_offset %u.\n",
+            iface, root_parameter_index, data, dst_offset);
+
+    d3d12_command_list_set_root_constants(list, VK_PIPELINE_BIND_POINT_COMPUTE,
+            root_parameter_index, dst_offset, 1, &data);
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_SetGraphicsRoot32BitConstant(ID3D12GraphicsCommandList1 *iface,
+        UINT root_parameter_index, UINT data, UINT dst_offset)
+{
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+
+    TRACE("iface %p, root_parameter_index %u, data 0x%08x, dst_offset %u.\n",
+            iface, root_parameter_index, data, dst_offset);
+
+    d3d12_command_list_set_root_constants(list, VK_PIPELINE_BIND_POINT_GRAPHICS,
+            root_parameter_index, dst_offset, 1, &data);
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_SetComputeRoot32BitConstants(ID3D12GraphicsCommandList1 *iface,
+        UINT root_parameter_index, UINT constant_count, const void *data, UINT dst_offset)
+{
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+
+    TRACE("iface %p, root_parameter_index %u, constant_count %u, data %p, dst_offset %u.\n",
+            iface, root_parameter_index, constant_count, data, dst_offset);
+
+    d3d12_command_list_set_root_constants(list, VK_PIPELINE_BIND_POINT_COMPUTE,
+            root_parameter_index, dst_offset, constant_count, data);
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_SetGraphicsRoot32BitConstants(ID3D12GraphicsCommandList1 *iface,
+        UINT root_parameter_index, UINT constant_count, const void *data, UINT dst_offset)
+{
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+
+    TRACE("iface %p, root_parameter_index %u, constant_count %u, data %p, dst_offset %u.\n",
+            iface, root_parameter_index, constant_count, data, dst_offset);
+
+    d3d12_command_list_set_root_constants(list, VK_PIPELINE_BIND_POINT_GRAPHICS,
+            root_parameter_index, dst_offset, constant_count, data);
+}
+
+static void d3d12_command_list_set_root_cbv(struct d3d12_command_list *list,
+        VkPipelineBindPoint bind_point, unsigned int index, D3D12_GPU_VIRTUAL_ADDRESS gpu_address)
+{
+    struct vkd3d_pipeline_bindings *bindings = &list->pipeline_bindings[bind_point];
+    const struct d3d12_root_signature *root_signature = bindings->root_signature;
+    const struct vkd3d_vk_device_procs *vk_procs = &list->device->vk_procs;
+    const struct vkd3d_vulkan_info *vk_info = &list->device->vk_info;
+    const struct d3d12_root_parameter *root_parameter;
+    struct VkWriteDescriptorSet descriptor_write;
+    struct VkDescriptorBufferInfo buffer_info;
+    struct d3d12_resource *resource;
+
+    root_parameter = root_signature_get_root_descriptor(root_signature, index);
+    assert(root_parameter->parameter_type == D3D12_ROOT_PARAMETER_TYPE_CBV);
+
+    resource = vkd3d_gpu_va_allocator_dereference(&list->device->gpu_va_allocator, gpu_address);
+    buffer_info.buffer = resource->u.vk_buffer;
+    buffer_info.offset = gpu_address - resource->gpu_address;
+    buffer_info.range = resource->desc.Width - buffer_info.offset;
+    buffer_info.range = min(buffer_info.range, vk_info->device_limits.maxUniformBufferRange);
+
+    if (vk_info->KHR_push_descriptor)
+    {
+        vk_write_descriptor_set_from_root_descriptor(&descriptor_write,
+                root_parameter, VK_NULL_HANDLE, NULL, &buffer_info);
+        VK_CALL(vkCmdPushDescriptorSetKHR(list->vk_command_buffer, bind_point,
+                root_signature->vk_pipeline_layout, 0, 1, &descriptor_write));
+    }
+    else
+    {
+        d3d12_command_list_prepare_descriptors(list, bind_point);
+        vk_write_descriptor_set_from_root_descriptor(&descriptor_write,
+                root_parameter, bindings->descriptor_set, NULL, &buffer_info);
+        VK_CALL(vkUpdateDescriptorSets(list->device->vk_device, 1, &descriptor_write, 0, NULL));
+
+        assert(index < ARRAY_SIZE(bindings->push_descriptors));
+        bindings->push_descriptors[index].u.cbv.vk_buffer = buffer_info.buffer;
+        bindings->push_descriptors[index].u.cbv.offset = buffer_info.offset;
+        bindings->push_descriptor_dirty_mask |= 1u << index;
+        bindings->push_descriptor_active_mask |= 1u << index;
+    }
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_SetComputeRootConstantBufferView(
+        ID3D12GraphicsCommandList1 *iface, UINT root_parameter_index, D3D12_GPU_VIRTUAL_ADDRESS address)
+{
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+
+    TRACE("iface %p, root_parameter_index %u, address %#"PRIx64".\n",
+            iface, root_parameter_index, address);
+
+    d3d12_command_list_set_root_cbv(list, VK_PIPELINE_BIND_POINT_COMPUTE, root_parameter_index, address);
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_SetGraphicsRootConstantBufferView(
+        ID3D12GraphicsCommandList1 *iface, UINT root_parameter_index, D3D12_GPU_VIRTUAL_ADDRESS address)
+{
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+
+    TRACE("iface %p, root_parameter_index %u, address %#"PRIx64".\n",
+            iface, root_parameter_index, address);
+
+    d3d12_command_list_set_root_cbv(list, VK_PIPELINE_BIND_POINT_GRAPHICS, root_parameter_index, address);
+}
+
+static void d3d12_command_list_set_root_descriptor(struct d3d12_command_list *list,
+        VkPipelineBindPoint bind_point, unsigned int index, D3D12_GPU_VIRTUAL_ADDRESS gpu_address)
+{
+    struct vkd3d_pipeline_bindings *bindings = &list->pipeline_bindings[bind_point];
+    const struct d3d12_root_signature *root_signature = bindings->root_signature;
+    const struct vkd3d_vk_device_procs *vk_procs = &list->device->vk_procs;
+    const struct vkd3d_vulkan_info *vk_info = &list->device->vk_info;
+    const struct d3d12_root_parameter *root_parameter;
+    struct VkWriteDescriptorSet descriptor_write;
+    VkDevice vk_device = list->device->vk_device;
+    VkBufferView vk_buffer_view;
+
+    root_parameter = root_signature_get_root_descriptor(root_signature, index);
+    assert(root_parameter->parameter_type != D3D12_ROOT_PARAMETER_TYPE_CBV);
+
+    /* FIXME: Re-use buffer views. */
+    if (!vkd3d_create_raw_buffer_view(list->device, gpu_address, &vk_buffer_view))
+    {
+        ERR("Failed to create buffer view.\n");
+        return;
+    }
+
+    if (!(d3d12_command_allocator_add_buffer_view(list->allocator, vk_buffer_view)))
+    {
+        ERR("Failed to add buffer view.\n");
+        VK_CALL(vkDestroyBufferView(vk_device, vk_buffer_view, NULL));
+        return;
+    }
+
+    if (vk_info->KHR_push_descriptor)
+    {
+        vk_write_descriptor_set_from_root_descriptor(&descriptor_write,
+                root_parameter, VK_NULL_HANDLE, &vk_buffer_view, NULL);
+        VK_CALL(vkCmdPushDescriptorSetKHR(list->vk_command_buffer, bind_point,
+                root_signature->vk_pipeline_layout, 0, 1, &descriptor_write));
+    }
+    else
+    {
+        d3d12_command_list_prepare_descriptors(list, bind_point);
+        vk_write_descriptor_set_from_root_descriptor(&descriptor_write,
+                root_parameter, bindings->descriptor_set, &vk_buffer_view,  NULL);
+        VK_CALL(vkUpdateDescriptorSets(list->device->vk_device, 1, &descriptor_write, 0, NULL));
+
+        assert(index < ARRAY_SIZE(bindings->push_descriptors));
+        bindings->push_descriptors[index].u.vk_buffer_view = vk_buffer_view;
+        bindings->push_descriptor_dirty_mask |= 1u << index;
+        bindings->push_descriptor_active_mask |= 1u << index;
+    }
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_SetComputeRootShaderResourceView(
+        ID3D12GraphicsCommandList1 *iface, UINT root_parameter_index, D3D12_GPU_VIRTUAL_ADDRESS address)
+{
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+
+    TRACE("iface %p, root_parameter_index %u, address %#"PRIx64".\n",
+            iface, root_parameter_index, address);
+
+    d3d12_command_list_set_root_descriptor(list, VK_PIPELINE_BIND_POINT_COMPUTE,
+            root_parameter_index, address);
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_SetGraphicsRootShaderResourceView(
+        ID3D12GraphicsCommandList1 *iface, UINT root_parameter_index, D3D12_GPU_VIRTUAL_ADDRESS address)
+{
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+
+    TRACE("iface %p, root_parameter_index %u, address %#"PRIx64".\n",
+            iface, root_parameter_index, address);
+
+    d3d12_command_list_set_root_descriptor(list, VK_PIPELINE_BIND_POINT_GRAPHICS,
+            root_parameter_index, address);
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_SetComputeRootUnorderedAccessView(
+        ID3D12GraphicsCommandList1 *iface, UINT root_parameter_index, D3D12_GPU_VIRTUAL_ADDRESS address)
+{
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+
+    TRACE("iface %p, root_parameter_index %u, address %#"PRIx64".\n",
+            iface, root_parameter_index, address);
+
+    d3d12_command_list_set_root_descriptor(list, VK_PIPELINE_BIND_POINT_COMPUTE,
+            root_parameter_index, address);
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_SetGraphicsRootUnorderedAccessView(
+        ID3D12GraphicsCommandList1 *iface, UINT root_parameter_index, D3D12_GPU_VIRTUAL_ADDRESS address)
+{
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+
+    TRACE("iface %p, root_parameter_index %u, address %#"PRIx64".\n",
+            iface, root_parameter_index, address);
+
+    d3d12_command_list_set_root_descriptor(list, VK_PIPELINE_BIND_POINT_GRAPHICS,
+            root_parameter_index, address);
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_IASetIndexBuffer(ID3D12GraphicsCommandList1 *iface,
+        const D3D12_INDEX_BUFFER_VIEW *view)
+{
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+    const struct vkd3d_vk_device_procs *vk_procs;
+    struct d3d12_resource *resource;
+    enum VkIndexType index_type;
+
+    TRACE("iface %p, view %p.\n", iface, view);
+
+    if (!view)
+    {
+        WARN("Ignoring NULL index buffer view.\n");
+        return;
+    }
+
+    vk_procs = &list->device->vk_procs;
+
+    switch (view->Format)
+    {
+        case DXGI_FORMAT_R16_UINT:
+            index_type = VK_INDEX_TYPE_UINT16;
+            break;
+        case DXGI_FORMAT_R32_UINT:
+            index_type = VK_INDEX_TYPE_UINT32;
+            break;
+        default:
+            WARN("Invalid index format %#x.\n", view->Format);
+            return;
+    }
+
+    list->index_buffer_format = view->Format;
+
+    resource = vkd3d_gpu_va_allocator_dereference(&list->device->gpu_va_allocator, view->BufferLocation);
+    VK_CALL(vkCmdBindIndexBuffer(list->vk_command_buffer, resource->u.vk_buffer,
+            view->BufferLocation - resource->gpu_address, index_type));
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_IASetVertexBuffers(ID3D12GraphicsCommandList1 *iface,
+        UINT start_slot, UINT view_count, const D3D12_VERTEX_BUFFER_VIEW *views)
+{
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+    const struct vkd3d_null_resources *null_resources;
+    struct vkd3d_gpu_va_allocator *gpu_va_allocator;
+    VkDeviceSize offsets[ARRAY_SIZE(list->strides)];
+    const struct vkd3d_vk_device_procs *vk_procs;
+    VkBuffer buffers[ARRAY_SIZE(list->strides)];
+    struct d3d12_resource *resource;
+    bool invalidate = false;
+    unsigned int i, stride;
+
+    TRACE("iface %p, start_slot %u, view_count %u, views %p.\n", iface, start_slot, view_count, views);
+
+    vk_procs = &list->device->vk_procs;
+    null_resources = &list->device->null_resources;
+    gpu_va_allocator = &list->device->gpu_va_allocator;
+
+    if (start_slot >= ARRAY_SIZE(list->strides) || view_count > ARRAY_SIZE(list->strides) - start_slot)
+    {
+        WARN("Invalid start slot %u / view count %u.\n", start_slot, view_count);
+        return;
+    }
+
+    for (i = 0; i < view_count; ++i)
+    {
+        if (views[i].BufferLocation)
+        {
+            resource = vkd3d_gpu_va_allocator_dereference(gpu_va_allocator, views[i].BufferLocation);
+            buffers[i] = resource->u.vk_buffer;
+            offsets[i] = views[i].BufferLocation - resource->gpu_address;
+            stride = views[i].StrideInBytes;
+        }
+        else
+        {
+            buffers[i] = null_resources->vk_buffer;
+            offsets[i] = 0;
+            stride = 0;
+        }
+
+        invalidate |= list->strides[start_slot + i] != stride;
+        list->strides[start_slot + i] = stride;
+    }
+
+    if (view_count)
+        VK_CALL(vkCmdBindVertexBuffers(list->vk_command_buffer, start_slot, view_count, buffers, offsets));
+
+    if (invalidate)
+        d3d12_command_list_invalidate_current_pipeline(list);
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_SOSetTargets(ID3D12GraphicsCommandList1 *iface,
+        UINT start_slot, UINT view_count, const D3D12_STREAM_OUTPUT_BUFFER_VIEW *views)
+{
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+    VkDeviceSize offsets[ARRAY_SIZE(list->so_counter_buffers)];
+    VkDeviceSize sizes[ARRAY_SIZE(list->so_counter_buffers)];
+    VkBuffer buffers[ARRAY_SIZE(list->so_counter_buffers)];
+    struct vkd3d_gpu_va_allocator *gpu_va_allocator;
+    const struct vkd3d_vk_device_procs *vk_procs;
+    struct d3d12_resource *resource;
+    unsigned int i, first, count;
+
+    TRACE("iface %p, start_slot %u, view_count %u, views %p.\n", iface, start_slot, view_count, views);
+
+    d3d12_command_list_end_current_render_pass(list);
+
+    if (!list->device->vk_info.EXT_transform_feedback)
+    {
+        FIXME("Transform feedback is not supported by Vulkan implementation.\n");
+        return;
+    }
+
+    if (start_slot >= ARRAY_SIZE(buffers) || view_count > ARRAY_SIZE(buffers) - start_slot)
+    {
+        WARN("Invalid start slot %u / view count %u.\n", start_slot, view_count);
+        return;
+    }
+
+    vk_procs = &list->device->vk_procs;
+    gpu_va_allocator = &list->device->gpu_va_allocator;
+
+    count = 0;
+    first = start_slot;
+    for (i = 0; i < view_count; ++i)
+    {
+        if (views[i].BufferLocation && views[i].SizeInBytes)
+        {
+            resource = vkd3d_gpu_va_allocator_dereference(gpu_va_allocator, views[i].BufferLocation);
+            buffers[count] = resource->u.vk_buffer;
+            offsets[count] = views[i].BufferLocation - resource->gpu_address;
+            sizes[count] = views[i].SizeInBytes;
+
+            resource = vkd3d_gpu_va_allocator_dereference(gpu_va_allocator, views[i].BufferFilledSizeLocation);
+            list->so_counter_buffers[start_slot + i] = resource->u.vk_buffer;
+            list->so_counter_buffer_offsets[start_slot + i] = views[i].BufferFilledSizeLocation - resource->gpu_address;
+            ++count;
+        }
+        else
+        {
+            if (count)
+                VK_CALL(vkCmdBindTransformFeedbackBuffersEXT(list->vk_command_buffer, first, count, buffers, offsets, sizes));
+            count = 0;
+            first = start_slot + i + 1;
+
+            list->so_counter_buffers[start_slot + i] = VK_NULL_HANDLE;
+            list->so_counter_buffer_offsets[start_slot + i] = 0;
+
+            WARN("Trying to unbind transform feedback buffer %u. Ignoring.\n", start_slot + i);
+        }
+    }
+
+    if (count)
+        VK_CALL(vkCmdBindTransformFeedbackBuffersEXT(list->vk_command_buffer, first, count, buffers, offsets, sizes));
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_OMSetRenderTargets(ID3D12GraphicsCommandList1 *iface,
+        UINT render_target_descriptor_count, const D3D12_CPU_DESCRIPTOR_HANDLE *render_target_descriptors,
+        BOOL single_descriptor_handle, const D3D12_CPU_DESCRIPTOR_HANDLE *depth_stencil_descriptor)
+{
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+    const struct d3d12_rtv_desc *rtv_desc;
+    const struct d3d12_dsv_desc *dsv_desc;
+    VkFormat prev_dsv_format;
+    struct vkd3d_view *view;
+    unsigned int i;
+
+    TRACE("iface %p, render_target_descriptor_count %u, render_target_descriptors %p, "
+            "single_descriptor_handle %#x, depth_stencil_descriptor %p.\n",
+            iface, render_target_descriptor_count, render_target_descriptors,
+            single_descriptor_handle, depth_stencil_descriptor);
+
+    if (render_target_descriptor_count > ARRAY_SIZE(list->rtvs))
+    {
+        WARN("Descriptor count %u > %zu, ignoring extra descriptors.\n",
+                render_target_descriptor_count, ARRAY_SIZE(list->rtvs));
+        render_target_descriptor_count = ARRAY_SIZE(list->rtvs);
+    }
+
+    list->fb_width = 0;
+    list->fb_height = 0;
+    list->fb_layer_count = 0;
+    for (i = 0; i < render_target_descriptor_count; ++i)
+    {
+        if (single_descriptor_handle)
+        {
+            if ((rtv_desc = d3d12_rtv_desc_from_cpu_handle(*render_target_descriptors)))
+                rtv_desc += i;
+        }
+        else
+        {
+            rtv_desc = d3d12_rtv_desc_from_cpu_handle(render_target_descriptors[i]);
+        }
+
+        if (!rtv_desc || !rtv_desc->resource)
+        {
+            WARN("RTV descriptor %u is not initialized.\n", i);
+            list->rtvs[i] = VK_NULL_HANDLE;
+            continue;
+        }
+
+        d3d12_command_list_track_resource_usage(list, rtv_desc->resource);
+
+        /* In D3D12 CPU descriptors are consumed when a command is recorded. */
+        view = rtv_desc->view;
+        if (!d3d12_command_allocator_add_view(list->allocator, view))
+        {
+            WARN("Failed to add view.\n");
+        }
+
+        list->rtvs[i] = view->u.vk_image_view;
+        list->fb_width = max(list->fb_width, rtv_desc->width);
+        list->fb_height = max(list->fb_height, rtv_desc->height);
+        list->fb_layer_count = max(list->fb_layer_count, rtv_desc->layer_count);
+    }
+
+    prev_dsv_format = list->dsv_format;
+    list->dsv = VK_NULL_HANDLE;
+    list->dsv_format = VK_FORMAT_UNDEFINED;
+    if (depth_stencil_descriptor)
+    {
+        if ((dsv_desc = d3d12_dsv_desc_from_cpu_handle(*depth_stencil_descriptor))
+                && dsv_desc->resource)
+        {
+            d3d12_command_list_track_resource_usage(list, dsv_desc->resource);
+
+            /* In D3D12 CPU descriptors are consumed when a command is recorded. */
+            view = dsv_desc->view;
+            if (!d3d12_command_allocator_add_view(list->allocator, view))
+            {
+                WARN("Failed to add view.\n");
+                list->dsv = VK_NULL_HANDLE;
+            }
+
+            list->dsv = view->u.vk_image_view;
+            list->fb_width = max(list->fb_width, dsv_desc->width);
+            list->fb_height = max(list->fb_height, dsv_desc->height);
+            list->fb_layer_count = max(list->fb_layer_count, dsv_desc->layer_count);
+            list->dsv_format = dsv_desc->format->vk_format;
+        }
+        else
+        {
+            WARN("DSV descriptor is not initialized.\n");
+        }
+    }
+
+    if (prev_dsv_format != list->dsv_format && d3d12_pipeline_state_has_unknown_dsv_format(list->state))
+        d3d12_command_list_invalidate_current_pipeline(list);
+
+    d3d12_command_list_invalidate_current_framebuffer(list);
+    d3d12_command_list_invalidate_current_render_pass(list);
+}
+
+static void d3d12_command_list_clear(struct d3d12_command_list *list,
+        const struct VkAttachmentDescription *attachment_desc,
+        const struct VkAttachmentReference *color_reference, const struct VkAttachmentReference *ds_reference,
+        struct vkd3d_view *view, size_t width, size_t height, unsigned int layer_count,
+        const union VkClearValue *clear_value, unsigned int rect_count, const D3D12_RECT *rects)
+{
+    const struct vkd3d_vk_device_procs *vk_procs = &list->device->vk_procs;
+    struct VkSubpassDescription sub_pass_desc;
+    struct VkRenderPassCreateInfo pass_desc;
+    struct VkRenderPassBeginInfo begin_desc;
+    struct VkFramebufferCreateInfo fb_desc;
+    VkFramebuffer vk_framebuffer;
+    VkRenderPass vk_render_pass;
+    D3D12_RECT full_rect;
+    unsigned int i;
+    VkResult vr;
+
+    d3d12_command_list_end_current_render_pass(list);
+
+    if (!rect_count)
+    {
+        full_rect.top = 0;
+        full_rect.left = 0;
+        full_rect.bottom = height;
+        full_rect.right = width;
+
+        rect_count = 1;
+        rects = &full_rect;
+    }
+
+    sub_pass_desc.flags = 0;
+    sub_pass_desc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
+    sub_pass_desc.inputAttachmentCount = 0;
+    sub_pass_desc.pInputAttachments = NULL;
+    sub_pass_desc.colorAttachmentCount = !!color_reference;
+    sub_pass_desc.pColorAttachments = color_reference;
+    sub_pass_desc.pResolveAttachments = NULL;
+    sub_pass_desc.pDepthStencilAttachment = ds_reference;
+    sub_pass_desc.preserveAttachmentCount = 0;
+    sub_pass_desc.pPreserveAttachments = NULL;
+
+    pass_desc.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
+    pass_desc.pNext = NULL;
+    pass_desc.flags = 0;
+    pass_desc.attachmentCount = 1;
+    pass_desc.pAttachments = attachment_desc;
+    pass_desc.subpassCount = 1;
+    pass_desc.pSubpasses = &sub_pass_desc;
+    pass_desc.dependencyCount = 0;
+    pass_desc.pDependencies = NULL;
+    if ((vr = VK_CALL(vkCreateRenderPass(list->device->vk_device, &pass_desc, NULL, &vk_render_pass))) < 0)
+    {
+        WARN("Failed to create Vulkan render pass, vr %d.\n", vr);
+        return;
+    }
+
+    if (!d3d12_command_allocator_add_render_pass(list->allocator, vk_render_pass))
+    {
+        WARN("Failed to add render pass.\n");
+        VK_CALL(vkDestroyRenderPass(list->device->vk_device, vk_render_pass, NULL));
+        return;
+    }
+
+    if (!d3d12_command_allocator_add_view(list->allocator, view))
+    {
+        WARN("Failed to add view.\n");
+    }
+
+    fb_desc.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
+    fb_desc.pNext = NULL;
+    fb_desc.flags = 0;
+    fb_desc.renderPass = vk_render_pass;
+    fb_desc.attachmentCount = 1;
+    fb_desc.pAttachments = &view->u.vk_image_view;
+    fb_desc.width = width;
+    fb_desc.height = height;
+    fb_desc.layers = layer_count;
+    if ((vr = VK_CALL(vkCreateFramebuffer(list->device->vk_device, &fb_desc, NULL, &vk_framebuffer))) < 0)
+    {
+        WARN("Failed to create Vulkan framebuffer, vr %d.\n", vr);
+        return;
+    }
+
+    if (!d3d12_command_allocator_add_framebuffer(list->allocator, vk_framebuffer))
+    {
+        WARN("Failed to add framebuffer.\n");
+        VK_CALL(vkDestroyFramebuffer(list->device->vk_device, vk_framebuffer, NULL));
+        return;
+    }
+
+    begin_desc.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
+    begin_desc.pNext = NULL;
+    begin_desc.renderPass = vk_render_pass;
+    begin_desc.framebuffer = vk_framebuffer;
+    begin_desc.clearValueCount = 1;
+    begin_desc.pClearValues = clear_value;
+
+    for (i = 0; i < rect_count; ++i)
+    {
+        begin_desc.renderArea.offset.x = rects[i].left;
+        begin_desc.renderArea.offset.y = rects[i].top;
+        begin_desc.renderArea.extent.width = rects[i].right - rects[i].left;
+        begin_desc.renderArea.extent.height = rects[i].bottom - rects[i].top;
+        VK_CALL(vkCmdBeginRenderPass(list->vk_command_buffer, &begin_desc, VK_SUBPASS_CONTENTS_INLINE));
+        VK_CALL(vkCmdEndRenderPass(list->vk_command_buffer));
+    }
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_ClearDepthStencilView(ID3D12GraphicsCommandList1 *iface,
+        D3D12_CPU_DESCRIPTOR_HANDLE dsv, D3D12_CLEAR_FLAGS flags, float depth, UINT8 stencil,
+        UINT rect_count, const D3D12_RECT *rects)
+{
+    const union VkClearValue clear_value = {.depthStencil = {depth, stencil}};
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+    const struct d3d12_dsv_desc *dsv_desc = d3d12_dsv_desc_from_cpu_handle(dsv);
+    struct VkAttachmentDescription attachment_desc;
+    struct VkAttachmentReference ds_reference;
+
+    TRACE("iface %p, dsv %#lx, flags %#x, depth %.8e, stencil 0x%02x, rect_count %u, rects %p.\n",
+            iface, dsv.ptr, flags, depth, stencil, rect_count, rects);
+
+    d3d12_command_list_track_resource_usage(list, dsv_desc->resource);
+
+    attachment_desc.flags = 0;
+    attachment_desc.format = dsv_desc->format->vk_format;
+    attachment_desc.samples = dsv_desc->sample_count;
+    if (flags & D3D12_CLEAR_FLAG_DEPTH)
+    {
+        attachment_desc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+        attachment_desc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
+    }
+    else
+    {
+        attachment_desc.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+        attachment_desc.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+    }
+    if (flags & D3D12_CLEAR_FLAG_STENCIL)
+    {
+        attachment_desc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+        attachment_desc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE;
+    }
+    else
+    {
+        attachment_desc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+        attachment_desc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+    }
+    attachment_desc.initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+    attachment_desc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+
+    ds_reference.attachment = 0;
+    ds_reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+
+    d3d12_command_list_clear(list, &attachment_desc, NULL, &ds_reference,
+            dsv_desc->view, dsv_desc->width, dsv_desc->height, dsv_desc->layer_count,
+            &clear_value, rect_count, rects);
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_ClearRenderTargetView(ID3D12GraphicsCommandList1 *iface,
+        D3D12_CPU_DESCRIPTOR_HANDLE rtv, const FLOAT color[4], UINT rect_count, const D3D12_RECT *rects)
+{
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+    const struct d3d12_rtv_desc *rtv_desc = d3d12_rtv_desc_from_cpu_handle(rtv);
+    struct VkAttachmentDescription attachment_desc;
+    struct VkAttachmentReference color_reference;
+    VkClearValue clear_value;
+
+    TRACE("iface %p, rtv %#lx, color %p, rect_count %u, rects %p.\n",
+            iface, rtv.ptr, color, rect_count, rects);
+
+    d3d12_command_list_track_resource_usage(list, rtv_desc->resource);
+
+    attachment_desc.flags = 0;
+    attachment_desc.format = rtv_desc->format->vk_format;
+    attachment_desc.samples = rtv_desc->sample_count;
+    attachment_desc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+    attachment_desc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
+    attachment_desc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+    attachment_desc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+    attachment_desc.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+    attachment_desc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+
+    color_reference.attachment = 0;
+    color_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+
+    if (rtv_desc->format->type == VKD3D_FORMAT_TYPE_UINT)
+    {
+        clear_value.color.uint32[0] = max(0, color[0]);
+        clear_value.color.uint32[1] = max(0, color[1]);
+        clear_value.color.uint32[2] = max(0, color[2]);
+        clear_value.color.uint32[3] = max(0, color[3]);
+    }
+    else if (rtv_desc->format->type == VKD3D_FORMAT_TYPE_SINT)
+    {
+        clear_value.color.int32[0] = color[0];
+        clear_value.color.int32[1] = color[1];
+        clear_value.color.int32[2] = color[2];
+        clear_value.color.int32[3] = color[3];
+    }
+    else
+    {
+        clear_value.color.float32[0] = color[0];
+        clear_value.color.float32[1] = color[1];
+        clear_value.color.float32[2] = color[2];
+        clear_value.color.float32[3] = color[3];
+    }
+
+    d3d12_command_list_clear(list, &attachment_desc, &color_reference, NULL,
+            rtv_desc->view, rtv_desc->width, rtv_desc->height, rtv_desc->layer_count,
+            &clear_value, rect_count, rects);
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_ClearUnorderedAccessViewUint(ID3D12GraphicsCommandList1 *iface,
+        D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle, D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle, ID3D12Resource *resource,
+        const UINT values[4], UINT rect_count, const D3D12_RECT *rects)
+{
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+    const struct vkd3d_vk_device_procs *vk_procs;
+    const struct vkd3d_vulkan_info *vk_info;
+    const struct d3d12_desc *cpu_descriptor;
+    struct d3d12_resource *resource_impl;
+    VkBufferMemoryBarrier buffer_barrier;
+    VkImageMemoryBarrier image_barrier;
+    VkPipelineStageFlags stage_mask;
+    VkImageSubresourceRange range;
+    VkClearColorValue color;
+
+    TRACE("iface %p, gpu_handle %#"PRIx64", cpu_handle %lx, resource %p, values %p, rect_count %u, rects %p.\n",
+            iface, gpu_handle.ptr, cpu_handle.ptr, resource, values, rect_count, rects);
+
+    vk_procs = &list->device->vk_procs;
+    vk_info = &list->device->vk_info;
+
+    resource_impl = unsafe_impl_from_ID3D12Resource(resource);
+
+    d3d12_command_list_track_resource_usage(list, resource_impl);
+
+    if (rect_count)
+    {
+        FIXME("Clear rects not supported.\n");
+        return;
+    }
+
+    d3d12_command_list_end_current_render_pass(list);
+
+    cpu_descriptor = d3d12_desc_from_cpu_handle(cpu_handle);
+
+    if (d3d12_resource_is_buffer(resource_impl))
+    {
+        if (cpu_descriptor->u.view->format->vk_format != VK_FORMAT_R32_UINT)
+        {
+            FIXME("Not supported for UAV descriptor %p.\n", cpu_descriptor);
+            return;
+        }
+
+        VK_CALL(vkCmdFillBuffer(list->vk_command_buffer, resource_impl->u.vk_buffer,
+                cpu_descriptor->u.view->info.buffer.offset, cpu_descriptor->u.view->info.buffer.size, values[0]));
+
+        buffer_barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
+        buffer_barrier.pNext = NULL;
+        buffer_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+        buffer_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+        buffer_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+        buffer_barrier.buffer = resource_impl->u.vk_buffer;
+        buffer_barrier.offset = cpu_descriptor->u.view->info.buffer.offset;
+        buffer_barrier.size = cpu_descriptor->u.view->info.buffer.size;
+
+        vk_barrier_parameters_from_d3d12_resource_state(D3D12_RESOURCE_STATE_UNORDERED_ACCESS, 0,
+                resource_impl, list->vk_queue_flags, vk_info, &buffer_barrier.dstAccessMask, &stage_mask, NULL);
+
+        VK_CALL(vkCmdPipelineBarrier(list->vk_command_buffer,
+                VK_PIPELINE_STAGE_TRANSFER_BIT, stage_mask, 0,
+                0, NULL, 1, &buffer_barrier, 0, NULL));
+    }
+    else
+    {
+        color.uint32[0] = values[0];
+        color.uint32[1] = values[1];
+        color.uint32[2] = values[2];
+        color.uint32[3] = values[3];
+
+        range.aspectMask = cpu_descriptor->u.view->format->vk_aspect_mask;
+        range.baseMipLevel = cpu_descriptor->u.view->info.texture.miplevel_idx;
+        range.levelCount = 1;
+        range.baseArrayLayer = cpu_descriptor->u.view->info.texture.layer_idx;
+        range.layerCount = cpu_descriptor->u.view->info.texture.layer_count;
+
+        VK_CALL(vkCmdClearColorImage(list->vk_command_buffer,
+                resource_impl->u.vk_image, VK_IMAGE_LAYOUT_GENERAL, &color, 1, &range));
+
+        image_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+        image_barrier.pNext = NULL;
+        image_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+        image_barrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL;
+        image_barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
+        image_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+        image_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+        image_barrier.image = resource_impl->u.vk_image;
+        image_barrier.subresourceRange = range;
+
+        vk_barrier_parameters_from_d3d12_resource_state(D3D12_RESOURCE_STATE_UNORDERED_ACCESS, 0,
+                resource_impl, list->vk_queue_flags, vk_info, &image_barrier.dstAccessMask, &stage_mask, NULL);
+
+        VK_CALL(vkCmdPipelineBarrier(list->vk_command_buffer,
+                VK_PIPELINE_STAGE_TRANSFER_BIT, stage_mask, 0,
+                0, NULL, 0, NULL, 1, &image_barrier));
+    }
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_ClearUnorderedAccessViewFloat(ID3D12GraphicsCommandList1 *iface,
+        D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle, D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle, ID3D12Resource *resource,
+        const float values[4], UINT rect_count, const D3D12_RECT *rects)
+{
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+    struct d3d12_resource *resource_impl;
+
+    FIXME("iface %p, gpu_handle %#"PRIx64", cpu_handle %lx, resource %p, values %p, rect_count %u, rects %p stub!\n",
+            iface, gpu_handle.ptr, cpu_handle.ptr, resource, values, rect_count, rects);
+
+    resource_impl = unsafe_impl_from_ID3D12Resource(resource);
+
+    d3d12_command_list_track_resource_usage(list, resource_impl);
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_DiscardResource(ID3D12GraphicsCommandList1 *iface,
+        ID3D12Resource *resource, const D3D12_DISCARD_REGION *region)
+{
+    FIXME_ONCE("iface %p, resource %p, region %p stub!\n", iface, resource, region);
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_BeginQuery(ID3D12GraphicsCommandList1 *iface,
+        ID3D12QueryHeap *heap, D3D12_QUERY_TYPE type, UINT index)
+{
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+    struct d3d12_query_heap *query_heap = unsafe_impl_from_ID3D12QueryHeap(heap);
+    const struct vkd3d_vk_device_procs *vk_procs;
+    VkQueryControlFlags flags = 0;
+
+    TRACE("iface %p, heap %p, type %#x, index %u.\n", iface, heap, type, index);
+
+    vk_procs = &list->device->vk_procs;
+
+    d3d12_command_list_end_current_render_pass(list);
+
+    VK_CALL(vkCmdResetQueryPool(list->vk_command_buffer, query_heap->vk_query_pool, index, 1));
+
+    if (type == D3D12_QUERY_TYPE_OCCLUSION)
+        flags = VK_QUERY_CONTROL_PRECISE_BIT;
+
+    if (D3D12_QUERY_TYPE_SO_STATISTICS_STREAM0 <= type && type <= D3D12_QUERY_TYPE_SO_STATISTICS_STREAM3)
+    {
+        unsigned int stream_index = type - D3D12_QUERY_TYPE_SO_STATISTICS_STREAM0;
+        VK_CALL(vkCmdBeginQueryIndexedEXT(list->vk_command_buffer,
+                query_heap->vk_query_pool, index, flags, stream_index));
+        return;
+    }
+
+    VK_CALL(vkCmdBeginQuery(list->vk_command_buffer, query_heap->vk_query_pool, index, flags));
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_EndQuery(ID3D12GraphicsCommandList1 *iface,
+        ID3D12QueryHeap *heap, D3D12_QUERY_TYPE type, UINT index)
+{
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+    struct d3d12_query_heap *query_heap = unsafe_impl_from_ID3D12QueryHeap(heap);
+    const struct vkd3d_vk_device_procs *vk_procs;
+
+    TRACE("iface %p, heap %p, type %#x, index %u.\n", iface, heap, type, index);
+
+    vk_procs = &list->device->vk_procs;
+
+    d3d12_command_list_end_current_render_pass(list);
+
+    d3d12_query_heap_mark_result_as_available(query_heap, index);
+
+    if (type == D3D12_QUERY_TYPE_TIMESTAMP)
+    {
+        VK_CALL(vkCmdResetQueryPool(list->vk_command_buffer, query_heap->vk_query_pool, index, 1));
+        VK_CALL(vkCmdWriteTimestamp(list->vk_command_buffer,
+                VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, query_heap->vk_query_pool, index));
+        return;
+    }
+
+    if (D3D12_QUERY_TYPE_SO_STATISTICS_STREAM0 <= type && type <= D3D12_QUERY_TYPE_SO_STATISTICS_STREAM3)
+    {
+        unsigned int stream_index = type - D3D12_QUERY_TYPE_SO_STATISTICS_STREAM0;
+        VK_CALL(vkCmdEndQueryIndexedEXT(list->vk_command_buffer,
+                query_heap->vk_query_pool, index, stream_index));
+        return;
+    }
+
+    VK_CALL(vkCmdEndQuery(list->vk_command_buffer, query_heap->vk_query_pool, index));
+}
+
+static size_t get_query_stride(D3D12_QUERY_TYPE type)
+{
+    if (type == D3D12_QUERY_TYPE_PIPELINE_STATISTICS)
+        return sizeof(D3D12_QUERY_DATA_PIPELINE_STATISTICS);
+
+    if (D3D12_QUERY_TYPE_SO_STATISTICS_STREAM0 <= type && type <= D3D12_QUERY_TYPE_SO_STATISTICS_STREAM3)
+        return sizeof(D3D12_QUERY_DATA_SO_STATISTICS);
+
+    return sizeof(uint64_t);
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_ResolveQueryData(ID3D12GraphicsCommandList1 *iface,
+        ID3D12QueryHeap *heap, D3D12_QUERY_TYPE type, UINT start_index, UINT query_count,
+        ID3D12Resource *dst_buffer, UINT64 aligned_dst_buffer_offset)
+{
+    const struct d3d12_query_heap *query_heap = unsafe_impl_from_ID3D12QueryHeap(heap);
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+    struct d3d12_resource *buffer = unsafe_impl_from_ID3D12Resource(dst_buffer);
+    const struct vkd3d_vk_device_procs *vk_procs;
+    unsigned int i, first, count;
+    VkDeviceSize offset, stride;
+
+    TRACE("iface %p, heap %p, type %#x, start_index %u, query_count %u, "
+            "dst_buffer %p, aligned_dst_buffer_offset %#"PRIx64".\n",
+            iface, heap, type, start_index, query_count,
+            dst_buffer, aligned_dst_buffer_offset);
+
+    vk_procs = &list->device->vk_procs;
+
+    /* Vulkan is less strict than D3D12 here. Vulkan implementations are free
+     * to return any non-zero result for binary occlusion with at least one
+     * sample passing, while D3D12 guarantees that the result is 1 then.
+     *
+     * For example, the Nvidia binary blob drivers on Linux seem to always
+     * count precisely, even when it was signalled that non-precise is enough.
+     */
+    if (type == D3D12_QUERY_TYPE_BINARY_OCCLUSION)
+        FIXME_ONCE("D3D12 guarantees binary occlusion queries result in only 0 and 1.\n");
+
+    if (!d3d12_resource_is_buffer(buffer))
+    {
+        WARN("Destination resource is not a buffer.\n");
+        return;
+    }
+
+    d3d12_command_list_end_current_render_pass(list);
+
+    stride = get_query_stride(type);
+
+    count = 0;
+    first = start_index;
+    offset = aligned_dst_buffer_offset;
+    for (i = 0; i < query_count; ++i)
+    {
+        if (d3d12_query_heap_is_result_available(query_heap, start_index + i))
+        {
+            ++count;
+        }
+        else
+        {
+            if (count)
+            {
+                VK_CALL(vkCmdCopyQueryPoolResults(list->vk_command_buffer,
+                        query_heap->vk_query_pool, first, count, buffer->u.vk_buffer,
+                        offset, stride, VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT));
+            }
+            count = 0;
+            first = start_index + i;
+            offset = aligned_dst_buffer_offset + i * stride;
+
+            /* We cannot copy query results if a query was not issued:
+             *
+             *   "If the query does not become available in a finite amount of
+             *   time (e.g. due to not issuing a query since the last reset),
+             *   a VK_ERROR_DEVICE_LOST error may occur."
+             */
+            VK_CALL(vkCmdFillBuffer(list->vk_command_buffer,
+                    buffer->u.vk_buffer, offset, stride, 0x00000000));
+
+            ++first;
+            offset += stride;
+        }
+    }
+
+    if (count)
+    {
+        VK_CALL(vkCmdCopyQueryPoolResults(list->vk_command_buffer,
+                query_heap->vk_query_pool, first, count, buffer->u.vk_buffer,
+                offset, stride, VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT));
+    }
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_SetPredication(ID3D12GraphicsCommandList1 *iface,
+        ID3D12Resource *buffer, UINT64 aligned_buffer_offset, D3D12_PREDICATION_OP operation)
+{
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+    struct d3d12_resource *resource = unsafe_impl_from_ID3D12Resource(buffer);
+    const struct vkd3d_vulkan_info *vk_info = &list->device->vk_info;
+    const struct vkd3d_vk_device_procs *vk_procs;
+
+    TRACE("iface %p, buffer %p, aligned_buffer_offset %#"PRIx64", operation %#x.\n",
+            iface, buffer, aligned_buffer_offset, operation);
+
+    if (!vk_info->EXT_conditional_rendering)
+    {
+        FIXME("Vulkan conditional rendering extension not present. Conditional rendering not supported.\n");
+        return;
+    }
+
+    vk_procs = &list->device->vk_procs;
+
+    /* FIXME: Add support for conditional rendering in render passes. */
+    d3d12_command_list_end_current_render_pass(list);
+
+    if (resource)
+    {
+        VkConditionalRenderingBeginInfoEXT cond_info;
+
+        if (aligned_buffer_offset & (sizeof(uint64_t) - 1))
+        {
+            WARN("Unaligned predicate argument buffer offset %#"PRIx64".\n", aligned_buffer_offset);
+            return;
+        }
+
+        if (!d3d12_resource_is_buffer(resource))
+        {
+            WARN("Predicate arguments must be stored in a buffer resource.\n");
+            return;
+        }
+
+        FIXME_ONCE("Predication doesn't support clear and copy commands, "
+                "and predication values are treated as 32-bit values.\n");
+
+        cond_info.sType = VK_STRUCTURE_TYPE_CONDITIONAL_RENDERING_BEGIN_INFO_EXT;
+        cond_info.pNext = NULL;
+        cond_info.buffer = resource->u.vk_buffer;
+        cond_info.offset = aligned_buffer_offset;
+        switch (operation)
+        {
+            case D3D12_PREDICATION_OP_EQUAL_ZERO:
+                cond_info.flags = 0;
+                break;
+
+            case D3D12_PREDICATION_OP_NOT_EQUAL_ZERO:
+                cond_info.flags = VK_CONDITIONAL_RENDERING_INVERTED_BIT_EXT;
+                break;
+
+            default:
+                FIXME("Unhandled predication operation %#x.\n", operation);
+                return;
+        }
+
+        if (list->is_predicated)
+            VK_CALL(vkCmdEndConditionalRenderingEXT(list->vk_command_buffer));
+        VK_CALL(vkCmdBeginConditionalRenderingEXT(list->vk_command_buffer, &cond_info));
+        list->is_predicated = true;
+    }
+    else if (list->is_predicated)
+    {
+        VK_CALL(vkCmdEndConditionalRenderingEXT(list->vk_command_buffer));
+        list->is_predicated = false;
+    }
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_SetMarker(ID3D12GraphicsCommandList1 *iface,
+        UINT metadata, const void *data, UINT size)
+{
+    FIXME("iface %p, metadata %#x, data %p, size %u stub!\n", iface, metadata, data, size);
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_BeginEvent(ID3D12GraphicsCommandList1 *iface,
+        UINT metadata, const void *data, UINT size)
+{
+    FIXME("iface %p, metadata %#x, data %p, size %u stub!\n", iface, metadata, data, size);
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_EndEvent(ID3D12GraphicsCommandList1 *iface)
+{
+    FIXME("iface %p stub!\n", iface);
+}
+
+STATIC_ASSERT(sizeof(VkDispatchIndirectCommand) == sizeof(D3D12_DISPATCH_ARGUMENTS));
+STATIC_ASSERT(sizeof(VkDrawIndexedIndirectCommand) == sizeof(D3D12_DRAW_INDEXED_ARGUMENTS));
+STATIC_ASSERT(sizeof(VkDrawIndirectCommand) == sizeof(D3D12_DRAW_ARGUMENTS));
+
+static void STDMETHODCALLTYPE d3d12_command_list_ExecuteIndirect(ID3D12GraphicsCommandList1 *iface,
+        ID3D12CommandSignature *command_signature, UINT max_command_count, ID3D12Resource *arg_buffer,
+        UINT64 arg_buffer_offset, ID3D12Resource *count_buffer, UINT64 count_buffer_offset)
+{
+    struct d3d12_command_signature *sig_impl = unsafe_impl_from_ID3D12CommandSignature(command_signature);
+    struct d3d12_resource *count_impl = unsafe_impl_from_ID3D12Resource(count_buffer);
+    struct d3d12_resource *arg_impl = unsafe_impl_from_ID3D12Resource(arg_buffer);
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList1(iface);
+    const D3D12_COMMAND_SIGNATURE_DESC *signature_desc;
+    const struct vkd3d_vk_device_procs *vk_procs;
+    unsigned int i;
+
+    TRACE("iface %p, command_signature %p, max_command_count %u, arg_buffer %p, "
+            "arg_buffer_offset %#"PRIx64", count_buffer %p, count_buffer_offset %#"PRIx64".\n",
+            iface, command_signature, max_command_count, arg_buffer, arg_buffer_offset,
+            count_buffer, count_buffer_offset);
+
+    vk_procs = &list->device->vk_procs;
+
+    if (count_buffer && !list->device->vk_info.KHR_draw_indirect_count)
+    {
+        FIXME("Count buffers not supported by Vulkan implementation.\n");
+        return;
+    }
+
+    signature_desc = &sig_impl->desc;
+    for (i = 0; i < signature_desc->NumArgumentDescs; ++i)
+    {
+        const D3D12_INDIRECT_ARGUMENT_DESC *arg_desc = &signature_desc->pArgumentDescs[i];
+
+        switch (arg_desc->Type)
+        {
+            case D3D12_INDIRECT_ARGUMENT_TYPE_DRAW:
+                if (!d3d12_command_list_begin_render_pass(list))
+                {
+                    WARN("Failed to begin render pass, ignoring draw.\n");
+                    break;
+                }
+
+                if (count_buffer)
+                {
+                    VK_CALL(vkCmdDrawIndirectCountKHR(list->vk_command_buffer, arg_impl->u.vk_buffer,
+                            arg_buffer_offset, count_impl->u.vk_buffer, count_buffer_offset,
+                            max_command_count, signature_desc->ByteStride));
+                }
+                else
+                {
+                    VK_CALL(vkCmdDrawIndirect(list->vk_command_buffer, arg_impl->u.vk_buffer,
+                            arg_buffer_offset, max_command_count, signature_desc->ByteStride));
+                }
+                break;
+
+            case D3D12_INDIRECT_ARGUMENT_TYPE_DRAW_INDEXED:
+                if (!d3d12_command_list_begin_render_pass(list))
+                {
+                    WARN("Failed to begin render pass, ignoring draw.\n");
+                    break;
+                }
+
+                d3d12_command_list_check_index_buffer_strip_cut_value(list);
+
+                if (count_buffer)
+                {
+                    VK_CALL(vkCmdDrawIndexedIndirectCountKHR(list->vk_command_buffer, arg_impl->u.vk_buffer,
+                            arg_buffer_offset, count_impl->u.vk_buffer, count_buffer_offset,
+                            max_command_count, signature_desc->ByteStride));
+                }
+                else
+                {
+                    VK_CALL(vkCmdDrawIndexedIndirect(list->vk_command_buffer, arg_impl->u.vk_buffer,
+                            arg_buffer_offset, max_command_count, signature_desc->ByteStride));
+                }
+                break;
+
+            case D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH:
+                if (max_command_count != 1)
+                    FIXME("Ignoring command count %u.\n", max_command_count);
+
+                if (count_buffer)
+                {
+                    FIXME("Count buffers not supported for indirect dispatch.\n");
+                    break;
+                }
+
+                if (!d3d12_command_list_update_compute_state(list))
+                {
+                    WARN("Failed to update compute state, ignoring dispatch.\n");
+                    return;
+                }
+
+                VK_CALL(vkCmdDispatchIndirect(list->vk_command_buffer,
+                        arg_impl->u.vk_buffer, arg_buffer_offset));
+                break;
+
+            default:
+                FIXME("Ignoring unhandled argument type %#x.\n", arg_desc->Type);
+                break;
+        }
+    }
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_AtomicCopyBufferUINT(ID3D12GraphicsCommandList1 *iface,
+        ID3D12Resource *dst_buffer, UINT64 dst_offset,
+        ID3D12Resource *src_buffer, UINT64 src_offset,
+        UINT dependent_resource_count, ID3D12Resource * const *dependent_resources,
+        const D3D12_SUBRESOURCE_RANGE_UINT64 *dependent_sub_resource_ranges)
+{
+    FIXME("iface %p, dst_resource %p, dst_offset %#"PRIx64", src_resource %p, "
+            "src_offset %#"PRIx64", dependent_resource_count %u, "
+            "dependent_resources %p, dependent_sub_resource_ranges %p stub!\n",
+            iface, dst_buffer, dst_offset, src_buffer, src_offset,
+            dependent_resource_count, dependent_resources, dependent_sub_resource_ranges);
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_AtomicCopyBufferUINT64(ID3D12GraphicsCommandList1 *iface,
+        ID3D12Resource *dst_buffer, UINT64 dst_offset,
+        ID3D12Resource *src_buffer, UINT64 src_offset,
+        UINT dependent_resource_count, ID3D12Resource * const *dependent_resources,
+        const D3D12_SUBRESOURCE_RANGE_UINT64 *dependent_sub_resource_ranges)
+{
+    FIXME("iface %p, dst_resource %p, dst_offset %#"PRIx64", src_resource %p, "
+            "src_offset %#"PRIx64", dependent_resource_count %u, "
+            "dependent_resources %p, dependent_sub_resource_ranges %p stub!\n",
+            iface, dst_buffer, dst_offset, src_buffer, src_offset,
+            dependent_resource_count, dependent_resources, dependent_sub_resource_ranges);
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_OMSetDepthBounds(ID3D12GraphicsCommandList1 *iface,
+        FLOAT min, FLOAT max)
+{
+    FIXME("iface %p, min %.8e, max %.8e stub!\n", iface, min, max);
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_SetSamplePositions(ID3D12GraphicsCommandList1 *iface,
+        UINT sample_count, UINT pixel_count, D3D12_SAMPLE_POSITION *sample_positions)
+{
+    FIXME("iface %p, sample_count %u, pixel_count %u, sample_positions %p stub!\n",
+            iface, sample_count, pixel_count, sample_positions);
+}
+
+static void STDMETHODCALLTYPE d3d12_command_list_ResolveSubresourceRegion(ID3D12GraphicsCommandList1 *iface,
+        ID3D12Resource *dst_resource, UINT dst_sub_resource_idx, UINT dst_x, UINT dst_y,
+        ID3D12Resource *src_resource, UINT src_sub_resource_idx,
+        D3D12_RECT *src_rect, DXGI_FORMAT format, D3D12_RESOLVE_MODE mode)
+{
+    FIXME("iface %p, dst_resource %p, dst_sub_resource_idx %u, "
+            "dst_x %u, dst_y %u, src_resource %p, src_sub_resource_idx %u, "
+            "src_rect %p, format %#x, mode %#x stub!\n",
+            iface, dst_resource, dst_sub_resource_idx, dst_x, dst_y,
+            src_resource, src_sub_resource_idx, src_rect, format, mode);
+}
+
+static const struct ID3D12GraphicsCommandList1Vtbl d3d12_command_list_vtbl =
+{
+    /* IUnknown methods */
+    d3d12_command_list_QueryInterface,
+    d3d12_command_list_AddRef,
+    d3d12_command_list_Release,
+    /* ID3D12Object methods */
+    d3d12_command_list_GetPrivateData,
+    d3d12_command_list_SetPrivateData,
+    d3d12_command_list_SetPrivateDataInterface,
+    d3d12_command_list_SetName,
+    /* ID3D12DeviceChild methods */
+    d3d12_command_list_GetDevice,
+    /* ID3D12CommandList methods */
+    d3d12_command_list_GetType,
+    /* ID3D12GraphicsCommandList methods */
+    d3d12_command_list_Close,
+    d3d12_command_list_Reset,
+    d3d12_command_list_ClearState,
+    d3d12_command_list_DrawInstanced,
+    d3d12_command_list_DrawIndexedInstanced,
+    d3d12_command_list_Dispatch,
+    d3d12_command_list_CopyBufferRegion,
+    d3d12_command_list_CopyTextureRegion,
+    d3d12_command_list_CopyResource,
+    d3d12_command_list_CopyTiles,
+    d3d12_command_list_ResolveSubresource,
+    d3d12_command_list_IASetPrimitiveTopology,
+    d3d12_command_list_RSSetViewports,
+    d3d12_command_list_RSSetScissorRects,
+    d3d12_command_list_OMSetBlendFactor,
+    d3d12_command_list_OMSetStencilRef,
+    d3d12_command_list_SetPipelineState,
+    d3d12_command_list_ResourceBarrier,
+    d3d12_command_list_ExecuteBundle,
+    d3d12_command_list_SetDescriptorHeaps,
+    d3d12_command_list_SetComputeRootSignature,
+    d3d12_command_list_SetGraphicsRootSignature,
+    d3d12_command_list_SetComputeRootDescriptorTable,
+    d3d12_command_list_SetGraphicsRootDescriptorTable,
+    d3d12_command_list_SetComputeRoot32BitConstant,
+    d3d12_command_list_SetGraphicsRoot32BitConstant,
+    d3d12_command_list_SetComputeRoot32BitConstants,
+    d3d12_command_list_SetGraphicsRoot32BitConstants,
+    d3d12_command_list_SetComputeRootConstantBufferView,
+    d3d12_command_list_SetGraphicsRootConstantBufferView,
+    d3d12_command_list_SetComputeRootShaderResourceView,
+    d3d12_command_list_SetGraphicsRootShaderResourceView,
+    d3d12_command_list_SetComputeRootUnorderedAccessView,
+    d3d12_command_list_SetGraphicsRootUnorderedAccessView,
+    d3d12_command_list_IASetIndexBuffer,
+    d3d12_command_list_IASetVertexBuffers,
+    d3d12_command_list_SOSetTargets,
+    d3d12_command_list_OMSetRenderTargets,
+    d3d12_command_list_ClearDepthStencilView,
+    d3d12_command_list_ClearRenderTargetView,
+    d3d12_command_list_ClearUnorderedAccessViewUint,
+    d3d12_command_list_ClearUnorderedAccessViewFloat,
+    d3d12_command_list_DiscardResource,
+    d3d12_command_list_BeginQuery,
+    d3d12_command_list_EndQuery,
+    d3d12_command_list_ResolveQueryData,
+    d3d12_command_list_SetPredication,
+    d3d12_command_list_SetMarker,
+    d3d12_command_list_BeginEvent,
+    d3d12_command_list_EndEvent,
+    d3d12_command_list_ExecuteIndirect,
+    /* ID3D12GraphicsCommandList1 methods */
+    d3d12_command_list_AtomicCopyBufferUINT,
+    d3d12_command_list_AtomicCopyBufferUINT64,
+    d3d12_command_list_OMSetDepthBounds,
+    d3d12_command_list_SetSamplePositions,
+    d3d12_command_list_ResolveSubresourceRegion,
+};
+
+static struct d3d12_command_list *unsafe_impl_from_ID3D12CommandList(ID3D12CommandList *iface)
+{
+    if (!iface)
+        return NULL;
+    assert(iface->lpVtbl == (struct ID3D12CommandListVtbl *)&d3d12_command_list_vtbl);
+    return CONTAINING_RECORD(iface, struct d3d12_command_list, ID3D12GraphicsCommandList1_iface);
+}
+
+static HRESULT d3d12_command_list_init(struct d3d12_command_list *list, struct d3d12_device *device,
+        D3D12_COMMAND_LIST_TYPE type, struct d3d12_command_allocator *allocator,
+        ID3D12PipelineState *initial_pipeline_state)
+{
+    HRESULT hr;
+
+    list->ID3D12GraphicsCommandList1_iface.lpVtbl = &d3d12_command_list_vtbl;
+    list->refcount = 1;
+
+    list->type = type;
+
+    if (FAILED(hr = vkd3d_private_store_init(&list->private_store)))
+        return hr;
+
+    d3d12_device_add_ref(list->device = device);
+
+    list->allocator = allocator;
+
+    if (SUCCEEDED(hr = d3d12_command_allocator_allocate_command_buffer(allocator, list)))
+    {
+        d3d12_command_list_reset_state(list, initial_pipeline_state);
+    }
+    else
+    {
+        vkd3d_private_store_destroy(&list->private_store);
+        d3d12_device_release(device);
+    }
+
+    return hr;
+}
+
+HRESULT d3d12_command_list_create(struct d3d12_device *device,
+        UINT node_mask, D3D12_COMMAND_LIST_TYPE type, ID3D12CommandAllocator *allocator_iface,
+        ID3D12PipelineState *initial_pipeline_state, struct d3d12_command_list **list)
+{
+    struct d3d12_command_allocator *allocator;
+    struct d3d12_command_list *object;
+    HRESULT hr;
+
+    if (!(allocator = unsafe_impl_from_ID3D12CommandAllocator(allocator_iface)))
+    {
+        WARN("Command allocator is NULL.\n");
+        return E_INVALIDARG;
+    }
+
+    if (allocator->type != type)
+    {
+        WARN("Command list types do not match (allocator %#x, list %#x).\n",
+                allocator->type, type);
+        return E_INVALIDARG;
+    }
+
+    debug_ignored_node_mask(node_mask);
+
+    if (!(object = vkd3d_malloc(sizeof(*object))))
+        return E_OUTOFMEMORY;
+
+    if (FAILED(hr = d3d12_command_list_init(object, device, type, allocator, initial_pipeline_state)))
+    {
+        vkd3d_free(object);
+        return hr;
+    }
+
+    TRACE("Created command list %p.\n", object);
+
+    *list = object;
+
+    return S_OK;
+}
+
+/* ID3D12CommandQueue */
+static inline struct d3d12_command_queue *impl_from_ID3D12CommandQueue(ID3D12CommandQueue *iface)
+{
+    return CONTAINING_RECORD(iface, struct d3d12_command_queue, ID3D12CommandQueue_iface);
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_command_queue_QueryInterface(ID3D12CommandQueue *iface,
+        REFIID riid, void **object)
+{
+    TRACE("iface %p, riid %s, object %p.\n", iface, debugstr_guid(riid), object);
+
+    if (IsEqualGUID(riid, &IID_ID3D12CommandQueue)
+            || IsEqualGUID(riid, &IID_ID3D12Pageable)
+            || IsEqualGUID(riid, &IID_ID3D12DeviceChild)
+            || IsEqualGUID(riid, &IID_ID3D12Object)
+            || IsEqualGUID(riid, &IID_IUnknown))
+    {
+        ID3D12CommandQueue_AddRef(iface);
+        *object = iface;
+        return S_OK;
+    }
+
+    WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(riid));
+
+    *object = NULL;
+    return E_NOINTERFACE;
+}
+
+static ULONG STDMETHODCALLTYPE d3d12_command_queue_AddRef(ID3D12CommandQueue *iface)
+{
+    struct d3d12_command_queue *command_queue = impl_from_ID3D12CommandQueue(iface);
+    ULONG refcount = InterlockedIncrement(&command_queue->refcount);
+
+    TRACE("%p increasing refcount to %u.\n", command_queue, refcount);
+
+    return refcount;
+}
+
+static ULONG STDMETHODCALLTYPE d3d12_command_queue_Release(ID3D12CommandQueue *iface)
+{
+    struct d3d12_command_queue *command_queue = impl_from_ID3D12CommandQueue(iface);
+    ULONG refcount = InterlockedDecrement(&command_queue->refcount);
+
+    TRACE("%p decreasing refcount to %u.\n", command_queue, refcount);
+
+    if (!refcount)
+    {
+        struct d3d12_device *device = command_queue->device;
+
+        vkd3d_private_store_destroy(&command_queue->private_store);
+
+        vkd3d_free(command_queue);
+
+        d3d12_device_release(device);
+    }
+
+    return refcount;
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_command_queue_GetPrivateData(ID3D12CommandQueue *iface,
+        REFGUID guid, UINT *data_size, void *data)
+{
+    struct d3d12_command_queue *command_queue = impl_from_ID3D12CommandQueue(iface);
+
+    TRACE("iface %p, guid %s, data_size %p, data %p.\n", iface, debugstr_guid(guid), data_size, data);
+
+    return vkd3d_get_private_data(&command_queue->private_store, guid, data_size, data);
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_command_queue_SetPrivateData(ID3D12CommandQueue *iface,
+        REFGUID guid, UINT data_size, const void *data)
+{
+    struct d3d12_command_queue *command_queue = impl_from_ID3D12CommandQueue(iface);
+
+    TRACE("iface %p, guid %s, data_size %u, data %p.\n", iface, debugstr_guid(guid), data_size, data);
+
+    return vkd3d_set_private_data(&command_queue->private_store, guid, data_size, data);
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_command_queue_SetPrivateDataInterface(ID3D12CommandQueue *iface,
+        REFGUID guid, const IUnknown *data)
+{
+    struct d3d12_command_queue *command_queue = impl_from_ID3D12CommandQueue(iface);
+
+    TRACE("iface %p, guid %s, data %p.\n", iface, debugstr_guid(guid), data);
+
+    return vkd3d_set_private_data_interface(&command_queue->private_store, guid, data);
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_command_queue_SetName(ID3D12CommandQueue *iface, const WCHAR *name)
+{
+    struct d3d12_command_queue *command_queue = impl_from_ID3D12CommandQueue(iface);
+    VkQueue vk_queue;
+    HRESULT hr;
+
+    TRACE("iface %p, name %s.\n", iface, debugstr_w(name, command_queue->device->wchar_size));
+
+    if (!(vk_queue = vkd3d_queue_acquire(command_queue->vkd3d_queue)))
+    {
+        ERR("Failed to acquire queue %p.\n", command_queue->vkd3d_queue);
+        return E_FAIL;
+    }
+
+    hr = vkd3d_set_vk_object_name(command_queue->device, (uint64_t)(uintptr_t)vk_queue,
+            VK_DEBUG_REPORT_OBJECT_TYPE_QUEUE_EXT, name);
+    vkd3d_queue_release(command_queue->vkd3d_queue);
+    return hr;
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_command_queue_GetDevice(ID3D12CommandQueue *iface, REFIID iid, void **device)
+{
+    struct d3d12_command_queue *command_queue = impl_from_ID3D12CommandQueue(iface);
+
+    TRACE("iface %p, iid %s, device %p.\n", iface, debugstr_guid(iid), device);
+
+    return d3d12_device_query_interface(command_queue->device, iid, device);
+}
+
+static void STDMETHODCALLTYPE d3d12_command_queue_UpdateTileMappings(ID3D12CommandQueue *iface,
+        ID3D12Resource *resource, UINT region_count,
+        const D3D12_TILED_RESOURCE_COORDINATE *region_start_coordinates,
+        const D3D12_TILE_REGION_SIZE *region_sizes,
+        UINT range_count,
+        const D3D12_TILE_RANGE_FLAGS *range_flags,
+        UINT *heap_range_offsets,
+        UINT *range_tile_counts,
+        D3D12_TILE_MAPPING_FLAGS flags)
+{
+    FIXME("iface %p, resource %p, region_count %u, region_start_coordinates %p, "
+            "region_sizes %p, range_count %u, range_flags %p, heap_range_offsets %p, "
+            "range_tile_counts %p, flags %#x stub!\n",
+            iface, resource, region_count, region_start_coordinates, region_sizes, range_count,
+            range_flags, heap_range_offsets, range_tile_counts, flags);
+}
+
+static void STDMETHODCALLTYPE d3d12_command_queue_CopyTileMappings(ID3D12CommandQueue *iface,
+        ID3D12Resource *dst_resource,
+        const D3D12_TILED_RESOURCE_COORDINATE *dst_region_start_coordinate,
+        ID3D12Resource *src_resource,
+        const D3D12_TILED_RESOURCE_COORDINATE *src_region_start_coordinate,
+        const D3D12_TILE_REGION_SIZE *region_size,
+        D3D12_TILE_MAPPING_FLAGS flags)
+{
+    FIXME("iface %p, dst_resource %p, dst_region_start_coordinate %p, "
+            "src_resource %p, src_region_start_coordinate %p, region_size %p, flags %#x stub!\n",
+            iface, dst_resource, dst_region_start_coordinate, src_resource,
+            src_region_start_coordinate, region_size, flags);
+}
+
+static void STDMETHODCALLTYPE d3d12_command_queue_ExecuteCommandLists(ID3D12CommandQueue *iface,
+        UINT command_list_count, ID3D12CommandList * const *command_lists)
+{
+    struct d3d12_command_queue *command_queue = impl_from_ID3D12CommandQueue(iface);
+    const struct vkd3d_vk_device_procs *vk_procs;
+    struct d3d12_command_list *cmd_list;
+    struct VkSubmitInfo submit_desc;
+    VkCommandBuffer *buffers;
+    VkQueue vk_queue;
+    unsigned int i;
+    VkResult vr;
+
+    TRACE("iface %p, command_list_count %u, command_lists %p.\n",
+            iface, command_list_count, command_lists);
+
+    vk_procs = &command_queue->device->vk_procs;
+
+    if (!(buffers = vkd3d_calloc(command_list_count, sizeof(*buffers))))
+    {
+        ERR("Failed to allocate command buffer array.\n");
+        return;
+    }
+
+    for (i = 0; i < command_list_count; ++i)
+    {
+        cmd_list = unsafe_impl_from_ID3D12CommandList(command_lists[i]);
+
+        if (cmd_list->is_recording)
+        {
+            d3d12_device_mark_as_removed(command_queue->device, DXGI_ERROR_INVALID_CALL,
+                    "Command list %p is in recording state.\n", command_lists[i]);
+            vkd3d_free(buffers);
+            return;
+        }
+
+        buffers[i] = cmd_list->vk_command_buffer;
+    }
+
+    submit_desc.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+    submit_desc.pNext = NULL;
+    submit_desc.waitSemaphoreCount = 0;
+    submit_desc.pWaitSemaphores = NULL;
+    submit_desc.pWaitDstStageMask = NULL;
+    submit_desc.commandBufferCount = command_list_count;
+    submit_desc.pCommandBuffers = buffers;
+    submit_desc.signalSemaphoreCount = 0;
+    submit_desc.pSignalSemaphores = NULL;
+
+    if (!(vk_queue = vkd3d_queue_acquire(command_queue->vkd3d_queue)))
+    {
+        ERR("Failed to acquire queue %p.\n", command_queue->vkd3d_queue);
+        vkd3d_free(buffers);
+        return;
+    }
+
+    if ((vr = VK_CALL(vkQueueSubmit(vk_queue, 1, &submit_desc, VK_NULL_HANDLE))) < 0)
+        ERR("Failed to submit queue(s), vr %d.\n", vr);
+
+    vkd3d_queue_release(command_queue->vkd3d_queue);
+
+    vkd3d_free(buffers);
+}
+
+static void STDMETHODCALLTYPE d3d12_command_queue_SetMarker(ID3D12CommandQueue *iface,
+        UINT metadata, const void *data, UINT size)
+{
+    FIXME("iface %p, metadata %#x, data %p, size %u stub!\n",
+            iface, metadata, data, size);
+}
+
+static void STDMETHODCALLTYPE d3d12_command_queue_BeginEvent(ID3D12CommandQueue *iface,
+        UINT metadata, const void *data, UINT size)
+{
+    FIXME("iface %p, metatdata %#x, data %p, size %u stub!\n",
+            iface, metadata, data, size);
+}
+
+static void STDMETHODCALLTYPE d3d12_command_queue_EndEvent(ID3D12CommandQueue *iface)
+{
+    FIXME("iface %p stub!\n", iface);
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_command_queue_Signal(ID3D12CommandQueue *iface,
+        ID3D12Fence *fence_iface, UINT64 value)
+{
+    struct d3d12_command_queue *command_queue = impl_from_ID3D12CommandQueue(iface);
+    const struct vkd3d_vk_device_procs *vk_procs;
+    VkSemaphore vk_semaphore = VK_NULL_HANDLE;
+    VkFence vk_fence = VK_NULL_HANDLE;
+    struct vkd3d_queue *vkd3d_queue;
+    struct d3d12_device *device;
+    struct d3d12_fence *fence;
+    VkSubmitInfo submit_info;
+    uint64_t sequence_number;
+    VkQueue vk_queue;
+    VkResult vr;
+    HRESULT hr;
+
+    TRACE("iface %p, fence %p, value %#"PRIx64".\n", iface, fence_iface, value);
+
+    device = command_queue->device;
+    vk_procs = &device->vk_procs;
+    vkd3d_queue = command_queue->vkd3d_queue;
+
+    fence = unsafe_impl_from_ID3D12Fence(fence_iface);
+
+    if ((vr = d3d12_fence_create_vk_fence(fence, &vk_fence)) < 0)
+    {
+        WARN("Failed to create Vulkan fence, vr %d.\n", vr);
+        goto fail_vkresult;
+    }
+
+    if (!(vk_queue = vkd3d_queue_acquire(vkd3d_queue)))
+    {
+        ERR("Failed to acquire queue %p.\n", vkd3d_queue);
+        hr = E_FAIL;
+        goto fail;
+    }
+
+    if ((vr = vkd3d_queue_create_vk_semaphore_locked(vkd3d_queue, device, &vk_semaphore)) < 0)
+    {
+        ERR("Failed to create Vulkan semaphore, vr %d.\n", vr);
+        vk_semaphore = VK_NULL_HANDLE;
+    }
+
+    submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+    submit_info.pNext = NULL;
+    submit_info.waitSemaphoreCount = 0;
+    submit_info.pWaitSemaphores = NULL;
+    submit_info.pWaitDstStageMask = NULL;
+    submit_info.commandBufferCount = 0;
+    submit_info.pCommandBuffers = NULL;
+    submit_info.signalSemaphoreCount = vk_semaphore ? 1 : 0;
+    submit_info.pSignalSemaphores = &vk_semaphore;
+
+    if ((vr = VK_CALL(vkQueueSubmit(vk_queue, 1, &submit_info, vk_fence))) >= 0)
+    {
+        sequence_number = ++vkd3d_queue->submitted_sequence_number;
+
+        /* We don't expect to overrun the 64-bit counter, but we handle it gracefully anyway. */
+        if (!sequence_number)
+            sequence_number = vkd3d_queue_reset_sequence_number_locked(vkd3d_queue);
+    }
+
+    vkd3d_queue_release(vkd3d_queue);
+
+    if (vr < 0)
+    {
+        WARN("Failed to submit signal operation, vr %d.\n", vr);
+        goto fail_vkresult;
+    }
+
+    if (vk_semaphore && SUCCEEDED(hr = d3d12_fence_add_vk_semaphore(fence, vk_semaphore, vk_fence, value)))
+        vk_semaphore = VK_NULL_HANDLE;
+
+    vr = VK_CALL(vkGetFenceStatus(device->vk_device, vk_fence));
+    if (vr == VK_NOT_READY)
+    {
+        if (SUCCEEDED(hr = vkd3d_enqueue_gpu_fence(&device->fence_worker, vk_fence, fence, value, vkd3d_queue, sequence_number)))
+            vk_fence = VK_NULL_HANDLE;
+    }
+    else if (vr == VK_SUCCESS)
+    {
+        TRACE("Already signaled %p, value %#"PRIx64".\n", fence, value);
+        hr = d3d12_fence_signal(fence, value, vk_fence);
+        vk_fence = VK_NULL_HANDLE;
+        vkd3d_queue_update_sequence_number(vkd3d_queue, sequence_number, device);
+    }
+    else
+    {
+        FIXME("Failed to get fence status, vr %d.\n", vr);
+        hr = hresult_from_vk_result(vr);
+    }
+
+    if (vk_fence || vk_semaphore)
+    {
+        /* In case of an unexpected failure, try to safely destroy Vulkan objects. */
+        vkd3d_queue_wait_idle(vkd3d_queue, vk_procs);
+        goto fail;
+    }
+
+    return hr;
+
+fail_vkresult:
+    hr = hresult_from_vk_result(vr);
+fail:
+    VK_CALL(vkDestroyFence(device->vk_device, vk_fence, NULL));
+    VK_CALL(vkDestroySemaphore(device->vk_device, vk_semaphore, NULL));
+    return hr;
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_command_queue_Wait(ID3D12CommandQueue *iface,
+        ID3D12Fence *fence_iface, UINT64 value)
+{
+    static const VkPipelineStageFlagBits wait_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
+    struct d3d12_command_queue *command_queue = impl_from_ID3D12CommandQueue(iface);
+    const struct vkd3d_vk_device_procs *vk_procs;
+    struct vkd3d_signaled_semaphore *semaphore;
+    uint64_t completed_value = 0;
+    struct vkd3d_queue *queue;
+    struct d3d12_fence *fence;
+    VkSubmitInfo submit_info;
+    VkQueue vk_queue;
+    VkResult vr;
+    HRESULT hr;
+
+    TRACE("iface %p, fence %p, value %#"PRIx64".\n", iface, fence_iface, value);
+
+    vk_procs = &command_queue->device->vk_procs;
+    queue = command_queue->vkd3d_queue;
+
+    fence = unsafe_impl_from_ID3D12Fence(fence_iface);
+
+    semaphore = d3d12_fence_acquire_vk_semaphore(fence, value, &completed_value);
+    if (!semaphore && completed_value >= value)
+    {
+        /* We don't get a Vulkan semaphore if the fence was signaled on CPU. */
+        TRACE("Already signaled %p, value %#"PRIx64".\n", fence, completed_value);
+        return S_OK;
+    }
+
+    if (!(vk_queue = vkd3d_queue_acquire(queue)))
+    {
+        ERR("Failed to acquire queue %p.\n", queue);
+        hr = E_FAIL;
+        goto fail;
+    }
+
+    if (!semaphore)
+    {
+        if (command_queue->last_waited_fence == fence && command_queue->last_waited_fence_value >= value)
+        {
+            WARN("Already waited on fence %p, value %#"PRIx64".\n", fence, value);
+        }
+        else
+        {
+            FIXME("Failed to acquire Vulkan semaphore for fence %p, value %#"PRIx64
+                    ", completed value %#"PRIx64".\n", fence, value, completed_value);
+        }
+
+        vkd3d_queue_release(queue);
+        return S_OK;
+    }
+
+    submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+    submit_info.pNext = NULL;
+    submit_info.waitSemaphoreCount = 1;
+    submit_info.pWaitSemaphores = &semaphore->vk_semaphore;
+    submit_info.pWaitDstStageMask = &wait_stage_mask;
+    submit_info.commandBufferCount = 0;
+    submit_info.pCommandBuffers = NULL;
+    submit_info.signalSemaphoreCount = 0;
+    submit_info.pSignalSemaphores = NULL;
+
+    if (!vkd3d_array_reserve((void **)&queue->semaphores, &queue->semaphores_size,
+            queue->semaphore_count + 1, sizeof(*queue->semaphores)))
+    {
+        ERR("Failed to allocate memory for semaphore.\n");
+        vkd3d_queue_release(queue);
+        hr = E_OUTOFMEMORY;
+        goto fail;
+    }
+
+    if ((vr = VK_CALL(vkQueueSubmit(vk_queue, 1, &submit_info, VK_NULL_HANDLE))) >= 0)
+    {
+        queue->semaphores[queue->semaphore_count].vk_semaphore = semaphore->vk_semaphore;
+        queue->semaphores[queue->semaphore_count].sequence_number = queue->submitted_sequence_number + 1;
+        ++queue->semaphore_count;
+
+        command_queue->last_waited_fence = fence;
+        command_queue->last_waited_fence_value = value;
+    }
+
+    vkd3d_queue_release(queue);
+
+    if (vr < 0)
+    {
+        WARN("Failed to submit wait operation, vr %d.\n", vr);
+        hr = hresult_from_vk_result(vr);
+        goto fail;
+    }
+
+    d3d12_fence_remove_vk_semaphore(fence, semaphore);
+    return S_OK;
+
+fail:
+    d3d12_fence_release_vk_semaphore(fence, semaphore);
+    return hr;
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_command_queue_GetTimestampFrequency(ID3D12CommandQueue *iface,
+        UINT64 *frequency)
+{
+    struct d3d12_command_queue *command_queue = impl_from_ID3D12CommandQueue(iface);
+    struct d3d12_device *device = command_queue->device;
+
+    TRACE("iface %p, frequency %p.\n", iface, frequency);
+
+    if (!command_queue->vkd3d_queue->timestamp_bits)
+    {
+        WARN("Timestamp queries not supported.\n");
+        return E_FAIL;
+    }
+
+    *frequency = 1000000000 / device->vk_info.device_limits.timestampPeriod;
+
+    return S_OK;
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_command_queue_GetClockCalibration(ID3D12CommandQueue *iface,
+        UINT64 *gpu_timestamp, UINT64 *cpu_timestamp)
+{
+    FIXME("iface %p, gpu_timestamp %p, cpu_timestamp %p stub!\n",
+            iface, gpu_timestamp, cpu_timestamp);
+
+    return E_NOTIMPL;
+}
+
+static D3D12_COMMAND_QUEUE_DESC * STDMETHODCALLTYPE d3d12_command_queue_GetDesc(ID3D12CommandQueue *iface,
+        D3D12_COMMAND_QUEUE_DESC *desc)
+{
+    struct d3d12_command_queue *command_queue = impl_from_ID3D12CommandQueue(iface);
+
+    TRACE("iface %p, desc %p.\n", iface, desc);
+
+    *desc = command_queue->desc;
+    return desc;
+}
+
+static const struct ID3D12CommandQueueVtbl d3d12_command_queue_vtbl =
+{
+    /* IUnknown methods */
+    d3d12_command_queue_QueryInterface,
+    d3d12_command_queue_AddRef,
+    d3d12_command_queue_Release,
+    /* ID3D12Object methods */
+    d3d12_command_queue_GetPrivateData,
+    d3d12_command_queue_SetPrivateData,
+    d3d12_command_queue_SetPrivateDataInterface,
+    d3d12_command_queue_SetName,
+    /* ID3D12DeviceChild methods */
+    d3d12_command_queue_GetDevice,
+    /* ID3D12CommandQueue methods */
+    d3d12_command_queue_UpdateTileMappings,
+    d3d12_command_queue_CopyTileMappings,
+    d3d12_command_queue_ExecuteCommandLists,
+    d3d12_command_queue_SetMarker,
+    d3d12_command_queue_BeginEvent,
+    d3d12_command_queue_EndEvent,
+    d3d12_command_queue_Signal,
+    d3d12_command_queue_Wait,
+    d3d12_command_queue_GetTimestampFrequency,
+    d3d12_command_queue_GetClockCalibration,
+    d3d12_command_queue_GetDesc,
+};
+
+static HRESULT d3d12_command_queue_init(struct d3d12_command_queue *queue,
+        struct d3d12_device *device, const D3D12_COMMAND_QUEUE_DESC *desc)
+{
+    HRESULT hr;
+
+    queue->ID3D12CommandQueue_iface.lpVtbl = &d3d12_command_queue_vtbl;
+    queue->refcount = 1;
+
+    queue->desc = *desc;
+    if (!queue->desc.NodeMask)
+        queue->desc.NodeMask = 0x1;
+
+    if (!(queue->vkd3d_queue = d3d12_device_get_vkd3d_queue(device, desc->Type)))
+        return E_NOTIMPL;
+
+    queue->last_waited_fence = NULL;
+    queue->last_waited_fence_value = 0;
+
+    if (desc->Priority == D3D12_COMMAND_QUEUE_PRIORITY_GLOBAL_REALTIME)
+    {
+        FIXME("Global realtime priority is not implemented.\n");
+        return E_NOTIMPL;
+    }
+
+    if (desc->Priority)
+        FIXME("Ignoring priority %#x.\n", desc->Priority);
+    if (desc->Flags)
+        FIXME("Ignoring flags %#x.\n", desc->Flags);
+
+    if (FAILED(hr = vkd3d_private_store_init(&queue->private_store)))
+        return hr;
+
+    d3d12_device_add_ref(queue->device = device);
+
+    return S_OK;
+}
+
+HRESULT d3d12_command_queue_create(struct d3d12_device *device,
+        const D3D12_COMMAND_QUEUE_DESC *desc, struct d3d12_command_queue **queue)
+{
+    struct d3d12_command_queue *object;
+    HRESULT hr;
+
+    if (!(object = vkd3d_malloc(sizeof(*object))))
+        return E_OUTOFMEMORY;
+
+    if (FAILED(hr = d3d12_command_queue_init(object, device, desc)))
+    {
+        vkd3d_free(object);
+        return hr;
+    }
+
+    TRACE("Created command queue %p.\n", object);
+
+    *queue = object;
+
+    return S_OK;
+}
+
+uint32_t vkd3d_get_vk_queue_family_index(ID3D12CommandQueue *queue)
+{
+    struct d3d12_command_queue *d3d12_queue = impl_from_ID3D12CommandQueue(queue);
+
+    return d3d12_queue->vkd3d_queue->vk_family_index;
+}
+
+VkQueue vkd3d_acquire_vk_queue(ID3D12CommandQueue *queue)
+{
+    struct d3d12_command_queue *d3d12_queue = impl_from_ID3D12CommandQueue(queue);
+
+    return vkd3d_queue_acquire(d3d12_queue->vkd3d_queue);
+}
+
+void vkd3d_release_vk_queue(ID3D12CommandQueue *queue)
+{
+    struct d3d12_command_queue *d3d12_queue = impl_from_ID3D12CommandQueue(queue);
+
+    return vkd3d_queue_release(d3d12_queue->vkd3d_queue);
+}
+
+/* ID3D12CommandSignature */
+static inline struct d3d12_command_signature *impl_from_ID3D12CommandSignature(ID3D12CommandSignature *iface)
+{
+    return CONTAINING_RECORD(iface, struct d3d12_command_signature, ID3D12CommandSignature_iface);
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_command_signature_QueryInterface(ID3D12CommandSignature *iface,
+        REFIID iid, void **out)
+{
+    TRACE("iface %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out);
+
+    if (IsEqualGUID(iid, &IID_ID3D12CommandSignature)
+            || IsEqualGUID(iid, &IID_ID3D12Pageable)
+            || IsEqualGUID(iid, &IID_ID3D12DeviceChild)
+            || IsEqualGUID(iid, &IID_ID3D12Object)
+            || IsEqualGUID(iid, &IID_IUnknown))
+    {
+        ID3D12CommandSignature_AddRef(iface);
+        *out = iface;
+        return S_OK;
+    }
+
+    WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid));
+
+    *out = NULL;
+    return E_NOINTERFACE;
+}
+
+static ULONG STDMETHODCALLTYPE d3d12_command_signature_AddRef(ID3D12CommandSignature *iface)
+{
+    struct d3d12_command_signature *signature = impl_from_ID3D12CommandSignature(iface);
+    ULONG refcount = InterlockedIncrement(&signature->refcount);
+
+    TRACE("%p increasing refcount to %u.\n", signature, refcount);
+
+    return refcount;
+}
+
+static ULONG STDMETHODCALLTYPE d3d12_command_signature_Release(ID3D12CommandSignature *iface)
+{
+    struct d3d12_command_signature *signature = impl_from_ID3D12CommandSignature(iface);
+    ULONG refcount = InterlockedDecrement(&signature->refcount);
+
+    TRACE("%p decreasing refcount to %u.\n", signature, refcount);
+
+    if (!refcount)
+    {
+        struct d3d12_device *device = signature->device;
+
+        vkd3d_private_store_destroy(&signature->private_store);
+
+        vkd3d_free((void *)signature->desc.pArgumentDescs);
+        vkd3d_free(signature);
+
+        d3d12_device_release(device);
+    }
+
+    return refcount;
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_command_signature_GetPrivateData(ID3D12CommandSignature *iface,
+        REFGUID guid, UINT *data_size, void *data)
+{
+    struct d3d12_command_signature *signature = impl_from_ID3D12CommandSignature(iface);
+
+    TRACE("iface %p, guid %s, data_size %p, data %p.\n", iface, debugstr_guid(guid), data_size, data);
+
+    return vkd3d_get_private_data(&signature->private_store, guid, data_size, data);
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_command_signature_SetPrivateData(ID3D12CommandSignature *iface,
+        REFGUID guid, UINT data_size, const void *data)
+{
+    struct d3d12_command_signature *signature = impl_from_ID3D12CommandSignature(iface);
+
+    TRACE("iface %p, guid %s, data_size %u, data %p.\n", iface, debugstr_guid(guid), data_size, data);
+
+    return vkd3d_set_private_data(&signature->private_store, guid, data_size, data);
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_command_signature_SetPrivateDataInterface(ID3D12CommandSignature *iface,
+        REFGUID guid, const IUnknown *data)
+{
+    struct d3d12_command_signature *signature = impl_from_ID3D12CommandSignature(iface);
+
+    TRACE("iface %p, guid %s, data %p.\n", iface, debugstr_guid(guid), data);
+
+    return vkd3d_set_private_data_interface(&signature->private_store, guid, data);
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_command_signature_SetName(ID3D12CommandSignature *iface, const WCHAR *name)
+{
+    struct d3d12_command_signature *signature = impl_from_ID3D12CommandSignature(iface);
+
+    TRACE("iface %p, name %s.\n", iface, debugstr_w(name, signature->device->wchar_size));
+
+    return name ? S_OK : E_INVALIDARG;
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_command_signature_GetDevice(ID3D12CommandSignature *iface, REFIID iid, void **device)
+{
+    struct d3d12_command_signature *signature = impl_from_ID3D12CommandSignature(iface);
+
+    TRACE("iface %p, iid %s, device %p.\n", iface, debugstr_guid(iid), device);
+
+    return d3d12_device_query_interface(signature->device, iid, device);
+}
+
+static const struct ID3D12CommandSignatureVtbl d3d12_command_signature_vtbl =
+{
+    /* IUnknown methods */
+    d3d12_command_signature_QueryInterface,
+    d3d12_command_signature_AddRef,
+    d3d12_command_signature_Release,
+    /* ID3D12Object methods */
+    d3d12_command_signature_GetPrivateData,
+    d3d12_command_signature_SetPrivateData,
+    d3d12_command_signature_SetPrivateDataInterface,
+    d3d12_command_signature_SetName,
+    /* ID3D12DeviceChild methods */
+    d3d12_command_signature_GetDevice,
+};
+
+struct d3d12_command_signature *unsafe_impl_from_ID3D12CommandSignature(ID3D12CommandSignature *iface)
+{
+    if (!iface)
+        return NULL;
+    assert(iface->lpVtbl == &d3d12_command_signature_vtbl);
+    return CONTAINING_RECORD(iface, struct d3d12_command_signature, ID3D12CommandSignature_iface);
+}
+
+HRESULT d3d12_command_signature_create(struct d3d12_device *device, const D3D12_COMMAND_SIGNATURE_DESC *desc,
+        struct d3d12_command_signature **signature)
+{
+    struct d3d12_command_signature *object;
+    unsigned int i;
+    HRESULT hr;
+
+    for (i = 0; i < desc->NumArgumentDescs; ++i)
+    {
+        const D3D12_INDIRECT_ARGUMENT_DESC *argument_desc = &desc->pArgumentDescs[i];
+        switch (argument_desc->Type)
+        {
+            case D3D12_INDIRECT_ARGUMENT_TYPE_DRAW:
+            case D3D12_INDIRECT_ARGUMENT_TYPE_DRAW_INDEXED:
+            case D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH:
+                if (i != desc->NumArgumentDescs - 1)
+                {
+                    WARN("Draw/dispatch must be the last element of a command signature.\n");
+                    return E_INVALIDARG;
+                }
+                break;
+            default:
+                break;
+        }
+    }
+
+    if (!(object = vkd3d_malloc(sizeof(*object))))
+        return E_OUTOFMEMORY;
+
+    object->ID3D12CommandSignature_iface.lpVtbl = &d3d12_command_signature_vtbl;
+    object->refcount = 1;
+
+    object->desc = *desc;
+    if (!(object->desc.pArgumentDescs = vkd3d_calloc(desc->NumArgumentDescs, sizeof(*desc->pArgumentDescs))))
+    {
+        vkd3d_free(object);
+        return E_OUTOFMEMORY;
+    }
+    memcpy((void *)object->desc.pArgumentDescs, desc->pArgumentDescs,
+            desc->NumArgumentDescs * sizeof(*desc->pArgumentDescs));
+
+    if (FAILED(hr = vkd3d_private_store_init(&object->private_store)))
+    {
+        vkd3d_free((void *)object->desc.pArgumentDescs);
+        vkd3d_free(object);
+        return hr;
+    }
+
+    d3d12_device_add_ref(object->device = device);
+
+    TRACE("Created command signature %p.\n", object);
+
+    *signature = object;
+
+    return S_OK;
+}
diff --git a/libs/vkd3d/device.c b/libs/vkd3d/device.c
index 0624318..0a29988 100644
--- a/libs/vkd3d/device.c
+++ b/libs/vkd3d/device.c
@@ -1436,11 +1436,25 @@ static HRESULT vkd3d_init_device_caps(struct d3d12_device *device,
     }
 
     if (vulkan_info->EXT_descriptor_indexing && descriptor_indexing
-            && (descriptor_indexing->descriptorBindingUniformBufferUpdateAfterBind
-            || descriptor_indexing->descriptorBindingStorageBufferUpdateAfterBind
+        && descriptor_indexing->descriptorBindingUniformBufferUpdateAfterBind
+        && descriptor_indexing->descriptorBindingSampledImageUpdateAfterBind
+        && descriptor_indexing->descriptorBindingStorageImageUpdateAfterBind
+        && descriptor_indexing->descriptorBindingUniformTexelBufferUpdateAfterBind)
+    {
+        TRACE("Enabling VK_EXT_descriptor_indexing for volatile descriptor updates.\n");
+    }
+    else
+    {
+        WARN("VK_EXT_descriptor indexing not supported in sufficient capacity. Volatile descriptor updates will not work.\n");
+        vulkan_info->EXT_descriptor_indexing = false;
+    }
+
+    if (vulkan_info->EXT_descriptor_indexing && descriptor_indexing
+        && (descriptor_indexing->descriptorBindingUniformBufferUpdateAfterBind
             || descriptor_indexing->descriptorBindingUniformTexelBufferUpdateAfterBind
+            || descriptor_indexing->descriptorBindingStorageBufferUpdateAfterBind
             || descriptor_indexing->descriptorBindingStorageTexelBufferUpdateAfterBind)
-            && !physical_device_info->descriptor_indexing_properties.robustBufferAccessUpdateAfterBind)
+        && !physical_device_info->descriptor_indexing_properties.robustBufferAccessUpdateAfterBind)
     {
         WARN("Disabling robust buffer access for the update after bind feature.\n");
         features->robustBufferAccess = VK_FALSE;
diff --git a/libs/vkd3d/state.c b/libs/vkd3d/state.c
index a321fa4..50cff1f 100644
--- a/libs/vkd3d/state.c
+++ b/libs/vkd3d/state.c
@@ -745,21 +745,43 @@ static HRESULT vkd3d_create_descriptor_set_layout(struct d3d12_device *device,
         VkDescriptorSetLayoutCreateFlags flags, unsigned int binding_count,
         const VkDescriptorSetLayoutBinding *bindings, VkDescriptorSetLayout *set_layout)
 {
+    unsigned int i;
     const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
     VkDescriptorSetLayoutCreateInfo set_desc;
     VkResult vr;
+    VkDescriptorSetLayoutBindingFlagsCreateInfoEXT flags_info;
+    VkDescriptorBindingFlagsEXT *binding_flags = NULL;
 
     set_desc.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
     set_desc.pNext = NULL;
     set_desc.flags = flags;
     set_desc.bindingCount = binding_count;
     set_desc.pBindings = bindings;
+
+    if (!(flags & VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR) && device->vk_info.EXT_descriptor_indexing)
+    {
+        set_desc.flags |= VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT_EXT;
+        flags_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO_EXT;
+        flags_info.pNext = NULL;
+        flags_info.bindingCount = binding_count;
+        binding_flags = vkd3d_malloc(sizeof(*binding_flags) * binding_count);
+        for (i = 0; i < binding_count; i++)
+        {
+            binding_flags[i] = VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT_EXT |
+                               VK_DESCRIPTOR_BINDING_UPDATE_UNUSED_WHILE_PENDING_BIT_EXT;
+        }
+        flags_info.pBindingFlags = binding_flags;
+        set_desc.pNext = &flags_info;
+    }
+
     if ((vr = VK_CALL(vkCreateDescriptorSetLayout(device->vk_device, &set_desc, NULL, set_layout))) < 0)
     {
         WARN("Failed to create Vulkan descriptor set layout, vr %d.\n", vr);
+        vkd3d_free(binding_flags);
         return hresult_from_vk_result(vr);
     }
 
+    vkd3d_free(binding_flags);
     return S_OK;
 }
 
diff --git a/libs/vkd3d/vkd3d_private.h b/libs/vkd3d/vkd3d_private.h
index 84b5ff2..9dd0973 100644
--- a/libs/vkd3d/vkd3d_private.h
+++ b/libs/vkd3d/vkd3d_private.h
@@ -896,13 +896,16 @@ struct vkd3d_pipeline_bindings
     const struct d3d12_root_signature *root_signature;
 
     VkDescriptorSet descriptor_set;
+    VkDescriptorSet uav_counter_descriptor_set;
+    VkDescriptorSet descriptor_set_bound;
+    VkDescriptorSet uav_counter_descriptor_set_bound;
     bool in_use;
+    bool uav_counter_in_use;
 
     D3D12_GPU_DESCRIPTOR_HANDLE descriptor_tables[D3D12_MAX_ROOT_COST];
     uint64_t descriptor_table_dirty_mask;
     uint64_t descriptor_table_active_mask;
 
-    VkBufferView vk_uav_counter_views[VKD3D_SHADER_MAX_UNORDERED_ACCESS_VIEWS];
     uint8_t uav_counter_dirty_mask;
 
     /* Needed when VK_KHR_push_descriptor is not available. */
@@ -911,6 +914,16 @@ struct vkd3d_pipeline_bindings
     uint32_t push_descriptor_active_mask;
 };
 
+struct d3d12_deferred_descriptor_set_update
+{
+    const struct d3d12_desc *base_descriptor;
+    unsigned int index;
+
+    const struct d3d12_root_signature *root_signature;
+    VkDescriptorSet descriptor_set;
+    bool uav;
+};
+
 /* ID3D12CommandList */
 struct d3d12_command_list
 {
@@ -954,6 +967,10 @@ struct d3d12_command_list
     VkBuffer so_counter_buffers[D3D12_SO_BUFFER_SLOT_COUNT];
     VkDeviceSize so_counter_buffer_offsets[D3D12_SO_BUFFER_SLOT_COUNT];
 
+    struct d3d12_deferred_descriptor_set_update *descriptor_updates;
+    size_t descriptor_updates_size;
+    size_t descriptor_updates_count;
+
     struct vkd3d_private_store private_store;
 };
 
diff --git a/libs/vkd3d/vkd3d_private.h.orig b/libs/vkd3d/vkd3d_private.h.orig
new file mode 100644
index 0000000..84b5ff2
--- /dev/null
+++ b/libs/vkd3d/vkd3d_private.h.orig
@@ -0,0 +1,1299 @@
+/*
+ * Copyright 2016 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
+ */
+
+#ifndef __VKD3D_PRIVATE_H
+#define __VKD3D_PRIVATE_H
+
+#define COBJMACROS
+#define NONAMELESSUNION
+#define VK_NO_PROTOTYPES
+
+#include "vkd3d_common.h"
+#include "vkd3d_memory.h"
+#include "vkd3d_utf8.h"
+#include "list.h"
+#include "rbtree.h"
+
+#include "vkd3d.h"
+#include "vkd3d_shader.h"
+
+#include <assert.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <pthread.h>
+#include <stdbool.h>
+
+#define VK_CALL(f) (vk_procs->f)
+
+#define VKD3D_DESCRIPTOR_MAGIC_FREE    0x00000000u
+#define VKD3D_DESCRIPTOR_MAGIC_CBV     0x00564243u
+#define VKD3D_DESCRIPTOR_MAGIC_SRV     0x00565253u
+#define VKD3D_DESCRIPTOR_MAGIC_UAV     0x00564155u
+#define VKD3D_DESCRIPTOR_MAGIC_SAMPLER 0x504d4153u
+#define VKD3D_DESCRIPTOR_MAGIC_DSV     0x00565344u
+#define VKD3D_DESCRIPTOR_MAGIC_RTV     0x00565452u
+
+#define VKD3D_MAX_COMPATIBLE_FORMAT_COUNT 6u
+#define VKD3D_MAX_QUEUE_FAMILY_COUNT      3u
+#define VKD3D_MAX_SHADER_EXTENSIONS       1u
+#define VKD3D_MAX_SHADER_STAGES           5u
+#define VKD3D_MAX_VK_SYNC_OBJECTS         4u
+
+struct d3d12_command_list;
+struct d3d12_device;
+struct d3d12_resource;
+
+struct vkd3d_vk_global_procs
+{
+    PFN_vkCreateInstance vkCreateInstance;
+    PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties;
+    PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
+};
+
+#define DECLARE_VK_PFN(name) PFN_##name name;
+struct vkd3d_vk_instance_procs
+{
+#define VK_INSTANCE_PFN     DECLARE_VK_PFN
+#define VK_INSTANCE_EXT_PFN DECLARE_VK_PFN
+#include "vulkan_procs.h"
+};
+
+struct vkd3d_vk_device_procs
+{
+#define VK_INSTANCE_PFN   DECLARE_VK_PFN
+#define VK_DEVICE_PFN     DECLARE_VK_PFN
+#define VK_DEVICE_EXT_PFN DECLARE_VK_PFN
+#include "vulkan_procs.h"
+};
+#undef DECLARE_VK_PFN
+
+HRESULT hresult_from_errno(int rc) DECLSPEC_HIDDEN;
+HRESULT hresult_from_vk_result(VkResult vr) DECLSPEC_HIDDEN;
+HRESULT hresult_from_vkd3d_result(int vkd3d_result) DECLSPEC_HIDDEN;
+
+struct vkd3d_vulkan_info
+{
+    /* KHR instance extensions */
+    bool KHR_get_physical_device_properties2;
+    /* EXT instance extensions */
+    bool EXT_debug_report;
+
+    /* KHR device extensions */
+    bool KHR_dedicated_allocation;
+    bool KHR_draw_indirect_count;
+    bool KHR_get_memory_requirements2;
+    bool KHR_image_format_list;
+    bool KHR_maintenance3;
+    bool KHR_push_descriptor;
+    /* EXT device extensions */
+    bool EXT_conditional_rendering;
+    bool EXT_debug_marker;
+    bool EXT_depth_clip_enable;
+    bool EXT_descriptor_indexing;
+    bool EXT_shader_demote_to_helper_invocation;
+    bool EXT_texel_buffer_alignment;
+    bool EXT_transform_feedback;
+    bool EXT_vertex_attribute_divisor;
+
+    bool rasterization_stream;
+    bool transform_feedback_queries;
+
+    bool vertex_attrib_zero_divisor;
+    unsigned int max_vertex_attrib_divisor;
+
+    VkPhysicalDeviceLimits device_limits;
+    VkPhysicalDeviceSparseProperties sparse_properties;
+
+    VkPhysicalDeviceTexelBufferAlignmentPropertiesEXT texel_buffer_alignment_properties;
+
+    unsigned int shader_extension_count;
+    enum vkd3d_shader_target_extension shader_extensions[VKD3D_MAX_SHADER_EXTENSIONS];
+
+    D3D_FEATURE_LEVEL max_feature_level;
+};
+
+enum vkd3d_config_flags
+{
+    VKD3D_CONFIG_FLAG_VULKAN_DEBUG = 0x00000001,
+};
+
+struct vkd3d_instance
+{
+    VkInstance vk_instance;
+    struct vkd3d_vk_instance_procs vk_procs;
+
+    PFN_vkd3d_signal_event signal_event;
+    PFN_vkd3d_create_thread create_thread;
+    PFN_vkd3d_join_thread join_thread;
+    size_t wchar_size;
+
+    struct vkd3d_vulkan_info vk_info;
+    struct vkd3d_vk_global_procs vk_global_procs;
+    void *libvulkan;
+
+    uint64_t config_flags;
+
+    VkDebugReportCallbackEXT vk_debug_callback;
+
+    LONG refcount;
+};
+
+union vkd3d_thread_handle
+{
+    pthread_t pthread;
+    void *handle;
+};
+
+HRESULT vkd3d_create_thread(struct vkd3d_instance *instance,
+        PFN_vkd3d_thread thread_main, void *data, union vkd3d_thread_handle *thread) DECLSPEC_HIDDEN;
+HRESULT vkd3d_join_thread(struct vkd3d_instance *instance, union vkd3d_thread_handle *thread) DECLSPEC_HIDDEN;
+
+struct vkd3d_waiting_fence
+{
+    struct d3d12_fence *fence;
+    uint64_t value;
+    struct vkd3d_queue *queue;
+    uint64_t queue_sequence_number;
+};
+
+struct vkd3d_fence_worker
+{
+    union vkd3d_thread_handle thread;
+    pthread_mutex_t mutex;
+    pthread_cond_t cond;
+    pthread_cond_t fence_destruction_cond;
+    bool should_exit;
+    bool pending_fence_destruction;
+
+    size_t enqueued_fence_count;
+    struct vkd3d_enqueued_fence
+    {
+        VkFence vk_fence;
+        struct vkd3d_waiting_fence waiting_fence;
+    } *enqueued_fences;
+    size_t enqueued_fences_size;
+
+    size_t fence_count;
+    VkFence *vk_fences;
+    size_t vk_fences_size;
+    struct vkd3d_waiting_fence *fences;
+    size_t fences_size;
+
+    struct d3d12_device *device;
+};
+
+HRESULT vkd3d_fence_worker_start(struct vkd3d_fence_worker *worker,
+        struct d3d12_device *device) DECLSPEC_HIDDEN;
+HRESULT vkd3d_fence_worker_stop(struct vkd3d_fence_worker *worker,
+        struct d3d12_device *device) DECLSPEC_HIDDEN;
+
+struct vkd3d_gpu_va_allocation
+{
+    D3D12_GPU_VIRTUAL_ADDRESS base;
+    SIZE_T size;
+    void *ptr;
+};
+
+struct vkd3d_gpu_va_slab
+{
+    SIZE_T size;
+    void *ptr;
+};
+
+struct vkd3d_gpu_va_allocator
+{
+    pthread_mutex_t mutex;
+
+    D3D12_GPU_VIRTUAL_ADDRESS fallback_floor;
+    struct vkd3d_gpu_va_allocation *fallback_allocations;
+    size_t fallback_allocations_size;
+    size_t fallback_allocation_count;
+
+    struct vkd3d_gpu_va_slab *slabs;
+    struct vkd3d_gpu_va_slab *free_slab;
+};
+
+D3D12_GPU_VIRTUAL_ADDRESS vkd3d_gpu_va_allocator_allocate(struct vkd3d_gpu_va_allocator *allocator,
+        size_t alignment, size_t size, void *ptr) DECLSPEC_HIDDEN;
+void *vkd3d_gpu_va_allocator_dereference(struct vkd3d_gpu_va_allocator *allocator,
+        D3D12_GPU_VIRTUAL_ADDRESS address) DECLSPEC_HIDDEN;
+void vkd3d_gpu_va_allocator_free(struct vkd3d_gpu_va_allocator *allocator,
+        D3D12_GPU_VIRTUAL_ADDRESS address) DECLSPEC_HIDDEN;
+
+struct vkd3d_render_pass_key
+{
+    unsigned int attachment_count;
+    bool depth_enable;
+    bool stencil_enable;
+    bool depth_stencil_write;
+    bool padding;
+    unsigned int sample_count;
+    VkFormat vk_formats[D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT + 1];
+};
+
+struct vkd3d_render_pass_entry;
+
+struct vkd3d_render_pass_cache
+{
+    struct vkd3d_render_pass_entry *render_passes;
+    size_t render_pass_count;
+    size_t render_passes_size;
+};
+
+void vkd3d_render_pass_cache_cleanup(struct vkd3d_render_pass_cache *cache,
+        struct d3d12_device *device) DECLSPEC_HIDDEN;
+HRESULT vkd3d_render_pass_cache_find(struct vkd3d_render_pass_cache *cache,
+        struct d3d12_device *device, const struct vkd3d_render_pass_key *key,
+        VkRenderPass *vk_render_pass) DECLSPEC_HIDDEN;
+void vkd3d_render_pass_cache_init(struct vkd3d_render_pass_cache *cache) DECLSPEC_HIDDEN;
+
+struct vkd3d_private_store
+{
+    pthread_mutex_t mutex;
+
+    struct list content;
+};
+
+struct vkd3d_private_data
+{
+    struct list entry;
+
+    GUID tag;
+    unsigned int size;
+    bool is_object;
+    union
+    {
+        BYTE data[1];
+        IUnknown *object;
+    } u;
+};
+
+static inline void vkd3d_private_data_destroy(struct vkd3d_private_data *data)
+{
+    if (data->is_object)
+        IUnknown_Release(data->u.object);
+    list_remove(&data->entry);
+    vkd3d_free(data);
+}
+
+static inline HRESULT vkd3d_private_store_init(struct vkd3d_private_store *store)
+{
+    int rc;
+
+    list_init(&store->content);
+
+    if ((rc = pthread_mutex_init(&store->mutex, NULL)))
+        ERR("Failed to initialize mutex, error %d.\n", rc);
+
+    return hresult_from_errno(rc);
+}
+
+static inline void vkd3d_private_store_destroy(struct vkd3d_private_store *store)
+{
+    struct vkd3d_private_data *data, *cursor;
+
+    LIST_FOR_EACH_ENTRY_SAFE(data, cursor, &store->content, struct vkd3d_private_data, entry)
+    {
+        vkd3d_private_data_destroy(data);
+    }
+
+    pthread_mutex_destroy(&store->mutex);
+}
+
+HRESULT vkd3d_get_private_data(struct vkd3d_private_store *store,
+        const GUID *tag, unsigned int *out_size, void *out) DECLSPEC_HIDDEN;
+HRESULT vkd3d_set_private_data(struct vkd3d_private_store *store,
+        const GUID *tag, unsigned int data_size, const void *data) DECLSPEC_HIDDEN;
+HRESULT vkd3d_set_private_data_interface(struct vkd3d_private_store *store,
+        const GUID *tag, const IUnknown *object) DECLSPEC_HIDDEN;
+
+struct vkd3d_signaled_semaphore
+{
+    struct list entry;
+    uint64_t value;
+    VkSemaphore vk_semaphore;
+    VkFence vk_fence;
+    bool is_acquired;
+};
+
+/* ID3D12Fence */
+struct d3d12_fence
+{
+    ID3D12Fence ID3D12Fence_iface;
+    LONG refcount;
+
+    uint64_t value;
+    pthread_mutex_t mutex;
+
+    struct vkd3d_waiting_event
+    {
+        uint64_t value;
+        HANDLE event;
+    } *events;
+    size_t events_size;
+    size_t event_count;
+
+    struct list semaphores;
+    unsigned int semaphore_count;
+
+    LONG pending_worker_operation_count;
+
+    VkFence old_vk_fences[VKD3D_MAX_VK_SYNC_OBJECTS];
+
+    struct d3d12_device *device;
+
+    struct vkd3d_private_store private_store;
+};
+
+HRESULT d3d12_fence_create(struct d3d12_device *device,
+        uint64_t initial_value, D3D12_FENCE_FLAGS flags, struct d3d12_fence **fence) DECLSPEC_HIDDEN;
+
+/* ID3D12Heap */
+struct d3d12_heap
+{
+    ID3D12Heap ID3D12Heap_iface;
+    LONG refcount;
+
+    bool is_private;
+    D3D12_HEAP_DESC desc;
+
+    pthread_mutex_t mutex;
+
+    VkDeviceMemory vk_memory;
+    void *map_ptr;
+    unsigned int map_count;
+    uint32_t vk_memory_type;
+
+    struct d3d12_device *device;
+
+    struct vkd3d_private_store private_store;
+};
+
+HRESULT d3d12_heap_create(struct d3d12_device *device, const D3D12_HEAP_DESC *desc,
+        const struct d3d12_resource *resource, struct d3d12_heap **heap) DECLSPEC_HIDDEN;
+struct d3d12_heap *unsafe_impl_from_ID3D12Heap(ID3D12Heap *iface) DECLSPEC_HIDDEN;
+
+#define VKD3D_RESOURCE_PUBLIC_FLAGS \
+        (VKD3D_RESOURCE_INITIAL_STATE_TRANSITION | VKD3D_RESOURCE_PRESENT_STATE_TRANSITION)
+#define VKD3D_RESOURCE_EXTERNAL       0x00000004
+#define VKD3D_RESOURCE_DEDICATED_HEAP 0x00000008
+#define VKD3D_RESOURCE_LINEAR_TILING  0x00000010
+
+/* ID3D12Resource */
+struct d3d12_resource
+{
+    ID3D12Resource ID3D12Resource_iface;
+    LONG refcount;
+    LONG internal_refcount;
+
+    D3D12_RESOURCE_DESC desc;
+
+    D3D12_GPU_VIRTUAL_ADDRESS gpu_address;
+    union
+    {
+        VkBuffer vk_buffer;
+        VkImage vk_image;
+    } u;
+    unsigned int flags;
+
+    unsigned int map_count;
+
+    struct d3d12_heap *heap;
+    uint64_t heap_offset;
+
+    D3D12_RESOURCE_STATES initial_state;
+    D3D12_RESOURCE_STATES present_state;
+
+    struct d3d12_device *device;
+
+    struct vkd3d_private_store private_store;
+};
+
+static inline bool d3d12_resource_is_buffer(const struct d3d12_resource *resource)
+{
+    return resource->desc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER;
+}
+
+static inline bool d3d12_resource_is_texture(const struct d3d12_resource *resource)
+{
+    return resource->desc.Dimension != D3D12_RESOURCE_DIMENSION_BUFFER;
+}
+
+bool d3d12_resource_is_cpu_accessible(const struct d3d12_resource *resource) DECLSPEC_HIDDEN;
+HRESULT d3d12_resource_validate_desc(const D3D12_RESOURCE_DESC *desc) DECLSPEC_HIDDEN;
+
+HRESULT d3d12_committed_resource_create(struct d3d12_device *device,
+        const D3D12_HEAP_PROPERTIES *heap_properties, D3D12_HEAP_FLAGS heap_flags,
+        const D3D12_RESOURCE_DESC *desc, D3D12_RESOURCE_STATES initial_state,
+        const D3D12_CLEAR_VALUE *optimized_clear_value, struct d3d12_resource **resource) DECLSPEC_HIDDEN;
+HRESULT d3d12_placed_resource_create(struct d3d12_device *device, struct d3d12_heap *heap, uint64_t heap_offset,
+        const D3D12_RESOURCE_DESC *desc, D3D12_RESOURCE_STATES initial_state,
+        const D3D12_CLEAR_VALUE *optimized_clear_value, struct d3d12_resource **resource) DECLSPEC_HIDDEN;
+HRESULT d3d12_reserved_resource_create(struct d3d12_device *device,
+        const D3D12_RESOURCE_DESC *desc, D3D12_RESOURCE_STATES initial_state,
+        const D3D12_CLEAR_VALUE *optimized_clear_value, struct d3d12_resource **resource) DECLSPEC_HIDDEN;
+struct d3d12_resource *unsafe_impl_from_ID3D12Resource(ID3D12Resource *iface) DECLSPEC_HIDDEN;
+
+HRESULT vkd3d_allocate_buffer_memory(struct d3d12_device *device, VkBuffer vk_buffer,
+        const D3D12_HEAP_PROPERTIES *heap_properties, D3D12_HEAP_FLAGS heap_flags,
+        VkDeviceMemory *vk_memory, uint32_t *vk_memory_type, VkDeviceSize *vk_memory_size) DECLSPEC_HIDDEN;
+HRESULT vkd3d_create_buffer(struct d3d12_device *device,
+        const D3D12_HEAP_PROPERTIES *heap_properties, D3D12_HEAP_FLAGS heap_flags,
+        const D3D12_RESOURCE_DESC *desc, VkBuffer *vk_buffer) DECLSPEC_HIDDEN;
+HRESULT vkd3d_get_image_allocation_info(struct d3d12_device *device,
+        const D3D12_RESOURCE_DESC *desc, D3D12_RESOURCE_ALLOCATION_INFO *allocation_info) DECLSPEC_HIDDEN;
+
+enum vkd3d_view_type
+{
+    VKD3D_VIEW_TYPE_BUFFER,
+    VKD3D_VIEW_TYPE_IMAGE,
+    VKD3D_VIEW_TYPE_SAMPLER,
+};
+
+struct vkd3d_view
+{
+    LONG refcount;
+    enum vkd3d_view_type type;
+    union
+    {
+        VkBufferView vk_buffer_view;
+        VkImageView vk_image_view;
+        VkSampler vk_sampler;
+    } u;
+    VkBufferView vk_counter_view;
+    const struct vkd3d_format *format;
+    union
+    {
+        struct
+        {
+            VkDeviceSize offset;
+            VkDeviceSize size;
+        } buffer;
+        struct
+        {
+            VkImageViewType vk_view_type;
+            unsigned int miplevel_idx;
+            unsigned int layer_idx;
+            unsigned int layer_count;
+        } texture;
+    } info;
+};
+
+void vkd3d_view_decref(struct vkd3d_view *view, struct d3d12_device *device) DECLSPEC_HIDDEN;
+void vkd3d_view_incref(struct vkd3d_view *view) DECLSPEC_HIDDEN;
+
+struct d3d12_desc
+{
+    uint32_t magic;
+    VkDescriptorType vk_descriptor_type;
+    union
+    {
+        VkDescriptorBufferInfo vk_cbv_info;
+        struct vkd3d_view *view;
+    } u;
+};
+
+static inline struct d3d12_desc *d3d12_desc_from_cpu_handle(D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle)
+{
+    return (struct d3d12_desc *)cpu_handle.ptr;
+}
+
+static inline struct d3d12_desc *d3d12_desc_from_gpu_handle(D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle)
+{
+    return (struct d3d12_desc *)(intptr_t)gpu_handle.ptr;
+}
+
+void d3d12_desc_copy(struct d3d12_desc *dst, const struct d3d12_desc *src,
+        struct d3d12_device *device) DECLSPEC_HIDDEN;
+void d3d12_desc_create_cbv(struct d3d12_desc *descriptor,
+        struct d3d12_device *device, const D3D12_CONSTANT_BUFFER_VIEW_DESC *desc) DECLSPEC_HIDDEN;
+void d3d12_desc_create_srv(struct d3d12_desc *descriptor,
+        struct d3d12_device *device, struct d3d12_resource *resource,
+        const D3D12_SHADER_RESOURCE_VIEW_DESC *desc) DECLSPEC_HIDDEN;
+void d3d12_desc_create_uav(struct d3d12_desc *descriptor, struct d3d12_device *device,
+        struct d3d12_resource *resource, struct d3d12_resource *counter_resource,
+        const D3D12_UNORDERED_ACCESS_VIEW_DESC *desc) DECLSPEC_HIDDEN;
+void d3d12_desc_create_sampler(struct d3d12_desc *sampler,
+        struct d3d12_device *device, const D3D12_SAMPLER_DESC *desc) DECLSPEC_HIDDEN;
+void d3d12_desc_write_atomic(struct d3d12_desc *dst, const struct d3d12_desc *src,
+        struct d3d12_device *device) DECLSPEC_HIDDEN;
+
+bool vkd3d_create_raw_buffer_view(struct d3d12_device *device,
+        D3D12_GPU_VIRTUAL_ADDRESS gpu_address, VkBufferView *vk_buffer_view) DECLSPEC_HIDDEN;
+HRESULT vkd3d_create_static_sampler(struct d3d12_device *device,
+        const D3D12_STATIC_SAMPLER_DESC *desc, VkSampler *vk_sampler) DECLSPEC_HIDDEN;
+
+struct d3d12_rtv_desc
+{
+    uint32_t magic;
+    VkSampleCountFlagBits sample_count;
+    const struct vkd3d_format *format;
+    uint64_t width;
+    unsigned int height;
+    unsigned int layer_count;
+    struct vkd3d_view *view;
+    struct d3d12_resource *resource;
+};
+
+static inline struct d3d12_rtv_desc *d3d12_rtv_desc_from_cpu_handle(D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle)
+{
+    return (struct d3d12_rtv_desc *)cpu_handle.ptr;
+}
+
+void d3d12_rtv_desc_create_rtv(struct d3d12_rtv_desc *rtv_desc, struct d3d12_device *device,
+        struct d3d12_resource *resource, const D3D12_RENDER_TARGET_VIEW_DESC *desc) DECLSPEC_HIDDEN;
+
+struct d3d12_dsv_desc
+{
+    uint32_t magic;
+    VkSampleCountFlagBits sample_count;
+    const struct vkd3d_format *format;
+    uint64_t width;
+    unsigned int height;
+    unsigned int layer_count;
+    struct vkd3d_view *view;
+    struct d3d12_resource *resource;
+};
+
+static inline struct d3d12_dsv_desc *d3d12_dsv_desc_from_cpu_handle(D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle)
+{
+    return (struct d3d12_dsv_desc *)cpu_handle.ptr;
+}
+
+void d3d12_dsv_desc_create_dsv(struct d3d12_dsv_desc *dsv_desc, struct d3d12_device *device,
+        struct d3d12_resource *resource, const D3D12_DEPTH_STENCIL_VIEW_DESC *desc) DECLSPEC_HIDDEN;
+
+/* ID3D12DescriptorHeap */
+struct d3d12_descriptor_heap
+{
+    ID3D12DescriptorHeap ID3D12DescriptorHeap_iface;
+    LONG refcount;
+
+    D3D12_DESCRIPTOR_HEAP_DESC desc;
+
+    struct d3d12_device *device;
+
+    struct vkd3d_private_store private_store;
+
+    BYTE descriptors[];
+};
+
+HRESULT d3d12_descriptor_heap_create(struct d3d12_device *device,
+        const D3D12_DESCRIPTOR_HEAP_DESC *desc, struct d3d12_descriptor_heap **descriptor_heap) DECLSPEC_HIDDEN;
+
+/* ID3D12QueryHeap */
+struct d3d12_query_heap
+{
+    ID3D12QueryHeap ID3D12QueryHeap_iface;
+    LONG refcount;
+
+    VkQueryPool vk_query_pool;
+
+    struct d3d12_device *device;
+
+    struct vkd3d_private_store private_store;
+
+    uint64_t availability_mask[];
+};
+
+HRESULT d3d12_query_heap_create(struct d3d12_device *device, const D3D12_QUERY_HEAP_DESC *desc,
+        struct d3d12_query_heap **heap) DECLSPEC_HIDDEN;
+struct d3d12_query_heap *unsafe_impl_from_ID3D12QueryHeap(ID3D12QueryHeap *iface) DECLSPEC_HIDDEN;
+
+/* A Vulkan query has to be issued at least one time before the result is
+ * available. In D3D12 it is legal to get query reults for not issued queries.
+ */
+static inline bool d3d12_query_heap_is_result_available(const struct d3d12_query_heap *heap,
+        unsigned int query_index)
+{
+    unsigned int index = query_index / (sizeof(*heap->availability_mask) * CHAR_BIT);
+    unsigned int shift = query_index % (sizeof(*heap->availability_mask) * CHAR_BIT);
+    return heap->availability_mask[index] & ((uint64_t)1 << shift);
+}
+
+static inline void d3d12_query_heap_mark_result_as_available(struct d3d12_query_heap *heap,
+        unsigned int query_index)
+{
+    unsigned int index = query_index / (sizeof(*heap->availability_mask) * CHAR_BIT);
+    unsigned int shift = query_index % (sizeof(*heap->availability_mask) * CHAR_BIT);
+    heap->availability_mask[index] |= (uint64_t)1 << shift;
+}
+
+struct d3d12_root_descriptor_table_range
+{
+    unsigned int offset;
+    unsigned int descriptor_count;
+    uint32_t binding;
+
+    uint32_t descriptor_magic;
+    unsigned int base_register_idx;
+};
+
+struct d3d12_root_descriptor_table
+{
+    unsigned int range_count;
+    struct d3d12_root_descriptor_table_range *ranges;
+};
+
+struct d3d12_root_constant
+{
+    VkShaderStageFlags stage_flags;
+    uint32_t offset;
+};
+
+struct d3d12_root_descriptor
+{
+    uint32_t binding;
+};
+
+struct d3d12_root_parameter
+{
+    D3D12_ROOT_PARAMETER_TYPE parameter_type;
+    union
+    {
+        struct d3d12_root_constant constant;
+        struct d3d12_root_descriptor descriptor;
+        struct d3d12_root_descriptor_table descriptor_table;
+    } u;
+};
+
+/* ID3D12RootSignature */
+struct d3d12_root_signature
+{
+    ID3D12RootSignature ID3D12RootSignature_iface;
+    LONG refcount;
+
+    VkPipelineLayout vk_pipeline_layout;
+    VkDescriptorSetLayout vk_push_set_layout;
+    VkDescriptorSetLayout vk_set_layout;
+
+    struct d3d12_root_parameter *parameters;
+    unsigned int parameter_count;
+    uint32_t main_set;
+
+    uint64_t descriptor_table_mask;
+    uint32_t push_descriptor_mask;
+
+    D3D12_ROOT_SIGNATURE_FLAGS flags;
+
+    unsigned int descriptor_count;
+    struct vkd3d_shader_resource_binding *descriptor_mapping;
+
+    unsigned int root_constant_count;
+    struct vkd3d_shader_push_constant_buffer *root_constants;
+
+    unsigned int root_descriptor_count;
+
+    unsigned int push_constant_range_count;
+    /* Only a single push constant range may include the same stage in Vulkan. */
+    VkPushConstantRange push_constant_ranges[D3D12_SHADER_VISIBILITY_PIXEL + 1];
+
+    unsigned int static_sampler_count;
+    VkSampler *static_samplers;
+
+    struct d3d12_device *device;
+
+    struct vkd3d_private_store private_store;
+};
+
+HRESULT d3d12_root_signature_create(struct d3d12_device *device, const void *bytecode,
+        size_t bytecode_length, struct d3d12_root_signature **root_signature) DECLSPEC_HIDDEN;
+struct d3d12_root_signature *unsafe_impl_from_ID3D12RootSignature(ID3D12RootSignature *iface) DECLSPEC_HIDDEN;
+
+int vkd3d_parse_root_signature_v_1_0(const struct vkd3d_shader_code *dxbc,
+        struct vkd3d_versioned_root_signature_desc *desc) DECLSPEC_HIDDEN;
+
+struct d3d12_graphics_pipeline_state
+{
+    VkPipelineShaderStageCreateInfo stages[VKD3D_MAX_SHADER_STAGES];
+    size_t stage_count;
+
+    VkVertexInputAttributeDescription attributes[D3D12_VS_INPUT_REGISTER_COUNT];
+    VkVertexInputRate input_rates[D3D12_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT];
+    VkVertexInputBindingDivisorDescriptionEXT instance_divisors[D3D12_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT];
+    size_t instance_divisor_count;
+    size_t attribute_count;
+
+    VkPipelineColorBlendAttachmentState blend_attachments[D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT];
+    unsigned int rt_count;
+    unsigned int null_attachment_mask;
+    VkFormat dsv_format;
+    VkFormat rtv_formats[D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT];
+    VkRenderPass render_pass;
+
+    D3D12_INDEX_BUFFER_STRIP_CUT_VALUE index_buffer_strip_cut_value;
+    VkPipelineRasterizationStateCreateInfo rs_desc;
+    VkPipelineMultisampleStateCreateInfo ms_desc;
+    VkPipelineDepthStencilStateCreateInfo ds_desc;
+
+    VkSampleMask sample_mask[2];
+    VkPipelineRasterizationDepthClipStateCreateInfoEXT rs_depth_clip_info;
+    VkPipelineRasterizationStateStreamCreateInfoEXT rs_stream_info;
+
+    const struct d3d12_root_signature *root_signature;
+
+    struct list compiled_pipelines;
+
+    bool xfb_enabled;
+};
+
+static inline unsigned int dsv_attachment_mask(const struct d3d12_graphics_pipeline_state *graphics)
+{
+    return 1u << graphics->rt_count;
+}
+
+struct d3d12_compute_pipeline_state
+{
+    VkPipeline vk_pipeline;
+};
+
+/* ID3D12PipelineState */
+struct d3d12_pipeline_state
+{
+    ID3D12PipelineState ID3D12PipelineState_iface;
+    LONG refcount;
+
+    union
+    {
+        struct d3d12_graphics_pipeline_state graphics;
+        struct d3d12_compute_pipeline_state compute;
+    } u;
+    VkPipelineBindPoint vk_bind_point;
+
+    VkPipelineLayout vk_pipeline_layout;
+    VkDescriptorSetLayout vk_set_layout;
+    uint32_t set_index;
+
+    struct vkd3d_shader_uav_counter_binding *uav_counters;
+    uint8_t uav_counter_mask;
+
+    struct d3d12_device *device;
+
+    struct vkd3d_private_store private_store;
+};
+
+static inline bool d3d12_pipeline_state_is_compute(const struct d3d12_pipeline_state *state)
+{
+    return state && state->vk_bind_point == VK_PIPELINE_BIND_POINT_COMPUTE;
+}
+
+static inline bool d3d12_pipeline_state_is_graphics(const struct d3d12_pipeline_state *state)
+{
+    return state && state->vk_bind_point == VK_PIPELINE_BIND_POINT_GRAPHICS;
+}
+
+static inline bool d3d12_pipeline_state_has_unknown_dsv_format(struct d3d12_pipeline_state *state)
+{
+    if (d3d12_pipeline_state_is_graphics(state))
+    {
+        struct d3d12_graphics_pipeline_state *graphics = &state->u.graphics;
+
+        return graphics->null_attachment_mask & dsv_attachment_mask(graphics);
+    }
+
+    return false;
+}
+
+HRESULT d3d12_pipeline_state_create_compute(struct d3d12_device *device,
+        const D3D12_COMPUTE_PIPELINE_STATE_DESC *desc, struct d3d12_pipeline_state **state) DECLSPEC_HIDDEN;
+HRESULT d3d12_pipeline_state_create_graphics(struct d3d12_device *device,
+        const D3D12_GRAPHICS_PIPELINE_STATE_DESC *desc, struct d3d12_pipeline_state **state) DECLSPEC_HIDDEN;
+VkPipeline d3d12_pipeline_state_get_or_create_pipeline(struct d3d12_pipeline_state *state,
+        D3D12_PRIMITIVE_TOPOLOGY topology, const uint32_t *strides, VkFormat dsv_format,
+        VkRenderPass *vk_render_pass) DECLSPEC_HIDDEN;
+struct d3d12_pipeline_state *unsafe_impl_from_ID3D12PipelineState(ID3D12PipelineState *iface) DECLSPEC_HIDDEN;
+
+struct vkd3d_buffer
+{
+    VkBuffer vk_buffer;
+    VkDeviceMemory vk_memory;
+};
+
+/* ID3D12CommandAllocator */
+struct d3d12_command_allocator
+{
+    ID3D12CommandAllocator ID3D12CommandAllocator_iface;
+    LONG refcount;
+
+    D3D12_COMMAND_LIST_TYPE type;
+    VkQueueFlags vk_queue_flags;
+
+    VkCommandPool vk_command_pool;
+
+    VkDescriptorPool vk_descriptor_pool;
+
+    VkDescriptorPool *free_descriptor_pools;
+    size_t free_descriptor_pools_size;
+    size_t free_descriptor_pool_count;
+
+    VkRenderPass *passes;
+    size_t passes_size;
+    size_t pass_count;
+
+    VkFramebuffer *framebuffers;
+    size_t framebuffers_size;
+    size_t framebuffer_count;
+
+    VkDescriptorPool *descriptor_pools;
+    size_t descriptor_pools_size;
+    size_t descriptor_pool_count;
+
+    struct vkd3d_view **views;
+    size_t views_size;
+    size_t view_count;
+
+    VkBufferView *buffer_views;
+    size_t buffer_views_size;
+    size_t buffer_view_count;
+
+    struct vkd3d_buffer *transfer_buffers;
+    size_t transfer_buffers_size;
+    size_t transfer_buffer_count;
+
+    VkCommandBuffer *command_buffers;
+    size_t command_buffers_size;
+    size_t command_buffer_count;
+
+    struct d3d12_command_list *current_command_list;
+    struct d3d12_device *device;
+
+    struct vkd3d_private_store private_store;
+};
+
+HRESULT d3d12_command_allocator_create(struct d3d12_device *device,
+        D3D12_COMMAND_LIST_TYPE type, struct d3d12_command_allocator **allocator) DECLSPEC_HIDDEN;
+
+struct vkd3d_push_descriptor
+{
+    union
+    {
+        VkBufferView vk_buffer_view;
+        struct
+        {
+            VkBuffer vk_buffer;
+            VkDeviceSize offset;
+        } cbv;
+    } u;
+};
+
+struct vkd3d_pipeline_bindings
+{
+    const struct d3d12_root_signature *root_signature;
+
+    VkDescriptorSet descriptor_set;
+    bool in_use;
+
+    D3D12_GPU_DESCRIPTOR_HANDLE descriptor_tables[D3D12_MAX_ROOT_COST];
+    uint64_t descriptor_table_dirty_mask;
+    uint64_t descriptor_table_active_mask;
+
+    VkBufferView vk_uav_counter_views[VKD3D_SHADER_MAX_UNORDERED_ACCESS_VIEWS];
+    uint8_t uav_counter_dirty_mask;
+
+    /* Needed when VK_KHR_push_descriptor is not available. */
+    struct vkd3d_push_descriptor push_descriptors[D3D12_MAX_ROOT_COST / 2];
+    uint32_t push_descriptor_dirty_mask;
+    uint32_t push_descriptor_active_mask;
+};
+
+/* ID3D12CommandList */
+struct d3d12_command_list
+{
+    ID3D12GraphicsCommandList1 ID3D12GraphicsCommandList1_iface;
+    LONG refcount;
+
+    D3D12_COMMAND_LIST_TYPE type;
+    VkQueueFlags vk_queue_flags;
+
+    bool is_recording;
+    bool is_valid;
+    VkCommandBuffer vk_command_buffer;
+
+    uint32_t strides[D3D12_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT];
+    D3D12_PRIMITIVE_TOPOLOGY primitive_topology;
+
+    DXGI_FORMAT index_buffer_format;
+
+    VkImageView rtvs[D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT];
+    VkImageView dsv;
+    unsigned int fb_width;
+    unsigned int fb_height;
+    unsigned int fb_layer_count;
+    VkFormat dsv_format;
+
+    bool xfb_enabled;
+
+    bool is_predicated;
+
+    VkFramebuffer current_framebuffer;
+    VkPipeline current_pipeline;
+    VkRenderPass pso_render_pass;
+    VkRenderPass current_render_pass;
+    struct vkd3d_pipeline_bindings pipeline_bindings[VK_PIPELINE_BIND_POINT_RANGE_SIZE];
+
+    struct d3d12_pipeline_state *state;
+
+    struct d3d12_command_allocator *allocator;
+    struct d3d12_device *device;
+
+    VkBuffer so_counter_buffers[D3D12_SO_BUFFER_SLOT_COUNT];
+    VkDeviceSize so_counter_buffer_offsets[D3D12_SO_BUFFER_SLOT_COUNT];
+
+    struct vkd3d_private_store private_store;
+};
+
+HRESULT d3d12_command_list_create(struct d3d12_device *device,
+        UINT node_mask, D3D12_COMMAND_LIST_TYPE type, ID3D12CommandAllocator *allocator_iface,
+        ID3D12PipelineState *initial_pipeline_state, struct d3d12_command_list **list) DECLSPEC_HIDDEN;
+
+struct vkd3d_queue
+{
+    /* Access to VkQueue must be externally synchronized. */
+    pthread_mutex_t mutex;
+
+    VkQueue vk_queue;
+
+    uint64_t completed_sequence_number;
+    uint64_t submitted_sequence_number;
+
+    uint32_t vk_family_index;
+    VkQueueFlags vk_queue_flags;
+    uint32_t timestamp_bits;
+
+    struct
+    {
+        VkSemaphore vk_semaphore;
+        uint64_t sequence_number;
+    } *semaphores;
+    size_t semaphores_size;
+    size_t semaphore_count;
+
+    VkSemaphore old_vk_semaphores[VKD3D_MAX_VK_SYNC_OBJECTS];
+};
+
+VkQueue vkd3d_queue_acquire(struct vkd3d_queue *queue) DECLSPEC_HIDDEN;
+HRESULT vkd3d_queue_create(struct d3d12_device *device,
+        uint32_t family_index, const VkQueueFamilyProperties *properties,
+        struct vkd3d_queue **queue) DECLSPEC_HIDDEN;
+void vkd3d_queue_destroy(struct vkd3d_queue *queue, struct d3d12_device *device) DECLSPEC_HIDDEN;
+void vkd3d_queue_release(struct vkd3d_queue *queue) DECLSPEC_HIDDEN;
+
+/* ID3D12CommandQueue */
+struct d3d12_command_queue
+{
+    ID3D12CommandQueue ID3D12CommandQueue_iface;
+    LONG refcount;
+
+    D3D12_COMMAND_QUEUE_DESC desc;
+
+    struct vkd3d_queue *vkd3d_queue;
+
+    const struct d3d12_fence *last_waited_fence;
+    uint64_t last_waited_fence_value;
+
+    struct d3d12_device *device;
+
+    struct vkd3d_private_store private_store;
+};
+
+HRESULT d3d12_command_queue_create(struct d3d12_device *device,
+        const D3D12_COMMAND_QUEUE_DESC *desc, struct d3d12_command_queue **queue) DECLSPEC_HIDDEN;
+
+/* ID3D12CommandSignature */
+struct d3d12_command_signature
+{
+    ID3D12CommandSignature ID3D12CommandSignature_iface;
+    LONG refcount;
+
+    D3D12_COMMAND_SIGNATURE_DESC desc;
+
+    struct d3d12_device *device;
+
+    struct vkd3d_private_store private_store;
+};
+
+HRESULT d3d12_command_signature_create(struct d3d12_device *device, const D3D12_COMMAND_SIGNATURE_DESC *desc,
+        struct d3d12_command_signature **signature) DECLSPEC_HIDDEN;
+struct d3d12_command_signature *unsafe_impl_from_ID3D12CommandSignature(ID3D12CommandSignature *iface) DECLSPEC_HIDDEN;
+
+/* NULL resources */
+struct vkd3d_null_resources
+{
+    VkBuffer vk_buffer;
+    VkDeviceMemory vk_buffer_memory;
+
+    VkBuffer vk_storage_buffer;
+    VkDeviceMemory vk_storage_buffer_memory;
+
+    VkImage vk_2d_image;
+    VkDeviceMemory vk_2d_image_memory;
+
+    VkImage vk_2d_storage_image;
+    VkDeviceMemory vk_2d_storage_image_memory;
+};
+
+HRESULT vkd3d_init_null_resources(struct vkd3d_null_resources *null_resources,
+        struct d3d12_device *device) DECLSPEC_HIDDEN;
+void vkd3d_destroy_null_resources(struct vkd3d_null_resources *null_resources,
+        struct d3d12_device *device) DECLSPEC_HIDDEN;
+
+struct vkd3d_format_compatibility_list
+{
+    DXGI_FORMAT typeless_format;
+    unsigned int format_count;
+    VkFormat vk_formats[VKD3D_MAX_COMPATIBLE_FORMAT_COUNT];
+};
+
+/* ID3D12Device */
+struct d3d12_device
+{
+    ID3D12Device ID3D12Device_iface;
+    LONG refcount;
+
+    VkDevice vk_device;
+    VkPhysicalDevice vk_physical_device;
+    struct vkd3d_vk_device_procs vk_procs;
+    PFN_vkd3d_signal_event signal_event;
+    size_t wchar_size;
+
+    struct vkd3d_gpu_va_allocator gpu_va_allocator;
+    struct vkd3d_fence_worker fence_worker;
+
+    pthread_mutex_t mutex;
+    pthread_mutex_t desc_mutex[8];
+    struct vkd3d_render_pass_cache render_pass_cache;
+    VkPipelineCache vk_pipeline_cache;
+
+    VkPhysicalDeviceMemoryProperties memory_properties;
+
+    D3D12_FEATURE_DATA_D3D12_OPTIONS feature_options;
+
+    struct vkd3d_vulkan_info vk_info;
+
+    struct vkd3d_queue *direct_queue;
+    struct vkd3d_queue *compute_queue;
+    struct vkd3d_queue *copy_queue;
+    uint32_t queue_family_indices[VKD3D_MAX_QUEUE_FAMILY_COUNT];
+    unsigned int queue_family_count;
+
+    struct vkd3d_instance *vkd3d_instance;
+
+    IUnknown *parent;
+    LUID adapter_luid;
+
+    struct vkd3d_private_store private_store;
+
+    HRESULT removed_reason;
+
+    const struct vkd3d_format *depth_stencil_formats;
+    unsigned int format_compatibility_list_count;
+    const struct vkd3d_format_compatibility_list *format_compatibility_lists;
+    struct vkd3d_null_resources null_resources;
+};
+
+HRESULT d3d12_device_create(struct vkd3d_instance *instance,
+        const struct vkd3d_device_create_info *create_info, struct d3d12_device **device) DECLSPEC_HIDDEN;
+struct vkd3d_queue *d3d12_device_get_vkd3d_queue(struct d3d12_device *device,
+        D3D12_COMMAND_LIST_TYPE type) DECLSPEC_HIDDEN;
+bool d3d12_device_is_uma(struct d3d12_device *device, bool *coherent) DECLSPEC_HIDDEN;
+void d3d12_device_mark_as_removed(struct d3d12_device *device, HRESULT reason,
+        const char *message, ...) VKD3D_PRINTF_FUNC(3, 4) DECLSPEC_HIDDEN;
+struct d3d12_device *unsafe_impl_from_ID3D12Device(ID3D12Device *iface) DECLSPEC_HIDDEN;
+
+static inline HRESULT d3d12_device_query_interface(struct d3d12_device *device, REFIID iid, void **object)
+{
+    return ID3D12Device_QueryInterface(&device->ID3D12Device_iface, iid, object);
+}
+
+static inline ULONG d3d12_device_add_ref(struct d3d12_device *device)
+{
+    return ID3D12Device_AddRef(&device->ID3D12Device_iface);
+}
+
+static inline ULONG d3d12_device_release(struct d3d12_device *device)
+{
+    return ID3D12Device_Release(&device->ID3D12Device_iface);
+}
+
+static inline unsigned int d3d12_device_get_descriptor_handle_increment_size(struct d3d12_device *device,
+        D3D12_DESCRIPTOR_HEAP_TYPE descriptor_type)
+{
+    return ID3D12Device_GetDescriptorHandleIncrementSize(&device->ID3D12Device_iface, descriptor_type);
+}
+
+static inline pthread_mutex_t *d3d12_device_get_descriptor_mutex(struct d3d12_device *device,
+        const struct d3d12_desc *descriptor)
+{
+    STATIC_ASSERT(!(ARRAY_SIZE(device->desc_mutex) & (ARRAY_SIZE(device->desc_mutex) - 1)));
+    uintptr_t idx = (uintptr_t)descriptor;
+
+    idx ^= idx >> 12;
+    idx ^= idx >> 6;
+    idx ^= idx >> 3;
+
+    return &device->desc_mutex[idx & (ARRAY_SIZE(device->desc_mutex) - 1)];
+}
+
+/* utils */
+enum vkd3d_format_type
+{
+    VKD3D_FORMAT_TYPE_OTHER,
+    VKD3D_FORMAT_TYPE_TYPELESS,
+    VKD3D_FORMAT_TYPE_SINT,
+    VKD3D_FORMAT_TYPE_UINT,
+};
+
+struct vkd3d_format
+{
+    DXGI_FORMAT dxgi_format;
+    VkFormat vk_format;
+    size_t byte_count;
+    size_t block_width;
+    size_t block_height;
+    size_t block_byte_count;
+    VkImageAspectFlags vk_aspect_mask;
+    unsigned int plane_count;
+    enum vkd3d_format_type type;
+    bool is_emulated;
+};
+
+static inline size_t vkd3d_format_get_data_offset(const struct vkd3d_format *format,
+        unsigned int row_pitch, unsigned int slice_pitch,
+        unsigned int x, unsigned int y, unsigned int z)
+{
+    return z * slice_pitch
+            + (y / format->block_height) * row_pitch
+            + (x / format->block_width) * format->byte_count * format->block_byte_count;
+}
+
+static inline bool vkd3d_format_is_compressed(const struct vkd3d_format *format)
+{
+    return format->block_byte_count != 1;
+}
+
+void vkd3d_format_copy_data(const struct vkd3d_format *format, const uint8_t *src,
+        unsigned int src_row_pitch, unsigned int src_slice_pitch, uint8_t *dst, unsigned int dst_row_pitch,
+        unsigned int dst_slice_pitch, unsigned int w, unsigned int h, unsigned int d) DECLSPEC_HIDDEN;
+
+const struct vkd3d_format *vkd3d_get_format(const struct d3d12_device *device,
+        DXGI_FORMAT dxgi_format, bool depth_stencil) DECLSPEC_HIDDEN;
+
+HRESULT vkd3d_init_format_info(struct d3d12_device *device) DECLSPEC_HIDDEN;
+void vkd3d_cleanup_format_info(struct d3d12_device *device) DECLSPEC_HIDDEN;
+
+static inline const struct vkd3d_format *vkd3d_format_from_d3d12_resource_desc(
+        const struct d3d12_device *device, const D3D12_RESOURCE_DESC *desc, DXGI_FORMAT view_format)
+{
+    return vkd3d_get_format(device, view_format ? view_format : desc->Format,
+            desc->Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL);
+}
+
+static inline bool d3d12_box_is_empty(const D3D12_BOX *box)
+{
+    return box->right <= box->left || box->bottom <= box->top || box->back <= box->front;
+}
+
+static inline unsigned int d3d12_resource_desc_get_width(const D3D12_RESOURCE_DESC *desc,
+        unsigned int miplevel_idx)
+{
+    return max(1, desc->Width >> miplevel_idx);
+}
+
+static inline unsigned int d3d12_resource_desc_get_height(const D3D12_RESOURCE_DESC *desc,
+        unsigned int miplevel_idx)
+{
+    return max(1, desc->Height >> miplevel_idx);
+}
+
+static inline unsigned int d3d12_resource_desc_get_depth(const D3D12_RESOURCE_DESC *desc,
+        unsigned int miplevel_idx)
+{
+    unsigned int d = desc->Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE3D ? 1 : desc->DepthOrArraySize;
+    return max(1, d >> miplevel_idx);
+}
+
+static inline unsigned int d3d12_resource_desc_get_layer_count(const D3D12_RESOURCE_DESC *desc)
+{
+    return desc->Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE3D ? desc->DepthOrArraySize : 1;
+}
+
+static inline unsigned int d3d12_resource_desc_get_sub_resource_count(const D3D12_RESOURCE_DESC *desc)
+{
+    return d3d12_resource_desc_get_layer_count(desc) * desc->MipLevels;
+}
+
+VkCompareOp vk_compare_op_from_d3d12(D3D12_COMPARISON_FUNC op) DECLSPEC_HIDDEN;
+VkSampleCountFlagBits vk_samples_from_dxgi_sample_desc(const DXGI_SAMPLE_DESC *desc) DECLSPEC_HIDDEN;
+VkSampleCountFlagBits vk_samples_from_sample_count(unsigned int sample_count) DECLSPEC_HIDDEN;
+
+bool is_valid_feature_level(D3D_FEATURE_LEVEL feature_level) DECLSPEC_HIDDEN;
+
+bool is_valid_resource_state(D3D12_RESOURCE_STATES state) DECLSPEC_HIDDEN;
+bool is_write_resource_state(D3D12_RESOURCE_STATES state) DECLSPEC_HIDDEN;
+
+HRESULT return_interface(void *iface, REFIID iface_iid,
+        REFIID requested_iid, void **object) DECLSPEC_HIDDEN;
+
+const char *debug_d3d12_box(const D3D12_BOX *box) DECLSPEC_HIDDEN;
+const char *debug_d3d12_shader_component_mapping(unsigned int mapping) DECLSPEC_HIDDEN;
+const char *debug_vk_extent_3d(VkExtent3D extent) DECLSPEC_HIDDEN;
+const char *debug_vk_memory_heap_flags(VkMemoryHeapFlags flags) DECLSPEC_HIDDEN;
+const char *debug_vk_memory_property_flags(VkMemoryPropertyFlags flags) DECLSPEC_HIDDEN;
+const char *debug_vk_queue_flags(VkQueueFlags flags) DECLSPEC_HIDDEN;
+
+static inline void debug_ignored_node_mask(unsigned int mask)
+{
+    if (mask && mask != 1)
+        FIXME("Ignoring node mask 0x%08x.\n", mask);
+}
+
+HRESULT vkd3d_load_vk_global_procs(struct vkd3d_vk_global_procs *procs,
+        PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr) DECLSPEC_HIDDEN;
+HRESULT vkd3d_load_vk_instance_procs(struct vkd3d_vk_instance_procs *procs,
+        const struct vkd3d_vk_global_procs *global_procs, VkInstance instance) DECLSPEC_HIDDEN;
+HRESULT vkd3d_load_vk_device_procs(struct vkd3d_vk_device_procs *procs,
+        const struct vkd3d_vk_instance_procs *parent_procs, VkDevice device) DECLSPEC_HIDDEN;
+
+extern const char vkd3d_build[];
+
+bool vkd3d_get_program_name(char program_name[PATH_MAX]) DECLSPEC_HIDDEN;
+
+static inline void vkd3d_set_thread_name(const char *name)
+{
+#if defined(HAVE_PTHREAD_SETNAME_NP_2)
+    pthread_setname_np(pthread_self(), name);
+#elif defined(HAVE_PTHREAD_SETNAME_NP_1)
+    pthread_setname_np(name);
+#endif
+}
+
+VkResult vkd3d_set_vk_object_name_utf8(struct d3d12_device *device, uint64_t vk_object,
+        VkDebugReportObjectTypeEXT vk_object_type, const char *name) DECLSPEC_HIDDEN;
+HRESULT vkd3d_set_vk_object_name(struct d3d12_device *device, uint64_t vk_object,
+        VkDebugReportObjectTypeEXT vk_object_type, const WCHAR *name) DECLSPEC_HIDDEN;
+
+static inline void vk_prepend_struct(void *header, void *structure)
+{
+    VkBaseOutStructure *vk_header = header, *vk_structure = structure;
+
+    assert(!vk_structure->pNext);
+    vk_structure->pNext = vk_header->pNext;
+    vk_header->pNext = vk_structure;
+}
+
+#endif  /* __VKD3D_PRIVATE_H */
diff --git a/tests/d3d12.c b/tests/d3d12.c
index 5284138..2c5d4be 100644
--- a/tests/d3d12.c
+++ b/tests/d3d12.c
@@ -16177,7 +16177,7 @@ static void test_update_descriptor_heap_after_closing_command_list(void)
             D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
     get_texture_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
     value = get_readback_uint(&rb, 0, 0, 0);
-    todo ok(value == 0xff00ff00, "Got unexpected value %#x.\n", value);
+    ok(value == 0xff00ff00, "Got unexpected value %#x.\n", value);
     release_resource_readback(&rb);
 
     ID3D12DescriptorHeap_Release(cpu_heap);
diff --git a/tests/d3d12.c.orig b/tests/d3d12.c.orig
new file mode 100644
index 0000000..5284138
--- /dev/null
+++ b/tests/d3d12.c.orig
@@ -0,0 +1,32850 @@
+/*
+ * 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"
+
+static PFN_D3D12_CREATE_VERSIONED_ROOT_SIGNATURE_DESERIALIZER pfn_D3D12CreateVersionedRootSignatureDeserializer;
+static PFN_D3D12_SERIALIZE_VERSIONED_ROOT_SIGNATURE pfn_D3D12SerializeVersionedRootSignature;
+
+struct vec2
+{
+    float x, y;
+};
+
+struct vec4
+{
+    float x, y, z, w;
+};
+
+struct uvec4
+{
+    unsigned int x, y, z, w;
+};
+
+struct ivec4
+{
+    int x, y, z, w;
+};
+
+static bool compare_float(float f, float g, unsigned int ulps)
+{
+    int x, y;
+    union
+    {
+        float f;
+        int i;
+    } u;
+
+    u.f = f;
+    x = u.i;
+    u.f = g;
+    y = u.i;
+
+    if (x < 0)
+        x = INT_MIN - x;
+    if (y < 0)
+        y = INT_MIN - y;
+
+    if (abs(x - y) > ulps)
+        return false;
+
+    return true;
+}
+
+static bool compare_vec4(const struct vec4 *v1, const struct vec4 *v2, unsigned int ulps)
+{
+    return compare_float(v1->x, v2->x, ulps)
+            && compare_float(v1->y, v2->y, ulps)
+            && compare_float(v1->z, v2->z, ulps)
+            && compare_float(v1->w, v2->w, ulps);
+}
+
+static bool compare_uvec4(const struct uvec4* v1, const struct uvec4 *v2)
+{
+    return v1->x == v2->x && v1->y == v2->y && v1->z == v2->z && v1->w == v2->w;
+}
+
+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 bool compare_uint64(uint64_t a, uint64_t b, unsigned int max_diff)
+{
+    return llabs(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_(__LINE__, (IUnknown *)a, b, c)
+static void check_interface_(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_(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_(__LINE__, a, b)
+static void check_heap_properties_(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_(line)(properties->Type == expected.Type,
+            "Got type %#x, expected %#x.\n", properties->Type, expected.Type);
+    ok_(line)(properties->CPUPageProperty == expected.CPUPageProperty,
+            "Got CPU page properties %#x, expected %#x.\n",
+            properties->CPUPageProperty, expected.CPUPageProperty);
+    ok_(line)(properties->MemoryPoolPreference == expected.MemoryPoolPreference,
+            "Got memory pool %#x, expected %#x.\n",
+            properties->MemoryPoolPreference, expected.MemoryPoolPreference);
+    ok_(line)(properties->CreationNodeMask == expected.CreationNodeMask,
+            "Got creation node mask %#x, expected %#x.\n",
+            properties->CreationNodeMask, expected.CreationNodeMask);
+    ok_(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_(__LINE__, a, b)
+static void check_heap_desc_(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_(line)(desc->SizeInBytes == expected.SizeInBytes,
+            "Got size %"PRIu64", expected %"PRIu64".\n",
+            desc->SizeInBytes, expected.SizeInBytes);
+    check_heap_properties_(line, &desc->Properties, &expected.Properties);
+    ok_(line)(desc->Alignment == expected.Alignment,
+            "Got alignment %"PRIu64", expected %"PRIu64".\n",
+            desc->Alignment, expected.Alignment);
+    ok_(line)(desc->Flags == expected.Flags,
+            "Got flags %#x, expected %#x.\n", desc->Flags, expected.Flags);
+}
+
+#define check_alignment(a, b) check_alignment_(__LINE__, a, b)
+static void check_alignment_(unsigned int line, uint64_t size, uint64_t alignment)
+{
+    uint64_t aligned_size = align(size, alignment);
+    ok_(line)(aligned_size == size, "Got unaligned size %"PRIu64", expected %"PRIu64".\n",
+            size, aligned_size);
+}
+
+static void uav_barrier(ID3D12GraphicsCommandList *list, ID3D12Resource *resource)
+{
+    D3D12_RESOURCE_BARRIER barrier;
+
+    barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV;
+    barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
+    barrier.UAV.pResource = resource;
+
+    ID3D12GraphicsCommandList_ResourceBarrier(list, 1, &barrier);
+}
+
+static void copy_sub_resource_data(const D3D12_MEMCPY_DEST *dst, const D3D12_SUBRESOURCE_DATA *src,
+        unsigned int row_count, unsigned int slice_count, size_t row_size)
+{
+    const BYTE *src_slice_ptr;
+    BYTE *dst_slice_ptr;
+    unsigned int z, y;
+
+    for (z = 0; z < slice_count; ++z)
+    {
+        dst_slice_ptr = (BYTE *)dst->pData + z * dst->SlicePitch;
+        src_slice_ptr = (const BYTE*)src->pData + z * src->SlicePitch;
+        for (y = 0; y < row_count; ++y)
+            memcpy(dst_slice_ptr + y * dst->RowPitch, src_slice_ptr + y * src->RowPitch, row_size);
+    }
+}
+
+#define upload_buffer_data(a, b, c, d, e, f) upload_buffer_data_(__LINE__, a, b, c, d, e, f)
+static void upload_buffer_data_(unsigned int line, ID3D12Resource *buffer, size_t offset,
+        size_t size, const void *data, ID3D12CommandQueue *queue, ID3D12GraphicsCommandList *command_list)
+{
+    ID3D12Resource *upload_buffer;
+    ID3D12Device *device;
+    HRESULT hr;
+
+    hr = ID3D12Resource_GetDevice(buffer, &IID_ID3D12Device, (void **)&device);
+    ok_(line)(SUCCEEDED(hr), "Failed to get device, hr %#x.\n", hr);
+
+    upload_buffer = create_upload_buffer_(line, device, size, data);
+
+    ID3D12GraphicsCommandList_CopyBufferRegion(command_list, buffer, offset,
+            upload_buffer, 0, size);
+
+    hr = ID3D12GraphicsCommandList_Close(command_list);
+    ok_(line)(SUCCEEDED(hr), "Failed to close command list, hr %#x.\n", hr);
+    exec_command_list(queue, command_list);
+    wait_queue_idle(device, queue);
+
+    ID3D12Resource_Release(upload_buffer);
+    ID3D12Device_Release(device);
+}
+
+#define upload_texture_data(a, b, c, d, e) upload_texture_data_(__LINE__, a, b, c, d, e)
+static void upload_texture_data_(unsigned int line, ID3D12Resource *texture,
+        const D3D12_SUBRESOURCE_DATA *data, unsigned int sub_resource_count,
+        ID3D12CommandQueue *queue, ID3D12GraphicsCommandList *command_list)
+{
+    D3D12_TEXTURE_COPY_LOCATION dst_location, src_location;
+    D3D12_PLACED_SUBRESOURCE_FOOTPRINT *layouts;
+    uint64_t *row_sizes, required_size;
+    D3D12_RESOURCE_DESC resource_desc;
+    ID3D12Resource *upload_buffer;
+    D3D12_MEMCPY_DEST dst_data;
+    ID3D12Device *device;
+    UINT *row_counts;
+    unsigned int i;
+    HRESULT hr;
+    void *ptr;
+
+    layouts = calloc(sub_resource_count, sizeof(*layouts));
+    ok(layouts, "Failed to allocate memory.\n");
+    row_counts = calloc(sub_resource_count, sizeof(*row_counts));
+    ok(row_counts, "Failed to allocate memory.\n");
+    row_sizes = calloc(sub_resource_count, sizeof(*row_sizes));
+    ok(row_sizes, "Failed to allocate memory.\n");
+
+    resource_desc = ID3D12Resource_GetDesc(texture);
+    hr = ID3D12Resource_GetDevice(texture, &IID_ID3D12Device, (void **)&device);
+    ok_(line)(SUCCEEDED(hr), "Failed to get device, hr %#x.\n", hr);
+
+    ID3D12Device_GetCopyableFootprints(device, &resource_desc, 0, sub_resource_count,
+            0, layouts, row_counts, row_sizes, &required_size);
+
+    upload_buffer = create_upload_buffer_(line, device, required_size, NULL);
+
+    hr = ID3D12Resource_Map(upload_buffer, 0, NULL, (void **)&ptr);
+    ok_(line)(SUCCEEDED(hr), "Failed to map upload buffer, hr %#x.\n", hr);
+    for (i = 0; i < sub_resource_count; ++i)
+    {
+        dst_data.pData = (BYTE *)ptr + layouts[i].Offset;
+        dst_data.RowPitch = layouts[i].Footprint.RowPitch;
+        dst_data.SlicePitch = layouts[i].Footprint.RowPitch * row_counts[i];
+        copy_sub_resource_data(&dst_data, &data[i],
+                row_counts[i], layouts[i].Footprint.Depth, row_sizes[i]);
+    }
+    ID3D12Resource_Unmap(upload_buffer, 0, NULL);
+
+    for (i = 0; i < sub_resource_count; ++i)
+    {
+        dst_location.pResource = texture;
+        dst_location.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
+        dst_location.SubresourceIndex = i;
+
+        src_location.pResource = upload_buffer;
+        src_location.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
+        src_location.PlacedFootprint = layouts[i];
+
+        ID3D12GraphicsCommandList_CopyTextureRegion(command_list,
+                &dst_location, 0, 0, 0, &src_location, NULL);
+    }
+
+    hr = ID3D12GraphicsCommandList_Close(command_list);
+    ok_(line)(SUCCEEDED(hr), "Failed to close command list, hr %#x.\n", hr);
+
+    exec_command_list(queue, command_list);
+    wait_queue_idle(device, queue);
+
+    ID3D12Resource_Release(upload_buffer);
+    ID3D12Device_Release(device);
+
+    free(layouts);
+    free(row_counts);
+    free(row_sizes);
+}
+
+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 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->width = width;
+    rb->height = height;
+    rb->depth = depth;
+    rb->resource = buffer;
+    rb->row_pitch = row_pitch;
+    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->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 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->width = resource_desc.Width / format_size(format);
+    rb->height = 1;
+    rb->depth = 1;
+    rb->resource = rb_buffer;
+    rb->row_pitch = resource_desc.Width;
+    rb->data = NULL;
+
+    read_range.Begin = 0;
+    read_range.End = resource_desc.Width;
+    hr = ID3D12Resource_Map(rb_buffer, 0, &read_range, &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));
+}
+
+static uint64_t get_readback_uint64(struct resource_readback *rb, unsigned int x, unsigned int y)
+{
+    return *(uint64_t *)get_readback_data(rb, x, y, 0, sizeof(uint64_t));
+}
+
+static float get_readback_float(struct resource_readback *rb, unsigned int x, unsigned int y)
+{
+    return *(float *)get_readback_data(rb, x, y, 0, sizeof(float));
+}
+
+static const struct vec4 *get_readback_vec4(struct resource_readback *rb, unsigned int x, unsigned int y)
+{
+    return get_readback_data(rb, x, y, 0, sizeof(struct vec4));
+}
+
+static const struct uvec4 *get_readback_uvec4(struct resource_readback *rb, unsigned int x, unsigned int y)
+{
+    return get_readback_data(rb, x, y, 0, sizeof(struct uvec4));
+}
+
+#define check_readback_data_float(a, b, c, d) check_readback_data_float_(__LINE__, a, b, c, d)
+static void check_readback_data_float_(unsigned int line, struct resource_readback *rb,
+        const RECT *rect, float expected, unsigned int max_diff)
+{
+    RECT r = {0, 0, rb->width, rb->height};
+    unsigned int x = 0, y;
+    bool all_match = true;
+    float 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_float(rb, x, y);
+            if (!compare_float(got, expected, max_diff))
+            {
+                all_match = false;
+                break;
+            }
+        }
+        if (!all_match)
+            break;
+    }
+    ok_(line)(all_match, "Got %.8e, expected %.8e at (%u, %u).\n", got, expected, x, y);
+}
+
+#define check_sub_resource_float(a, b, c, d, e, f) check_sub_resource_float_(__LINE__, a, b, c, d, e, f)
+static void check_sub_resource_float_(unsigned int line, ID3D12Resource *texture,
+        unsigned int sub_resource_idx, ID3D12CommandQueue *queue, ID3D12GraphicsCommandList *command_list,
+        float expected, unsigned int max_diff)
+{
+    struct resource_readback rb;
+
+    get_texture_readback_with_command_list(texture, sub_resource_idx, &rb, queue, command_list);
+    check_readback_data_float_(line, &rb, NULL, expected, max_diff);
+    release_resource_readback(&rb);
+}
+
+#define check_readback_data_uint8(a, b, c, d) check_readback_data_uint8_(__LINE__, a, b, c, d)
+static void check_readback_data_uint8_(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_(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_(__LINE__, a, b, c, d, e, f)
+static void check_sub_resource_uint8_(unsigned int line, ID3D12Resource *texture,
+        unsigned int sub_resource_idx, ID3D12CommandQueue *queue, ID3D12GraphicsCommandList *command_list,
+        uint8_t expected, unsigned int max_diff)
+{
+    struct resource_readback rb;
+
+    get_texture_readback_with_command_list(texture, sub_resource_idx, &rb, queue, command_list);
+    check_readback_data_uint8_(line, &rb, NULL, expected, max_diff);
+    release_resource_readback(&rb);
+}
+
+#define check_readback_data_uint16(a, b, c, d) check_readback_data_uint16_(__LINE__, a, b, c, d)
+static void check_readback_data_uint16_(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_(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_(__LINE__, a, b, c, d, e, f)
+static void check_sub_resource_uint16_(unsigned int line, ID3D12Resource *texture,
+        unsigned int sub_resource_idx, ID3D12CommandQueue *queue, ID3D12GraphicsCommandList *command_list,
+        uint16_t expected, unsigned int max_diff)
+{
+    struct resource_readback rb;
+
+    get_texture_readback_with_command_list(texture, sub_resource_idx, &rb, queue, command_list);
+    check_readback_data_uint16_(line, &rb, NULL, expected, max_diff);
+    release_resource_readback(&rb);
+}
+
+#define check_readback_data_uint64(a, b, c, d) check_readback_data_uint64_(__LINE__, a, b, c, d)
+static void check_readback_data_uint64_(unsigned int line, struct resource_readback *rb,
+        const RECT *rect, uint64_t expected, unsigned int max_diff)
+{
+    RECT r = {0, 0, rb->width, rb->height};
+    unsigned int x = 0, y;
+    bool all_match = true;
+    uint64_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_uint64(rb, x, y);
+            if (!compare_uint64(got, expected, max_diff))
+            {
+                all_match = false;
+                break;
+            }
+        }
+        if (!all_match)
+            break;
+    }
+    ok_(line)(all_match, "Got %#"PRIx64", expected %#"PRIx64" at (%u, %u).\n", got, expected, x, y);
+}
+
+#define check_sub_resource_uint64(a, b, c, d, e, f) check_sub_resource_uint64_(__LINE__, a, b, c, d, e, f)
+static void check_sub_resource_uint64_(unsigned int line, ID3D12Resource *texture,
+        unsigned int sub_resource_idx, ID3D12CommandQueue *queue, ID3D12GraphicsCommandList *command_list,
+        uint64_t expected, unsigned int max_diff)
+{
+    struct resource_readback rb;
+
+    get_texture_readback_with_command_list(texture, sub_resource_idx, &rb, queue, command_list);
+    check_readback_data_uint64_(line, &rb, NULL, expected, max_diff);
+    release_resource_readback(&rb);
+}
+
+#define check_sub_resource_vec4(a, b, c, d, e, f) check_sub_resource_vec4_(__LINE__, a, b, c, d, e, f)
+static void check_sub_resource_vec4_(unsigned int line, ID3D12Resource *texture,
+        unsigned int sub_resource_idx, ID3D12CommandQueue *queue, ID3D12GraphicsCommandList *command_list,
+        const struct vec4 *expected, unsigned int max_diff)
+{
+    struct resource_readback rb;
+    unsigned int x = 0, y;
+    bool all_match = true;
+    struct vec4 got = {0};
+
+    get_texture_readback_with_command_list(texture, sub_resource_idx, &rb, queue, command_list);
+    for (y = 0; y < rb.height; ++y)
+    {
+        for (x = 0; x < rb.width; ++x)
+        {
+            got = *get_readback_vec4(&rb, x, y);
+            if (!compare_vec4(&got, expected, max_diff))
+            {
+                all_match = false;
+                break;
+            }
+        }
+        if (!all_match)
+            break;
+    }
+    release_resource_readback(&rb);
+
+    ok_(line)(all_match, "Got {%.8e, %.8e, %.8e, %.8e}, expected {%.8e, %.8e, %.8e, %.8e} at (%u, %u).\n",
+            got.x, got.y, got.z, got.w, expected->x, expected->y, expected->z, expected->w, x, y);
+}
+
+#define check_sub_resource_uvec4(a, b, c, d, e) check_sub_resource_uvec4_(__LINE__, a, b, c, d, e)
+static void check_sub_resource_uvec4_(unsigned int line, ID3D12Resource *texture,
+        unsigned int sub_resource_idx, ID3D12CommandQueue *queue, ID3D12GraphicsCommandList *command_list,
+        const struct uvec4 *expected_value)
+{
+    struct resource_readback rb;
+    struct uvec4 value = {0};
+    unsigned int x = 0, y;
+    bool all_match = true;
+
+    get_texture_readback_with_command_list(texture, sub_resource_idx, &rb, queue, command_list);
+    for (y = 0; y < rb.height; ++y)
+    {
+        for (x = 0; x < rb.width; ++x)
+        {
+            value = *get_readback_uvec4(&rb, x, y);
+            if (!compare_uvec4(&value, expected_value))
+            {
+                all_match = false;
+                break;
+            }
+        }
+        if (!all_match)
+            break;
+    }
+    release_resource_readback(&rb);
+
+    ok_(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);
+}
+
+static bool broken_on_warp(bool condition)
+{
+    return broken(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;
+}
+
+#define create_cb_root_signature(a, b, c, e) create_cb_root_signature_(__LINE__, a, b, c, e)
+static ID3D12RootSignature *create_cb_root_signature_(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_(line)(SUCCEEDED(hr), "Failed to create root signature, hr %#x.\n", hr);
+
+    return root_signature;
+}
+
+#define create_32bit_constants_root_signature(a, b, c, e) \
+        create_32bit_constants_root_signature_(__LINE__, a, b, c, e, 0)
+static ID3D12RootSignature *create_32bit_constants_root_signature_(unsigned int line,
+        ID3D12Device *device, unsigned int reg_idx, unsigned int element_count,
+        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_32BIT_CONSTANTS;
+    root_parameter.Constants.ShaderRegister = reg_idx;
+    root_parameter.Constants.RegisterSpace = 0;
+    root_parameter.Constants.Num32BitValues = element_count;
+    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_(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_(__LINE__, a, b, c, d, NULL)
+static ID3D12RootSignature *create_texture_root_signature_(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_(line)(SUCCEEDED(hr), "Failed to create root signature, hr %#x.\n", hr);
+
+    return root_signature;
+}
+
+#define create_compute_pipeline_state(a, b, c) create_compute_pipeline_state_(__LINE__, a, b, c)
+static ID3D12PipelineState *create_compute_pipeline_state_(unsigned int line, ID3D12Device *device,
+        ID3D12RootSignature *root_signature, const D3D12_SHADER_BYTECODE cs)
+{
+    D3D12_COMPUTE_PIPELINE_STATE_DESC pipeline_state_desc;
+    ID3D12PipelineState *pipeline_state;
+    HRESULT hr;
+
+    memset(&pipeline_state_desc, 0, sizeof(pipeline_state_desc));
+    pipeline_state_desc.pRootSignature = root_signature;
+    pipeline_state_desc.CS = cs;
+    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_(line)(SUCCEEDED(hr), "Failed to create compute pipeline state, hr %#x.\n", hr);
+
+    return pipeline_state;
+}
+
+#define create_command_signature(a, b) create_command_signature_(__LINE__, a, b)
+static ID3D12CommandSignature *create_command_signature_(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_(line)(hr == S_OK, "Failed to create command signature, hr %#x.\n", hr);
+
+    return command_signature;
+}
+
+#define init_compute_test_context(context) init_compute_test_context_(__LINE__, context)
+static bool init_compute_test_context_(unsigned int line, struct test_context *context)
+{
+    ID3D12Device *device;
+    HRESULT hr;
+
+    memset(context, 0, sizeof(*context));
+
+    if (!(context->device = create_device()))
+    {
+        skip_(line)("Failed to create device.\n");
+        return false;
+    }
+    device = context->device;
+
+    context->queue = create_command_queue_(line, device,
+            D3D12_COMMAND_LIST_TYPE_COMPUTE, D3D12_COMMAND_QUEUE_PRIORITY_NORMAL);
+
+    hr = ID3D12Device_CreateCommandAllocator(device, D3D12_COMMAND_LIST_TYPE_COMPUTE,
+            &IID_ID3D12CommandAllocator, (void **)&context->allocator);
+    ok_(line)(hr == S_OK, "Failed to create command allocator, hr %#x.\n", hr);
+
+    hr = ID3D12Device_CreateCommandList(device, 0, D3D12_COMMAND_LIST_TYPE_COMPUTE,
+            context->allocator, NULL, &IID_ID3D12GraphicsCommandList, (void **)&context->list);
+    ok_(line)(hr == S_OK, "Failed to create command list, hr %#x.\n", hr);
+
+    return true;
+}
+
+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_(__LINE__, a, b, c, d, e, f, g, h, i)
+static void init_depth_stencil_(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_(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_(__LINE__, depth_stencil)
+static void destroy_depth_stencil_(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_FORMAT_INFO format_info;
+    unsigned int expected_plane_count;
+    ID3D12Device *device;
+    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_set_context("format %#x", format);
+
+        switch (format)
+        {
+            case DXGI_FORMAT_R32G8X24_TYPELESS:
+            case DXGI_FORMAT_D32_FLOAT_S8X24_UINT:
+            case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS:
+            case DXGI_FORMAT_X32_TYPELESS_G8X24_UINT:
+            case DXGI_FORMAT_D24_UNORM_S8_UINT:
+            case DXGI_FORMAT_R24G8_TYPELESS:
+            case DXGI_FORMAT_R24_UNORM_X8_TYPELESS:
+            case DXGI_FORMAT_X24_TYPELESS_G8_UINT:
+            case DXGI_FORMAT_NV12:
+            case DXGI_FORMAT_P010:
+            case DXGI_FORMAT_P016:
+            case DXGI_FORMAT_NV11:
+                expected_plane_count = 2;
+                break;
+            default:
+                expected_plane_count = 1;
+                break;
+        }
+
+        is_todo = format == DXGI_FORMAT_R9G9B9E5_SHAREDEXP
+                || format == DXGI_FORMAT_R8G8_B8G8_UNORM
+                || format == DXGI_FORMAT_G8R8_G8B8_UNORM
+                || format == DXGI_FORMAT_B5G6R5_UNORM
+                || format == DXGI_FORMAT_B5G5R5A1_UNORM
+                || format == DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM
+                || (DXGI_FORMAT_AYUV <= format && format <= DXGI_FORMAT_B4G4R4A4_UNORM);
+
+        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);
+            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_set_context(NULL);
+
+    /* 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);
+
+    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 suppots 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));
+    todo 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_set_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_set_context(NULL);
+
+    /* DXGI_FORMAT_UNKNOWN */
+    for (i = 1; i < ARRAY_SIZE(sample_counts); ++i)
+    {
+        vkd3d_test_set_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_set_context(NULL);
+
+    /* 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_set_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_set_context(NULL);
+
+    /* 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)
+{
+    D3D12_GPU_VIRTUAL_ADDRESS gpu_address;
+    D3D12_HEAP_PROPERTIES heap_properties;
+    D3D12_RESOURCE_DESC resource_desc;
+    ID3D12Device *device, *tmp_device;
+    D3D12_CLEAR_VALUE clear_value;
+    D3D12_RESOURCE_STATES state;
+    ID3D12Resource *resource;
+    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},
+        {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;
+
+    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);
+    todo ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
+    if (SUCCEEDED(hr))
+        ID3D12Resource_Release(resource);
+
+    /* 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_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);
+
+    /* For D3D12_HEAP_TYPE_UPLOAD the state must be D3D12_RESOURCE_STATE_GENERIC_READ. */
+    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);
+    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);
+
+    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);
+
+    /* For D3D12_HEAP_TYPE_READBACK the state must be D3D12_RESOURCE_STATE_COPY_DEST. */
+    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);
+    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);
+    }
+
+    refcount = ID3D12Device_Release(device);
+    ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount);
+}
+
+static void test_create_heap(void)
+{
+    D3D12_FEATURE_DATA_ARCHITECTURE architecture;
+    D3D12_FEATURE_DATA_D3D12_OPTIONS options;
+    D3D12_HEAP_DESC desc, result_desc;
+    ID3D12Device *device, *tmp_device;
+    bool is_pool_L1_supported;
+    HRESULT hr, expected_hr;
+    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, E_INVALIDARG},
+        {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_set_context("Test %u, %u", i, j);
+
+            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, "Test %u, %s: Got hr %#x, expected %#x.\n",
+                    i, heap_flags[j].name, hr, tests[i].expected_hr);
+            if (FAILED(hr))
+                continue;
+
+            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_set_context(NULL);
+
+    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_set_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_set_context(NULL);
+
+    is_pool_L1_supported = is_memory_pool_L1_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_set_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].pool_preference != D3D12_MEMORY_POOL_L1 || is_pool_L1_supported) ? custom_tests[i].expected_hr : E_INVALIDARG;
+        ok(hr == expected_hr, "Test %u, page_property %u, pool_preference %u: Got hr %#x, expected %#x.\n",
+                i, custom_tests[i].page_property, custom_tests[i].pool_preference, hr, expected_hr);
+        if (FAILED(hr))
+            continue;
+
+        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_set_context(NULL);
+
+done:
+    refcount = ID3D12Device_Release(device);
+    ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount);
+}
+
+static void test_create_placed_resource(void)
+{
+    D3D12_GPU_VIRTUAL_ADDRESS gpu_address;
+    D3D12_RESOURCE_DESC resource_desc;
+    ID3D12Device *device, *tmp_device;
+    D3D12_CLEAR_VALUE clear_value;
+    D3D12_RESOURCE_STATES state;
+    D3D12_HEAP_DESC heap_desc;
+    ID3D12Resource *resource;
+    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;
+    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);
+
+    refcount = ID3D12Resource_Release(resource);
+    ok(!refcount, "ID3D12Resource has %u references left.\n", (unsigned int)refcount);
+
+    /* 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);
+
+    ID3D12Heap_Release(heap);
+
+    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(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_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);
+    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);
+    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)
+{
+    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
+    D3D12_DESCRIPTOR_RANGE descriptor_ranges[2];
+    D3D12_ROOT_PARAMETER root_parameters[3];
+    ID3D12RootSignature *root_signature;
+    ID3D12Device *device, *tmp_device;
+    ULONG refcount;
+    HRESULT hr;
+
+    if (!(device = create_device()))
+    {
+        skip("Failed to create device.\n");
+        return;
+    }
+
+    /* 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);
+
+    refcount = ID3D12RootSignature_Release(root_signature);
+    ok(!refcount, "ID3D12RootSignature has %u references left.\n", (unsigned int)refcount);
+
+    /* 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 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);
+    refcount = ID3D12RootSignature_Release(root_signature);
+    ok(!refcount, "ID3D12RootSignature has %u references left.\n", (unsigned int)refcount);
+
+    /* 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);
+    todo 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);
+    todo 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);
+
+    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);
+
+    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;
+    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
+    ID3D12RootSignature *root_signature;
+    ID3D12PipelineState *pipeline_state;
+    ID3D12Device *device, *tmp_device;
+    ULONG refcount;
+    HRESULT hr;
+
+    static const DWORD dxbc_code[] =
+    {
+#if 0
+        [numthreads(1, 1, 1)]
+        void main() { }
+#endif
+        0x43425844, 0x1acc3ad0, 0x71c7b057, 0xc72c4306, 0xf432cb57, 0x00000001, 0x00000074, 0x00000003,
+        0x0000002c, 0x0000003c, 0x0000004c, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
+        0x00000008, 0x00000000, 0x00000008, 0x58454853, 0x00000020, 0x00050050, 0x00000008, 0x0100086a,
+        0x0400009b, 0x00000001, 0x00000001, 0x00000001, 0x0100003e,
+    };
+
+    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);
+
+    memset(&pipeline_state_desc, 0, sizeof(pipeline_state_desc));
+    pipeline_state_desc.pRootSignature = root_signature;
+    pipeline_state_desc.CS = shader_bytecode(dxbc_code, sizeof(dxbc_code));
+    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);
+
+    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);
+
+    refcount = ID3D12Device_Release(device);
+    ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount);
+}
+
+static void test_create_graphics_pipeline_state(void)
+{
+    D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
+    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
+    ID3D12RootSignature *root_signature;
+    ID3D12PipelineState *pipeline_state;
+    ID3D12Device *device, *tmp_device;
+    D3D12_BLEND_DESC *blend;
+    ULONG refcount;
+    unsigned int i;
+    HRESULT hr;
+
+    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);
+
+    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);
+
+    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_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_set_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_set_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_set_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_set_context("command signature");
+            unknown = (IUnknown *)create_command_signature(device, D3D12_INDIRECT_ARGUMENT_TYPE_DRAW);
+        }
+        else if (IsEqualGUID(tests[i], &IID_ID3D12DescriptorHeap))
+        {
+            vkd3d_test_set_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_set_context("device");
+            unknown = (IUnknown *)create_device();
+        }
+        else if (IsEqualGUID(tests[i], &IID_ID3D12Fence))
+        {
+            vkd3d_test_set_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_set_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_set_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_set_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_set_context("resource");
+            unknown = (IUnknown *)create_readback_buffer(device, 512);
+        }
+        else if (IsEqualGUID(tests[i], &IID_ID3D12RootSignature))
+        {
+            vkd3d_test_set_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_set_context(NULL);
+    }
+
+    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 < 100; ++i)
+    {
+        ++next_value;
+        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);
+    }
+
+    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 = 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);
+    }
+
+    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;
+    unsigned int rtv_increment_size;
+    ID3D12DescriptorHeap *rtv_heap;
+    D3D12_CLEAR_VALUE clear_value;
+    struct test_context_desc desc;
+    struct resource_readback rb;
+    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_set_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_set_context(NULL);
+
+    /* 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_set_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_set_context(NULL);
+
+    /* 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 = 1;
+
+        ID3D12Device_CreateRenderTargetView(device, resource, &rtv_desc, rtv_handle);
+
+        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, rtv_handle, &array_colors[i].x, 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 = 1;
+
+        ID3D12Device_CreateRenderTargetView(device, resource, &rtv_desc, rtv_handle);
+
+        ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, rtv_handle, &array_colors[i].x, 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_texture_readback_with_command_list(resource, 0, &rb, queue, command_list);
+    set_box(&box, 0, 0, 0, 32, 32, 2);
+    check_readback_data_uint(&rb, &box, 0xbf4c7f19, 1);
+    set_box(&box, 0, 0, 2, 32, 32, 4);
+    check_readback_data_uint(&rb, &box, 0xff00ff00, 1);
+    set_box(&box, 0, 0, 4, 32, 32, 32);
+    check_readback_data_uint(&rb, &box, 0xbf4c7f19, 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 test_context_desc desc;
+    struct test_context context;
+    struct resource_readback rb;
+    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, false, true},
+        {DXGI_FORMAT_R32_FLOAT, { 0, BUFFER_SIZE / sizeof(uint32_t), 0, 0, D3D12_BUFFER_UAV_FLAG_NONE},
+                {1, 0, 0, 0}, 1, false, true},
+        {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, true},
+
+        {DXGI_FORMAT_R16G16_UINT, { 0, BUFFER_SIZE / sizeof(uint32_t), 0, 0, D3D12_BUFFER_UAV_FLAG_NONE},
+                {0x1234, 0xabcd, 0, 0}, 0xabcd1234, false, true},
+        {DXGI_FORMAT_R16G16_UINT, { 0, BUFFER_SIZE / sizeof(uint32_t), 0, 0, D3D12_BUFFER_UAV_FLAG_NONE},
+                {0x10000, 0, 0, 0}, 0, false, true},
+
+        {DXGI_FORMAT_R16G16_UNORM, { 0, BUFFER_SIZE / sizeof(uint32_t), 0, 0, D3D12_BUFFER_UAV_FLAG_NONE},
+                {0x1234, 0xabcd, 0, 0}, 0xabcd1234, false, true},
+        {DXGI_FORMAT_R16G16_UNORM, { 0, BUFFER_SIZE / sizeof(uint32_t), 0, 0, D3D12_BUFFER_UAV_FLAG_NONE},
+                {0x3f000000 /* 0.5f */, 0x3f800000 /* 1.0f */, 0, 0}, 0xffff8000, true, 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, 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, true},
+
+        {DXGI_FORMAT_R16G16_FLOAT, { 0, BUFFER_SIZE / sizeof(uint32_t), 0, 0, D3D12_BUFFER_UAV_FLAG_NONE},
+                {0x1234, 0xabcd, 0, 0}, 0xabcd1234, false, true},
+        {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, true},
+
+        {DXGI_FORMAT_R8G8B8A8_UINT, { 0, BUFFER_SIZE / sizeof(uint32_t), 0, 0, D3D12_BUFFER_UAV_FLAG_NONE},
+                {0x11, 0x22, 0x33, 0x44}, 0x44332211, false, true},
+        {DXGI_FORMAT_R8G8B8A8_UINT, { 0, BUFFER_SIZE / sizeof(uint32_t), 0, 0, D3D12_BUFFER_UAV_FLAG_NONE},
+                {0x100, 0, 0, 0}, 0, false, true},
+
+        {DXGI_FORMAT_R11G11B10_FLOAT, { 0, BUFFER_SIZE / sizeof(uint32_t), 0, 0, D3D12_BUFFER_UAV_FLAG_NONE},
+                {0, 0, 0, 0}, 0, false, true},
+        {DXGI_FORMAT_R11G11B10_FLOAT, { 0, BUFFER_SIZE / sizeof(uint32_t), 0, 0, D3D12_BUFFER_UAV_FLAG_NONE},
+                {0x7ff, 0x7ff, 0x3ff, 0}, 0xffffffff, false, true},
+        {DXGI_FORMAT_R11G11B10_FLOAT, { 0, BUFFER_SIZE / sizeof(uint32_t), 0, 0, D3D12_BUFFER_UAV_FLAG_NONE},
+                {0x7ff, 0, 0x3ff, 0}, 0xffc007ff, false, true},
+        {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, 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, 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_set_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_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, &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, &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, &box, clear_value[0], 0);
+        release_resource_readback(&rb);
+
+        reset_command_list(command_list, context.allocator);
+        ID3D12Resource_Release(buffer);
+    }
+    vkd3d_test_set_context(NULL);
+
+    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_image(void)
+{
+    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;
+    unsigned int image_size, image_depth;
+    D3D12_RESOURCE_DESC resource_desc;
+    struct test_context_desc desc;
+    struct test_context context;
+    struct resource_readback rb;
+    ID3D12CommandQueue *queue;
+    bool is_inside, success;
+    ID3D12Resource *texture;
+    ID3D12Device *device;
+    UINT clear_value[4];
+    HRESULT hr;
+
+#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;
+    }
+    tests[] =
+    {
+        /* Test clearing a specific mip level. */
+        {DXGI_FORMAT_R32_FLOAT,       2, 1, 0, 0, 1, 0, {}, {1,          0, 0, 0}, 1,          false, false},
+        {DXGI_FORMAT_R32_FLOAT,       2, 1, 1, 0, 1, 0, {}, {1,          0, 0, 0}, 1,          false, false},
+        {DXGI_FORMAT_R32_FLOAT,       2, 1, 0, 0, 1, 0, {}, {0x3f000000, 0, 0, 0}, 0x3f000000, true, true},
+        {DXGI_FORMAT_R32_FLOAT,       2, 1, 1, 0, 1, 0, {}, {0x3f000000, 0, 0, 0}, 0x3f000000, true, true},
+        /* Test clearing specific array layers. */
+        {DXGI_FORMAT_R32_FLOAT,       1, IMAGE_SIZE, 0, 0, IMAGE_SIZE, 0, {}, {1, 0, 0, 0}, 1, false, false},
+        {DXGI_FORMAT_R32_FLOAT,       1, IMAGE_SIZE, 0, 3, 2,          0, {}, {1, 0, 0, 0}, 1, false, false},
+        {DXGI_FORMAT_R32_FLOAT,       1, IMAGE_SIZE, 0, 0, IMAGE_SIZE, 0, {},
+                {0x3f000000, 0, 0, 0}, 0x3f000000, true, true},
+        {DXGI_FORMAT_R32_FLOAT,       1, IMAGE_SIZE, 0, 3, 2,          0, {},
+                {0x3f000000, 0, 0, 0}, 0x3f000000, true, true},
+        /* 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,          false, true},
+        {DXGI_FORMAT_R32_FLOAT,       1, 1, 0, 0, 1, 1, {{1, 2, IMAGE_SIZE - 4, IMAGE_SIZE - 2}},
+                {0x3f000000, 0, 0, 0}, 0x3f000000, true,  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,          false, true},
+        {DXGI_FORMAT_R32_FLOAT,       1, 1, 0, 0, 1, 2, {{1, 2, 3, 4}, {5, 6, 7, 8}},
+                {0x3f000000, 0, 0, 0}, 0x3f000000, true,  true},
+        /* Test uint clears with formats. */
+        {DXGI_FORMAT_R16G16_UINT,     1, 1, 0, 0, 1, 0, {}, {1,       2, 3, 4}, 0x00020001, false, false},
+        {DXGI_FORMAT_R16G16_UINT,     1, 1, 0, 0, 1, 0, {}, {0x12345, 0, 0, 0}, 0x00002345, false, true},
+        {DXGI_FORMAT_R16G16_UNORM,    1, 1, 0, 0, 1, 0, {}, {1,       2, 3, 4}, 0x00020001, false, true},
+        {DXGI_FORMAT_R16G16_FLOAT,    1, 1, 0, 0, 1, 0, {}, {1,       2, 3, 4}, 0x00020001, false, true},
+        {DXGI_FORMAT_R8G8B8A8_UINT,   1, 1, 0, 0, 1, 0, {}, {1,       2, 3, 4}, 0x04030201, false, false},
+        {DXGI_FORMAT_R8G8B8A8_UINT,   1, 1, 0, 0, 1, 0, {}, {0x123,   0, 0, 0}, 0x00000023, false, true},
+        {DXGI_FORMAT_R8G8B8A8_UNORM,  1, 1, 0, 0, 1, 0, {}, {1,       2, 3, 4}, 0x04030201, false, true},
+        {DXGI_FORMAT_R11G11B10_FLOAT, 1, 1, 0, 0, 1, 0, {}, {1,       2, 3, 4}, 0x00c01001, false, true},
+        /* Test float clears with formats. */
+        {DXGI_FORMAT_R16G16_UNORM,    1, 1, 0, 0, 1, 0, {},
+                {0x3f000000 /* 0.5f */, 0x3f800000 /* 1.0f */, 0, 0}, 0xffff8000, true, true},
+        {DXGI_FORMAT_R16G16_FLOAT,    1, 1, 0, 0, 1, 0, {},
+                {0x3f000000 /* 0.5f */, 0x3f800000 /* 1.0f */, 0, 0}, 0x3c003800, true, true},
+        {DXGI_FORMAT_R8G8B8A8_UNORM,  1, 1, 0, 0, 1, 0, {},
+                {0x3f000000 /* 0.5f */, 0x3f800000 /* 1.0f */, 0, 0}, 0x0000ff80, true, true},
+        {DXGI_FORMAT_R8G8B8A8_UNORM,  1, 1, 0, 0, 1, 0, {},
+                {0, 0, 0x3f000000 /* 0.5f */, 0x3f800000 /* 1.0f */}, 0xff800000, true, 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, true},
+    };
+
+    static const struct
+    {
+        D3D12_RESOURCE_DIMENSION resource_dim;
+        D3D12_UAV_DIMENSION view_dim;
+        bool is_layered;
+    }
+    uav_dimensions[] =
+    {
+        {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)
+        {
+            vkd3d_test_set_context("Dim %u, Test %u", d, i);
+
+            if (tests[i].image_layers > 1 && !uav_dimensions[d].is_layered)
+                continue;
+
+            resource_desc.Dimension = uav_dimensions[d].resource_dim;
+            resource_desc.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
+            resource_desc.Width = IMAGE_SIZE;
+            resource_desc.Height = IMAGE_SIZE;
+            if (uav_dimensions[d].resource_dim == D3D12_RESOURCE_DIMENSION_TEXTURE1D)
+                resource_desc.Height = 1;
+            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;
+
+            if (FAILED(hr = ID3D12Device_CreateCommittedResource(device, &heap_properties,
+                    D3D12_HEAP_FLAG_NONE, &resource_desc, D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
+                    NULL, &IID_ID3D12Resource, (void **)&texture)))
+            {
+                skip("Failed to create texture, hr %#x.\n", hr);
+                continue;
+            }
+
+            uav_desc.Format = tests[i].format;
+            uav_desc.ViewDimension = uav_dimensions[d].view_dim;
+
+            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));
+            }
+
+            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_texture_readback_with_command_list(texture,
+                        tests[i].mip_level + (layer * tests[i].image_mips),
+                        &rb, queue, command_list);
+
+                for (p = 0; p < image_depth * 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 = get_readback_uint(&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);
+        }
+    }
+
+    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 (!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.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(ib);
+    ibv.SizeInBytes = sizeof(indices);
+    ibv.Format = DXGI_FORMAT_R16_UINT;
+
+    ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, context.rtv, white, 0, NULL);
+
+    if (!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);
+    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;
+    unsigned int i;
+    HRESULT hr;
+
+    static const DWORD ps_code[] =
+    {
+#if 0
+        void main(out float4 target0 : SV_Target0, out float4 target1 : SV_Target1,
+                out float4 target2 : SV_Target2)
+        {
+            target0 = float4(1.0f, 0.0f, 0.0f, 1.0f);
+            target1 = float4(2.0f, 0.0f, 0.0f, 1.0f);
+            target2 = float4(3.0f, 0.0f, 0.0f, 1.0f);
+        }
+#endif
+        0x43425844, 0xc4325131, 0x8ba4a693, 0x08d15431, 0xcb990885, 0x00000001, 0x0000013c, 0x00000003,
+        0x0000002c, 0x0000003c, 0x000000a0, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
+        0x0000005c, 0x00000003, 0x00000008, 0x00000050, 0x00000000, 0x00000000, 0x00000003, 0x00000000,
+        0x0000000f, 0x00000050, 0x00000001, 0x00000000, 0x00000003, 0x00000001, 0x0000000f, 0x00000050,
+        0x00000002, 0x00000000, 0x00000003, 0x00000002, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074,
+        0x58454853, 0x00000094, 0x00000050, 0x00000025, 0x0100086a, 0x03000065, 0x001020f2, 0x00000000,
+        0x03000065, 0x001020f2, 0x00000001, 0x03000065, 0x001020f2, 0x00000002, 0x08000036, 0x001020f2,
+        0x00000000, 0x00004002, 0x3f800000, 0x00000000, 0x00000000, 0x3f800000, 0x08000036, 0x001020f2,
+        0x00000001, 0x00004002, 0x40000000, 0x00000000, 0x00000000, 0x3f800000, 0x08000036, 0x001020f2,
+        0x00000002, 0x00004002, 0x40400000, 0x00000000, 0x00000000, 0x3f800000, 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_descriptor_count = ARRAY_SIZE(rtvs);
+    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, 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);
+}
+
+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;
+    unsigned int i;
+    HRESULT hr;
+
+    static const DWORD ps_code[] =
+    {
+#if 0
+        void main(out float4 target1 : SV_Target1, out float4 target2 : SV_Target2)
+        {
+            target1 = float4(2.0f, 0.0f, 0.0f, 1.0f);
+            target2 = float4(3.0f, 0.0f, 0.0f, 1.0f);
+        }
+#endif
+        0x43425844, 0x980554be, 0xb8743fb0, 0xf5bb8deb, 0x639feaf8, 0x00000001, 0x000000f4, 0x00000003,
+        0x0000002c, 0x0000003c, 0x00000088, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
+        0x00000044, 0x00000002, 0x00000008, 0x00000038, 0x00000001, 0x00000000, 0x00000003, 0x00000001,
+        0x0000000f, 0x00000038, 0x00000002, 0x00000000, 0x00000003, 0x00000002, 0x0000000f, 0x545f5653,
+        0x65677261, 0xabab0074, 0x52444853, 0x00000064, 0x00000040, 0x00000019, 0x03000065, 0x001020f2,
+        0x00000001, 0x03000065, 0x001020f2, 0x00000002, 0x08000036, 0x001020f2, 0x00000001, 0x00004002,
+        0x40000000, 0x00000000, 0x00000000, 0x3f800000, 0x08000036, 0x001020f2, 0x00000002, 0x00004002,
+        0x40400000, 0x00000000, 0x00000000, 0x3f800000, 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_descriptor_count = 16;
+    desc.no_pipeline = true;
+    if (!init_test_context(&context, &desc))
+        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], &white.x, 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);
+}
+
+static void test_unknown_dsv_format(void)
+{
+    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
+    ID3D12GraphicsCommandList *command_list;
+    struct depth_stencil_resource ds;
+    D3D12_CLEAR_VALUE clear_value;
+    struct test_context_desc desc;
+    struct test_context context;
+    ID3D12CommandQueue *queue;
+    HRESULT hr;
+
+    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 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};
+
+    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;
+
+    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);
+}
+
+static void test_append_aligned_element(void)
+{
+    ID3D12GraphicsCommandList *command_list;
+    D3D12_INPUT_LAYOUT_DESC input_layout;
+    D3D12_VERTEX_BUFFER_VIEW vbv[6];
+    struct test_context_desc desc;
+    struct test_context context;
+    struct resource_readback rb;
+    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_texture_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
+    color = get_readback_uint(&rb, 80, 16, 0);
+    ok(compare_color(color, 0xff0000ff, 1), "Got unexpected color 0x%08x.\n", color);
+    color = get_readback_uint(&rb, 240, 16, 0);
+    ok(compare_color(color, 0xff00ff00, 1), "Got unexpected color 0x%08x.\n", color);
+    color = get_readback_uint(&rb, 400, 16, 0);
+    ok(compare_color(color, 0xffff0000, 1), "Got unexpected color 0x%08x.\n", color);
+    color = get_readback_uint(&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 test_context_desc desc;
+    D3D12_VERTEX_BUFFER_VIEW vbv;
+    struct test_context context;
+    struct resource_readback rb;
+    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_set_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_texture_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
+        all_match = true;
+        for (y = 0; y < rb.height; ++y)
+        {
+            for (x = 0; x < rb.width; ++x)
+            {
+                v = get_readback_vec4(&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_set_context(NULL);
+
+    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;
+    D3D12_INPUT_LAYOUT_DESC input_layout;
+    struct test_context_desc desc;
+    D3D12_VERTEX_BUFFER_VIEW vbv;
+    struct test_context context;
+    struct resource_readback rb;
+    ID3D12CommandQueue *queue;
+    D3D12_VIEWPORT viewport;
+    unsigned int i, x, y;
+    ID3D12Resource *vb;
+
+    static const DWORD vs_code[] =
+    {
+#if 0
+        void main(in float4 in_position : POSITION,
+                in float2 in_texcoord : TEXCOORD,
+                out float4 position : SV_Position,
+                out float2 texcoord : TEXCOORD)
+        {
+            position = in_position;
+            texcoord = in_texcoord;
+        }
+#endif
+        0x43425844, 0x4df282ca, 0x85c8bbfc, 0xd44ad19f, 0x1158be97, 0x00000001, 0x00000148, 0x00000003,
+        0x0000002c, 0x00000080, 0x000000d8, 0x4e475349, 0x0000004c, 0x00000002, 0x00000008, 0x00000038,
+        0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000f0f, 0x00000041, 0x00000000, 0x00000000,
+        0x00000003, 0x00000001, 0x00000303, 0x49534f50, 0x4e4f4954, 0x58455400, 0x524f4f43, 0xabab0044,
+        0x4e47534f, 0x00000050, 0x00000002, 0x00000008, 0x00000038, 0x00000000, 0x00000001, 0x00000003,
+        0x00000000, 0x0000000f, 0x00000044, 0x00000000, 0x00000000, 0x00000003, 0x00000001, 0x00000c03,
+        0x505f5653, 0x7469736f, 0x006e6f69, 0x43584554, 0x44524f4f, 0xababab00, 0x52444853, 0x00000068,
+        0x00010040, 0x0000001a, 0x0300005f, 0x001010f2, 0x00000000, 0x0300005f, 0x00101032, 0x00000001,
+        0x04000067, 0x001020f2, 0x00000000, 0x00000001, 0x03000065, 0x00102032, 0x00000001, 0x05000036,
+        0x001020f2, 0x00000000, 0x00101e46, 0x00000000, 0x05000036, 0x00102032, 0x00000001, 0x00101046,
+        0x00000001, 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,
+                float2 texcoord : TEXCOORD) : SV_Target
+        {
+            return float4(position.xy, texcoord);
+        }
+#endif
+        0x43425844, 0xa15616bc, 0x6862ab1c, 0x28b915c0, 0xdb0df67c, 0x00000001, 0x0000011c, 0x00000003,
+        0x0000002c, 0x00000084, 0x000000b8, 0x4e475349, 0x00000050, 0x00000002, 0x00000008, 0x00000038,
+        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000030f, 0x00000044, 0x00000000, 0x00000000,
+        0x00000003, 0x00000001, 0x00000303, 0x505f5653, 0x7469736f, 0x006e6f69, 0x43584554, 0x44524f4f,
+        0xababab00, 0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000,
+        0x00000003, 0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x52444853, 0x0000005c,
+        0x00000040, 0x00000017, 0x04002064, 0x00101032, 0x00000000, 0x00000001, 0x03001062, 0x00101032,
+        0x00000001, 0x03000065, 0x001020f2, 0x00000000, 0x05000036, 0x00102032, 0x00000000, 0x00101046,
+        0x00000000, 0x05000036, 0x001020c2, 0x00000000, 0x00101406, 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_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,
+    };
+
+    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_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_texture_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
+        for (y = 0; y < rb.height; ++y)
+        {
+            for (x = 0; x < rb.width; ++x)
+            {
+                const struct vec4 *v = get_readback_vec4(&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);
+}
+
+static void test_scissor(void)
+{
+    ID3D12GraphicsCommandList *command_list;
+    struct test_context_desc desc;
+    struct test_context context;
+    struct resource_readback rb;
+    ID3D12CommandQueue *queue;
+    unsigned int color;
+    RECT scissor_rect;
+
+    static const DWORD ps_code[] =
+    {
+#if 0
+        float4 main(float4 position : SV_POSITION) : SV_Target
+        {
+            return float4(0.0, 1.0, 0.0, 1.0);
+        }
+#endif
+        0x43425844, 0x30240e72, 0x012f250c, 0x8673c6ea, 0x392e4cec, 0x00000001, 0x000000d4, 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, 0x52444853, 0x00000038, 0x00000040,
+        0x0000000e, 0x03000065, 0x001020f2, 0x00000000, 0x08000036, 0x001020f2, 0x00000000, 0x00004002,
+        0x00000000, 0x3f800000, 0x00000000, 0x3f800000, 0x0100003e,
+    };
+    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};
+    static const float red[] = {1.0f, 0.0f, 0.0f, 1.0f};
+
+    memset(&desc, 0, sizeof(desc));
+    desc.rt_width = 640;
+    desc.rt_height = 480;
+    desc.ps = &ps;
+    if (!init_test_context(&context, &desc))
+        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_texture_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
+    color = get_readback_uint(&rb, 320, 60, 0);
+    ok(compare_color(color, 0xff0000ff, 1), "Got unexpected color 0x%08x.\n", color);
+    color = get_readback_uint(&rb, 80, 240, 0);
+    ok(compare_color(color, 0xff0000ff, 1), "Got unexpected color 0x%08x.\n", color);
+    color = get_readback_uint(&rb, 320, 240, 0);
+    ok(compare_color(color, 0xff00ff00, 1), "Got unexpected color 0x%08x.\n", color);
+    color = get_readback_uint(&rb, 560, 240, 0);
+    ok(compare_color(color, 0xff0000ff, 1), "Got unexpected color 0x%08x.\n", color);
+    color = get_readback_uint(&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);
+}
+
+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;
+    ID3D12Resource *vb;
+    HRESULT hr;
+
+    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 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)};
+
+    memset(&desc, 0, sizeof(desc));
+    desc.no_render_target = true;
+    if (!init_test_context(&context, &desc))
+        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);
+    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_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 = 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_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_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_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);
+    check_sub_resource_float(ds.texture, 0, queue, command_list, 0.5f, 1);
+
+    destroy_depth_stencil(&ds);
+    ID3D12Resource_Release(vb);
+    destroy_test_context(&context);
+}
+
+static void test_draw_depth_only(void)
+{
+    D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
+    ID3D12GraphicsCommandList *command_list;
+    struct depth_stencil_resource ds;
+    struct test_context_desc desc;
+    struct resource_readback rb;
+    struct test_context context;
+    ID3D12CommandQueue *queue;
+    unsigned int i, j;
+    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
+    {
+        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},
+    };
+
+    memset(&desc, 0, sizeof(desc));
+    desc.no_render_target = true;
+    if (!init_test_context(&context, &desc))
+        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_set_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_set_context(NULL);
+
+    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_texture_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, 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);
+}
+
+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))
+    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];
+    D3D12_RESOURCE_DESC resource_desc;
+    ID3D12Resource *readback_buffer;
+    struct test_context_desc desc;
+    struct resource_readback rb;
+    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, 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 test_context context;
+    struct resource_readback rb;
+    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 (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_texture_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
+    for (y = 0; y < rb.height; ++y)
+    {
+        for (x = 0; x < rb.width; ++x)
+        {
+           unsigned int v = get_readback_uint(&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_texture_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
+    for (y = 0; y < rb.height; ++y)
+    {
+        for (x = 0; x < rb.width; ++x)
+        {
+           unsigned int v = get_readback_uint(&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;
+    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_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_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 << 2;
+        }
+#endif
+        0x43425844, 0x5b749bf4, 0xe24de3dc, 0xbbd75bc9, 0xc6fc9eca, 0x00000001, 0x000000c0, 0x00000003,
+        0x0000002c, 0x0000003c, 0x00000070, 0x4e475349, 0x00000008, 0x00000000, 0x00000008, 0x4e47534f,
+        0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000001, 0x00000000,
+        0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x52444853, 0x00000048, 0x00000040, 0x00000012,
+        0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x03000065, 0x001020f2, 0x00000000, 0x08000029,
+        0x001020f2, 0x00000000, 0x00208e46, 0x00000000, 0x00000000, 0x00004001, 0x00000002, 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 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}}},
+
+        /* FIXME: Ordered/unordered comparisons are broken on Mesa. */
+        {&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_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;
+        } input;
+        union
+        {
+            struct uvec4 u;
+            struct ivec4 i;
+            struct vec4 f;
+        } output;
+        bool skip_on_warp;
+    }
+    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_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, {{{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_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;
+
+    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)
+    {
+        vkd3d_test_set_context("%u:%s", i, tests[i].ps->name);
+
+        if (tests[i].skip_on_warp && use_warp_device)
+        {
+            skip("Skipping shader '%s' test on WARP.\n", tests[i].ps->name);
+            continue;
+        }
+
+        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_set_context(NULL);
+
+    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_set_context("%u:%s", i, uint_tests[i].ps->name);
+
+        if (uint_tests[i].skip_on_warp && use_warp_device)
+        {
+            skip("Skipping shader '%s' test on WARP.\n", uint_tests[i].ps->name);
+            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);
+        }
+
+        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);
+        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_set_context(NULL);
+
+    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 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, 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_interstage_interface(void)
+{
+    static const float white[] = {1.0f, 1.0f, 1.0f, 1.0f};
+    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;
+    ID3D12Resource *vb;
+
+    static const DWORD vs_code[] =
+    {
+#if 0
+        struct vertex
+        {
+            float4 position : SV_Position;
+            float2 t0 : TEXCOORD0;
+            nointerpolation float t1 : TEXCOORD1;
+            uint t2 : TEXCOORD2;
+            uint t3 : TEXCOORD3;
+            float t4 : TEXCOORD4;
+        };
+
+        void main(in vertex vin, out vertex vout)
+        {
+            vout = vin;
+        }
+#endif
+        0x43425844, 0x561ea178, 0x7b8f454c, 0x69091b4f, 0xf28d9a01, 0x00000001, 0x000002c0, 0x00000003,
+        0x0000002c, 0x000000e4, 0x0000019c, 0x4e475349, 0x000000b0, 0x00000006, 0x00000008, 0x00000098,
+        0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000f0f, 0x000000a4, 0x00000000, 0x00000000,
+        0x00000003, 0x00000001, 0x00000303, 0x000000a4, 0x00000001, 0x00000000, 0x00000003, 0x00000002,
+        0x00000101, 0x000000a4, 0x00000002, 0x00000000, 0x00000001, 0x00000003, 0x00000101, 0x000000a4,
+        0x00000003, 0x00000000, 0x00000001, 0x00000004, 0x00000101, 0x000000a4, 0x00000004, 0x00000000,
+        0x00000003, 0x00000005, 0x00000101, 0x505f5653, 0x7469736f, 0x006e6f69, 0x43584554, 0x44524f4f,
+        0xababab00, 0x4e47534f, 0x000000b0, 0x00000006, 0x00000008, 0x00000098, 0x00000000, 0x00000001,
+        0x00000003, 0x00000000, 0x0000000f, 0x000000a4, 0x00000000, 0x00000000, 0x00000003, 0x00000001,
+        0x00000c03, 0x000000a4, 0x00000004, 0x00000000, 0x00000003, 0x00000001, 0x00000b04, 0x000000a4,
+        0x00000001, 0x00000000, 0x00000003, 0x00000002, 0x00000e01, 0x000000a4, 0x00000002, 0x00000000,
+        0x00000001, 0x00000002, 0x00000d02, 0x000000a4, 0x00000003, 0x00000000, 0x00000001, 0x00000002,
+        0x00000b04, 0x505f5653, 0x7469736f, 0x006e6f69, 0x43584554, 0x44524f4f, 0xababab00, 0x58454853,
+        0x0000011c, 0x00010050, 0x00000047, 0x0100086a, 0x0300005f, 0x001010f2, 0x00000000, 0x0300005f,
+        0x00101032, 0x00000001, 0x0300005f, 0x00101012, 0x00000002, 0x0300005f, 0x00101012, 0x00000003,
+        0x0300005f, 0x00101012, 0x00000004, 0x0300005f, 0x00101012, 0x00000005, 0x04000067, 0x001020f2,
+        0x00000000, 0x00000001, 0x03000065, 0x00102032, 0x00000001, 0x03000065, 0x00102042, 0x00000001,
+        0x03000065, 0x00102012, 0x00000002, 0x03000065, 0x00102022, 0x00000002, 0x03000065, 0x00102042,
+        0x00000002, 0x05000036, 0x001020f2, 0x00000000, 0x00101e46, 0x00000000, 0x05000036, 0x00102032,
+        0x00000001, 0x00101046, 0x00000001, 0x05000036, 0x00102042, 0x00000001, 0x0010100a, 0x00000005,
+        0x05000036, 0x00102012, 0x00000002, 0x0010100a, 0x00000002, 0x05000036, 0x00102022, 0x00000002,
+        0x0010100a, 0x00000003, 0x05000036, 0x00102042, 0x00000002, 0x0010100a, 0x00000004, 0x0100003e,
+    };
+    static const D3D12_SHADER_BYTECODE vs = {vs_code, sizeof(vs_code)};
+    static const DWORD ps_code[] =
+    {
+#if 0
+        void main(float4 position : SV_Position, float2 t0 : TEXCOORD0,
+                nointerpolation float t1 : TEXCOORD1, uint t2 : TEXCOORD2,
+                uint t3 : TEXCOORD3, float t4 : TEXCOORD4, out float4 o : SV_Target)
+        {
+            o.x = t0.y + t1;
+            o.y = t2 + t3;
+            o.z = t4;
+            o.w = t0.x;
+        }
+#endif
+        0x43425844, 0x21076b15, 0x493d36f1, 0x0cd125d6, 0x1e92c724, 0x00000001, 0x000001e0, 0x00000003,
+        0x0000002c, 0x000000e4, 0x00000118, 0x4e475349, 0x000000b0, 0x00000006, 0x00000008, 0x00000098,
+        0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000000f, 0x000000a4, 0x00000000, 0x00000000,
+        0x00000003, 0x00000001, 0x00000303, 0x000000a4, 0x00000004, 0x00000000, 0x00000003, 0x00000001,
+        0x00000404, 0x000000a4, 0x00000001, 0x00000000, 0x00000003, 0x00000002, 0x00000101, 0x000000a4,
+        0x00000002, 0x00000000, 0x00000001, 0x00000002, 0x00000202, 0x000000a4, 0x00000003, 0x00000000,
+        0x00000001, 0x00000002, 0x00000404, 0x505f5653, 0x7469736f, 0x006e6f69, 0x43584554, 0x44524f4f,
+        0xababab00, 0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000,
+        0x00000003, 0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x000000c0,
+        0x00000050, 0x00000030, 0x0100086a, 0x03001062, 0x00101032, 0x00000001, 0x03001062, 0x00101042,
+        0x00000001, 0x03000862, 0x00101012, 0x00000002, 0x03000862, 0x00101022, 0x00000002, 0x03000862,
+        0x00101042, 0x00000002, 0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000001, 0x0700001e,
+        0x00100012, 0x00000000, 0x0010101a, 0x00000002, 0x0010102a, 0x00000002, 0x05000056, 0x00102022,
+        0x00000000, 0x0010000a, 0x00000000, 0x07000000, 0x00102012, 0x00000000, 0x0010101a, 0x00000001,
+        0x0010100a, 0x00000002, 0x05000036, 0x001020c2, 0x00000000, 0x001012a6, 0x00000001, 0x0100003e,
+    };
+    static const D3D12_SHADER_BYTECODE ps = {ps_code, sizeof(ps_code)};
+    static const D3D12_INPUT_ELEMENT_DESC layout_desc[] =
+    {
+        {"SV_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},
+        {"TEXCOORD",    1, DXGI_FORMAT_R32_FLOAT,    0, 16, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
+        {"TEXCOORD",    2, DXGI_FORMAT_R32_UINT,     0, 20, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
+        {"TEXCOORD",    3, DXGI_FORMAT_R32_UINT,     0, 24, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
+        {"TEXCOORD",    4, DXGI_FORMAT_R32_FLOAT,    0, 28, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
+    };
+    static const struct
+    {
+        struct vec2 position;
+        struct vec2 t0;
+        float t1;
+        unsigned int t2;
+        unsigned int t3;
+        float t4;
+    }
+    quad[] =
+    {
+        {{-1.0f, -1.0f}, {3.0f, 5.0f}, 5.0f, 2, 6, 7.0f},
+        {{-1.0f,  1.0f}, {3.0f, 5.0f}, 5.0f, 2, 6, 7.0f},
+        {{ 1.0f, -1.0f}, {3.0f, 5.0f}, 5.0f, 2, 6, 7.0f},
+        {{ 1.0f,  1.0f}, {3.0f, 5.0f}, 5.0f, 2, 6, 7.0f},
+    };
+    static const struct vec4 expected_result = {10.0f, 8.0f, 7.0f, 3.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_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);
+
+    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);
+    check_sub_resource_vec4(context.render_target, 0, queue, command_list, &expected_result, 0);
+
+    ID3D12Resource_Release(vb);
+    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_set_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_set_context(NULL);
+
+    ID3D12Resource_Release(vb);
+    ID3D12Resource_Release(uint_render_target);
+    destroy_test_context(&context);
+}
+
+static void check_descriptor_range_(unsigned int line, const D3D12_DESCRIPTOR_RANGE *range,
+        const D3D12_DESCRIPTOR_RANGE *expected_range)
+{
+    ok_(line)(range->RangeType == expected_range->RangeType,
+            "Got range type %#x, expected %#x.\n", range->RangeType, expected_range->RangeType);
+    ok_(line)(range->NumDescriptors == expected_range->NumDescriptors,
+            "Got descriptor count %u, expected %u.\n", range->NumDescriptors, expected_range->NumDescriptors);
+    ok_(line)(range->BaseShaderRegister == expected_range->BaseShaderRegister,
+            "Got base shader register %u, expected %u.\n",
+            range->BaseShaderRegister, expected_range->BaseShaderRegister);
+    ok_(line)(range->RegisterSpace == expected_range->RegisterSpace,
+            "Got register space %u, expected %u.\n", range->RegisterSpace, expected_range->RegisterSpace);
+    ok_(line)(range->OffsetInDescriptorsFromTableStart == expected_range->OffsetInDescriptorsFromTableStart,
+            "Got offset %u, expected %u.\n", range->OffsetInDescriptorsFromTableStart,
+            expected_range->OffsetInDescriptorsFromTableStart);
+}
+
+static void check_descriptor_range1_(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_(line)(range->RangeType == expected_range->RangeType,
+            "Got range type %#x, expected %#x.\n", range->RangeType, expected_range->RangeType);
+    ok_(line)(range->NumDescriptors == expected_range->NumDescriptors,
+            "Got descriptor count %u, expected %u.\n", range->NumDescriptors, expected_range->NumDescriptors);
+    ok_(line)(range->BaseShaderRegister == expected_range->BaseShaderRegister,
+            "Got base shader register %u, expected %u.\n",
+            range->BaseShaderRegister, expected_range->BaseShaderRegister);
+    ok_(line)(range->RegisterSpace == expected_range->RegisterSpace,
+            "Got register space %u, expected %u.\n", range->RegisterSpace, expected_range->RegisterSpace);
+    ok_(line)(range->Flags == expected_flags,
+            "Got descriptor range flags %#x, expected %#x.\n", range->Flags, expected_flags);
+    ok_(line)(range->OffsetInDescriptorsFromTableStart == expected_range->OffsetInDescriptorsFromTableStart,
+            "Got offset %u, expected %u.\n", range->OffsetInDescriptorsFromTableStart,
+            expected_range->OffsetInDescriptorsFromTableStart);
+}
+
+static void check_root_parameter_(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_(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_(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_(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_(line)(constants->ShaderRegister == expected_constants->ShaderRegister,
+                    "Got shader register %u, expected %u.\n",
+                    constants->ShaderRegister, expected_constants->ShaderRegister);
+            ok_(line)(constants->RegisterSpace == expected_constants->RegisterSpace,
+                    "Got register space %u, expected %u.\n",
+                    constants->RegisterSpace, expected_constants->RegisterSpace);
+            ok_(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_(line)(descriptor->ShaderRegister == expected_descriptor->ShaderRegister,
+                    "Got shader register %u, expected %u.\n",
+                    descriptor->ShaderRegister, expected_descriptor->ShaderRegister);
+            ok_(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_(line)(parameter->ShaderVisibility == expected_parameter->ShaderVisibility,
+            "Got shader visibility %#x, expected %#x.\n",
+            parameter->ShaderVisibility, expected_parameter->ShaderVisibility);
+}
+
+static void check_root_parameter1_(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_(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_(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_(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_(line)(constants->ShaderRegister == expected_constants->ShaderRegister,
+                    "Got shader register %u, expected %u.\n",
+                    constants->ShaderRegister, expected_constants->ShaderRegister);
+            ok_(line)(constants->RegisterSpace == expected_constants->RegisterSpace,
+                    "Got register space %u, expected %u.\n",
+                    constants->RegisterSpace, expected_constants->RegisterSpace);
+            ok_(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_(line)(descriptor->ShaderRegister == expected_descriptor->ShaderRegister,
+                    "Got shader register %u, expected %u.\n",
+                    descriptor->ShaderRegister, expected_descriptor->ShaderRegister);
+            ok_(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_(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_(line)(parameter->ShaderVisibility == expected_parameter->ShaderVisibility,
+            "Got shader visibility %#x, expected %#x.\n",
+            parameter->ShaderVisibility, expected_parameter->ShaderVisibility);
+}
+
+static void check_static_sampler_(unsigned int line, const D3D12_STATIC_SAMPLER_DESC *sampler,
+        const D3D12_STATIC_SAMPLER_DESC *expected_sampler)
+{
+    ok_(line)(sampler->Filter == expected_sampler->Filter,
+            "Got filter %#x, expected %#x.\n", sampler->Filter, expected_sampler->Filter);
+    ok_(line)(sampler->AddressU == expected_sampler->AddressU,
+            "Got address U %#x, expected %#x.\n", sampler->AddressU, expected_sampler->AddressU);
+    ok_(line)(sampler->AddressV == expected_sampler->AddressV,
+            "Got address V %#x, expected %#x.\n", sampler->AddressV, expected_sampler->AddressV);
+    ok_(line)(sampler->AddressW == expected_sampler->AddressW,
+            "Got address W %#x, expected %#x.\n", sampler->AddressW, expected_sampler->AddressW);
+    ok_(line)(sampler->MipLODBias == expected_sampler->MipLODBias,
+            "Got mip LOD bias %.8e, expected %.8e.\n", sampler->MipLODBias, expected_sampler->MipLODBias);
+    ok_(line)(sampler->MaxAnisotropy == expected_sampler->MaxAnisotropy,
+            "Got max anisotropy %u, expected %u.\n", sampler->MaxAnisotropy, expected_sampler->MaxAnisotropy);
+    ok_(line)(sampler->ComparisonFunc == expected_sampler->ComparisonFunc,
+            "Got comparison func %#x, expected %#x.\n", sampler->ComparisonFunc, expected_sampler->ComparisonFunc);
+    ok_(line)(sampler->BorderColor == expected_sampler->BorderColor,
+            "Got border color %#x, expected %#x.\n", sampler->BorderColor, expected_sampler->BorderColor);
+    ok_(line)(sampler->MinLOD == expected_sampler->MinLOD,
+            "Got min LOD %.8e, expected %.8e.\n", sampler->MinLOD, expected_sampler->MinLOD);
+    ok_(line)(sampler->MaxLOD == expected_sampler->MaxLOD,
+            "Got max LOD %.8e, expected %.8e.\n", sampler->MaxLOD, expected_sampler->MaxLOD);
+    ok_(line)(sampler->ShaderRegister == expected_sampler->ShaderRegister,
+            "Got shader register %u, expected %u.\n", sampler->ShaderRegister, expected_sampler->ShaderRegister);
+    ok_(line)(sampler->RegisterSpace == expected_sampler->RegisterSpace,
+            "Got register space %u, expected %u.\n", sampler->RegisterSpace, expected_sampler->RegisterSpace);
+    ok_(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_(__LINE__, desc, expected)
+static void check_root_signature_desc_(unsigned int line, const D3D12_ROOT_SIGNATURE_DESC *desc,
+        const D3D12_ROOT_SIGNATURE_DESC *expected_desc)
+{
+    unsigned int i;
+
+    ok_(line)(desc->NumParameters == expected_desc->NumParameters,
+            "Got parameter count %u, expected %u.\n",
+            desc->NumParameters, expected_desc->NumParameters);
+    if (!expected_desc->pParameters)
+    {
+        ok_(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_(line, &desc->pParameters[i], &expected_desc->pParameters[i]);
+    }
+    ok_(line)(desc->NumStaticSamplers == expected_desc->NumStaticSamplers,
+            "Got static sampler count %u, expected %u.\n",
+            desc->NumStaticSamplers, expected_desc->NumStaticSamplers);
+    if (!expected_desc->pStaticSamplers)
+    {
+        ok_(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_(line, &desc->pStaticSamplers[i], &expected_desc->pStaticSamplers[i]);
+    }
+    ok_(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_(__LINE__, a, b, c)
+static void check_root_signature_desc1_(unsigned int line, const D3D12_ROOT_SIGNATURE_DESC1 *desc,
+        const D3D12_ROOT_SIGNATURE_DESC1 *expected_desc, bool converted)
+{
+    unsigned int i;
+
+    ok_(line)(desc->NumParameters == expected_desc->NumParameters,
+            "Got parameter count %u, expected %u.\n",
+            desc->NumParameters, expected_desc->NumParameters);
+    if (!expected_desc->pParameters)
+    {
+        ok_(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_(line, &desc->pParameters[i], &expected_desc->pParameters[i], converted);
+    }
+    ok_(line)(desc->NumStaticSamplers == expected_desc->NumStaticSamplers,
+            "Got static sampler count %u, expected %u.\n",
+            desc->NumStaticSamplers, expected_desc->NumStaticSamplers);
+    if (!expected_desc->pStaticSamplers)
+    {
+        ok_(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_(line, &desc->pStaticSamplers[i], &expected_desc->pStaticSamplers[i]);
+    }
+    ok_(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_(__LINE__, a, b, c)
+static void check_root_signature_deserialization_(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_(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_(line, desc, expected_desc);
+
+    refcount = ID3D12RootSignatureDeserializer_Release(deserializer);
+    ok_(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_(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_(line, &versioned_desc->Desc_1_0, expected_desc);
+
+    hr = ID3D12VersionedRootSignatureDeserializer_GetRootSignatureDescAtVersion(versioned_deserializer,
+            D3D_ROOT_SIGNATURE_VERSION_1_0, &versioned_desc2);
+    ok_(line)(hr == S_OK, "Failed to get root signature 1.0, hr %#x.\n", hr);
+    ok_(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_(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_(line, &versioned_desc->Desc_1_1, expected_desc1, true);
+
+    refcount = ID3D12VersionedRootSignatureDeserializer_Release(versioned_deserializer);
+    ok_(line)(!refcount, "ID3D12VersionedRootSignatureDeserializer has %u references left.\n", (unsigned int)refcount);
+}
+
+#define check_root_signature_deserialization1(a, b, c) check_root_signature_deserialization1_(__LINE__, a, b, c)
+static void check_root_signature_deserialization1_(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_(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_(line, &versioned_desc->Desc_1_1, expected_desc1, false);
+
+    hr = ID3D12VersionedRootSignatureDeserializer_GetRootSignatureDescAtVersion(versioned_deserializer,
+            D3D_ROOT_SIGNATURE_VERSION_1_1, &versioned_desc2);
+    ok_(line)(hr == S_OK, "Failed to get root signature 1.1, hr %#x.\n", hr);
+    ok_(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_(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_(line, &versioned_desc->Desc_1_0, expected_desc);
+
+    refcount = ID3D12VersionedRootSignatureDeserializer_Release(versioned_deserializer);
+    ok_(line)(!refcount, "ID3D12VersionedRootSignatureDeserializer has %u references left.\n", (unsigned int)refcount);
+
+    hr = D3D12CreateRootSignatureDeserializer(code->pShaderBytecode, code->BytecodeLength,
+            &IID_ID3D12RootSignatureDeserializer, (void **)&deserializer);
+    ok_(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_(line, desc, expected_desc);
+
+    refcount = ID3D12RootSignatureDeserializer_Release(deserializer);
+    ok_(line)(!refcount, "ID3D12RootSignatureDeserializer has %u references left.\n", (unsigned int)refcount);
+}
+
+#define check_root_signature_serialization(a, b) check_root_signature_serialization_(__LINE__, a, b)
+static void check_root_signature_serialization_(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_(line)(hr == S_OK, "Failed to serialize root signature, hr %#x.\n", hr);
+    ok_(line)(!error_blob, "Got unexpected error blob %p.\n", error_blob);
+
+    blob_buffer = ID3D10Blob_GetBufferPointer(blob);
+    blob_size = ID3D10Blob_GetBufferSize(blob);
+    ok_(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_(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_(__LINE__, a, b)
+static void check_root_signature_serialization1_(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_(line)(hr == S_OK, "Failed to serialize root signature, hr %#x.\n", hr);
+    ok_(line)(!error_blob, "Got unexpected error blob %p.\n", error_blob);
+
+    blob_buffer = ID3D10Blob_GetBufferPointer(blob);
+    blob_size = ID3D10Blob_GetBufferSize(blob);
+    ok_(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_(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_set_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)
+            continue;
+
+        check_root_signature_deserialization1(&t->code1, t->desc, t->desc1);
+        check_root_signature_serialization1(&t->code1, t->desc1);
+    }
+    vkd3d_test_set_context(NULL);
+
+    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);
+
+    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;
+    ID3D12Resource *resource, *cb;
+    unsigned int descriptor_size;
+    struct resource_readback rb;
+    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, 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, 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 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.width; ++i)
+    {
+        unsigned int got = get_readback_uint(&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;
+    D3D12_SAMPLER_DESC sampler_desc;
+    struct test_context_desc desc;
+    struct resource_readback rb;
+    struct test_context context;
+    unsigned int x_step, y_step;
+    ID3D12CommandQueue *queue;
+    ID3D12Resource *texture;
+    unsigned int i, x, 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;
+    }
+    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},
+        {&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},
+        {&ps_sample_l, &rgba_texture,        POINT, 0.0f, 0.0f, MIP_MAX, {0.5f},                   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},
+        {&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},
+        {&ps_sample_l, &rgba_texture, POINT_LINEAR, 2.0f, 0.0f, MIP_MAX, {0.0f},                   level_2_colors},
+        {&ps_sample_l, &rgba_texture, POINT_LINEAR, 2.0f, 0.0f, MIP_MAX, {1.0f},                   level_2_colors},
+        {&ps_sample_l, &rgba_texture, POINT_LINEAR, 2.0f, 0.0f, MIP_MAX, {1.5f},                   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_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)
+    {
+        vkd3d_test_set_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_texture_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)
+            {
+                unsigned int color = get_readback_uint(&rb, x * x_step + x_step / 2, y * y_step + y_step / 2, 0);
+                ok(compare_color(color, tests[i].expected_data[tests[i].texture->width * y + x], 1),
+                        "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_set_context(NULL);
+
+    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 (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_set_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_set_context(NULL);
+
+    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 test_context_desc desc;
+    struct resource_readback rb;
+    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, &white.x, 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_texture_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
+    for (y = 0; y < rb.height; ++y)
+    {
+        for (x = 0; x < rb.width; ++x)
+        {
+            const struct vec4 *expected = &expected_gather4[y * rb.width + x];
+            const struct vec4 *got = get_readback_vec4(&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, &white.x, 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_texture_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
+    for (y = 0; y < rb.height; ++y)
+    {
+        for (x = 0; x < rb.width; ++x)
+        {
+            const struct vec4 *expected = &expected_gather4_offset[y * rb.width + x];
+            const struct vec4 *got = get_readback_vec4(&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, &white.x, 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_texture_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
+    for (y = 0; y < rb.height; ++y)
+    {
+        for (x = 0; x < rb.width; ++x)
+        {
+            const struct vec4 *expected = &expected_gather4_green[y * rb.width + x];
+            const struct vec4 *got = get_readback_vec4(&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, &white.x, 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_texture_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
+    for (y = 0; y < rb.height; ++y)
+    {
+        for (x = 0; x < rb.width; ++x)
+        {
+            const struct vec4 *expected = &expected_gather4_offset[y * rb.width + x];
+            const struct vec4 *got = get_readback_vec4(&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, &white.x, 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_texture_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
+    for (y = 0; y < rb.height; ++y)
+    {
+        for (x = 0; x < rb.width; ++x)
+        {
+            const struct vec4 *expected = &expected_gather4[y * rb.width + x];
+            const struct vec4 *got = get_readback_vec4(&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 test_context_desc desc;
+    struct resource_readback rb;
+    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_(__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, &white.x, 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_texture_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
+    for (y = 0; y < rb.height; ++y)
+    {
+        for (x = 0; x < rb.width; ++x)
+        {
+            const struct vec4 *expected = &expected_gather4_c[y * rb.width + x];
+            const struct vec4 *got = get_readback_vec4(&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, &white.x, 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_texture_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
+    for (y = 0; y < rb.height; ++y)
+    {
+        for (x = 0; x < rb.width; ++x)
+        {
+            const struct vec4 *expected = &expected_gather4_po_c[y * rb.width + x];
+            const struct vec4 *got = get_readback_vec4(&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, &white.x, 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_texture_readback_with_command_list(context.render_target, 0, &rb, queue, command_list);
+    for (y = 0; y < rb.height; ++y)
+    {
+        for (x = 0; x < rb.width; ++x)
+        {
+            const struct vec4 *expected = &expected_gather4_c[y * rb.width + x];
+            const struct vec4 *got = get_readback_vec4(&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)
+{
+    D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc;
+    ID3D12GraphicsCommandList *command_list;
+    D3D12_DEPTH_STENCIL_VIEW_DESC dsv_desc;
+    D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle;
+    struct depth_stencil_resource ds;
+    struct test_context_desc desc;
+    struct resource_readback rb;
+    struct test_context context;
+    ID3D12DescriptorHeap *heap;
+    ID3D12CommandQueue *queue;
+    struct vec4 ps_constant;
+    ID3D12Device *device;
+    unsigned int i;
+    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_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 float depth_values[] = {1.0f, 0.0f, 0.5f, 0.6f, 0.4f, 0.1f};
+    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},
+
+        {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},
+    };
+
+    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;