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,
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));
if (!ret)
return NULL;
......@@ -2106,6 +2108,8 @@ void context_destroy(struct wined3d_device *device, struct wined3d_context *cont
TRACE("Destroying ctx %p\n", context);
wined3d_from_cs(device->cs);
/* We delay destroying a context when it is active. The context_release()
* function invokes context_destroy() again while leaving the last level. */
if (context->level)
......@@ -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);
wined3d_from_cs(device->cs);
if (current_context && current_context->destroyed)
current_context = NULL;
......
......@@ -4715,6 +4715,8 @@ void device_resource_add(struct wined3d_device *device, struct wined3d_resource
{
TRACE("device %p, resource %p.\n", device, resource);
wined3d_not_from_cs(device->cs);
list_add_head(&device->resources, &resource->resource_list_entry);
}
......@@ -4722,6 +4724,8 @@ static void device_resource_remove(struct wined3d_device *device, struct wined3d
{
TRACE("device %p, resource %p.\n", device, resource);
wined3d_not_from_cs(device->cs);
list_remove(&resource->resource_list_entry);
}
......@@ -4896,6 +4900,8 @@ void device_invalidate_state(const struct wined3d_device *device, DWORD state)
BYTE shift;
UINT i;
wined3d_from_cs(device->cs);
if (STATE_IS_COMPUTE(state))
{
for (i = 0; i < device->context_count; ++i)
......
......@@ -37,6 +37,7 @@ static void wined3d_query_init(struct wined3d_query *query, struct wined3d_devic
query->data = data;
query->data_size = data_size;
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)
......@@ -260,6 +261,9 @@ static void wined3d_query_destroy_object(void *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
* deleting the query will obviously leak it, but that's still better
* 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,
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;
}
if (data)
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)
{
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);
if (flags & WINED3DISSUE_BEGIN)
......@@ -449,7 +465,7 @@ enum wined3d_query_type CDECL wined3d_query_get_type(const struct wined3d_query
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);
......@@ -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);
wined3d_event_query_issue(event_query, query->device);
return TRUE;
}
else if (flags & WINED3DISSUE_BEGIN)
{
/* Started implicitly at query creation. */
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_device *device = query->device;
const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
struct wined3d_context *context;
BOOL poll = FALSE;
TRACE("query %p, flags %#x.\n", query, flags);
......@@ -479,7 +499,7 @@ static void wined3d_occlusion_query_ops_issue(struct wined3d_query *query, DWORD
* restart. */
if (flags & WINED3DISSUE_BEGIN)
{
if (query->state == QUERY_BUILDING)
if (oq->started)
{
if ((context = context_reacquire(device, oq->context)))
{
......@@ -506,13 +526,14 @@ static void wined3d_occlusion_query_ops_issue(struct wined3d_query *query, DWORD
checkGLcall("glBeginQuery()");
context_release(context);
oq->started = TRUE;
}
if (flags & WINED3DISSUE_END)
{
/* 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,
* so avoid generating an error. */
if (query->state == QUERY_BUILDING)
if (oq->started)
{
if ((context = context_reacquire(device, oq->context)))
{
......@@ -520,13 +541,17 @@ static void wined3d_occlusion_query_ops_issue(struct wined3d_query *query, DWORD
checkGLcall("glEndQuery()");
context_release(context);
poll = TRUE;
}
else
{
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)
......@@ -565,7 +590,7 @@ static BOOL wined3d_timestamp_query_ops_poll(struct wined3d_query *query, DWORD
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);
const struct wined3d_gl_info *gl_info;
......@@ -587,7 +612,11 @@ static void wined3d_timestamp_query_ops_issue(struct wined3d_query *query, DWORD
GL_EXTCALL(glQueryCounter(tq->id, GL_TIMESTAMP));
checkGLcall("glQueryCounter()");
context_release(context);
return TRUE;
}
return FALSE;
}
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
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);
return FALSE;
}
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,
* where appropriate. */
struct wined3d_settings wined3d_settings =
{
FALSE, /* No multithreaded CS by default. */
MAKEDWORD_VERSION(1, 0), /* Default to legacy OpenGL */
TRUE, /* Use of GLSL enabled by default */
ORM_FBO, /* Use FBOs to do offscreen rendering */
......@@ -204,6 +205,8 @@ static BOOL wined3d_dll_init(HINSTANCE hInstDLL)
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 (tmpvalue != wined3d_settings.max_gl_version)
......@@ -300,6 +303,7 @@ static BOOL wined3d_dll_init(HINSTANCE hInstDLL)
if (!get_config_key(hkey, appkey, "StrictDrawOrdering", buffer, size)
&& !strcmp(buffer,"enabled"))
{
ERR_(winediag)("\"StrictDrawOrdering\" is deprecated, please use \"csmt\" instead.");
TRACE("Enforcing strict draw ordering.\n");
wined3d_settings.strict_draw_ordering = TRUE;
}
......
......@@ -31,6 +31,7 @@
#define WINE_GLAPI
#endif
#include <assert.h>
#include <stdarg.h>
#include <math.h>
#include <limits.h>
......@@ -361,6 +362,13 @@ static inline unsigned int wined3d_popcount(unsigned int x)
#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_FBO 1
......@@ -371,6 +379,7 @@ static inline unsigned int wined3d_popcount(unsigned int x)
* values in wined3d_main.c as well. */
struct wined3d_settings
{
unsigned int cs_multithreaded;
DWORD max_gl_version;
BOOL glslRequested;
int offscreen_rendering_mode;
......@@ -1564,7 +1573,7 @@ enum wined3d_query_state
struct wined3d_query_ops
{
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
......@@ -1578,6 +1587,9 @@ struct wined3d_query
const void *data;
DWORD data_size;
const struct wined3d_query_ops *query_ops;
LONG counter_main, counter_retrieved;
struct list poll_list_entry;
};
union wined3d_gl_query_object
......@@ -1619,6 +1631,7 @@ struct wined3d_occlusion_query
GLuint id;
struct wined3d_context *context;
UINT64 samples;
BOOL started;
};
struct wined3d_timestamp_query
......@@ -2806,11 +2819,6 @@ static inline void wined3d_resource_release(struct wined3d_resource *resource)
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;
HRESULT resource_init(struct wined3d_resource *resource, struct wined3d_device *device,
enum wined3d_resource_type type, const struct wined3d_format *format,
......@@ -3220,6 +3228,16 @@ enum wined3d_push_constants
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
{
void *(*require_space)(struct wined3d_cs *cs, size_t size);
......@@ -3234,9 +3252,16 @@ struct wined3d_cs
struct wined3d_device *device;
struct wined3d_fb_state fb;
struct wined3d_state state;
HANDLE thread;
DWORD thread_id;
struct wined3d_cs_queue queue;
size_t data_size, start, end;
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;
......@@ -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);
}
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
* fixed function semantics as D3DCOLOR or FLOAT16 */
enum wined3d_buffer_conversion_type
......@@ -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;
}
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 */
#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