[PATCH vkd3d 5/5] vkd3d: Introduce command list type bundle.

Conor McCarthy cmccarthy at codeweavers.com
Fri Sep 3 09:58:59 CDT 2021


Only DrawInstanced() is supported in this patch.

Modern compilers use a binary search for switch statements, so the
performance impact will be not too great when all valid command
list methods are handled.

Signed-off-by: Conor McCarthy <cmccarthy at codeweavers.com>
---
 Makefile.am                |    1 +
 libs/vkd3d/bundle.c        | 1102 ++++++++++++++++++++++++++++++++++++
 libs/vkd3d/command.c       |   60 +-
 libs/vkd3d/device.c        |   47 +-
 libs/vkd3d/vkd3d_private.h |   76 +++
 tests/d3d12.c              |    9 +-
 6 files changed, 1275 insertions(+), 20 deletions(-)
 create mode 100644 libs/vkd3d/bundle.c

diff --git a/Makefile.am b/Makefile.am
index f51233c2..9a0b868f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -200,6 +200,7 @@ libvkd3d_la_SOURCES = \
 	include/vkd3d_d3d12.idl \
 	include/vkd3d_d3dcommon.idl \
 	include/vkd3d_unknown.idl \
+	libs/vkd3d/bundle.c \
 	libs/vkd3d/command.c \
 	libs/vkd3d/device.c \
 	libs/vkd3d/resource.c \
diff --git a/libs/vkd3d/bundle.c b/libs/vkd3d/bundle.c
new file mode 100644
index 00000000..dd3aadf7
--- /dev/null
+++ b/libs/vkd3d/bundle.c
@@ -0,0 +1,1102 @@
+/*
+ * Copyright 2021 Conor McCarthy 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 void d3d12_bundle_command_buffer_reset(struct d3d12_bundle_command_buffer *command_buffer)
+{
+    command_buffer->commands = NULL;
+    command_buffer->size = 0;
+    command_buffer->count = 0;
+}
+
+static bool d3d12_bundle_command_buffer_reserve(struct d3d12_bundle_command_buffer *command_buffer,
+        size_t count, enum vkd3d_command_list_command_id id)
+{
+    size_t new_count = command_buffer->count + 1 + count;
+
+    if (!vkd3d_array_reserve((void **)&command_buffer->commands, &command_buffer->size,
+            max(new_count, 32), sizeof(*command_buffer->commands)))
+    {
+        ERR("Out of memory. Cannot record bundle command %#x.\n", id);
+        return false;
+    }
+
+    command_buffer->commands[command_buffer->count].header.count = count;
+    command_buffer->commands[command_buffer->count++].header.id = id;
+
+    return true;
+}
+
+static void d3d12_bundle_begin_command_buffer(struct d3d12_bundle *bundle)
+{
+    bundle->is_recording = true;
+    bundle->is_valid = true;
+}
+
+static void d3d12_bundle_allocator_destroyed(struct d3d12_bundle *bundle)
+{
+    TRACE("bundle %p.\n", bundle);
+
+    bundle->allocator = NULL;
+    d3d12_bundle_command_buffer_reset(&bundle->command_buffer);
+}
+
+/* ID3D12CommandAllocator */
+static inline struct d3d12_bundle_allocator *impl_from_ID3D12CommandAllocator(ID3D12CommandAllocator *iface)
+{
+    return CONTAINING_RECORD(iface, struct d3d12_bundle_allocator, ID3D12CommandAllocator_iface);
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_bundle_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_bundle_allocator_AddRef(ID3D12CommandAllocator *iface)
+{
+    struct d3d12_bundle_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_bundle_allocator_Release(ID3D12CommandAllocator *iface)
+{
+    struct d3d12_bundle_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;
+        size_t i;
+
+        vkd3d_private_store_destroy(&allocator->private_store);
+
+        if (allocator->current_bundle)
+            d3d12_bundle_allocator_destroyed(allocator->current_bundle);
+
+        for (i = 0; i < allocator->arg_buffer_count; ++i)
+            vkd3d_free(allocator->arg_buffers[i]);
+        vkd3d_free(allocator->arg_buffers);
+
+        vkd3d_free(allocator);
+
+        d3d12_device_release(device);
+    }
+
+    return refcount;
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_bundle_allocator_GetPrivateData(ID3D12CommandAllocator *iface,
+        REFGUID guid, UINT *data_size, void *data)
+{
+    struct d3d12_bundle_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_bundle_allocator_SetPrivateData(ID3D12CommandAllocator *iface,
+        REFGUID guid, UINT data_size, const void *data)
+{
+    struct d3d12_bundle_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_bundle_allocator_SetPrivateDataInterface(ID3D12CommandAllocator *iface,
+        REFGUID guid, const IUnknown *data)
+{
+    struct d3d12_bundle_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_bundle_allocator_SetName(ID3D12CommandAllocator *iface, const WCHAR *name)
+{
+    struct d3d12_bundle_allocator *allocator = impl_from_ID3D12CommandAllocator(iface);
+
+    TRACE("iface %p, name %s.\n", iface, debugstr_w(name, allocator->device->wchar_size));
+
+    return name ? S_OK : E_INVALIDARG;
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_bundle_allocator_GetDevice(ID3D12CommandAllocator *iface, REFIID iid, void **device)
+{
+    struct d3d12_bundle_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_bundle_allocator_Reset(ID3D12CommandAllocator *iface)
+{
+    struct d3d12_bundle_allocator *allocator = impl_from_ID3D12CommandAllocator(iface);
+    struct d3d12_bundle *bundle;
+    size_t i;
+
+    TRACE("iface %p.\n", iface);
+
+    if ((bundle = allocator->current_bundle))
+    {
+        if (bundle->is_recording)
+        {
+            WARN("A command list using this allocator is in the recording state.\n");
+            return E_FAIL;
+        }
+
+        TRACE("Resetting command list %p.\n", bundle);
+    }
+
+    for (i = 0; i < allocator->arg_buffer_count; ++i)
+        vkd3d_free(allocator->arg_buffers[i]);
+    allocator->arg_buffer_count = 0;
+
+    return S_OK;
+}
+
+static const struct ID3D12CommandAllocatorVtbl d3d12_bundle_allocator_vtbl =
+{
+    /* IUnknown methods */
+    d3d12_bundle_allocator_QueryInterface,
+    d3d12_bundle_allocator_AddRef,
+    d3d12_bundle_allocator_Release,
+    /* ID3D12Object methods */
+    d3d12_bundle_allocator_GetPrivateData,
+    d3d12_bundle_allocator_SetPrivateData,
+    d3d12_bundle_allocator_SetPrivateDataInterface,
+    d3d12_bundle_allocator_SetName,
+    /* ID3D12DeviceChild methods */
+    d3d12_bundle_allocator_GetDevice,
+    /* ID3D12CommandAllocator methods */
+    d3d12_bundle_allocator_Reset,
+};
+
+static struct d3d12_bundle_allocator *checked_impl_from_ID3D12CommandAllocator(ID3D12CommandAllocator *iface)
+{
+    if (iface->lpVtbl != &d3d12_bundle_allocator_vtbl)
+        return NULL;
+    return impl_from_ID3D12CommandAllocator(iface);
+}
+
+HRESULT d3d12_bundle_allocator_create(struct d3d12_device *device, struct d3d12_bundle_allocator **allocator)
+{
+    struct d3d12_bundle_allocator *object;
+    HRESULT hr;
+
+    if (!(object = vkd3d_calloc(1, sizeof(*object))))
+        return E_OUTOFMEMORY;
+
+    object->ID3D12CommandAllocator_iface.lpVtbl = &d3d12_bundle_allocator_vtbl;
+    object->refcount = 1;
+
+    if (FAILED(hr = vkd3d_private_store_init(&object->private_store)))
+    {
+        vkd3d_free(object);
+        return hr;
+    }
+
+    d3d12_device_add_ref(object->device = device);
+
+    TRACE("Created command allocator %p.\n", object);
+
+    *allocator = object;
+
+    return S_OK;
+}
+
+static HRESULT d3d12_bundle_allocator_init_command_buffer(struct d3d12_bundle_allocator *allocator,
+        struct d3d12_bundle *bundle)
+{
+    TRACE("allocator %p, bundle %p.\n", allocator, bundle);
+
+    if (allocator->current_bundle)
+    {
+        WARN("Command allocator is already in use.\n");
+        return E_INVALIDARG;
+    }
+
+    d3d12_bundle_command_buffer_reset(&bundle->command_buffer);
+
+    d3d12_bundle_begin_command_buffer(bundle);
+
+    allocator->current_bundle = bundle;
+
+    return S_OK;
+}
+
+static HRESULT d3d12_bundle_allocator_free_command_buffer(struct d3d12_bundle_allocator *allocator,
+        struct d3d12_bundle *bundle)
+{
+    TRACE("allocator %p, bundle %p.\n", allocator, bundle);
+
+    if (allocator->current_bundle == bundle)
+        allocator->current_bundle = NULL;
+
+    if (!vkd3d_array_reserve((void **)&allocator->arg_buffers, &allocator->arg_buffers_size,
+            allocator->arg_buffer_count + 1, sizeof(*allocator->arg_buffers)))
+    {
+        WARN("Failed to add command buffer.\n");
+        vkd3d_free(bundle->command_buffer.commands);
+        return E_OUTOFMEMORY;
+    }
+
+    allocator->arg_buffers[allocator->arg_buffer_count++] = bundle->command_buffer.commands;
+
+    return S_OK;
+}
+
+/* ID3D12GraphicsCommandList */
+static inline struct d3d12_bundle *impl_from_ID3D12GraphicsCommandList2(ID3D12GraphicsCommandList2 *iface)
+{
+    return CONTAINING_RECORD(iface, struct d3d12_bundle, ID3D12GraphicsCommandList2_iface);
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_bundle_QueryInterface(ID3D12GraphicsCommandList2 *iface,
+        REFIID iid, void **object)
+{
+    TRACE("iface %p, iid %s, object %p.\n", iface, debugstr_guid(iid), object);
+
+    if (IsEqualGUID(iid, &IID_ID3D12GraphicsCommandList2)
+            || IsEqualGUID(iid, &IID_ID3D12GraphicsCommandList1)
+            || IsEqualGUID(iid, &IID_ID3D12GraphicsCommandList)
+            || IsEqualGUID(iid, &IID_ID3D12CommandList)
+            || IsEqualGUID(iid, &IID_ID3D12DeviceChild)
+            || IsEqualGUID(iid, &IID_ID3D12Object)
+            || IsEqualGUID(iid, &IID_IUnknown))
+    {
+        ID3D12GraphicsCommandList2_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_bundle_AddRef(ID3D12GraphicsCommandList2 *iface)
+{
+    struct d3d12_bundle *bundle = impl_from_ID3D12GraphicsCommandList2(iface);
+    ULONG refcount = InterlockedIncrement(&bundle->refcount);
+
+    TRACE("%p increasing refcount to %u.\n", bundle, refcount);
+
+    return refcount;
+}
+
+static ULONG STDMETHODCALLTYPE d3d12_bundle_Release(ID3D12GraphicsCommandList2 *iface)
+{
+    struct d3d12_bundle *bundle = impl_from_ID3D12GraphicsCommandList2(iface);
+    ULONG refcount = InterlockedDecrement(&bundle->refcount);
+
+    TRACE("%p decreasing refcount to %u.\n", bundle, refcount);
+
+    if (!refcount)
+    {
+        struct d3d12_device *device = bundle->device;
+
+        vkd3d_private_store_destroy(&bundle->private_store);
+
+        if (bundle->allocator)
+            d3d12_bundle_allocator_free_command_buffer(bundle->allocator, bundle);
+
+        vkd3d_free(bundle);
+
+        d3d12_device_release(device);
+    }
+
+    return refcount;
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_bundle_GetPrivateData(ID3D12GraphicsCommandList2 *iface,
+        REFGUID guid, UINT *data_size, void *data)
+{
+    struct d3d12_bundle *bundle = impl_from_ID3D12GraphicsCommandList2(iface);
+
+    TRACE("iface %p, guid %s, data_size %p, data %p.\n", iface, debugstr_guid(guid), data_size, data);
+
+    return vkd3d_get_private_data(&bundle->private_store, guid, data_size, data);
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_bundle_SetPrivateData(ID3D12GraphicsCommandList2 *iface,
+        REFGUID guid, UINT data_size, const void *data)
+{
+    struct d3d12_bundle *bundle = impl_from_ID3D12GraphicsCommandList2(iface);
+
+    TRACE("iface %p, guid %s, data_size %u, data %p.\n", iface, debugstr_guid(guid), data_size, data);
+
+    return vkd3d_set_private_data(&bundle->private_store, guid, data_size, data);
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_bundle_SetPrivateDataInterface(ID3D12GraphicsCommandList2 *iface,
+        REFGUID guid, const IUnknown *data)
+{
+    struct d3d12_bundle *bundle = impl_from_ID3D12GraphicsCommandList2(iface);
+
+    TRACE("iface %p, guid %s, data %p.\n", iface, debugstr_guid(guid), data);
+
+    return vkd3d_set_private_data_interface(&bundle->private_store, guid, data);
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_bundle_SetName(ID3D12GraphicsCommandList2 *iface, const WCHAR *name)
+{
+    struct d3d12_bundle *bundle = impl_from_ID3D12GraphicsCommandList2(iface);
+
+    TRACE("iface %p, name %s.\n", iface, debugstr_w(name, bundle->device->wchar_size));
+
+    return name ? S_OK : E_INVALIDARG;
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_bundle_GetDevice(ID3D12GraphicsCommandList2 *iface, REFIID iid, void **device)
+{
+    struct d3d12_bundle *bundle = impl_from_ID3D12GraphicsCommandList2(iface);
+
+    TRACE("iface %p, iid %s, device %p.\n", iface, debugstr_guid(iid), device);
+
+    return d3d12_device_query_interface(bundle->device, iid, device);
+}
+
+static D3D12_COMMAND_LIST_TYPE STDMETHODCALLTYPE d3d12_bundle_GetType(ID3D12GraphicsCommandList2 *iface)
+{
+    TRACE("iface %p.\n", iface);
+
+    return D3D12_COMMAND_LIST_TYPE_BUNDLE;
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_bundle_Close(ID3D12GraphicsCommandList2 *iface)
+{
+    struct d3d12_bundle *bundle = impl_from_ID3D12GraphicsCommandList2(iface);
+    HRESULT hr = S_OK;
+
+    TRACE("iface %p.\n", iface);
+
+    if (!bundle->is_recording)
+    {
+        WARN("Command list is not in the recording state.\n");
+        return E_FAIL;
+    }
+
+    if (bundle->allocator)
+    {
+        hr = d3d12_bundle_allocator_free_command_buffer(bundle->allocator, bundle);
+        bundle->allocator = NULL;
+    }
+
+    bundle->is_recording = false;
+
+    if (!bundle->is_valid)
+    {
+        WARN("Error occurred during command list recording.\n");
+        return E_INVALIDARG;
+    }
+
+    return hr;
+}
+
+static void d3d12_bundle_init_state(struct d3d12_bundle *bundle, ID3D12PipelineState *initial_pipeline_state)
+{
+    ID3D12GraphicsCommandList2 *iface = &bundle->ID3D12GraphicsCommandList2_iface;
+
+    ID3D12GraphicsCommandList2_SetPipelineState(iface, initial_pipeline_state);
+    ID3D12GraphicsCommandList2_IASetPrimitiveTopology(iface, D3D_PRIMITIVE_TOPOLOGY_UNDEFINED);
+}
+
+static HRESULT STDMETHODCALLTYPE d3d12_bundle_Reset(ID3D12GraphicsCommandList2 *iface,
+        ID3D12CommandAllocator *allocator_iface, ID3D12PipelineState *initial_pipeline_state)
+{
+    struct d3d12_bundle *bundle = impl_from_ID3D12GraphicsCommandList2(iface);
+    struct d3d12_bundle_allocator *allocator;
+    HRESULT hr;
+
+    if (!allocator_iface)
+    {
+        WARN("Command allocator is NULL.\n");
+        return E_INVALIDARG;
+    }
+
+    if (!(allocator = checked_impl_from_ID3D12CommandAllocator(allocator_iface)))
+    {
+        WARN("Command allocator is not of type D3D12_COMMAND_LIST_TYPE_BUNDLE.\n");
+        return E_INVALIDARG;
+    }
+
+    if (bundle->is_recording)
+    {
+        WARN("Command list is in the recording state.\n");
+        return E_FAIL;
+    }
+
+    if (SUCCEEDED(hr = d3d12_bundle_allocator_init_command_buffer(allocator, bundle)))
+    {
+        bundle->allocator = allocator;
+        d3d12_bundle_init_state(bundle, initial_pipeline_state);
+    }
+
+    return hr;
+}
+
+static void d3d12_bundle_mark_as_invalid(ID3D12GraphicsCommandList2 *iface)
+{
+    impl_from_ID3D12GraphicsCommandList2(iface)->is_valid = false;
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_ClearState(ID3D12GraphicsCommandList2 *iface,
+        ID3D12PipelineState *pipeline_state)
+{
+    ERR("iface %p, pipline_state %p command invalid for bundles.\n", iface, pipeline_state);
+
+    d3d12_bundle_mark_as_invalid(iface);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_DrawInstanced(ID3D12GraphicsCommandList2 *iface,
+        UINT vertex_count_per_instance, UINT instance_count, UINT start_vertex_location,
+        UINT start_instance_location)
+{
+    struct d3d12_bundle *bundle = impl_from_ID3D12GraphicsCommandList2(iface);
+    struct d3d12_bundle_command_buffer *buffer = &bundle->command_buffer;
+
+    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);
+
+    if (!d3d12_bundle_command_buffer_reserve(buffer, 4, VKD3D_COMMAND_DRAW_INSTANCED))
+        return;
+
+    buffer->commands[buffer->count++].uint_arg = vertex_count_per_instance;
+    buffer->commands[buffer->count++].uint_arg = instance_count;
+    buffer->commands[buffer->count++].uint_arg = start_vertex_location;
+    buffer->commands[buffer->count++].uint_arg = start_instance_location;
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_DrawIndexedInstanced(ID3D12GraphicsCommandList2 *iface,
+        UINT index_count_per_instance, UINT instance_count, UINT start_vertex_location,
+        INT base_vertex_location, UINT start_instance_location)
+{
+    FIXME("iface %p, index_count_per_instance %u, instance_count %u, start_vertex_location %u, "
+            "base_vertex_location %d, start_instance_location %u stub!\n", iface, index_count_per_instance,
+            instance_count, start_vertex_location, base_vertex_location, start_instance_location);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_Dispatch(ID3D12GraphicsCommandList2 *iface,
+        UINT x, UINT y, UINT z)
+{
+    FIXME("iface %p, x %u, y %u, z %u stub!\n", iface, x, y, z);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_CopyBufferRegion(ID3D12GraphicsCommandList2 *iface,
+        ID3D12Resource *dst, UINT64 dst_offset, ID3D12Resource *src, UINT64 src_offset, UINT64 byte_count)
+{
+    ERR("iface %p, dst_resource %p, dst_offset %#"PRIx64", src_resource %p, "
+            "src_offset %#"PRIx64", byte_count %#"PRIx64" command invalid for bundles.\n",
+            iface, dst, dst_offset, src, src_offset, byte_count);
+
+    d3d12_bundle_mark_as_invalid(iface);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_CopyTextureRegion(ID3D12GraphicsCommandList2 *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)
+{
+    ERR("iface %p, dst %p, dst_x %u, dst_y %u, dst_z %u, src %p, src_box %p command invalid for bundles.\n",
+            iface, dst, dst_x, dst_y, dst_z, src, src_box);
+
+    d3d12_bundle_mark_as_invalid(iface);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_CopyResource(ID3D12GraphicsCommandList2 *iface,
+        ID3D12Resource *dst, ID3D12Resource *src)
+{
+    ERR("iface %p, dst_resource %p, src_resource %p command invalid for bundles.\n", iface, dst, src);
+
+    d3d12_bundle_mark_as_invalid(iface);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_CopyTiles(ID3D12GraphicsCommandList2 *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)
+{
+    ERR("iface %p, tiled_resource %p, tile_region_start_coordinate %p, tile_region_size %p, "
+            "buffer %p, buffer_offset %#"PRIx64", flags %#x command invalid for bundles.\n",
+            iface, tiled_resource, tile_region_start_coordinate, tile_region_size,
+            buffer, buffer_offset, flags);
+
+    d3d12_bundle_mark_as_invalid(iface);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_ResolveSubresource(ID3D12GraphicsCommandList2 *iface,
+        ID3D12Resource *dst, UINT dst_sub_resource_idx,
+        ID3D12Resource *src, UINT src_sub_resource_idx, DXGI_FORMAT format)
+{
+    ERR("iface %p, dst_resource %p, dst_sub_resource_idx %u, src_resource %p, src_sub_resource_idx %u, format %#x "
+            "command invalid for bundles.\n", iface, dst, dst_sub_resource_idx, src, src_sub_resource_idx, format);
+
+    d3d12_bundle_mark_as_invalid(iface);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_IASetPrimitiveTopology(ID3D12GraphicsCommandList2 *iface,
+        D3D12_PRIMITIVE_TOPOLOGY topology)
+{
+    FIXME("iface %p, topology %#x stub!\n", iface, topology);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_RSSetViewports(ID3D12GraphicsCommandList2 *iface,
+        UINT viewport_count, const D3D12_VIEWPORT *viewports)
+{
+    ERR("iface %p, viewport_count %u, viewports %p command invalid for bundles.\n", iface, viewport_count, viewports);
+
+    d3d12_bundle_mark_as_invalid(iface);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_RSSetScissorRects(ID3D12GraphicsCommandList2 *iface,
+        UINT rect_count, const D3D12_RECT *rects)
+{
+    ERR("iface %p, rect_count %u, rects %p command invalid for bundles.\n", iface, rect_count, rects);
+
+    d3d12_bundle_mark_as_invalid(iface);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_OMSetBlendFactor(ID3D12GraphicsCommandList2 *iface,
+        const FLOAT blend_factor[4])
+{
+    FIXME("iface %p, blend_factor %p stub!\n", iface, blend_factor);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_OMSetStencilRef(ID3D12GraphicsCommandList2 *iface, UINT stencil_ref)
+{
+    FIXME("iface %p, stencil_ref %u stub!\n", iface, stencil_ref);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_SetPipelineState(ID3D12GraphicsCommandList2 *iface,
+        ID3D12PipelineState *pipeline_state)
+{
+    FIXME("iface %p, pipeline_state %p stub!\n", iface, pipeline_state);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_ResourceBarrier(ID3D12GraphicsCommandList2 *iface,
+        UINT barrier_count, const D3D12_RESOURCE_BARRIER *barriers)
+{
+    ERR("iface %p, barrier_count %u, barriers %p command invalid for bundles.\n", iface, barrier_count, barriers);
+
+    d3d12_bundle_mark_as_invalid(iface);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_ExecuteBundle(ID3D12GraphicsCommandList2 *iface,
+        ID3D12GraphicsCommandList *bundle_iface)
+{
+    ERR("iface %p, bundle_iface %p command invalid for bundles.\n", iface, bundle_iface);
+
+    d3d12_bundle_mark_as_invalid(iface);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_SetDescriptorHeaps(ID3D12GraphicsCommandList2 *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.
+     *
+     * The D3D12 documentation states it is valid for bundles if the heaps are the same as
+     * those already set in the executing command list. ExecuteBundle() could validate this
+     * but we do not have an equivalent of the D3D12 Debug Layer. */
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_SetComputeRootSignature(ID3D12GraphicsCommandList2 *iface,
+        ID3D12RootSignature *root_signature)
+{
+    FIXME("iface %p, root_signature %p stub!\n", iface, root_signature);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_SetGraphicsRootSignature(ID3D12GraphicsCommandList2 *iface,
+        ID3D12RootSignature *root_signature)
+{
+    FIXME("iface %p, root_signature %p stub!\n", iface, root_signature);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_SetComputeRootDescriptorTable(ID3D12GraphicsCommandList2 *iface,
+        UINT root_parameter_index, D3D12_GPU_DESCRIPTOR_HANDLE base_descriptor)
+{
+    FIXME("iface %p, root_parameter_index %u, base_descriptor %#"PRIx64" stub!\n",
+            iface, root_parameter_index, base_descriptor.ptr);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_SetGraphicsRootDescriptorTable(ID3D12GraphicsCommandList2 *iface,
+        UINT root_parameter_index, D3D12_GPU_DESCRIPTOR_HANDLE base_descriptor)
+{
+    FIXME("iface %p, root_parameter_index %u, base_descriptor %#"PRIx64" stub!\n",
+            iface, root_parameter_index, base_descriptor.ptr);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_SetComputeRoot32BitConstant(ID3D12GraphicsCommandList2 *iface,
+        UINT root_parameter_index, UINT data, UINT dst_offset)
+{
+    FIXME("iface %p, root_parameter_index %u, data 0x%08x, dst_offset %u stub!\n",
+            iface, root_parameter_index, data, dst_offset);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_SetGraphicsRoot32BitConstant(ID3D12GraphicsCommandList2 *iface,
+        UINT root_parameter_index, UINT data, UINT dst_offset)
+{
+    FIXME("iface %p, root_parameter_index %u, data 0x%08x, dst_offset %u stub!\n",
+            iface, root_parameter_index, data, dst_offset);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_SetComputeRoot32BitConstants(ID3D12GraphicsCommandList2 *iface,
+        UINT root_parameter_index, UINT constant_count, const void *data, UINT dst_offset)
+{
+    FIXME("iface %p, root_parameter_index %u, constant_count %u, data %p, dst_offset %u stub!\n",
+            iface, root_parameter_index, constant_count, data, dst_offset);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_SetGraphicsRoot32BitConstants(ID3D12GraphicsCommandList2 *iface,
+        UINT root_parameter_index, UINT constant_count, const void *data, UINT dst_offset)
+{
+    FIXME("iface %p, root_parameter_index %u, constant_count %u, data %p, dst_offset %u stub!\n",
+            iface, root_parameter_index, constant_count, data, dst_offset);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_SetComputeRootConstantBufferView(
+        ID3D12GraphicsCommandList2 *iface, UINT root_parameter_index, D3D12_GPU_VIRTUAL_ADDRESS address)
+{
+    FIXME("iface %p, root_parameter_index %u, address %#"PRIx64" stub!\n", iface, root_parameter_index, address);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_SetGraphicsRootConstantBufferView(
+        ID3D12GraphicsCommandList2 *iface, UINT root_parameter_index, D3D12_GPU_VIRTUAL_ADDRESS address)
+{
+    FIXME("iface %p, root_parameter_index %u, address %#"PRIx64" stub!\n", iface, root_parameter_index, address);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_SetComputeRootShaderResourceView(
+        ID3D12GraphicsCommandList2 *iface, UINT root_parameter_index, D3D12_GPU_VIRTUAL_ADDRESS address)
+{
+    FIXME("iface %p, root_parameter_index %u, address %#"PRIx64" stub!\n", iface, root_parameter_index, address);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_SetGraphicsRootShaderResourceView(
+        ID3D12GraphicsCommandList2 *iface, UINT root_parameter_index, D3D12_GPU_VIRTUAL_ADDRESS address)
+{
+    FIXME("iface %p, root_parameter_index %u, address %#"PRIx64" stub!\n", iface, root_parameter_index, address);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_SetComputeRootUnorderedAccessView(
+        ID3D12GraphicsCommandList2 *iface, UINT root_parameter_index, D3D12_GPU_VIRTUAL_ADDRESS address)
+{
+    FIXME("iface %p, root_parameter_index %u, address %#"PRIx64" stub!\n", iface, root_parameter_index, address);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_SetGraphicsRootUnorderedAccessView(
+        ID3D12GraphicsCommandList2 *iface, UINT root_parameter_index, D3D12_GPU_VIRTUAL_ADDRESS address)
+{
+    FIXME("iface %p, root_parameter_index %u, address %#"PRIx64" stub!\n", iface, root_parameter_index, address);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_IASetIndexBuffer(ID3D12GraphicsCommandList2 *iface,
+        const D3D12_INDEX_BUFFER_VIEW *view)
+{
+    FIXME("iface %p, view %p stub!\n", iface, view);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_IASetVertexBuffers(ID3D12GraphicsCommandList2 *iface,
+        UINT start_slot, UINT view_count, const D3D12_VERTEX_BUFFER_VIEW *views)
+{
+    FIXME("iface %p, start_slot %u, view_count %u, views %p stub!\n", iface, start_slot, view_count, views);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_SOSetTargets(ID3D12GraphicsCommandList2 *iface,
+        UINT start_slot, UINT view_count, const D3D12_STREAM_OUTPUT_BUFFER_VIEW *views)
+{
+    ERR("iface %p, start_slot %u, view_count %u, views %p command invalid for bundles.\n",
+            iface, start_slot, view_count, views);
+
+    d3d12_bundle_mark_as_invalid(iface);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_OMSetRenderTargets(ID3D12GraphicsCommandList2 *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)
+{
+    ERR("iface %p, render_target_descriptor_count %u, render_target_descriptors %p, "
+            "single_descriptor_handle %#x, depth_stencil_descriptor %p command invalid for bundles.\n",
+            iface, render_target_descriptor_count, render_target_descriptors,
+            single_descriptor_handle, depth_stencil_descriptor);
+
+    d3d12_bundle_mark_as_invalid(iface);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_ClearDepthStencilView(ID3D12GraphicsCommandList2 *iface,
+        D3D12_CPU_DESCRIPTOR_HANDLE dsv, D3D12_CLEAR_FLAGS flags, float depth, UINT8 stencil,
+        UINT rect_count, const D3D12_RECT *rects)
+{
+    ERR("iface %p, dsv %#lx, flags %#x, depth %.8e, stencil 0x%02x, rect_count %u, rects %p "
+            "command invalid for bundles.\n", iface, dsv.ptr, flags, depth, stencil, rect_count, rects);
+
+    d3d12_bundle_mark_as_invalid(iface);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_ClearRenderTargetView(ID3D12GraphicsCommandList2 *iface,
+        D3D12_CPU_DESCRIPTOR_HANDLE rtv, const FLOAT color[4], UINT rect_count, const D3D12_RECT *rects)
+{
+    ERR("iface %p, rtv %#lx, color %p, rect_count %u, rects %p command invalid for bundles.\n",
+            iface, rtv.ptr, color, rect_count, rects);
+
+    d3d12_bundle_mark_as_invalid(iface);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_ClearUnorderedAccessViewUint(ID3D12GraphicsCommandList2 *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)
+{
+    ERR("iface %p, gpu_handle %#"PRIx64", cpu_handle %lx, resource %p, values %p, rect_count %u, rects %p command "
+            "invalid for bundles.\n", iface, gpu_handle.ptr, cpu_handle.ptr, resource, values, rect_count, rects);
+
+    d3d12_bundle_mark_as_invalid(iface);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_ClearUnorderedAccessViewFloat(ID3D12GraphicsCommandList2 *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)
+{
+    ERR("iface %p, gpu_handle %#"PRIx64", cpu_handle %lx, resource %p, values %p, rect_count %u, rects %p command "
+            "invalid for bundles.\n", iface, gpu_handle.ptr, cpu_handle.ptr, resource, values, rect_count, rects);
+
+    d3d12_bundle_mark_as_invalid(iface);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_DiscardResource(ID3D12GraphicsCommandList2 *iface,
+        ID3D12Resource *resource, const D3D12_DISCARD_REGION *region)
+{
+    ERR("iface %p, resource %p, region %p command invalid for bundles.\n", iface, resource, region);
+
+    d3d12_bundle_mark_as_invalid(iface);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_BeginQuery(ID3D12GraphicsCommandList2 *iface,
+        ID3D12QueryHeap *heap, D3D12_QUERY_TYPE type, UINT index)
+{
+    ERR("iface %p, heap %p, type %#x, index %u command invalid for bundles.\n", iface, heap, type, index);
+
+    d3d12_bundle_mark_as_invalid(iface);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_EndQuery(ID3D12GraphicsCommandList2 *iface,
+        ID3D12QueryHeap *heap, D3D12_QUERY_TYPE type, UINT index)
+{
+    ERR("iface %p, heap %p, type %#x, index %u command invalid for bundles.\n", iface, heap, type, index);
+
+    d3d12_bundle_mark_as_invalid(iface);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_ResolveQueryData(ID3D12GraphicsCommandList2 *iface,
+        ID3D12QueryHeap *heap, D3D12_QUERY_TYPE type, UINT start_index, UINT query_count,
+        ID3D12Resource *dst_buffer, UINT64 aligned_dst_buffer_offset)
+{
+    ERR("iface %p, heap %p, type %#x, start_index %u, query_count %u, "
+            "dst_buffer %p, aligned_dst_buffer_offset %#"PRIx64" command invalid for bundles.\n",
+            iface, heap, type, start_index, query_count, dst_buffer, aligned_dst_buffer_offset);
+
+    d3d12_bundle_mark_as_invalid(iface);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_SetPredication(ID3D12GraphicsCommandList2 *iface,
+        ID3D12Resource *buffer, UINT64 aligned_buffer_offset, D3D12_PREDICATION_OP operation)
+{
+    ERR("iface %p, buffer %p, aligned_buffer_offset %#"PRIx64", operation %#x command invalid for bundles.\n",
+            iface, buffer, aligned_buffer_offset, operation);
+
+    d3d12_bundle_mark_as_invalid(iface);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_SetMarker(ID3D12GraphicsCommandList2 *iface,
+        UINT metadata, const void *data, UINT size)
+{
+    /* Undocumented function used by Microsoft's PIX event runtime. Validity for bundles unknown. */
+    FIXME("iface %p, metadata %#x, data %p, size %u stub!\n", iface, metadata, data, size);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_BeginEvent(ID3D12GraphicsCommandList2 *iface,
+        UINT metadata, const void *data, UINT size)
+{
+    /* Undocumented function used by Microsoft's PIX event runtime. Validity for bundles unknown. */
+    FIXME("iface %p, metadata %#x, data %p, size %u stub!\n", iface, metadata, data, size);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_EndEvent(ID3D12GraphicsCommandList2 *iface)
+{
+    /* Undocumented function used by Microsoft's PIX event runtime. Validity for bundles unknown. */
+    FIXME("iface %p stub!\n", iface);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_ExecuteIndirect(ID3D12GraphicsCommandList2 *iface,
+        ID3D12CommandSignature *command_signature, UINT max_command_count, ID3D12Resource *arg_buffer,
+        UINT64 arg_buffer_offset, ID3D12Resource *count_buffer, UINT64 count_buffer_offset)
+{
+    FIXME("iface %p, command_signature %p, max_command_count %u, arg_buffer %p, "
+            "arg_buffer_offset %#"PRIx64", count_buffer %p, count_buffer_offset %#"PRIx64" stub!\n",
+            iface, command_signature, max_command_count, arg_buffer, arg_buffer_offset,
+            count_buffer, count_buffer_offset);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_AtomicCopyBufferUINT(ID3D12GraphicsCommandList2 *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)
+{
+    ERR("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 command invalid for bundles.\n",
+            iface, dst_buffer, dst_offset, src_buffer, src_offset,
+            dependent_resource_count, dependent_resources, dependent_sub_resource_ranges);
+
+    d3d12_bundle_mark_as_invalid(iface);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_AtomicCopyBufferUINT64(ID3D12GraphicsCommandList2 *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)
+{
+    ERR("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 command invalid for bundles.\n",
+            iface, dst_buffer, dst_offset, src_buffer, src_offset,
+            dependent_resource_count, dependent_resources, dependent_sub_resource_ranges);
+
+    d3d12_bundle_mark_as_invalid(iface);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_OMSetDepthBounds(ID3D12GraphicsCommandList2 *iface,
+        FLOAT min, FLOAT max)
+{
+    FIXME("iface %p, min %.8e, max %.8e stub!\n", iface, min, max);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_SetSamplePositions(ID3D12GraphicsCommandList2 *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_bundle_ResolveSubresourceRegion(ID3D12GraphicsCommandList2 *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)
+{
+    ERR("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 command invalid for bundles.\n",
+            iface, dst_resource, dst_sub_resource_idx, dst_x, dst_y,
+            src_resource, src_sub_resource_idx, src_rect, format, mode);
+
+    d3d12_bundle_mark_as_invalid(iface);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_SetViewInstanceMask(ID3D12GraphicsCommandList2 *iface, UINT mask)
+{
+    FIXME("iface %p, mask %#x stub!\n", iface, mask);
+}
+
+static void STDMETHODCALLTYPE d3d12_bundle_WriteBufferImmediate(ID3D12GraphicsCommandList2 *iface,
+        UINT count, const D3D12_WRITEBUFFERIMMEDIATE_PARAMETER *parameters,
+        const D3D12_WRITEBUFFERIMMEDIATE_MODE *modes)
+{
+    FIXME("iface %p, count %u, parameters %p, modes %p stub!\n", iface, count, parameters, modes);
+}
+
+static const struct ID3D12GraphicsCommandList2Vtbl d3d12_bundle_vtbl =
+{
+    /* IUnknown methods */
+    d3d12_bundle_QueryInterface,
+    d3d12_bundle_AddRef,
+    d3d12_bundle_Release,
+    /* ID3D12Object methods */
+    d3d12_bundle_GetPrivateData,
+    d3d12_bundle_SetPrivateData,
+    d3d12_bundle_SetPrivateDataInterface,
+    d3d12_bundle_SetName,
+    /* ID3D12DeviceChild methods */
+    d3d12_bundle_GetDevice,
+    /* ID3D12CommandList methods */
+    d3d12_bundle_GetType,
+    /* ID3D12GraphicsCommandList methods */
+    d3d12_bundle_Close,
+    d3d12_bundle_Reset,
+    d3d12_bundle_ClearState,
+    d3d12_bundle_DrawInstanced,
+    d3d12_bundle_DrawIndexedInstanced,
+    d3d12_bundle_Dispatch,
+    d3d12_bundle_CopyBufferRegion,
+    d3d12_bundle_CopyTextureRegion,
+    d3d12_bundle_CopyResource,
+    d3d12_bundle_CopyTiles,
+    d3d12_bundle_ResolveSubresource,
+    d3d12_bundle_IASetPrimitiveTopology,
+    d3d12_bundle_RSSetViewports,
+    d3d12_bundle_RSSetScissorRects,
+    d3d12_bundle_OMSetBlendFactor,
+    d3d12_bundle_OMSetStencilRef,
+    d3d12_bundle_SetPipelineState,
+    d3d12_bundle_ResourceBarrier,
+    d3d12_bundle_ExecuteBundle,
+    d3d12_bundle_SetDescriptorHeaps,
+    d3d12_bundle_SetComputeRootSignature,
+    d3d12_bundle_SetGraphicsRootSignature,
+    d3d12_bundle_SetComputeRootDescriptorTable,
+    d3d12_bundle_SetGraphicsRootDescriptorTable,
+    d3d12_bundle_SetComputeRoot32BitConstant,
+    d3d12_bundle_SetGraphicsRoot32BitConstant,
+    d3d12_bundle_SetComputeRoot32BitConstants,
+    d3d12_bundle_SetGraphicsRoot32BitConstants,
+    d3d12_bundle_SetComputeRootConstantBufferView,
+    d3d12_bundle_SetGraphicsRootConstantBufferView,
+    d3d12_bundle_SetComputeRootShaderResourceView,
+    d3d12_bundle_SetGraphicsRootShaderResourceView,
+    d3d12_bundle_SetComputeRootUnorderedAccessView,
+    d3d12_bundle_SetGraphicsRootUnorderedAccessView,
+    d3d12_bundle_IASetIndexBuffer,
+    d3d12_bundle_IASetVertexBuffers,
+    d3d12_bundle_SOSetTargets,
+    d3d12_bundle_OMSetRenderTargets,
+    d3d12_bundle_ClearDepthStencilView,
+    d3d12_bundle_ClearRenderTargetView,
+    d3d12_bundle_ClearUnorderedAccessViewUint,
+    d3d12_bundle_ClearUnorderedAccessViewFloat,
+    d3d12_bundle_DiscardResource,
+    d3d12_bundle_BeginQuery,
+    d3d12_bundle_EndQuery,
+    d3d12_bundle_ResolveQueryData,
+    d3d12_bundle_SetPredication,
+    d3d12_bundle_SetMarker,
+    d3d12_bundle_BeginEvent,
+    d3d12_bundle_EndEvent,
+    d3d12_bundle_ExecuteIndirect,
+    /* ID3D12GraphicsCommandList1 methods */
+    d3d12_bundle_AtomicCopyBufferUINT,
+    d3d12_bundle_AtomicCopyBufferUINT64,
+    d3d12_bundle_OMSetDepthBounds,
+    d3d12_bundle_SetSamplePositions,
+    d3d12_bundle_ResolveSubresourceRegion,
+    d3d12_bundle_SetViewInstanceMask,
+    /* ID3D12GraphicsCommandList2 methods */
+    d3d12_bundle_WriteBufferImmediate,
+};
+
+struct d3d12_bundle *bundle_impl_from_ID3D12GraphicsCommandList(ID3D12GraphicsCommandList *iface)
+{
+    if (!iface || iface->lpVtbl != (struct ID3D12GraphicsCommandListVtbl *)&d3d12_bundle_vtbl)
+        return NULL;
+
+    return impl_from_ID3D12GraphicsCommandList2((ID3D12GraphicsCommandList2 *)iface);
+}
+
+static HRESULT d3d12_bundle_init(struct d3d12_bundle *bundle, struct d3d12_device *device,
+        struct d3d12_bundle_allocator *allocator, ID3D12PipelineState *initial_pipeline_state)
+{
+    HRESULT hr;
+
+    bundle->ID3D12GraphicsCommandList2_iface.lpVtbl = &d3d12_bundle_vtbl;
+    bundle->refcount = 1;
+
+    if (FAILED(hr = vkd3d_private_store_init(&bundle->private_store)))
+        return hr;
+
+    bundle->allocator = allocator;
+
+    if (SUCCEEDED(hr = d3d12_bundle_allocator_init_command_buffer(allocator, bundle)))
+    {
+        d3d12_device_add_ref(bundle->device = device);
+        d3d12_bundle_init_state(bundle, initial_pipeline_state);
+    }
+    else
+    {
+        vkd3d_private_store_destroy(&bundle->private_store);
+    }
+
+    return hr;
+}
+
+HRESULT d3d12_bundle_create(struct d3d12_device *device,
+        UINT node_mask, D3D12_COMMAND_LIST_TYPE type, ID3D12CommandAllocator *allocator_iface,
+        ID3D12PipelineState *initial_pipeline_state, struct d3d12_bundle **bundle)
+{
+    struct d3d12_bundle_allocator *allocator;
+    struct d3d12_bundle *object;
+    HRESULT hr;
+
+    if (!allocator_iface)
+    {
+        WARN("Command allocator is NULL.\n");
+        return E_INVALIDARG;
+    }
+
+    if (!(allocator = checked_impl_from_ID3D12CommandAllocator(allocator_iface)))
+    {
+        WARN("Command allocator is not of type D3D12_COMMAND_LIST_TYPE_BUNDLE.\n");
+        return E_INVALIDARG;
+    }
+
+    debug_ignored_node_mask(node_mask);
+
+    if (!(object = vkd3d_calloc(1, sizeof(*object))))
+        return E_OUTOFMEMORY;
+
+    if (FAILED(hr = d3d12_bundle_init(object, device, allocator, initial_pipeline_state)))
+    {
+        vkd3d_free(object);
+        return hr;
+    }
+
+    TRACE("Created command list %p.\n", object);
+
+    *bundle = object;
+
+    return S_OK;
+}
diff --git a/libs/vkd3d/command.c b/libs/vkd3d/command.c
index 253b9128..2a6d0b76 100644
--- a/libs/vkd3d/command.c
+++ b/libs/vkd3d/command.c
@@ -4049,10 +4049,51 @@ static void STDMETHODCALLTYPE d3d12_command_list_ResourceBarrier(ID3D12GraphicsC
         WARN("Issuing split barrier(s) on D3D12_RESOURCE_BARRIER_FLAG_END_ONLY.\n");
 }
 
+static void d3d12_bundle_command_execute(unsigned int id, const union d3d12_bundle_command_arg *args,
+        ID3D12GraphicsCommandList2 *list);
+
 static void STDMETHODCALLTYPE d3d12_command_list_ExecuteBundle(ID3D12GraphicsCommandList2 *iface,
-        ID3D12GraphicsCommandList *command_list)
+        ID3D12GraphicsCommandList *bundle_iface)
 {
-    FIXME("iface %p, command_list %p stub!\n", iface, command_list);
+    const struct d3d12_bundle *bundle = bundle_impl_from_ID3D12GraphicsCommandList(bundle_iface);
+    struct d3d12_command_list *list = impl_from_ID3D12GraphicsCommandList2(iface);
+    const union d3d12_bundle_command_arg *commands;
+    enum vkd3d_command_list_command_id id;
+    unsigned int count;
+    size_t i;
+
+    TRACE("iface %p, bundle_iface %p.\n", iface, bundle_iface);
+
+    if (!bundle)
+    {
+        WARN("bundle_iface is not a bundle.\n");
+        list->is_valid = false;
+        return;
+    }
+
+    if (!bundle->is_valid)
+    {
+        WARN("Error occurred during bundle recording.\n");
+        list->is_valid = false;
+        return;
+    }
+
+    if (bundle->is_recording)
+    {
+        WARN("Bundle is in the recording state.\n");
+        list->is_valid = false;
+        return;
+    }
+
+    commands = bundle->command_buffer.commands;
+
+    for (i = 0; i < bundle->command_buffer.count; i += count)
+    {
+        count = commands[i].header.count;
+        id = commands[i++].header.id;
+        d3d12_bundle_command_execute(id, &commands[i], iface);
+    }
+    assert(i == bundle->command_buffer.count);
 }
 
 static void STDMETHODCALLTYPE d3d12_command_list_SetDescriptorHeaps(ID3D12GraphicsCommandList2 *iface,
@@ -5614,6 +5655,21 @@ static struct d3d12_command_list *unsafe_impl_from_ID3D12CommandList(ID3D12Comma
     return CONTAINING_RECORD(iface, struct d3d12_command_list, ID3D12GraphicsCommandList2_iface);
 }
 
+static void d3d12_bundle_command_execute(enum vkd3d_command_list_command_id id,
+        const union d3d12_bundle_command_arg *args, ID3D12GraphicsCommandList2 *list)
+{
+    switch (id)
+    {
+        case VKD3D_COMMAND_DRAW_INSTANCED:
+            d3d12_command_list_DrawInstanced(list, args[0].uint_arg, args[1].uint_arg,
+                    args[2].uint_arg, args[3].uint_arg);
+            break;
+        default:
+            FIXME("Unhandled command %u.\n", id);
+            break;
+    }
+}
+
 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)
diff --git a/libs/vkd3d/device.c b/libs/vkd3d/device.c
index 0fadb521..3ff1affb 100644
--- a/libs/vkd3d/device.c
+++ b/libs/vkd3d/device.c
@@ -2314,17 +2314,29 @@ static HRESULT STDMETHODCALLTYPE d3d12_device_CreateCommandAllocator(ID3D12Devic
         D3D12_COMMAND_LIST_TYPE type, REFIID riid, void **command_allocator)
 {
     struct d3d12_device *device = impl_from_ID3D12Device(iface);
-    struct d3d12_command_allocator *object;
     HRESULT hr;
 
     TRACE("iface %p, type %#x, riid %s, command_allocator %p.\n",
             iface, type, debugstr_guid(riid), command_allocator);
 
-    if (FAILED(hr = d3d12_command_allocator_create(device, type, &object)))
-        return hr;
+    if (type == D3D12_COMMAND_LIST_TYPE_BUNDLE)
+    {
+        struct d3d12_bundle_allocator *object;
+        if (FAILED(hr = d3d12_bundle_allocator_create(device, &object)))
+            return hr;
 
-    return return_interface(&object->ID3D12CommandAllocator_iface, &IID_ID3D12CommandAllocator,
-            riid, command_allocator);
+        return return_interface(&object->ID3D12CommandAllocator_iface,
+                &IID_ID3D12CommandAllocator, riid, command_allocator);
+    }
+    else
+    {
+        struct d3d12_command_allocator *object;
+        if (FAILED(hr = d3d12_command_allocator_create(device, type, &object)))
+            return hr;
+
+        return return_interface(&object->ID3D12CommandAllocator_iface,
+                &IID_ID3D12CommandAllocator, riid, command_allocator);
+    }
 }
 
 static HRESULT STDMETHODCALLTYPE d3d12_device_CreateGraphicsPipelineState(ID3D12Device *iface,
@@ -2366,7 +2378,6 @@ static HRESULT STDMETHODCALLTYPE d3d12_device_CreateCommandList(ID3D12Device *if
         ID3D12PipelineState *initial_pipeline_state, REFIID riid, void **command_list)
 {
     struct d3d12_device *device = impl_from_ID3D12Device(iface);
-    struct d3d12_command_list *object;
     HRESULT hr;
 
     TRACE("iface %p, node_mask 0x%08x, type %#x, command_allocator %p, "
@@ -2374,12 +2385,26 @@ static HRESULT STDMETHODCALLTYPE d3d12_device_CreateCommandList(ID3D12Device *if
             iface, node_mask, type, command_allocator,
             initial_pipeline_state, debugstr_guid(riid), command_list);
 
-    if (FAILED(hr = d3d12_command_list_create(device, node_mask, type, command_allocator,
-            initial_pipeline_state, &object)))
-        return hr;
+    if (type == D3D12_COMMAND_LIST_TYPE_BUNDLE)
+    {
+        struct d3d12_bundle *object;
+        if (FAILED(hr = d3d12_bundle_create(device, node_mask, type, command_allocator,
+                initial_pipeline_state, &object)))
+            return hr;
 
-    return return_interface(&object->ID3D12GraphicsCommandList2_iface,
-            &IID_ID3D12GraphicsCommandList2, riid, command_list);
+        return return_interface(&object->ID3D12GraphicsCommandList2_iface,
+                &IID_ID3D12GraphicsCommandList2, riid, command_list);
+    }
+    else
+    {
+        struct d3d12_command_list *object;
+        if (FAILED(hr = d3d12_command_list_create(device, node_mask, type, command_allocator,
+                initial_pipeline_state, &object)))
+            return hr;
+
+        return return_interface(&object->ID3D12GraphicsCommandList2_iface,
+                &IID_ID3D12GraphicsCommandList2, riid, command_list);
+    }
 }
 
 /* Direct3D feature levels restrict which formats can be optionally supported. */
diff --git a/libs/vkd3d/vkd3d_private.h b/libs/vkd3d/vkd3d_private.h
index 0b326b11..1d5fab65 100644
--- a/libs/vkd3d/vkd3d_private.h
+++ b/libs/vkd3d/vkd3d_private.h
@@ -987,6 +987,82 @@ 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);
 
+enum vkd3d_command_list_command_id
+{
+    VKD3D_COMMAND_DRAW_INSTANCED,
+};
+
+struct d3d12_bundle_command_header
+{
+    unsigned int count;
+    enum vkd3d_command_list_command_id id;
+};
+
+union d3d12_bundle_command_arg
+{
+    struct d3d12_bundle_command_header header;
+    D3D12_GPU_DESCRIPTOR_HANDLE gpu_descriptor_handle;
+    D3D12_GPU_VIRTUAL_ADDRESS gpu_address;
+    D3D12_PRIMITIVE_TOPOLOGY topology;
+    float float_arg;
+    ID3D12CommandSignature *command_signature;
+    ID3D12PipelineState *pipeline_state;
+    ID3D12Resource *resource;
+    ID3D12RootSignature *root_signature;
+    int int_arg;
+    uint64_t uint64_arg;
+    unsigned int uint_arg;
+};
+
+#define d3d12_bundle_command_arg_count_from_sizeof(size) \
+        (((size) + sizeof(union d3d12_bundle_command_arg) - 1) / sizeof(union d3d12_bundle_command_arg))
+
+struct d3d12_bundle_command_buffer
+{
+    union d3d12_bundle_command_arg *commands;
+    size_t size;
+    size_t count;
+};
+
+struct d3d12_bundle_allocator
+{
+    ID3D12CommandAllocator ID3D12CommandAllocator_iface;
+    LONG refcount;
+
+    union d3d12_bundle_command_arg **arg_buffers;
+    size_t arg_buffers_size;
+    size_t arg_buffer_count;
+
+    struct d3d12_bundle *current_bundle;
+    struct d3d12_device *device;
+
+    struct vkd3d_private_store private_store;
+};
+
+HRESULT d3d12_bundle_allocator_create(struct d3d12_device *device,
+        struct d3d12_bundle_allocator **allocator);
+
+struct d3d12_bundle
+{
+    ID3D12GraphicsCommandList2 ID3D12GraphicsCommandList2_iface;
+    LONG refcount;
+
+    bool is_recording;
+    bool is_valid;
+    struct d3d12_bundle_command_buffer command_buffer;
+
+    struct d3d12_device *device;
+    struct d3d12_bundle_allocator *allocator;
+
+    struct vkd3d_private_store private_store;
+};
+
+struct d3d12_bundle *bundle_impl_from_ID3D12GraphicsCommandList(ID3D12GraphicsCommandList *iface);
+
+HRESULT d3d12_bundle_create(struct d3d12_device *device,
+        UINT node_mask, D3D12_COMMAND_LIST_TYPE type, ID3D12CommandAllocator *allocator_iface,
+        ID3D12PipelineState *initial_pipeline_state, struct d3d12_bundle **bundle);
+
 struct vkd3d_queue
 {
     /* Access to VkQueue must be externally synchronized. */
diff --git a/tests/d3d12.c b/tests/d3d12.c
index 1e895dcd..f723dbaa 100644
--- a/tests/d3d12.c
+++ b/tests/d3d12.c
@@ -7380,13 +7380,6 @@ static void test_bundle_state_inheritance(void)
     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");
@@ -7569,6 +7562,7 @@ static void test_bundle_state_inheritance(void)
 
     transition_resource_state(command_list, context.render_target,
             D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
+    todo
     check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);
 
     reset_command_list(command_list, context.allocator);
@@ -7594,6 +7588,7 @@ static void test_bundle_state_inheritance(void)
 
     transition_resource_state(command_list, context.render_target,
             D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_SOURCE);
+    todo
     check_sub_resource_uint(context.render_target, 0, queue, command_list, 0xff00ff00, 0);
 
     ID3D12CommandAllocator_Release(bundle_allocator);
-- 
2.32.0




More information about the wine-devel mailing list