From 37df70888f1e9c12f63d5fc3a511ec34fce4b10d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20D=C3=B6singer?= <stefan@codeweavers.com>
Date: Tue, 1 Oct 2013 14:31:56 +0200
Subject: wined3d: Hackily introduce a multithreaded command stream

---
 dlls/wined3d/cs.c              | 380 ++++++++++++++++++++++++++++++++++++-----
 dlls/wined3d/wined3d_main.c    |   9 +
 dlls/wined3d/wined3d_private.h |  18 ++
 3 files changed, 368 insertions(+), 39 deletions(-)

diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c
index daebc94..40c04db 100644
--- a/dlls/wined3d/cs.c
+++ b/dlls/wined3d/cs.c
@@ -24,8 +24,19 @@ WINE_DEFAULT_DEBUG_CHANNEL(d3d);
 
 #define WINED3D_INITIAL_CS_SIZE 4096
 
+static CRITICAL_SECTION wined3d_cs_list_mutex;
+static CRITICAL_SECTION_DEBUG wined3d_cs_list_mutex_debug =
+{
+    0, 0, &wined3d_cs_list_mutex,
+    {&wined3d_cs_list_mutex_debug.ProcessLocksList,
+    &wined3d_cs_list_mutex_debug.ProcessLocksList},
+    0, 0, {(DWORD_PTR)(__FILE__ ": wined3d_cs_list_mutex")}
+};
+static CRITICAL_SECTION wined3d_cs_list_mutex = {&wined3d_cs_list_mutex_debug, -1, 0, 0, 0, 0};
+
 enum wined3d_cs_op
 {
+    WINED3D_CS_OP_FENCE,
     WINED3D_CS_OP_PRESENT,
     WINED3D_CS_OP_CLEAR,
     WINED3D_CS_OP_DRAW,
@@ -52,6 +63,18 @@ enum wined3d_cs_op
     WINED3D_CS_OP_SET_COLOR_KEY,
     WINED3D_CS_OP_SET_MATERIAL,
     WINED3D_CS_OP_RESET_STATE,
+    WINED3D_CS_OP_STOP,
+};
+
+struct wined3d_cs_stop
+{
+    enum wined3d_cs_op opcode;
+};
+
+struct wined3d_cs_fence
+{
+    enum wined3d_cs_op opcode;
+    BOOL *signalled;
 };
 
 struct wined3d_cs_present
@@ -102,7 +125,7 @@ struct wined3d_cs_set_viewport
 struct wined3d_cs_set_scissor_rect
 {
     enum wined3d_cs_op opcode;
-    const RECT *rect;
+    RECT rect;
 };
 
 struct wined3d_cs_set_rendertarget_view
@@ -230,20 +253,20 @@ struct wined3d_cs_set_transform
 {
     enum wined3d_cs_op opcode;
     enum wined3d_transform_state state;
-    const struct wined3d_matrix *matrix;
+    struct wined3d_matrix matrix;
 };
 
 struct wined3d_cs_set_clip_plane
 {
     enum wined3d_cs_op opcode;
     UINT plane_idx;
-    const struct wined3d_vec4 *plane;
+    struct wined3d_vec4 plane;
 };
 
 struct wined3d_cs_set_material
 {
     enum wined3d_cs_op opcode;
-    const struct wined3d_material *material;
+    struct wined3d_material material;
 };
 
 struct wined3d_cs_reset_state
@@ -251,7 +274,134 @@ struct wined3d_cs_reset_state
     enum wined3d_cs_op opcode;
 };
 
-static void wined3d_cs_exec_present(struct wined3d_cs *cs, const void *data)
+/* FIXME: The list synchronization probably isn't particularly fast. */
+static void wined3d_cs_list_enqueue(struct wined3d_cs_list *list, struct wined3d_cs_block *block)
+{
+    EnterCriticalSection(&wined3d_cs_list_mutex);
+    list_add_tail(&list->blocks, &block->entry);
+    LeaveCriticalSection(&wined3d_cs_list_mutex);
+}
+
+static struct wined3d_cs_block *wined3d_cs_list_dequeue(struct wined3d_cs_list *list)
+{
+    struct list *head;
+
+    EnterCriticalSection(&wined3d_cs_list_mutex);
+    if (!(head = list_head(&list->blocks)))
+    {
+        LeaveCriticalSection(&wined3d_cs_list_mutex);
+        return NULL;
+    }
+    list_remove(head);
+    LeaveCriticalSection(&wined3d_cs_list_mutex);
+
+    return LIST_ENTRY(head, struct wined3d_cs_block, entry);
+}
+
+static struct wined3d_cs_block *wined3d_cs_list_dequeue_blocking(struct wined3d_cs_list *list)
+{
+    struct wined3d_cs_block *block;
+
+    /* FIXME: Use an event to wait after a couple of spins. */
+    for (;;)
+    {
+        if ((block = wined3d_cs_list_dequeue(list)))
+            return block;
+    }
+}
+
+static void wined3d_cs_list_init(struct wined3d_cs_list *list)
+{
+    list_init(&list->blocks);
+}
+
+static struct wined3d_cs_block *wined3d_cs_get_thread_block(const struct wined3d_cs *cs)
+{
+    return TlsGetValue(cs->tls_idx);
+}
+
+static void wined3d_cs_set_thread_block(const struct wined3d_cs *cs, struct wined3d_cs_block *block)
+{
+    if (!TlsSetValue(cs->tls_idx, block))
+        ERR("Failed to set thread block.\n");
+}
+
+static void wined3d_cs_flush(struct wined3d_cs *cs)
+{
+    wined3d_cs_list_enqueue(&cs->exec_list, wined3d_cs_get_thread_block(cs));
+    wined3d_cs_set_thread_block(cs, NULL);
+}
+
+static struct wined3d_cs_block *wined3d_cs_get_block(struct wined3d_cs *cs)
+{
+    struct wined3d_cs_block *block;
+
+    if (!(block = wined3d_cs_list_dequeue(&cs->free_list)))
+    {
+        if (!(block = HeapAlloc(GetProcessHeap(), 0, sizeof(*block))))
+        {
+            ERR("Failed to get new block.\n");
+            return NULL;
+        }
+    }
+
+    block->pos = 0;
+
+    return block;
+}
+
+static void *wined3d_cs_mt_require_space(struct wined3d_cs *cs, size_t size)
+{
+    struct wined3d_cs_block *block = wined3d_cs_get_thread_block(cs);
+    void *data;
+
+    if (!block || block->pos + size > sizeof(block->data))
+    {
+        if (block)
+            wined3d_cs_flush(cs);
+        block = wined3d_cs_get_block(cs);
+        wined3d_cs_set_thread_block(cs, block);
+    }
+
+    data = &block->data[block->pos];
+    block->pos += size;
+
+    return data;
+}
+
+static UINT wined3d_cs_exec_fence(struct wined3d_cs *cs, const void *data)
+{
+    const struct wined3d_cs_fence *op = data;
+
+    InterlockedExchange(op->signalled, TRUE);
+
+    return sizeof(*op);
+}
+
+static void wined3d_cs_emit_fence(struct wined3d_cs *cs, BOOL *signalled)
+{
+    struct wined3d_cs_fence *op;
+
+    *signalled = FALSE;
+
+    op = cs->ops->require_space(cs, sizeof(*op));
+    op->opcode = WINED3D_CS_OP_FENCE;
+    op->signalled = signalled;
+}
+
+static void wined3d_cs_flush_and_wait(struct wined3d_cs *cs)
+{
+    BOOL fence;
+
+    wined3d_cs_emit_fence(cs, &fence);
+    wined3d_cs_flush(cs);
+
+    /* A busy wait should be fine, we're not supposed to have to wait very
+     * long. */
+    while (!InterlockedCompareExchange(&fence, TRUE, TRUE));
+}
+
+static UINT wined3d_cs_exec_present(struct wined3d_cs *cs, const void *data)
 {
     const struct wined3d_cs_present *op = data;
     struct wined3d_swapchain *swapchain;
@@ -261,6 +411,8 @@ static void wined3d_cs_exec_present(struct wined3d_cs *cs, const void *data)
 
     swapchain->swapchain_ops->swapchain_present(swapchain,
             op->src_rect, op->dst_rect, op->dirty_region, op->flags);
+
+    return sizeof(*op);
 }
 
 void wined3d_cs_emit_present(struct wined3d_cs *cs, struct wined3d_swapchain *swapchain,
@@ -281,7 +433,7 @@ void wined3d_cs_emit_present(struct wined3d_cs *cs, struct wined3d_swapchain *sw
     cs->ops->submit(cs);
 }
 
-static void wined3d_cs_exec_clear(struct wined3d_cs *cs, const void *data)
+static UINT wined3d_cs_exec_clear(struct wined3d_cs *cs, const void *data)
 {
     const struct wined3d_cs_clear *op = data;
     struct wined3d_device *device;
@@ -292,6 +444,8 @@ static void wined3d_cs_exec_clear(struct wined3d_cs *cs, const void *data)
     device_clear_render_targets(device, device->adapter->gl_info.limits.buffers,
             &cs->state.fb, op->rect_count, op->rects, &draw_rect, op->flags,
             op->color, op->depth, op->stencil);
+
+    return sizeof(*op);
 }
 
 void wined3d_cs_emit_clear(struct wined3d_cs *cs, DWORD rect_count, const RECT *rects,
@@ -311,12 +465,14 @@ void wined3d_cs_emit_clear(struct wined3d_cs *cs, DWORD rect_count, const RECT *
     cs->ops->submit(cs);
 }
 
-static void wined3d_cs_exec_draw(struct wined3d_cs *cs, const void *data)
+static UINT wined3d_cs_exec_draw(struct wined3d_cs *cs, const void *data)
 {
     const struct wined3d_cs_draw *op = data;
 
     draw_primitive(cs->device, op->start_idx, op->index_count,
             op->start_instance, op->instance_count, op->indexed);
+
+    return sizeof(*op);
 }
 
 void wined3d_cs_emit_draw(struct wined3d_cs *cs, UINT start_idx, UINT index_count,
@@ -335,12 +491,14 @@ void wined3d_cs_emit_draw(struct wined3d_cs *cs, UINT start_idx, UINT index_coun
     cs->ops->submit(cs);
 }
 
-static void wined3d_cs_exec_set_predication(struct wined3d_cs *cs, const void *data)
+static UINT wined3d_cs_exec_set_predication(struct wined3d_cs *cs, const void *data)
 {
     const struct wined3d_cs_set_predication *op = data;
 
     cs->state.predicate = op->predicate;
     cs->state.predicate_value = op->value;
+
+    return sizeof(*op);
 }
 
 void wined3d_cs_emit_set_predication(struct wined3d_cs *cs, struct wined3d_query *predicate, BOOL value)
@@ -355,12 +513,14 @@ void wined3d_cs_emit_set_predication(struct wined3d_cs *cs, struct wined3d_query
     cs->ops->submit(cs);
 }
 
-static void wined3d_cs_exec_set_viewport(struct wined3d_cs *cs, const void *data)
+static UINT wined3d_cs_exec_set_viewport(struct wined3d_cs *cs, const void *data)
 {
     const struct wined3d_cs_set_viewport *op = data;
 
     cs->state.viewport = *op->viewport;
     device_invalidate_state(cs->device, STATE_VIEWPORT);
+
+    return sizeof(*op);
 }
 
 void wined3d_cs_emit_set_viewport(struct wined3d_cs *cs, const struct wined3d_viewport *viewport)
@@ -374,12 +534,14 @@ void wined3d_cs_emit_set_viewport(struct wined3d_cs *cs, const struct wined3d_vi
     cs->ops->submit(cs);
 }
 
-static void wined3d_cs_exec_set_scissor_rect(struct wined3d_cs *cs, const void *data)
+static UINT wined3d_cs_exec_set_scissor_rect(struct wined3d_cs *cs, const void *data)
 {
     const struct wined3d_cs_set_scissor_rect *op = data;
 
-    cs->state.scissor_rect = *op->rect;
+    cs->state.scissor_rect = op->rect;
     device_invalidate_state(cs->device, STATE_SCISSORRECT);
+
+    return sizeof(*op);
 }
 
 void wined3d_cs_emit_set_scissor_rect(struct wined3d_cs *cs, const RECT *rect)
@@ -388,17 +550,19 @@ void wined3d_cs_emit_set_scissor_rect(struct wined3d_cs *cs, const RECT *rect)
 
     op = cs->ops->require_space(cs, sizeof(*op));
     op->opcode = WINED3D_CS_OP_SET_SCISSOR_RECT;
-    op->rect = rect;
+    op->rect = *rect;
 
     cs->ops->submit(cs);
 }
 
-static void wined3d_cs_exec_set_rendertarget_view(struct wined3d_cs *cs, const void *data)
+static UINT wined3d_cs_exec_set_rendertarget_view(struct wined3d_cs *cs, const void *data)
 {
     const struct wined3d_cs_set_rendertarget_view *op = data;
 
     cs->state.fb.render_targets[op->view_idx] = op->view;
     device_invalidate_state(cs->device, STATE_FRAMEBUFFER);
+
+    return sizeof(*op);
 }
 
 void wined3d_cs_emit_set_rendertarget_view(struct wined3d_cs *cs, unsigned int view_idx,
@@ -414,7 +578,7 @@ void wined3d_cs_emit_set_rendertarget_view(struct wined3d_cs *cs, unsigned int v
     cs->ops->submit(cs);
 }
 
-static void wined3d_cs_exec_set_depth_stencil_view(struct wined3d_cs *cs, const void *data)
+static UINT wined3d_cs_exec_set_depth_stencil_view(struct wined3d_cs *cs, const void *data)
 {
     const struct wined3d_cs_set_depth_stencil_view *op = data;
     struct wined3d_device *device = cs->device;
@@ -452,6 +616,8 @@ static void wined3d_cs_exec_set_depth_stencil_view(struct wined3d_cs *cs, const
     }
 
     device_invalidate_state(device, STATE_FRAMEBUFFER);
+
+    return sizeof(*op);
 }
 
 void wined3d_cs_emit_set_depth_stencil_view(struct wined3d_cs *cs, struct wined3d_rendertarget_view *view)
@@ -465,12 +631,14 @@ void wined3d_cs_emit_set_depth_stencil_view(struct wined3d_cs *cs, struct wined3
     cs->ops->submit(cs);
 }
 
-static void wined3d_cs_exec_set_vertex_declaration(struct wined3d_cs *cs, const void *data)
+static UINT wined3d_cs_exec_set_vertex_declaration(struct wined3d_cs *cs, const void *data)
 {
     const struct wined3d_cs_set_vertex_declaration *op = data;
 
     cs->state.vertex_declaration = op->declaration;
     device_invalidate_state(cs->device, STATE_VDECL);
+
+    return sizeof(*op);
 }
 
 void wined3d_cs_emit_set_vertex_declaration(struct wined3d_cs *cs, struct wined3d_vertex_declaration *declaration)
@@ -484,7 +652,7 @@ void wined3d_cs_emit_set_vertex_declaration(struct wined3d_cs *cs, struct wined3
     cs->ops->submit(cs);
 }
 
-static void wined3d_cs_exec_set_stream_source(struct wined3d_cs *cs, const void *data)
+static UINT wined3d_cs_exec_set_stream_source(struct wined3d_cs *cs, const void *data)
 {
     const struct wined3d_cs_set_stream_source *op = data;
     struct wined3d_stream_state *stream;
@@ -502,6 +670,8 @@ static void wined3d_cs_exec_set_stream_source(struct wined3d_cs *cs, const void
         InterlockedDecrement(&prev->resource.bind_count);
 
     device_invalidate_state(cs->device, STATE_STREAMSRC);
+
+    return sizeof(*op);
 }
 
 void wined3d_cs_emit_set_stream_source(struct wined3d_cs *cs, UINT stream_idx,
@@ -519,7 +689,7 @@ void wined3d_cs_emit_set_stream_source(struct wined3d_cs *cs, UINT stream_idx,
     cs->ops->submit(cs);
 }
 
-static void wined3d_cs_exec_set_stream_source_freq(struct wined3d_cs *cs, const void *data)
+static UINT wined3d_cs_exec_set_stream_source_freq(struct wined3d_cs *cs, const void *data)
 {
     const struct wined3d_cs_set_stream_source_freq *op = data;
     struct wined3d_stream_state *stream;
@@ -529,6 +699,8 @@ static void wined3d_cs_exec_set_stream_source_freq(struct wined3d_cs *cs, const
     stream->flags = op->flags;
 
     device_invalidate_state(cs->device, STATE_STREAMSRC);
+
+    return sizeof(*op);
 }
 
 void wined3d_cs_emit_set_stream_source_freq(struct wined3d_cs *cs, UINT stream_idx, UINT frequency, UINT flags)
@@ -544,7 +716,7 @@ void wined3d_cs_emit_set_stream_source_freq(struct wined3d_cs *cs, UINT stream_i
     cs->ops->submit(cs);
 }
 
-static void wined3d_cs_exec_set_stream_output(struct wined3d_cs *cs, const void *data)
+static UINT wined3d_cs_exec_set_stream_output(struct wined3d_cs *cs, const void *data)
 {
     const struct wined3d_cs_set_stream_output *op = data;
     struct wined3d_stream_output *stream;
@@ -559,6 +731,8 @@ static void wined3d_cs_exec_set_stream_output(struct wined3d_cs *cs, const void
         InterlockedIncrement(&op->buffer->resource.bind_count);
     if (prev)
         InterlockedDecrement(&prev->resource.bind_count);
+
+    return sizeof(*op);
 }
 
 void wined3d_cs_emit_set_stream_output(struct wined3d_cs *cs, UINT stream_idx,
@@ -575,7 +749,7 @@ void wined3d_cs_emit_set_stream_output(struct wined3d_cs *cs, UINT stream_idx,
     cs->ops->submit(cs);
 }
 
-static void wined3d_cs_exec_set_index_buffer(struct wined3d_cs *cs, const void *data)
+static UINT wined3d_cs_exec_set_index_buffer(struct wined3d_cs *cs, const void *data)
 {
     const struct wined3d_cs_set_index_buffer *op = data;
     struct wined3d_buffer *prev;
@@ -590,6 +764,8 @@ static void wined3d_cs_exec_set_index_buffer(struct wined3d_cs *cs, const void *
         InterlockedDecrement(&prev->resource.bind_count);
 
     device_invalidate_state(cs->device, STATE_INDEXBUFFER);
+
+    return sizeof(*op);
 }
 
 void wined3d_cs_emit_set_index_buffer(struct wined3d_cs *cs, struct wined3d_buffer *buffer,
@@ -605,7 +781,7 @@ void wined3d_cs_emit_set_index_buffer(struct wined3d_cs *cs, struct wined3d_buff
     cs->ops->submit(cs);
 }
 
-static void wined3d_cs_exec_set_constant_buffer(struct wined3d_cs *cs, const void *data)
+static UINT wined3d_cs_exec_set_constant_buffer(struct wined3d_cs *cs, const void *data)
 {
     const struct wined3d_cs_set_constant_buffer *op = data;
     struct wined3d_buffer *prev;
@@ -619,6 +795,7 @@ static void wined3d_cs_exec_set_constant_buffer(struct wined3d_cs *cs, const voi
         InterlockedDecrement(&prev->resource.bind_count);
 
     device_invalidate_state(cs->device, STATE_CONSTANT_BUFFER(op->type));
+    return sizeof(*op);
 }
 
 void wined3d_cs_emit_set_constant_buffer(struct wined3d_cs *cs, enum wined3d_shader_type type,
@@ -635,7 +812,7 @@ void wined3d_cs_emit_set_constant_buffer(struct wined3d_cs *cs, enum wined3d_sha
     cs->ops->submit(cs);
 }
 
-static void wined3d_cs_exec_set_texture(struct wined3d_cs *cs, const void *data)
+static UINT wined3d_cs_exec_set_texture(struct wined3d_cs *cs, const void *data)
 {
     const struct wined3d_d3d_info *d3d_info = &cs->device->adapter->d3d_info;
     const struct wined3d_cs_set_texture *op = data;
@@ -710,6 +887,8 @@ static void wined3d_cs_exec_set_texture(struct wined3d_cs *cs, const void *data)
 
     if (new_use_color_key)
         device_invalidate_state(cs->device, STATE_COLOR_KEY);
+
+    return sizeof(*op);
 }
 
 void wined3d_cs_emit_set_texture(struct wined3d_cs *cs, UINT stage, struct wined3d_texture *texture)
@@ -724,12 +903,14 @@ void wined3d_cs_emit_set_texture(struct wined3d_cs *cs, UINT stage, struct wined
     cs->ops->submit(cs);
 }
 
-static void wined3d_cs_exec_set_shader_resource_view(struct wined3d_cs *cs, const void *data)
+static UINT wined3d_cs_exec_set_shader_resource_view(struct wined3d_cs *cs, const void *data)
 {
     const struct wined3d_cs_set_shader_resource_view *op = data;
 
     cs->state.shader_resource_view[op->type][op->view_idx] = op->view;
     device_invalidate_state(cs->device, STATE_SHADER_RESOURCE_BINDING);
+
+    return sizeof(*op);
 }
 
 void wined3d_cs_emit_set_shader_resource_view(struct wined3d_cs *cs, enum wined3d_shader_type type,
@@ -746,12 +927,14 @@ void wined3d_cs_emit_set_shader_resource_view(struct wined3d_cs *cs, enum wined3
     cs->ops->submit(cs);
 }
 
-static void wined3d_cs_exec_set_sampler(struct wined3d_cs *cs, const void *data)
+static UINT wined3d_cs_exec_set_sampler(struct wined3d_cs *cs, const void *data)
 {
     const struct wined3d_cs_set_sampler *op = data;
 
     cs->state.sampler[op->type][op->sampler_idx] = op->sampler;
     device_invalidate_state(cs->device, STATE_SHADER_RESOURCE_BINDING);
+
+    return sizeof(*op);
 }
 
 void wined3d_cs_emit_set_sampler(struct wined3d_cs *cs, enum wined3d_shader_type type,
@@ -768,13 +951,15 @@ void wined3d_cs_emit_set_sampler(struct wined3d_cs *cs, enum wined3d_shader_type
     cs->ops->submit(cs);
 }
 
-static void wined3d_cs_exec_set_shader(struct wined3d_cs *cs, const void *data)
+static UINT wined3d_cs_exec_set_shader(struct wined3d_cs *cs, const void *data)
 {
     const struct wined3d_cs_set_shader *op = data;
 
     cs->state.shader[op->type] = op->shader;
     device_invalidate_state(cs->device, STATE_SHADER(op->type));
     device_invalidate_state(cs->device, STATE_SHADER_RESOURCE_BINDING);
+
+    return sizeof(*op);
 }
 
 void wined3d_cs_emit_set_shader(struct wined3d_cs *cs, enum wined3d_shader_type type, struct wined3d_shader *shader)
@@ -789,12 +974,14 @@ void wined3d_cs_emit_set_shader(struct wined3d_cs *cs, enum wined3d_shader_type
     cs->ops->submit(cs);
 }
 
-static void wined3d_cs_exec_set_render_state(struct wined3d_cs *cs, const void *data)
+static UINT wined3d_cs_exec_set_render_state(struct wined3d_cs *cs, const void *data)
 {
     const struct wined3d_cs_set_render_state *op = data;
 
     cs->state.render_states[op->state] = op->value;
     device_invalidate_state(cs->device, STATE_RENDER(op->state));
+
+    return sizeof(*op);
 }
 
 void wined3d_cs_emit_set_render_state(struct wined3d_cs *cs, enum wined3d_render_state state, DWORD value)
@@ -809,12 +996,14 @@ void wined3d_cs_emit_set_render_state(struct wined3d_cs *cs, enum wined3d_render
     cs->ops->submit(cs);
 }
 
-static void wined3d_cs_exec_set_texture_state(struct wined3d_cs *cs, const void *data)
+static UINT wined3d_cs_exec_set_texture_state(struct wined3d_cs *cs, const void *data)
 {
     const struct wined3d_cs_set_texture_state *op = data;
 
     cs->state.texture_states[op->stage][op->state] = op->value;
     device_invalidate_state(cs->device, STATE_TEXTURESTAGE(op->stage, op->state));
+
+    return sizeof(*op);
 }
 
 void wined3d_cs_emit_set_texture_state(struct wined3d_cs *cs, UINT stage,
@@ -831,12 +1020,14 @@ void wined3d_cs_emit_set_texture_state(struct wined3d_cs *cs, UINT stage,
     cs->ops->submit(cs);
 }
 
-static void wined3d_cs_exec_set_sampler_state(struct wined3d_cs *cs, const void *data)
+static UINT wined3d_cs_exec_set_sampler_state(struct wined3d_cs *cs, const void *data)
 {
     const struct wined3d_cs_set_sampler_state *op = data;
 
     cs->state.sampler_states[op->sampler_idx][op->state] = op->value;
     device_invalidate_state(cs->device, STATE_SAMPLER(op->sampler_idx));
+
+    return sizeof(*op);
 }
 
 void wined3d_cs_emit_set_sampler_state(struct wined3d_cs *cs, UINT sampler_idx,
@@ -853,13 +1044,15 @@ void wined3d_cs_emit_set_sampler_state(struct wined3d_cs *cs, UINT sampler_idx,
     cs->ops->submit(cs);
 }
 
-static void wined3d_cs_exec_set_transform(struct wined3d_cs *cs, const void *data)
+static UINT wined3d_cs_exec_set_transform(struct wined3d_cs *cs, const void *data)
 {
     const struct wined3d_cs_set_transform *op = data;
 
-    cs->state.transforms[op->state] = *op->matrix;
+    cs->state.transforms[op->state] = op->matrix;
     if (op->state < WINED3D_TS_WORLD_MATRIX(cs->device->adapter->d3d_info.limits.ffp_vertex_blend_matrices))
         device_invalidate_state(cs->device, STATE_TRANSFORM(op->state));
+
+    return sizeof(*op);
 }
 
 void wined3d_cs_emit_set_transform(struct wined3d_cs *cs, enum wined3d_transform_state state,
@@ -870,17 +1063,19 @@ void wined3d_cs_emit_set_transform(struct wined3d_cs *cs, enum wined3d_transform
     op = cs->ops->require_space(cs, sizeof(*op));
     op->opcode = WINED3D_CS_OP_SET_TRANSFORM;
     op->state = state;
-    op->matrix = matrix;
+    op->matrix = *matrix;
 
     cs->ops->submit(cs);
 }
 
-static void wined3d_cs_exec_set_clip_plane(struct wined3d_cs *cs, const void *data)
+static UINT wined3d_cs_exec_set_clip_plane(struct wined3d_cs *cs, const void *data)
 {
     const struct wined3d_cs_set_clip_plane *op = data;
 
-    cs->state.clip_planes[op->plane_idx] = *op->plane;
+    cs->state.clip_planes[op->plane_idx] = op->plane;
     device_invalidate_state(cs->device, STATE_CLIPPLANE(op->plane_idx));
+
+    return sizeof(*op);
 }
 
 void wined3d_cs_emit_set_clip_plane(struct wined3d_cs *cs, UINT plane_idx, const struct wined3d_vec4 *plane)
@@ -890,12 +1085,12 @@ void wined3d_cs_emit_set_clip_plane(struct wined3d_cs *cs, UINT plane_idx, const
     op = cs->ops->require_space(cs, sizeof(*op));
     op->opcode = WINED3D_CS_OP_SET_CLIP_PLANE;
     op->plane_idx = plane_idx;
-    op->plane = plane;
+    op->plane = *plane;
 
     cs->ops->submit(cs);
 }
 
-static void wined3d_cs_exec_set_color_key(struct wined3d_cs *cs, const void *data)
+static UINT wined3d_cs_exec_set_color_key(struct wined3d_cs *cs, const void *data)
 {
     const struct wined3d_cs_set_color_key *op = data;
     struct wined3d_texture *texture = op->texture;
@@ -956,6 +1151,8 @@ static void wined3d_cs_exec_set_color_key(struct wined3d_cs *cs, const void *dat
                 break;
         }
     }
+
+    return sizeof(*op);
 }
 
 void wined3d_cs_emit_set_color_key(struct wined3d_cs *cs, struct wined3d_texture *texture,
@@ -978,12 +1175,14 @@ void wined3d_cs_emit_set_color_key(struct wined3d_cs *cs, struct wined3d_texture
     cs->ops->submit(cs);
 }
 
-static void wined3d_cs_exec_set_material(struct wined3d_cs *cs, const void *data)
+static UINT wined3d_cs_exec_set_material(struct wined3d_cs *cs, const void *data)
 {
     const struct wined3d_cs_set_material *op = data;
 
-    cs->state.material = *op->material;
+    cs->state.material = op->material;
     device_invalidate_state(cs->device, STATE_MATERIAL);
+
+    return sizeof(*op);
 }
 
 void wined3d_cs_emit_set_material(struct wined3d_cs *cs, const struct wined3d_material *material)
@@ -992,12 +1191,12 @@ void wined3d_cs_emit_set_material(struct wined3d_cs *cs, const struct wined3d_ma
 
     op = cs->ops->require_space(cs, sizeof(*op));
     op->opcode = WINED3D_CS_OP_SET_MATERIAL;
-    op->material = material;
+    op->material = *material;
 
     cs->ops->submit(cs);
 }
 
-static void wined3d_cs_exec_reset_state(struct wined3d_cs *cs, const void *data)
+static UINT wined3d_cs_exec_reset_state(struct wined3d_cs *cs, const void *data)
 {
     struct wined3d_adapter *adapter = cs->device->adapter;
     HRESULT hr;
@@ -1007,6 +1206,8 @@ static void wined3d_cs_exec_reset_state(struct wined3d_cs *cs, const void *data)
     if (FAILED(hr = state_init(&cs->state, &adapter->gl_info, &adapter->d3d_info,
             WINED3D_STATE_NO_REF | WINED3D_STATE_INIT_DEFAULT)))
         ERR("Failed to initialize CS state, hr %#x.\n", hr);
+
+    return sizeof(struct wined3d_cs_reset_state);
 }
 
 void wined3d_cs_emit_reset_state(struct wined3d_cs *cs)
@@ -1019,8 +1220,9 @@ void wined3d_cs_emit_reset_state(struct wined3d_cs *cs)
     cs->ops->submit(cs);
 }
 
-static void (* const wined3d_cs_op_handlers[])(struct wined3d_cs *cs, const void *data) =
+static UINT (* const wined3d_cs_op_handlers[])(struct wined3d_cs *cs, const void *data) =
 {
+    /* WINED3D_CS_OP_FENCE                      */ wined3d_cs_exec_fence,
     /* WINED3D_CS_OP_PRESENT                    */ wined3d_cs_exec_present,
     /* WINED3D_CS_OP_CLEAR                      */ wined3d_cs_exec_clear,
     /* WINED3D_CS_OP_DRAW                       */ wined3d_cs_exec_draw,
@@ -1079,6 +1281,58 @@ static const struct wined3d_cs_ops wined3d_cs_st_ops =
     wined3d_cs_st_submit,
 };
 
+static const struct wined3d_cs_ops wined3d_cs_mt_ops =
+{
+    wined3d_cs_mt_require_space,
+    wined3d_cs_flush_and_wait,
+};
+
+/* FIXME: wined3d_device_uninit_3d() should either flush and wait, or be an
+ * OP itself. */
+static void wined3d_cs_emit_stop(struct wined3d_cs *cs)
+{
+    struct wined3d_cs_stop *op;
+
+    op = wined3d_cs_mt_require_space(cs, sizeof(*op));
+    op->opcode = WINED3D_CS_OP_STOP;
+
+    wined3d_cs_flush(cs);
+}
+
+static DWORD WINAPI wined3d_cs_run(void *thread_param)
+{
+    struct wined3d_cs *cs = thread_param;
+
+    TRACE("Started.\n");
+
+    for (;;)
+    {
+        struct wined3d_cs_block *block;
+        UINT pos = 0;
+
+        block = wined3d_cs_list_dequeue_blocking(&cs->exec_list);
+        while (pos < block->pos)
+        {
+            enum wined3d_cs_op opcode = *(const enum wined3d_cs_op *)&block->data[pos];
+
+            if (opcode >= WINED3D_CS_OP_STOP)
+            {
+                if (opcode > WINED3D_CS_OP_STOP)
+                    ERR("Invalid opcode %#x.\n", opcode);
+                goto done;
+            }
+
+            pos += wined3d_cs_op_handlers[opcode](cs, &block->data[pos]);
+        }
+
+        wined3d_cs_list_enqueue(&cs->free_list, block);
+    }
+
+done:
+    TRACE("Stopped.\n");
+    return 0;
+}
+
 struct wined3d_cs *wined3d_cs_create(struct wined3d_device *device)
 {
     const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
@@ -1104,12 +1358,60 @@ struct wined3d_cs *wined3d_cs_create(struct wined3d_device *device)
         return NULL;
     }
 
+    if ((cs->tls_idx = TlsAlloc()) == TLS_OUT_OF_INDEXES)
+    {
+        ERR("Failed to allocate cs TLS index, err %#x.\n", GetLastError());
+        HeapFree(GetProcessHeap(), 0, cs->data);
+        HeapFree(GetProcessHeap(), 0, cs);
+        return NULL;
+    }
+
+    if (wined3d_settings.cs_multithreaded)
+    {
+        cs->ops = &wined3d_cs_mt_ops;
+
+        wined3d_cs_list_init(&cs->free_list);
+        wined3d_cs_list_init(&cs->exec_list);
+
+        if (!(cs->thread = CreateThread(NULL, 0, wined3d_cs_run, cs, 0, NULL)))
+        {
+            ERR("Failed to create wined3d command stream thread.\n");
+            if (!TlsFree(cs->tls_idx))
+                ERR("Failed to free cs TLS index, err %#x.\n", GetLastError());
+            HeapFree(GetProcessHeap(), 0, cs->data);
+            HeapFree(GetProcessHeap(), 0, cs);
+            return NULL;
+        }
+    }
+
     return cs;
 }
 
 void wined3d_cs_destroy(struct wined3d_cs *cs)
 {
+    DWORD ret;
+
     state_cleanup(&cs->state);
+
+    if (wined3d_settings.cs_multithreaded)
+    {
+        wined3d_cs_emit_stop(cs);
+
+        ret = WaitForSingleObject(cs->thread, INFINITE);
+        CloseHandle(cs->thread);
+        if (ret != WAIT_OBJECT_0)
+            ERR("Wait failed (%#x).\n", ret);
+
+        /* FIXME: Cleanup the block lists on thread exit. */
+#if 0
+        wined3d_cs_list_cleanup(&cs->exec_list);
+        wined3d_cs_list_cleanup(&cs->free_list);
+#endif
+    }
+
+    if (!TlsFree(cs->tls_idx))
+        ERR("Failed to free cs TLS index, err %#x.\n", GetLastError());
+
     HeapFree(GetProcessHeap(), 0, cs->data);
     HeapFree(GetProcessHeap(), 0, cs);
 }
diff --git a/dlls/wined3d/wined3d_main.c b/dlls/wined3d/wined3d_main.c
index 08021a2..088e59a 100644
--- a/dlls/wined3d/wined3d_main.c
+++ b/dlls/wined3d/wined3d_main.c
@@ -85,6 +85,7 @@ struct wined3d_settings wined3d_settings =
     ~0U,            /* No GS shader model limit by default. */
     ~0U,            /* No PS shader model limit by default. */
     FALSE,          /* 3D support enabled by default. */
+    FALSE,          /* No multithreaded CS by default. */
 };
 
 struct wined3d * CDECL wined3d_create(DWORD flags)
@@ -304,6 +305,14 @@ static BOOL wined3d_dll_init(HINSTANCE hInstDLL)
             TRACE("Disabling 3D support.\n");
             wined3d_settings.no_3d = TRUE;
         }
+        if (!get_config_key(hkey, appkey, "CSMT", buffer, size)
+                && !strcmp(buffer,"enabled"))
+        {
+            TRACE("Enabling multithreaded command stream.\n");
+            wined3d_settings.cs_multithreaded = TRUE;
+            TRACE("Enforcing strict draw ordering for multithreaded command stream.\n");
+            wined3d_settings.strict_draw_ordering = TRUE;
+        }
     }
 
     if (appkey) RegCloseKey( appkey );
diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
index 59f9772..01c045b 100644
--- a/dlls/wined3d/wined3d_private.h
+++ b/dlls/wined3d/wined3d_private.h
@@ -279,6 +279,7 @@ struct wined3d_settings
     unsigned int max_sm_gs;
     unsigned int max_sm_ps;
     BOOL no_3d;
+    BOOL cs_multithreaded;
 };
 
 extern struct wined3d_settings wined3d_settings DECLSPEC_HIDDEN;
@@ -2589,6 +2590,18 @@ HRESULT state_init(struct wined3d_state *state, const struct wined3d_gl_info *gl
         const struct wined3d_d3d_info *d3d_info, DWORD flags) DECLSPEC_HIDDEN;
 void state_unbind_resources(struct wined3d_state *state) DECLSPEC_HIDDEN;
 
+struct wined3d_cs_list
+{
+    struct list blocks;
+};
+
+struct wined3d_cs_block
+{
+    struct list entry;
+    UINT pos;
+    BYTE data[4000]; /* FIXME? The size is somewhat arbitrary. */
+};
+
 struct wined3d_cs_ops
 {
     void *(*require_space)(struct wined3d_cs *cs, size_t size);
@@ -2600,9 +2613,14 @@ struct wined3d_cs
     const struct wined3d_cs_ops *ops;
     struct wined3d_device *device;
     struct wined3d_state state;
+    HANDLE thread;
+    DWORD tls_idx;
 
     size_t data_size;
     void *data;
+
+    struct wined3d_cs_list free_list;
+    struct wined3d_cs_list exec_list;
 };
 
 struct wined3d_cs *wined3d_cs_create(struct wined3d_device *device) DECLSPEC_HIDDEN;
-- 
2.4.4

