Commit e97ed10d authored by Henri Verbeet's avatar Henri Verbeet Committed by Alexandre Julliard

wined3d: Introduce a multi-threaded command stream implementation.

The primary purpose of this patch is to serialise draws from multiple threads, without the overhead of extra flushes that "StrictDrawOrdering" imposes. With additional work, offloading state processing and driver overhead to a separate thread may also allow for improved performance in some applications, but that's not a goal of this patch. Signed-off-by: 's avatarHenri Verbeet <hverbeet@codeweavers.com> Signed-off-by: 's avatarAlexandre Julliard <julliard@winehq.org>
parent 972d9dae
...@@ -1704,6 +1704,8 @@ struct wined3d_context *context_create(struct wined3d_swapchain *swapchain, ...@@ -1704,6 +1704,8 @@ struct wined3d_context *context_create(struct wined3d_swapchain *swapchain,
TRACE("swapchain %p, target %p, window %p.\n", swapchain, target, swapchain->win_handle); TRACE("swapchain %p, target %p, window %p.\n", swapchain, target, swapchain->win_handle);
wined3d_from_cs(device->cs);
ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*ret)); ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*ret));
if (!ret) if (!ret)
return NULL; return NULL;
...@@ -2106,6 +2108,8 @@ void context_destroy(struct wined3d_device *device, struct wined3d_context *cont ...@@ -2106,6 +2108,8 @@ void context_destroy(struct wined3d_device *device, struct wined3d_context *cont
TRACE("Destroying ctx %p\n", context); TRACE("Destroying ctx %p\n", context);
wined3d_from_cs(device->cs);
/* We delay destroying a context when it is active. The context_release() /* We delay destroying a context when it is active. The context_release()
* function invokes context_destroy() again while leaving the last level. */ * function invokes context_destroy() again while leaving the last level. */
if (context->level) if (context->level)
...@@ -3841,6 +3845,8 @@ struct wined3d_context *context_acquire(const struct wined3d_device *device, ...@@ -3841,6 +3845,8 @@ struct wined3d_context *context_acquire(const struct wined3d_device *device,
TRACE("device %p, texture %p, sub_resource_idx %u.\n", device, texture, sub_resource_idx); TRACE("device %p, texture %p, sub_resource_idx %u.\n", device, texture, sub_resource_idx);
wined3d_from_cs(device->cs);
if (current_context && current_context->destroyed) if (current_context && current_context->destroyed)
current_context = NULL; current_context = NULL;
......
...@@ -4715,6 +4715,8 @@ void device_resource_add(struct wined3d_device *device, struct wined3d_resource ...@@ -4715,6 +4715,8 @@ void device_resource_add(struct wined3d_device *device, struct wined3d_resource
{ {
TRACE("device %p, resource %p.\n", device, resource); TRACE("device %p, resource %p.\n", device, resource);
wined3d_not_from_cs(device->cs);
list_add_head(&device->resources, &resource->resource_list_entry); list_add_head(&device->resources, &resource->resource_list_entry);
} }
...@@ -4722,6 +4724,8 @@ static void device_resource_remove(struct wined3d_device *device, struct wined3d ...@@ -4722,6 +4724,8 @@ static void device_resource_remove(struct wined3d_device *device, struct wined3d
{ {
TRACE("device %p, resource %p.\n", device, resource); TRACE("device %p, resource %p.\n", device, resource);
wined3d_not_from_cs(device->cs);
list_remove(&resource->resource_list_entry); list_remove(&resource->resource_list_entry);
} }
...@@ -4896,6 +4900,8 @@ void device_invalidate_state(const struct wined3d_device *device, DWORD state) ...@@ -4896,6 +4900,8 @@ void device_invalidate_state(const struct wined3d_device *device, DWORD state)
BYTE shift; BYTE shift;
UINT i; UINT i;
wined3d_from_cs(device->cs);
if (STATE_IS_COMPUTE(state)) if (STATE_IS_COMPUTE(state))
{ {
for (i = 0; i < device->context_count; ++i) for (i = 0; i < device->context_count; ++i)
......
...@@ -37,6 +37,7 @@ static void wined3d_query_init(struct wined3d_query *query, struct wined3d_devic ...@@ -37,6 +37,7 @@ static void wined3d_query_init(struct wined3d_query *query, struct wined3d_devic
query->data = data; query->data = data;
query->data_size = data_size; query->data_size = data_size;
query->query_ops = query_ops; query->query_ops = query_ops;
list_init(&query->poll_list_entry);
} }
static struct wined3d_event_query *wined3d_event_query_from_query(struct wined3d_query *query) static struct wined3d_event_query *wined3d_event_query_from_query(struct wined3d_query *query)
...@@ -260,6 +261,9 @@ static void wined3d_query_destroy_object(void *object) ...@@ -260,6 +261,9 @@ static void wined3d_query_destroy_object(void *object)
{ {
struct wined3d_query *query = object; struct wined3d_query *query = object;
if (!list_empty(&query->poll_list_entry))
list_remove(&query->poll_list_entry);
/* Queries are specific to the GL context that created them. Not /* Queries are specific to the GL context that created them. Not
* deleting the query will obviously leak it, but that's still better * deleting the query will obviously leak it, but that's still better
* than potentially deleting a different query with the same id in this * than potentially deleting a different query with the same id in this
...@@ -328,8 +332,17 @@ HRESULT CDECL wined3d_query_get_data(struct wined3d_query *query, ...@@ -328,8 +332,17 @@ HRESULT CDECL wined3d_query_get_data(struct wined3d_query *query,
return WINED3DERR_INVALIDCALL; return WINED3DERR_INVALIDCALL;
} }
if (!query->query_ops->query_poll(query, flags)) if (!query->device->cs->thread)
{
if (!query->query_ops->query_poll(query, flags))
return S_FALSE;
}
else if (query->counter_main != query->counter_retrieved)
{
if (flags & WINED3DGETDATA_FLUSH)
wined3d_cs_emit_flush(query->device->cs);
return S_FALSE; return S_FALSE;
}
if (data) if (data)
memcpy(data, query->data, min(data_size, query->data_size)); memcpy(data, query->data, min(data_size, query->data_size));
...@@ -348,6 +361,9 @@ HRESULT CDECL wined3d_query_issue(struct wined3d_query *query, DWORD flags) ...@@ -348,6 +361,9 @@ HRESULT CDECL wined3d_query_issue(struct wined3d_query *query, DWORD flags)
{ {
TRACE("query %p, flags %#x.\n", query, flags); TRACE("query %p, flags %#x.\n", query, flags);
if (flags & WINED3DISSUE_END)
++query->counter_main;
wined3d_cs_emit_query_issue(query->device->cs, query, flags); wined3d_cs_emit_query_issue(query->device->cs, query, flags);
if (flags & WINED3DISSUE_BEGIN) if (flags & WINED3DISSUE_BEGIN)
...@@ -449,7 +465,7 @@ enum wined3d_query_type CDECL wined3d_query_get_type(const struct wined3d_query ...@@ -449,7 +465,7 @@ enum wined3d_query_type CDECL wined3d_query_get_type(const struct wined3d_query
return query->type; return query->type;
} }
static void wined3d_event_query_ops_issue(struct wined3d_query *query, DWORD flags) static BOOL wined3d_event_query_ops_issue(struct wined3d_query *query, DWORD flags)
{ {
TRACE("query %p, flags %#x.\n", query, flags); TRACE("query %p, flags %#x.\n", query, flags);
...@@ -458,20 +474,24 @@ static void wined3d_event_query_ops_issue(struct wined3d_query *query, DWORD fla ...@@ -458,20 +474,24 @@ static void wined3d_event_query_ops_issue(struct wined3d_query *query, DWORD fla
struct wined3d_event_query *event_query = wined3d_event_query_from_query(query); struct wined3d_event_query *event_query = wined3d_event_query_from_query(query);
wined3d_event_query_issue(event_query, query->device); wined3d_event_query_issue(event_query, query->device);
return TRUE;
} }
else if (flags & WINED3DISSUE_BEGIN) else if (flags & WINED3DISSUE_BEGIN)
{ {
/* Started implicitly at query creation. */ /* Started implicitly at query creation. */
ERR("Event query issued with START flag - what to do?\n"); ERR("Event query issued with START flag - what to do?\n");
} }
return FALSE;
} }
static void wined3d_occlusion_query_ops_issue(struct wined3d_query *query, DWORD flags) static BOOL wined3d_occlusion_query_ops_issue(struct wined3d_query *query, DWORD flags)
{ {
struct wined3d_occlusion_query *oq = wined3d_occlusion_query_from_query(query); struct wined3d_occlusion_query *oq = wined3d_occlusion_query_from_query(query);
struct wined3d_device *device = query->device; struct wined3d_device *device = query->device;
const struct wined3d_gl_info *gl_info = &device->adapter->gl_info; const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
struct wined3d_context *context; struct wined3d_context *context;
BOOL poll = FALSE;
TRACE("query %p, flags %#x.\n", query, flags); TRACE("query %p, flags %#x.\n", query, flags);
...@@ -479,7 +499,7 @@ static void wined3d_occlusion_query_ops_issue(struct wined3d_query *query, DWORD ...@@ -479,7 +499,7 @@ static void wined3d_occlusion_query_ops_issue(struct wined3d_query *query, DWORD
* restart. */ * restart. */
if (flags & WINED3DISSUE_BEGIN) if (flags & WINED3DISSUE_BEGIN)
{ {
if (query->state == QUERY_BUILDING) if (oq->started)
{ {
if ((context = context_reacquire(device, oq->context))) if ((context = context_reacquire(device, oq->context)))
{ {
...@@ -506,13 +526,14 @@ static void wined3d_occlusion_query_ops_issue(struct wined3d_query *query, DWORD ...@@ -506,13 +526,14 @@ static void wined3d_occlusion_query_ops_issue(struct wined3d_query *query, DWORD
checkGLcall("glBeginQuery()"); checkGLcall("glBeginQuery()");
context_release(context); context_release(context);
oq->started = TRUE;
} }
if (flags & WINED3DISSUE_END) if (flags & WINED3DISSUE_END)
{ {
/* MSDN says END on a non-building occlusion query returns an error, /* MSDN says END on a non-building occlusion query returns an error,
* but our tests show that it returns OK. But OpenGL doesn't like it, * but our tests show that it returns OK. But OpenGL doesn't like it,
* so avoid generating an error. */ * so avoid generating an error. */
if (query->state == QUERY_BUILDING) if (oq->started)
{ {
if ((context = context_reacquire(device, oq->context))) if ((context = context_reacquire(device, oq->context)))
{ {
...@@ -520,13 +541,17 @@ static void wined3d_occlusion_query_ops_issue(struct wined3d_query *query, DWORD ...@@ -520,13 +541,17 @@ static void wined3d_occlusion_query_ops_issue(struct wined3d_query *query, DWORD
checkGLcall("glEndQuery()"); checkGLcall("glEndQuery()");
context_release(context); context_release(context);
poll = TRUE;
} }
else else
{ {
FIXME("Wrong thread, can't end query.\n"); FIXME("Wrong thread, can't end query.\n");
} }
} }
oq->started = FALSE;
} }
return poll;
} }
static BOOL wined3d_timestamp_query_ops_poll(struct wined3d_query *query, DWORD flags) static BOOL wined3d_timestamp_query_ops_poll(struct wined3d_query *query, DWORD flags)
...@@ -565,7 +590,7 @@ static BOOL wined3d_timestamp_query_ops_poll(struct wined3d_query *query, DWORD ...@@ -565,7 +590,7 @@ static BOOL wined3d_timestamp_query_ops_poll(struct wined3d_query *query, DWORD
return available; return available;
} }
static void wined3d_timestamp_query_ops_issue(struct wined3d_query *query, DWORD flags) static BOOL wined3d_timestamp_query_ops_issue(struct wined3d_query *query, DWORD flags)
{ {
struct wined3d_timestamp_query *tq = wined3d_timestamp_query_from_query(query); struct wined3d_timestamp_query *tq = wined3d_timestamp_query_from_query(query);
const struct wined3d_gl_info *gl_info; const struct wined3d_gl_info *gl_info;
...@@ -587,7 +612,11 @@ static void wined3d_timestamp_query_ops_issue(struct wined3d_query *query, DWORD ...@@ -587,7 +612,11 @@ static void wined3d_timestamp_query_ops_issue(struct wined3d_query *query, DWORD
GL_EXTCALL(glQueryCounter(tq->id, GL_TIMESTAMP)); GL_EXTCALL(glQueryCounter(tq->id, GL_TIMESTAMP));
checkGLcall("glQueryCounter()"); checkGLcall("glQueryCounter()");
context_release(context); context_release(context);
return TRUE;
} }
return FALSE;
} }
static BOOL wined3d_timestamp_disjoint_query_ops_poll(struct wined3d_query *query, DWORD flags) static BOOL wined3d_timestamp_disjoint_query_ops_poll(struct wined3d_query *query, DWORD flags)
...@@ -597,9 +626,11 @@ static BOOL wined3d_timestamp_disjoint_query_ops_poll(struct wined3d_query *quer ...@@ -597,9 +626,11 @@ static BOOL wined3d_timestamp_disjoint_query_ops_poll(struct wined3d_query *quer
return TRUE; return TRUE;
} }
static void wined3d_timestamp_disjoint_query_ops_issue(struct wined3d_query *query, DWORD flags) static BOOL wined3d_timestamp_disjoint_query_ops_issue(struct wined3d_query *query, DWORD flags)
{ {
TRACE("query %p, flags %#x.\n", query, flags); TRACE("query %p, flags %#x.\n", query, flags);
return FALSE;
} }
static const struct wined3d_query_ops event_query_ops = static const struct wined3d_query_ops event_query_ops =
......
...@@ -72,6 +72,7 @@ static CRITICAL_SECTION wined3d_wndproc_cs = {&wined3d_wndproc_cs_debug, -1, 0, ...@@ -72,6 +72,7 @@ static CRITICAL_SECTION wined3d_wndproc_cs = {&wined3d_wndproc_cs_debug, -1, 0,
* where appropriate. */ * where appropriate. */
struct wined3d_settings wined3d_settings = struct wined3d_settings wined3d_settings =
{ {
FALSE, /* No multithreaded CS by default. */
MAKEDWORD_VERSION(1, 0), /* Default to legacy OpenGL */ MAKEDWORD_VERSION(1, 0), /* Default to legacy OpenGL */
TRUE, /* Use of GLSL enabled by default */ TRUE, /* Use of GLSL enabled by default */
ORM_FBO, /* Use FBOs to do offscreen rendering */ ORM_FBO, /* Use FBOs to do offscreen rendering */
...@@ -204,6 +205,8 @@ static BOOL wined3d_dll_init(HINSTANCE hInstDLL) ...@@ -204,6 +205,8 @@ static BOOL wined3d_dll_init(HINSTANCE hInstDLL)
if (hkey || appkey) if (hkey || appkey)
{ {
if (!get_config_key_dword(hkey, appkey, "csmt", &wined3d_settings.cs_multithreaded))
ERR_(winediag)("Setting multithreaded command stream to %#x.\n", wined3d_settings.cs_multithreaded);
if (!get_config_key_dword(hkey, appkey, "MaxVersionGL", &tmpvalue)) if (!get_config_key_dword(hkey, appkey, "MaxVersionGL", &tmpvalue))
{ {
if (tmpvalue != wined3d_settings.max_gl_version) if (tmpvalue != wined3d_settings.max_gl_version)
...@@ -300,6 +303,7 @@ static BOOL wined3d_dll_init(HINSTANCE hInstDLL) ...@@ -300,6 +303,7 @@ static BOOL wined3d_dll_init(HINSTANCE hInstDLL)
if (!get_config_key(hkey, appkey, "StrictDrawOrdering", buffer, size) if (!get_config_key(hkey, appkey, "StrictDrawOrdering", buffer, size)
&& !strcmp(buffer,"enabled")) && !strcmp(buffer,"enabled"))
{ {
ERR_(winediag)("\"StrictDrawOrdering\" is deprecated, please use \"csmt\" instead.");
TRACE("Enforcing strict draw ordering.\n"); TRACE("Enforcing strict draw ordering.\n");
wined3d_settings.strict_draw_ordering = TRUE; wined3d_settings.strict_draw_ordering = TRUE;
} }
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#define WINE_GLAPI #define WINE_GLAPI
#endif #endif
#include <assert.h>
#include <stdarg.h> #include <stdarg.h>
#include <math.h> #include <math.h>
#include <limits.h> #include <limits.h>
...@@ -361,6 +362,13 @@ static inline unsigned int wined3d_popcount(unsigned int x) ...@@ -361,6 +362,13 @@ static inline unsigned int wined3d_popcount(unsigned int x)
#endif #endif
} }
static inline void wined3d_pause(void)
{
#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
__asm__ __volatile__( "rep;nop" : : : "memory" );
#endif
}
#define ORM_BACKBUFFER 0 #define ORM_BACKBUFFER 0
#define ORM_FBO 1 #define ORM_FBO 1
...@@ -371,6 +379,7 @@ static inline unsigned int wined3d_popcount(unsigned int x) ...@@ -371,6 +379,7 @@ static inline unsigned int wined3d_popcount(unsigned int x)
* values in wined3d_main.c as well. */ * values in wined3d_main.c as well. */
struct wined3d_settings struct wined3d_settings
{ {
unsigned int cs_multithreaded;
DWORD max_gl_version; DWORD max_gl_version;
BOOL glslRequested; BOOL glslRequested;
int offscreen_rendering_mode; int offscreen_rendering_mode;
...@@ -1564,7 +1573,7 @@ enum wined3d_query_state ...@@ -1564,7 +1573,7 @@ enum wined3d_query_state
struct wined3d_query_ops struct wined3d_query_ops
{ {
BOOL (*query_poll)(struct wined3d_query *query, DWORD flags); BOOL (*query_poll)(struct wined3d_query *query, DWORD flags);
void (*query_issue)(struct wined3d_query *query, DWORD flags); BOOL (*query_issue)(struct wined3d_query *query, DWORD flags);
}; };
struct wined3d_query struct wined3d_query
...@@ -1578,6 +1587,9 @@ struct wined3d_query ...@@ -1578,6 +1587,9 @@ struct wined3d_query
const void *data; const void *data;
DWORD data_size; DWORD data_size;
const struct wined3d_query_ops *query_ops; const struct wined3d_query_ops *query_ops;
LONG counter_main, counter_retrieved;
struct list poll_list_entry;
}; };
union wined3d_gl_query_object union wined3d_gl_query_object
...@@ -1619,6 +1631,7 @@ struct wined3d_occlusion_query ...@@ -1619,6 +1631,7 @@ struct wined3d_occlusion_query
GLuint id; GLuint id;
struct wined3d_context *context; struct wined3d_context *context;
UINT64 samples; UINT64 samples;
BOOL started;
}; };
struct wined3d_timestamp_query struct wined3d_timestamp_query
...@@ -2806,11 +2819,6 @@ static inline void wined3d_resource_release(struct wined3d_resource *resource) ...@@ -2806,11 +2819,6 @@ static inline void wined3d_resource_release(struct wined3d_resource *resource)
InterlockedDecrement(&resource->access_count); InterlockedDecrement(&resource->access_count);
} }
static inline void wined3d_resource_wait_idle(struct wined3d_resource *resource)
{
while (InterlockedCompareExchange(&resource->access_count, 0, 0));
}
void resource_cleanup(struct wined3d_resource *resource) DECLSPEC_HIDDEN; void resource_cleanup(struct wined3d_resource *resource) DECLSPEC_HIDDEN;
HRESULT resource_init(struct wined3d_resource *resource, struct wined3d_device *device, HRESULT resource_init(struct wined3d_resource *resource, struct wined3d_device *device,
enum wined3d_resource_type type, const struct wined3d_format *format, enum wined3d_resource_type type, const struct wined3d_format *format,
...@@ -3220,6 +3228,16 @@ enum wined3d_push_constants ...@@ -3220,6 +3228,16 @@ enum wined3d_push_constants
WINED3D_PUSH_CONSTANTS_PS_B, WINED3D_PUSH_CONSTANTS_PS_B,
}; };
#define WINED3D_CS_QUERY_POLL_INTERVAL 10u
#define WINED3D_CS_QUEUE_SIZE 0x100000u
#define WINED3D_CS_SPIN_COUNT 10000000u
struct wined3d_cs_queue
{
LONG head, tail;
BYTE data[WINED3D_CS_QUEUE_SIZE];
};
struct wined3d_cs_ops struct wined3d_cs_ops
{ {
void *(*require_space)(struct wined3d_cs *cs, size_t size); void *(*require_space)(struct wined3d_cs *cs, size_t size);
...@@ -3234,9 +3252,16 @@ struct wined3d_cs ...@@ -3234,9 +3252,16 @@ struct wined3d_cs
struct wined3d_device *device; struct wined3d_device *device;
struct wined3d_fb_state fb; struct wined3d_fb_state fb;
struct wined3d_state state; struct wined3d_state state;
HANDLE thread;
DWORD thread_id;
struct wined3d_cs_queue queue;
size_t data_size, start, end; size_t data_size, start, end;
void *data; void *data;
struct list query_poll_list;
HANDLE event;
BOOL waiting_for_event;
}; };
struct wined3d_cs *wined3d_cs_create(struct wined3d_device *device) DECLSPEC_HIDDEN; struct wined3d_cs *wined3d_cs_create(struct wined3d_device *device) DECLSPEC_HIDDEN;
...@@ -3327,6 +3352,17 @@ static inline void wined3d_cs_push_constants(struct wined3d_cs *cs, enum wined3d ...@@ -3327,6 +3352,17 @@ static inline void wined3d_cs_push_constants(struct wined3d_cs *cs, enum wined3d
cs->ops->push_constants(cs, p, start_idx, count, constants); cs->ops->push_constants(cs, p, start_idx, count, constants);
} }
static inline void wined3d_resource_wait_idle(struct wined3d_resource *resource)
{
const struct wined3d_cs *cs = resource->device->cs;
if (!cs->thread || cs->thread_id == GetCurrentThreadId())
return;
while (InterlockedCompareExchange(&resource->access_count, 0, 0))
wined3d_pause();
}
/* TODO: Add tests and support for FLOAT16_4 POSITIONT, D3DCOLOR position, other /* TODO: Add tests and support for FLOAT16_4 POSITIONT, D3DCOLOR position, other
* fixed function semantics as D3DCOLOR or FLOAT16 */ * fixed function semantics as D3DCOLOR or FLOAT16 */
enum wined3d_buffer_conversion_type enum wined3d_buffer_conversion_type
...@@ -4012,6 +4048,17 @@ static inline struct wined3d_surface *context_get_rt_surface(const struct wined3 ...@@ -4012,6 +4048,17 @@ static inline struct wined3d_surface *context_get_rt_surface(const struct wined3
return texture->sub_resources[context->current_rt.sub_resource_idx].u.surface; return texture->sub_resources[context->current_rt.sub_resource_idx].u.surface;
} }
static inline void wined3d_from_cs(struct wined3d_cs *cs)
{
if (cs->thread)
assert(cs->thread_id == GetCurrentThreadId());
}
static inline void wined3d_not_from_cs(struct wined3d_cs *cs)
{
assert(cs->thread_id != GetCurrentThreadId());
}
/* The WNDCLASS-Name for the fake window which we use to retrieve the GL capabilities */ /* The WNDCLASS-Name for the fake window which we use to retrieve the GL capabilities */
#define WINED3D_OPENGL_WINDOW_CLASS_NAME "WineD3D_OpenGL" #define WINED3D_OPENGL_WINDOW_CLASS_NAME "WineD3D_OpenGL"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment