The GetWorkItemCount implementation may look strange. It's done this way because of how WaitForAllItems is implemented. Alternative solution is to introduce separate counter for GetWorkItemCount.
From: Piotr Caban piotr@codeweavers.com
Signed-off-by: Piotr Caban piotr@codeweavers.com --- dlls/d3dx10_43/d3dx10_43.spec | 2 +- dlls/d3dx10_43/d3dx10_43_main.c | 123 ++++++++++++++++++++++++++++++++ include/d3dx10core.h | 1 + 3 files changed, 125 insertions(+), 1 deletion(-)
diff --git a/dlls/d3dx10_43/d3dx10_43.spec b/dlls/d3dx10_43/d3dx10_43.spec index 95160a067c5..2359c7c6f02 100644 --- a/dlls/d3dx10_43/d3dx10_43.spec +++ b/dlls/d3dx10_43/d3dx10_43.spec @@ -1,4 +1,4 @@ -@ stub D3DX10CreateThreadPump(long long ptr) +@ stdcall D3DX10CreateThreadPump(long long ptr) @ stdcall D3DX10CheckVersion(long long) @ stub D3DX10CompileFromFileA(str ptr ptr str str long long ptr ptr ptr ptr) @ stub D3DX10CompileFromFileW(wstr ptr ptr str str long long ptr ptr ptr ptr) diff --git a/dlls/d3dx10_43/d3dx10_43_main.c b/dlls/d3dx10_43/d3dx10_43_main.c index ec7407508d3..357b0257496 100644 --- a/dlls/d3dx10_43/d3dx10_43_main.c +++ b/dlls/d3dx10_43/d3dx10_43_main.c @@ -176,3 +176,126 @@ HRESULT WINAPI D3DX10LoadTextureFromTexture(ID3D10Resource *src_texture, D3DX10_
return E_NOTIMPL; } + +struct thread_pump +{ + ID3DX10ThreadPump ID3DX10ThreadPump_iface; + LONG refcount; +}; + +static inline struct thread_pump *impl_from_ID3DX10ThreadPump(ID3DX10ThreadPump *iface) +{ + return CONTAINING_RECORD(iface, struct thread_pump, ID3DX10ThreadPump_iface); +} + +static HRESULT WINAPI thread_pump_QueryInterface(ID3DX10ThreadPump *iface, REFIID riid, void **out) +{ + TRACE("iface %p, riid %s, out %p.\n", iface, debugstr_guid(riid), out); + + if (IsEqualGUID(riid, &IID_ID3DX10ThreadPump) + || IsEqualGUID(riid, &IID_IUnknown)) + { + ID3DX10ThreadPump_AddRef(iface); + *out = iface; + return S_OK; + } + + WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(riid)); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI thread_pump_AddRef(ID3DX10ThreadPump *iface) +{ + struct thread_pump *thread_pump = impl_from_ID3DX10ThreadPump(iface); + ULONG refcount = InterlockedIncrement(&thread_pump->refcount); + + TRACE("%p increasing refcount to %lu.\n", iface, refcount); + + return refcount; +} + +static ULONG WINAPI thread_pump_Release(ID3DX10ThreadPump *iface) +{ + struct thread_pump *thread_pump = impl_from_ID3DX10ThreadPump(iface); + ULONG refcount = InterlockedDecrement(&thread_pump->refcount); + + TRACE("%p decreasing refcount to %lu.\n", iface, refcount); + + if (!refcount) + free(thread_pump); + + return refcount; +} + +static HRESULT WINAPI thread_pump_AddWorkItem(ID3DX10ThreadPump *iface, ID3DX10DataLoader *loader, + ID3DX10DataProcessor *processor, HRESULT *result, void **object) +{ + FIXME("iface %p, loader %p, processor %p, result %p, object %p stub!\n", + iface, loader, processor, result, object); + return E_NOTIMPL; +} + +static UINT WINAPI thread_pump_GetWorkItemCount(ID3DX10ThreadPump *iface) +{ + FIXME("iface %p stub!\n", iface); + return 0; +} + +static HRESULT WINAPI thread_pump_WaitForAllItems(ID3DX10ThreadPump *iface) +{ + FIXME("iface %p stub!\n", iface); + return E_NOTIMPL; +} + +static HRESULT WINAPI thread_pump_ProcessDeviceWorkItems(ID3DX10ThreadPump *iface, UINT count) +{ + FIXME("iface %p, count %u stub!\n", iface, count); + return E_NOTIMPL; +} + +static HRESULT WINAPI thread_pump_PurgeAllItems(ID3DX10ThreadPump *iface) +{ + FIXME("iface %p stub!\n", iface); + return E_NOTIMPL; +} + +static HRESULT WINAPI thread_pump_GetQueueStatus(ID3DX10ThreadPump *iface, + UINT *io_queue, UINT *process_queue, UINT *device_queue) +{ + FIXME("iface %p, io_queue %p, process_queue %p, device_queue %p stub!\n", + iface, io_queue, process_queue, device_queue); + return E_NOTIMPL; +} + +static const ID3DX10ThreadPumpVtbl thread_pump_vtbl = +{ + thread_pump_QueryInterface, + thread_pump_AddRef, + thread_pump_Release, + thread_pump_AddWorkItem, + thread_pump_GetWorkItemCount, + thread_pump_WaitForAllItems, + thread_pump_ProcessDeviceWorkItems, + thread_pump_PurgeAllItems, + thread_pump_GetQueueStatus +}; + +HRESULT WINAPI D3DX10CreateThreadPump(UINT io_threads, UINT proc_threads, ID3DX10ThreadPump **pump) +{ + struct thread_pump *object; + + TRACE("io_threads %u, proc_threads %u, pump %p.\n", io_threads, proc_threads, pump); + + if (io_threads >= 1024 || proc_threads >= 1024) + return E_FAIL; + + if (!(object = calloc(1, sizeof(*object)))) + return E_OUTOFMEMORY; + + object->ID3DX10ThreadPump_iface.lpVtbl = &thread_pump_vtbl; + object->refcount = 1; + + *pump = &object->ID3DX10ThreadPump_iface; + return S_OK; +} diff --git a/include/d3dx10core.h b/include/d3dx10core.h index a9ba7854e90..cca9052cc13 100644 --- a/include/d3dx10core.h +++ b/include/d3dx10core.h @@ -298,3 +298,4 @@ HRESULT WINAPI D3DX10CreateFontW(ID3D10Device *device, INT height, UINT width, U UINT miplevels, BOOL italic, UINT charset, UINT precision, UINT quality, UINT pitchandfamily, const WCHAR *facename, ID3DX10Font **font); HRESULT WINAPI D3DX10CreateSprite(ID3D10Device *device, UINT size, ID3DX10Sprite **sprite); +HRESULT WINAPI D3DX10CreateThreadPump(UINT io_threads, UINT proc_threads, ID3DX10ThreadPump **pump);
From: Piotr Caban piotr@codeweavers.com
Signed-off-by: Piotr Caban piotr@codeweavers.com --- dlls/d3dx10_43/d3dx10_43_main.c | 251 +++++++++++++++++++++++++++++++- 1 file changed, 248 insertions(+), 3 deletions(-)
diff --git a/dlls/d3dx10_43/d3dx10_43_main.c b/dlls/d3dx10_43/d3dx10_43_main.c index 357b0257496..d66d83d3e7f 100644 --- a/dlls/d3dx10_43/d3dx10_43_main.c +++ b/dlls/d3dx10_43/d3dx10_43_main.c @@ -29,10 +29,13 @@ #include "winbase.h" #include "winuser.h" #include "objbase.h" +#include "winternl.h"
#include "d3d10_1.h" #include "d3dx10.h"
+#include "wine/list.h" + WINE_DEFAULT_DEBUG_CHANNEL(d3dx);
/*********************************************************************** @@ -177,10 +180,45 @@ HRESULT WINAPI D3DX10LoadTextureFromTexture(ID3D10Resource *src_texture, D3DX10_ return E_NOTIMPL; }
+struct work_item +{ + struct list entry; + + ID3DX10DataLoader *loader; + ID3DX10DataProcessor *processor; + HRESULT *result; + void **object; +}; + +static inline void work_item_free(struct work_item *work_item, BOOL cancel) +{ + ID3DX10DataLoader_Destroy(work_item->loader); + ID3DX10DataProcessor_Destroy(work_item->processor); + if (cancel && work_item->result) + *work_item->result = S_FALSE; + free(work_item); +} + +#define THREAD_PUMP_EXITING UINT_MAX struct thread_pump { ID3DX10ThreadPump ID3DX10ThreadPump_iface; LONG refcount; + + SRWLOCK io_lock; + UINT io_count; + struct list io_queue; + + SRWLOCK proc_lock; + UINT proc_count; + struct list proc_queue; + + SRWLOCK device_lock; + UINT device_count; + struct list device_queue; + + int threads_no; + HANDLE threads[1]; };
static inline struct thread_pump *impl_from_ID3DX10ThreadPump(ID3DX10ThreadPump *iface) @@ -219,11 +257,48 @@ static ULONG WINAPI thread_pump_Release(ID3DX10ThreadPump *iface) { struct thread_pump *thread_pump = impl_from_ID3DX10ThreadPump(iface); ULONG refcount = InterlockedDecrement(&thread_pump->refcount); + struct work_item *iter, *next; + struct list list; + int i;
TRACE("%p decreasing refcount to %lu.\n", iface, refcount);
if (!refcount) + { + AcquireSRWLockExclusive(&thread_pump->io_lock); + thread_pump->io_count = THREAD_PUMP_EXITING; + ReleaseSRWLockExclusive(&thread_pump->io_lock); + RtlWakeAddressAll((void *)&thread_pump->io_count); + + AcquireSRWLockExclusive(&thread_pump->proc_lock); + thread_pump->proc_count = THREAD_PUMP_EXITING; + ReleaseSRWLockExclusive(&thread_pump->proc_lock); + RtlWakeAddressAll((void *)&thread_pump->proc_count); + + AcquireSRWLockExclusive(&thread_pump->device_lock); + thread_pump->device_count = THREAD_PUMP_EXITING; + ReleaseSRWLockExclusive(&thread_pump->device_lock); + + for (i = 0; i < thread_pump->threads_no; i++) + { + if (!thread_pump->threads[i]) continue; + + WaitForSingleObject(thread_pump->threads[i], INFINITE); + CloseHandle(thread_pump->threads[i]); + } + + list_init(&list); + list_move_tail(&list, &thread_pump->io_queue); + list_move_tail(&list, &thread_pump->proc_queue); + list_move_tail(&list, &thread_pump->device_queue); + LIST_FOR_EACH_ENTRY_SAFE(iter, next, &list, struct work_item, entry) + { + list_remove(&iter->entry); + work_item_free(iter, TRUE); + } + free(thread_pump); + }
return refcount; } @@ -231,9 +306,30 @@ static ULONG WINAPI thread_pump_Release(ID3DX10ThreadPump *iface) static HRESULT WINAPI thread_pump_AddWorkItem(ID3DX10ThreadPump *iface, ID3DX10DataLoader *loader, ID3DX10DataProcessor *processor, HRESULT *result, void **object) { - FIXME("iface %p, loader %p, processor %p, result %p, object %p stub!\n", + struct thread_pump *thread_pump = impl_from_ID3DX10ThreadPump(iface); + struct work_item *work_item; + + TRACE("iface %p, loader %p, processor %p, result %p, object %p.\n", iface, loader, processor, result, object); - return E_NOTIMPL; + + work_item = malloc(sizeof(*work_item)); + if (!work_item) + return E_OUTOFMEMORY; + + work_item->loader = loader; + work_item->processor = processor; + work_item->result = result; + work_item->object = object; + + if (object) + *object = NULL; + + AcquireSRWLockExclusive(&thread_pump->io_lock); + thread_pump->io_count++; + list_add_tail(&thread_pump->io_queue, &work_item->entry); + ReleaseSRWLockExclusive(&thread_pump->io_lock); + RtlWakeAddressAll((void *)&thread_pump->io_count); + return S_OK; }
static UINT WINAPI thread_pump_GetWorkItemCount(ID3DX10ThreadPump *iface) @@ -281,20 +377,169 @@ static const ID3DX10ThreadPumpVtbl thread_pump_vtbl = thread_pump_GetQueueStatus };
+static DWORD WINAPI io_thread(void *arg) +{ + struct thread_pump *thread_pump = arg; + struct work_item *work_item; + UINT zero = 0; + HRESULT hr; + + TRACE("%p thread started.\n", thread_pump); + + while (1) + { + RtlWaitOnAddress((void *)&thread_pump->io_count, &zero, sizeof(zero), NULL); + AcquireSRWLockExclusive(&thread_pump->io_lock); + if (!thread_pump->io_count) + { + ReleaseSRWLockExclusive(&thread_pump->io_lock); + continue; + } + if (thread_pump->io_count == THREAD_PUMP_EXITING) + { + ReleaseSRWLockExclusive(&thread_pump->io_lock); + return 0; + } + + thread_pump->io_count--; + work_item = LIST_ENTRY(list_head(&thread_pump->io_queue), struct work_item, entry); + list_remove(&work_item->entry); + ReleaseSRWLockExclusive(&thread_pump->io_lock); + + if (FAILED(hr = ID3DX10DataLoader_Load(work_item->loader))) + { + if (work_item->result) + *work_item->result = hr; + work_item_free(work_item, FALSE); + continue; + } + + AcquireSRWLockExclusive(&thread_pump->proc_lock); + if (thread_pump->proc_count == THREAD_PUMP_EXITING) + { + ReleaseSRWLockExclusive(&thread_pump->proc_lock); + work_item_free(work_item, TRUE); + return 0; + } + + list_add_tail(&thread_pump->proc_queue, &work_item->entry); + thread_pump->proc_count++; + ReleaseSRWLockExclusive(&thread_pump->proc_lock); + RtlWakeAddressAll((void *)&thread_pump->proc_count); + } + return 0; +} + +static DWORD WINAPI proc_thread(void *arg) +{ + struct thread_pump *thread_pump = arg; + struct work_item *work_item; + UINT zero = 0; + SIZE_T size; + void *data; + HRESULT hr; + + TRACE("%p thread started.\n", thread_pump); + + while (1) + { + RtlWaitOnAddress((void *)&thread_pump->proc_count, &zero, sizeof(zero), NULL); + AcquireSRWLockExclusive(&thread_pump->proc_lock); + if (!thread_pump->proc_count) + { + ReleaseSRWLockExclusive(&thread_pump->proc_lock); + continue; + } + if (thread_pump->proc_count == THREAD_PUMP_EXITING) + { + ReleaseSRWLockExclusive(&thread_pump->proc_lock); + return 0; + } + + thread_pump->proc_count--; + work_item = LIST_ENTRY(list_head(&thread_pump->proc_queue), struct work_item, entry); + list_remove(&work_item->entry); + ReleaseSRWLockExclusive(&thread_pump->proc_lock); + + if (FAILED(hr = ID3DX10DataLoader_Decompress(work_item->loader, &data, &size))) + { + if (work_item->result) + *work_item->result = hr; + work_item_free(work_item, FALSE); + continue; + } + + if (thread_pump->device_count == THREAD_PUMP_EXITING) + { + work_item_free(work_item, TRUE); + return 0; + } + + if (FAILED(hr = ID3DX10DataProcessor_Process(work_item->processor, data, size))) + { + if (work_item->result) + *work_item->result = hr; + work_item_free(work_item, FALSE); + continue; + } + + AcquireSRWLockExclusive(&thread_pump->device_lock); + if (thread_pump->device_count == THREAD_PUMP_EXITING) + { + ReleaseSRWLockExclusive(&thread_pump->device_lock); + work_item_free(work_item, TRUE); + return 0; + } + + list_add_tail(&thread_pump->device_queue, &work_item->entry); + thread_pump->device_count++; + ReleaseSRWLockExclusive(&thread_pump->device_lock); + } + return 0; +} + HRESULT WINAPI D3DX10CreateThreadPump(UINT io_threads, UINT proc_threads, ID3DX10ThreadPump **pump) { struct thread_pump *object; + int i;
TRACE("io_threads %u, proc_threads %u, pump %p.\n", io_threads, proc_threads, pump);
if (io_threads >= 1024 || proc_threads >= 1024) return E_FAIL;
- if (!(object = calloc(1, sizeof(*object)))) + if (!io_threads) + io_threads = 1; + if (!proc_threads) + { + SYSTEM_INFO info; + + GetSystemInfo(&info); + proc_threads = info.dwNumberOfProcessors; + } + + if (!(object = calloc(1, FIELD_OFFSET(struct thread_pump, threads[io_threads + proc_threads])))) return E_OUTOFMEMORY;
object->ID3DX10ThreadPump_iface.lpVtbl = &thread_pump_vtbl; object->refcount = 1; + InitializeSRWLock(&object->io_lock); + list_init(&object->io_queue); + InitializeSRWLock(&object->proc_lock); + list_init(&object->proc_queue); + InitializeSRWLock(&object->device_lock); + list_init(&object->device_queue); + object->threads_no = io_threads + proc_threads; + + for (i = 0; i < object->threads_no; i++) + { + object->threads[i] = CreateThread(NULL, 0, i < io_threads ? io_thread : proc_thread, object, 0, NULL); + if (!object->threads[i]) + { + ID3DX10ThreadPump_Release(&object->ID3DX10ThreadPump_iface); + return E_FAIL; + } + }
*pump = &object->ID3DX10ThreadPump_iface; return S_OK;
From: Piotr Caban piotr@codeweavers.com
Signed-off-by: Piotr Caban piotr@codeweavers.com --- dlls/d3dx10_43/d3dx10_43_main.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-)
diff --git a/dlls/d3dx10_43/d3dx10_43_main.c b/dlls/d3dx10_43/d3dx10_43_main.c index d66d83d3e7f..5d718f6de2b 100644 --- a/dlls/d3dx10_43/d3dx10_43_main.c +++ b/dlls/d3dx10_43/d3dx10_43_main.c @@ -205,6 +205,8 @@ struct thread_pump ID3DX10ThreadPump ID3DX10ThreadPump_iface; LONG refcount;
+ LONG processing_count; + SRWLOCK io_lock; UINT io_count; struct list io_queue; @@ -324,6 +326,7 @@ static HRESULT WINAPI thread_pump_AddWorkItem(ID3DX10ThreadPump *iface, ID3DX10D if (object) *object = NULL;
+ InterlockedIncrement(&thread_pump->processing_count); AcquireSRWLockExclusive(&thread_pump->io_lock); thread_pump->io_count++; list_add_tail(&thread_pump->io_queue, &work_item->entry); @@ -334,8 +337,15 @@ static HRESULT WINAPI thread_pump_AddWorkItem(ID3DX10ThreadPump *iface, ID3DX10D
static UINT WINAPI thread_pump_GetWorkItemCount(ID3DX10ThreadPump *iface) { - FIXME("iface %p stub!\n", iface); - return 0; + struct thread_pump *thread_pump = impl_from_ID3DX10ThreadPump(iface); + UINT ret; + + TRACE("iface %p.\n", iface); + + AcquireSRWLockExclusive(&thread_pump->device_lock); + ret = thread_pump->processing_count + thread_pump->device_count; + ReleaseSRWLockExclusive(&thread_pump->device_lock); + return ret; }
static HRESULT WINAPI thread_pump_WaitForAllItems(ID3DX10ThreadPump *iface) @@ -411,6 +421,7 @@ static DWORD WINAPI io_thread(void *arg) if (work_item->result) *work_item->result = hr; work_item_free(work_item, FALSE); + InterlockedDecrement(&thread_pump->processing_count); continue; }
@@ -466,6 +477,7 @@ static DWORD WINAPI proc_thread(void *arg) if (work_item->result) *work_item->result = hr; work_item_free(work_item, FALSE); + InterlockedDecrement(&thread_pump->processing_count); continue; }
@@ -480,6 +492,7 @@ static DWORD WINAPI proc_thread(void *arg) if (work_item->result) *work_item->result = hr; work_item_free(work_item, FALSE); + InterlockedDecrement(&thread_pump->processing_count); continue; }
@@ -493,6 +506,7 @@ static DWORD WINAPI proc_thread(void *arg)
list_add_tail(&thread_pump->device_queue, &work_item->entry); thread_pump->device_count++; + InterlockedDecrement(&thread_pump->processing_count); ReleaseSRWLockExclusive(&thread_pump->device_lock); } return 0;
From: Piotr Caban piotr@codeweavers.com
Signed-off-by: Piotr Caban piotr@codeweavers.com --- dlls/d3dx10_43/d3dx10_43_main.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-)
diff --git a/dlls/d3dx10_43/d3dx10_43_main.c b/dlls/d3dx10_43/d3dx10_43_main.c index 5d718f6de2b..795d052af83 100644 --- a/dlls/d3dx10_43/d3dx10_43_main.c +++ b/dlls/d3dx10_43/d3dx10_43_main.c @@ -356,8 +356,34 @@ static HRESULT WINAPI thread_pump_WaitForAllItems(ID3DX10ThreadPump *iface)
static HRESULT WINAPI thread_pump_ProcessDeviceWorkItems(ID3DX10ThreadPump *iface, UINT count) { - FIXME("iface %p, count %u stub!\n", iface, count); - return E_NOTIMPL; + struct thread_pump *thread_pump = impl_from_ID3DX10ThreadPump(iface); + struct work_item *work_item; + HRESULT hr; + UINT i; + + TRACE("iface %p, count %u.\n", iface, count); + + for (i = 0; i < count; i++) + { + AcquireSRWLockExclusive(&thread_pump->device_lock); + if (!thread_pump->device_count) + { + ReleaseSRWLockExclusive(&thread_pump->device_lock); + break; + } + + thread_pump->device_count--; + work_item = LIST_ENTRY(list_head(&thread_pump->device_queue), struct work_item, entry); + list_remove(&work_item->entry); + ReleaseSRWLockExclusive(&thread_pump->device_lock); + + hr = ID3DX10DataProcessor_CreateDeviceObject(work_item->processor, work_item->object); + if (work_item->result) + *work_item->result = hr; + work_item_free(work_item, FALSE); + } + + return S_OK; }
static HRESULT WINAPI thread_pump_PurgeAllItems(ID3DX10ThreadPump *iface)
From: Piotr Caban piotr@codeweavers.com
Signed-off-by: Piotr Caban piotr@codeweavers.com --- dlls/d3dx10_43/d3dx10_43_main.c | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-)
diff --git a/dlls/d3dx10_43/d3dx10_43_main.c b/dlls/d3dx10_43/d3dx10_43_main.c index 795d052af83..198cf14b7d0 100644 --- a/dlls/d3dx10_43/d3dx10_43_main.c +++ b/dlls/d3dx10_43/d3dx10_43_main.c @@ -350,8 +350,24 @@ static UINT WINAPI thread_pump_GetWorkItemCount(ID3DX10ThreadPump *iface)
static HRESULT WINAPI thread_pump_WaitForAllItems(ID3DX10ThreadPump *iface) { - FIXME("iface %p stub!\n", iface); - return E_NOTIMPL; + struct thread_pump *thread_pump = impl_from_ID3DX10ThreadPump(iface); + HRESULT hr; + LONG v; + + TRACE("iface %p.\n", iface); + + while(1) + { + if (FAILED((hr = ID3DX10ThreadPump_ProcessDeviceWorkItems(iface, UINT_MAX)))) + return hr; + + if (!(v = thread_pump->processing_count)) + break; + + RtlWaitOnAddress((void *)&thread_pump->processing_count, &v, sizeof(v), NULL); + } + + return ID3DX10ThreadPump_ProcessDeviceWorkItems(iface, UINT_MAX); }
static HRESULT WINAPI thread_pump_ProcessDeviceWorkItems(ID3DX10ThreadPump *iface, UINT count) @@ -447,7 +463,8 @@ static DWORD WINAPI io_thread(void *arg) if (work_item->result) *work_item->result = hr; work_item_free(work_item, FALSE); - InterlockedDecrement(&thread_pump->processing_count); + if (!InterlockedDecrement(&thread_pump->processing_count)) + RtlWakeAddressAll((void *)&thread_pump->processing_count); continue; }
@@ -503,7 +520,8 @@ static DWORD WINAPI proc_thread(void *arg) if (work_item->result) *work_item->result = hr; work_item_free(work_item, FALSE); - InterlockedDecrement(&thread_pump->processing_count); + if (!InterlockedDecrement(&thread_pump->processing_count)) + RtlWakeAddressAll((void *)&thread_pump->processing_count); continue; }
@@ -518,7 +536,8 @@ static DWORD WINAPI proc_thread(void *arg) if (work_item->result) *work_item->result = hr; work_item_free(work_item, FALSE); - InterlockedDecrement(&thread_pump->processing_count); + if (!InterlockedDecrement(&thread_pump->processing_count)) + RtlWakeAddressAll((void *)&thread_pump->processing_count); continue; }
@@ -533,6 +552,7 @@ static DWORD WINAPI proc_thread(void *arg) list_add_tail(&thread_pump->device_queue, &work_item->entry); thread_pump->device_count++; InterlockedDecrement(&thread_pump->processing_count); + RtlWakeAddressAll((void *)&thread_pump->processing_count); ReleaseSRWLockExclusive(&thread_pump->device_lock); } return 0;
From: Piotr Caban piotr@codeweavers.com
Signed-off-by: Piotr Caban piotr@codeweavers.com --- dlls/d3dx10_43/d3dx10_43_main.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/dlls/d3dx10_43/d3dx10_43_main.c b/dlls/d3dx10_43/d3dx10_43_main.c index 198cf14b7d0..5804a1058b0 100644 --- a/dlls/d3dx10_43/d3dx10_43_main.c +++ b/dlls/d3dx10_43/d3dx10_43_main.c @@ -411,9 +411,15 @@ static HRESULT WINAPI thread_pump_PurgeAllItems(ID3DX10ThreadPump *iface) static HRESULT WINAPI thread_pump_GetQueueStatus(ID3DX10ThreadPump *iface, UINT *io_queue, UINT *process_queue, UINT *device_queue) { - FIXME("iface %p, io_queue %p, process_queue %p, device_queue %p stub!\n", + struct thread_pump *thread_pump = impl_from_ID3DX10ThreadPump(iface); + + TRACE("iface %p, io_queue %p, process_queue %p, device_queue %p.\n", iface, io_queue, process_queue, device_queue); - return E_NOTIMPL; + + *io_queue = thread_pump->io_count; + *process_queue = thread_pump->proc_count; + *device_queue = thread_pump->device_count; + return S_OK; }
static const ID3DX10ThreadPumpVtbl thread_pump_vtbl =
From: Piotr Caban piotr@codeweavers.com
Signed-off-by: Piotr Caban piotr@codeweavers.com --- dlls/d3dx10_43/d3dx10_43_main.c | 48 +++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-)
diff --git a/dlls/d3dx10_43/d3dx10_43_main.c b/dlls/d3dx10_43/d3dx10_43_main.c index 5804a1058b0..df1e3f413f5 100644 --- a/dlls/d3dx10_43/d3dx10_43_main.c +++ b/dlls/d3dx10_43/d3dx10_43_main.c @@ -404,8 +404,52 @@ static HRESULT WINAPI thread_pump_ProcessDeviceWorkItems(ID3DX10ThreadPump *ifac
static HRESULT WINAPI thread_pump_PurgeAllItems(ID3DX10ThreadPump *iface) { - FIXME("iface %p stub!\n", iface); - return E_NOTIMPL; + struct thread_pump *thread_pump = impl_from_ID3DX10ThreadPump(iface); + struct list list = LIST_INIT(list); + struct work_item *work_item; + LONG v; + + TRACE("iface %p.\n", iface); + + while (1) + { + AcquireSRWLockExclusive(&thread_pump->io_lock); + list_move_tail(&list, &thread_pump->io_queue); + thread_pump->io_count = 0; + ReleaseSRWLockExclusive(&thread_pump->io_lock); + + AcquireSRWLockExclusive(&thread_pump->proc_lock); + list_move_tail(&list, &thread_pump->proc_queue); + thread_pump->proc_count = 0; + ReleaseSRWLockExclusive(&thread_pump->proc_lock); + + while (!list_empty(&list)) + { + work_item = LIST_ENTRY(list_head(&list), struct work_item, entry); + list_remove(&work_item->entry); + work_item_free(work_item, TRUE); + if (!InterlockedDecrement(&thread_pump->processing_count)) + RtlWakeAddressAll((void *)&thread_pump->processing_count); + } + + if (!(v = thread_pump->processing_count)) + break; + + RtlWaitOnAddress((void *)&thread_pump->processing_count, &v, sizeof(v), NULL); + } + + AcquireSRWLockExclusive(&thread_pump->device_lock); + list_move_tail(&list, &thread_pump->device_queue); + thread_pump->device_count = 0; + ReleaseSRWLockExclusive(&thread_pump->device_lock); + + while(!list_empty(&list)) + { + work_item = LIST_ENTRY(list_head(&list), struct work_item, entry); + list_remove(&work_item->entry); + work_item_free(work_item, TRUE); + } + return S_OK; }
static HRESULT WINAPI thread_pump_GetQueueStatus(ID3DX10ThreadPump *iface,
From: Piotr Caban piotr@codeweavers.com
Signed-off-by: Piotr Caban piotr@codeweavers.com --- dlls/d3dx10_43/tests/d3dx10.c | 355 ++++++++++++++++++++++++++++++++++ 1 file changed, 355 insertions(+)
diff --git a/dlls/d3dx10_43/tests/d3dx10.c b/dlls/d3dx10_43/tests/d3dx10.c index 1c28a62b700..d249bf78141 100644 --- a/dlls/d3dx10_43/tests/d3dx10.c +++ b/dlls/d3dx10_43/tests/d3dx10.c @@ -2065,6 +2065,360 @@ static void test_D3DX10CreateAsyncTextureProcessor(void) ID3D10Device_Release(device); }
+struct data_object +{ + ID3DX10DataLoader ID3DX10DataLoader_iface; + ID3DX10DataProcessor ID3DX10DataProcessor_iface; + + HANDLE load_started; + HANDLE load_done; + HANDLE decompress_done; + HRESULT load_ret; + + DWORD create_tid; + DWORD io_tid; + DWORD process_tid; +}; + +static struct data_object* data_object_from_ID3DX10DataLoader(ID3DX10DataLoader *iface) +{ + return CONTAINING_RECORD(iface, struct data_object, ID3DX10DataLoader_iface); +} + +static LONG data_loader_load_count; +static WINAPI HRESULT data_loader_Load(ID3DX10DataLoader *iface) +{ + struct data_object *data_object = data_object_from_ID3DX10DataLoader(iface); + DWORD ret; + + ok(InterlockedDecrement(&data_loader_load_count) >= 0, "unexpected call\n"); + + data_object->io_tid = GetCurrentThreadId(); + ok(data_object->io_tid != data_object->create_tid, "Load called in main thread.\n"); + + SetEvent(data_object->load_started); + ret = WaitForSingleObject(data_object->load_done, INFINITE); + ok(ret == WAIT_OBJECT_0, "WaitForSingleObject returned %x\n", ret); + return data_object->load_ret; +} + +static LONG data_loader_decompress_count; +static WINAPI HRESULT data_loader_Decompress(ID3DX10DataLoader *iface, void **data, SIZE_T *bytes) +{ + struct data_object *data_object = data_object_from_ID3DX10DataLoader(iface); + DWORD ret; + + ok(InterlockedDecrement(&data_loader_decompress_count) >= 0, "unexpected call\n"); + ok(data != NULL, "data == NULL\n"); + ok(bytes != NULL, "bytes == NULL\n"); + + data_object->process_tid = GetCurrentThreadId(); + ok(data_object->process_tid != data_object->create_tid, "Decompress called in main thread.\n"); + ok(data_object->process_tid != data_object->io_tid, "Decompress called in IO thread.\n"); + + *data = (void*)0xdeadbeef; + *bytes = 0xdead; + ret = WaitForSingleObject(data_object->decompress_done, INFINITE); + ok(ret == WAIT_OBJECT_0, "WaitForSingleObject returned %x\n", ret); + return S_OK; +} + +static LONG data_loader_destroy_count; +static WINAPI HRESULT data_loader_Destroy(ID3DX10DataLoader *iface) +{ + ok(InterlockedDecrement(&data_loader_destroy_count) >= 0, "unexpected call\n"); + return S_OK; +} + +static ID3DX10DataLoaderVtbl D3DX10DataLoaderVtbl = +{ + data_loader_Load, + data_loader_Decompress, + data_loader_Destroy +}; + +static struct data_object* data_object_from_ID3DX10DataProcessor(ID3DX10DataProcessor *iface) +{ + return CONTAINING_RECORD(iface, struct data_object, ID3DX10DataProcessor_iface); +} + +static LONG data_processor_process_count; +static HRESULT WINAPI data_processor_Process(ID3DX10DataProcessor *iface, void *data, SIZE_T bytes) +{ + struct data_object *data_object = data_object_from_ID3DX10DataProcessor(iface); + + ok(InterlockedDecrement(&data_processor_process_count) >= 0, "unexpected call\n"); + ok(GetCurrentThreadId() == data_object->process_tid, "Process called in different thread.\n"); + + ok(data == (void*)0xdeadbeef, "data = %p\n", data); + ok(bytes == 0xdead, "bytes = %lu\n", bytes); + return S_OK; +} + +static LONG data_processor_create_count; +static HRESULT WINAPI data_processor_CreateDeviceObject(ID3DX10DataProcessor *iface, void **object) +{ + struct data_object *data_object = data_object_from_ID3DX10DataProcessor(iface); + + ok(InterlockedDecrement(&data_processor_create_count) >= 0, "unexpected call\n"); + ok(GetCurrentThreadId() == data_object->create_tid, "CreateDeviceObject not called in main thread.\n"); + + *object = (void*)0xdeadf00d; + return S_OK; +} + +static LONG data_processor_destroy_count; +static HRESULT WINAPI data_processor_Destroy(ID3DX10DataProcessor *iface) +{ + struct data_object *data_object = data_object_from_ID3DX10DataProcessor(iface); + + ok(InterlockedDecrement(&data_processor_destroy_count) >= 0, "unexpected call\n"); + + CloseHandle(data_object->load_started); + CloseHandle(data_object->load_done); + CloseHandle(data_object->decompress_done); + free(data_object); + return S_OK; +} + +static ID3DX10DataProcessorVtbl D3DX10DataProcessorVtbl = +{ + data_processor_Process, + data_processor_CreateDeviceObject, + data_processor_Destroy +}; + +static struct data_object* create_data_object(HRESULT load_ret) +{ + struct data_object *data_object = malloc(sizeof(*data_object)); + + data_object->ID3DX10DataLoader_iface.lpVtbl = &D3DX10DataLoaderVtbl; + data_object->ID3DX10DataProcessor_iface.lpVtbl = &D3DX10DataProcessorVtbl; + + data_object->load_started = CreateEventW(NULL, FALSE, FALSE, NULL); + ok(data_object->load_started != NULL, "CreateEvent failed (%u)\n", GetLastError()); + data_object->load_done = CreateEventW(NULL, FALSE, FALSE, NULL); + ok(data_object->load_done != NULL, "CreateEvent failed (%u)\n", GetLastError()); + data_object->decompress_done = CreateEventW(NULL, FALSE, FALSE, NULL); + ok(data_object->decompress_done != NULL, "CreateEvent failed (%u)\n", GetLastError()); + data_object->load_ret = load_ret; + + data_object->create_tid = GetCurrentThreadId(); + return data_object; +} + +static void test_D3DX10CreateThreadPump(void) +{ + UINT io_count, process_count, device_count, count; + struct data_object *data_object[2]; + ID3DX10DataProcessor *processor; + D3DX10_IMAGE_INFO image_info; + ID3DX10DataLoader *loader; + HRESULT hr, work_item_hr; + ID3D10Resource *resource; + ID3DX10ThreadPump *pump; + ID3D10Device *device; + SYSTEM_INFO info; + void *object; + DWORD ret; + int i; + + hr = D3DX10CreateThreadPump(1024, 0, &pump); + ok(hr == E_FAIL, "Got unexpected hr %#x.\n", hr); + hr = D3DX10CreateThreadPump(0, 1024, &pump); + ok(hr == E_FAIL, "Got unexpected hr %#x.\n", hr); + + GetSystemInfo(&info); + if (info.dwNumberOfProcessors > 1) + hr = D3DX10CreateThreadPump(0, 0, &pump); + else + hr = D3DX10CreateThreadPump(0, 2, &pump); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + + count = ID3DX10ThreadPump_GetWorkItemCount(pump); + ok(!count, "GetWorkItemCount returned %u.\n", count); + hr = ID3DX10ThreadPump_GetQueueStatus(pump, &io_count, &process_count, &device_count); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + ok(!io_count, "Got unexpected io_count = %u.\n", io_count); + ok(!process_count, "Got unexpected process_count = %u.\n", process_count); + ok(!device_count, "Got unexpected device_count = %u.\n", device_count); + + data_object[0] = create_data_object(E_NOTIMPL); + data_object[1] = create_data_object(S_OK); + + data_loader_load_count = 1; + hr = ID3DX10ThreadPump_AddWorkItem(pump, &data_object[0]->ID3DX10DataLoader_iface, + &data_object[0]->ID3DX10DataProcessor_iface, &work_item_hr, NULL); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + ret = WaitForSingleObject(data_object[0]->load_started, INFINITE); + ok(ret == WAIT_OBJECT_0, "WaitForSingleObject returned %x.\n", ret); + ok(!data_loader_load_count, "Got unexpected data_loader_load_count %d.\n", data_loader_load_count); + count = ID3DX10ThreadPump_GetWorkItemCount(pump); + ok(count == 1, "GetWorkItemCount returned %u.\n", count); + hr = ID3DX10ThreadPump_GetQueueStatus(pump, &io_count, &process_count, &device_count); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + ok(!io_count, "Got unexpected io_count = %u.\n", io_count); + ok(!process_count, "Got unexpected process_count = %u.\n", process_count); + ok(!device_count, "Got unexpected device_count = %u.\n", device_count); + + hr = ID3DX10ThreadPump_AddWorkItem(pump, &data_object[1]->ID3DX10DataLoader_iface, + &data_object[1]->ID3DX10DataProcessor_iface, NULL, &object); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + ret = WaitForSingleObject(data_object[0]->load_started, 50); + ok(ret == WAIT_TIMEOUT, "WaitForSingleObject returned %x.\n", ret); + count = ID3DX10ThreadPump_GetWorkItemCount(pump); + ok(count == 2, "GetWorkItemCount returned %u.\n", count); + hr = ID3DX10ThreadPump_GetQueueStatus(pump, &io_count, &process_count, &device_count); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + ok(io_count == 1, "Got unexpected io_count = %u.\n", io_count); + ok(!process_count, "Got unexpected process_count = %u.\n", process_count); + ok(!device_count, "Got unexpected device_count = %u.\n", device_count); + + data_loader_load_count = 1; + data_loader_destroy_count = 1; + data_processor_destroy_count = 1; + SetEvent(data_object[0]->load_done); + ret = WaitForSingleObject(data_object[1]->load_started, INFINITE); + ok(ret == WAIT_OBJECT_0, "WaitForSingleObject returned %x.\n", ret); + ok(work_item_hr == E_NOTIMPL, "Got unexpected work_item_hr = %#x.\n", work_item_hr); + ok(!data_loader_destroy_count, "Got unexpected data_loader_destroy_count %d.\n", data_loader_destroy_count); + ok(!data_processor_destroy_count, "Got unexpected data_processor_destroy_count %d.\n", + data_processor_destroy_count); + ok(!data_loader_load_count, "Got unexpected data_loader_load_count %d.\n", data_loader_load_count); + + data_loader_decompress_count = 1; + data_processor_process_count = 1; + SetEvent(data_object[1]->load_done); + SetEvent(data_object[1]->decompress_done); + + data_processor_create_count = 1; + data_loader_destroy_count = 1; + data_processor_destroy_count = 1; + hr = ID3DX10ThreadPump_WaitForAllItems(pump); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + ok(object == (void*)0xdeadf00d, "Got unexpected object = %p.\n", object); + ok(!data_loader_decompress_count, "Got unexpected data_loader_decompress_count %d.\n", data_loader_decompress_count); + ok(!data_processor_process_count, "Got unexpected data_processor_process_count %d.\n", data_processor_process_count); + ok(!data_processor_create_count, "Got unexpected data_processor_create_count %d.\n", data_processor_create_count); + ok(!data_loader_destroy_count, "Got unexpected data_loader_destroy_count %d.\n", data_loader_destroy_count); + ok(!data_processor_destroy_count, "Got unexpected data_processor_destroy_count %d.\n", data_processor_destroy_count); + + data_object[0] = create_data_object(S_OK); + data_object[1] = create_data_object(S_OK); + SetEvent(data_object[0]->load_done); + SetEvent(data_object[1]->load_done); + SetEvent(data_object[1]->decompress_done); + + data_loader_load_count = 2; + data_loader_decompress_count = 2; + data_processor_process_count = 1; + hr = ID3DX10ThreadPump_AddWorkItem(pump, &data_object[0]->ID3DX10DataLoader_iface, + &data_object[0]->ID3DX10DataProcessor_iface, NULL, &object); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + hr = ID3DX10ThreadPump_AddWorkItem(pump, &data_object[1]->ID3DX10DataLoader_iface, + &data_object[1]->ID3DX10DataProcessor_iface, NULL, &object); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + while(1) + { + hr = ID3DX10ThreadPump_GetQueueStatus(pump, &io_count, &process_count, &device_count); + if (hr != S_OK || device_count) + break; + Sleep(1); + } + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + ok(!io_count, "Got unexpected io_count = %u.\n", io_count); + ok(!process_count, "Got unexpected process_count = %u.\n", process_count); + ok(device_count == 1, "Got unexpected device_count = %u.\n", device_count); + ok(!data_loader_load_count, "Got unexpected data_loader_load_count %d.\n", data_loader_load_count); + ok(!data_loader_decompress_count, "Got unexpected data_loader_decompress_count %d.\n", data_loader_decompress_count); + ok(!data_processor_process_count, "Got unexpected data_processor_process_count %d.\n", data_processor_process_count); + + hr = ID3DX10ThreadPump_ProcessDeviceWorkItems(pump, 0); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + hr = ID3DX10ThreadPump_GetQueueStatus(pump, &io_count, &process_count, &device_count); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + ok(!io_count, "Got unexpected io_count = %u.\n", io_count); + ok(!process_count, "Got unexpected process_count = %u.\n", process_count); + ok(device_count == 1, "Got unexpected device_count = %u.\n", device_count); + + data_processor_create_count = 1; + data_loader_destroy_count = 1; + data_processor_destroy_count = 1; + hr = ID3DX10ThreadPump_ProcessDeviceWorkItems(pump, 1); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + hr = ID3DX10ThreadPump_GetQueueStatus(pump, &io_count, &process_count, &device_count); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + ok(!io_count, "Got unexpected io_count = %u.\n", io_count); + ok(!process_count, "Got unexpected process_count = %u.\n", process_count); + ok(!device_count, "Got unexpected device_count = %u.\n", device_count); + ok(!data_processor_create_count, "Got unexpected data_processor_create_count %d.\n", data_processor_create_count); + ok(!data_loader_destroy_count, "Got unexpected data_loader_destroy_count %d.\n", data_loader_destroy_count); + ok(!data_processor_destroy_count, "Got unexpected data_processor_destroy_count %d.\n", data_processor_destroy_count); + + data_processor_process_count = 1; + data_loader_destroy_count = 1; + data_processor_destroy_count = 1; + SetEvent(data_object[0]->decompress_done); + hr = ID3DX10ThreadPump_PurgeAllItems(pump); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + data_processor_process_count = 0; + ok(!data_loader_destroy_count, "Got unexpected data_loader_destroy_count %d.\n", data_loader_destroy_count); + ok(!data_processor_destroy_count, "Got unexpected data_processor_destroy_count %d.\n", data_processor_destroy_count); + + device = create_device(); + if (!device) + { + skip("Failed to create device, skipping tests.\n"); + ID3DX10ThreadPump_Release(pump); + return; + } + + for (i = 0; i < ARRAY_SIZE(test_image); ++i) + { + winetest_push_context("Test %u", i); + + hr = D3DX10CreateAsyncMemoryLoader(test_image[i].data, test_image[i].size, &loader); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + hr = D3DX10CreateAsyncTextureInfoProcessor(&image_info, &processor); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + hr = ID3DX10ThreadPump_AddWorkItem(pump, loader, processor, &work_item_hr, NULL); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + hr = ID3DX10ThreadPump_WaitForAllItems(pump); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + ok(work_item_hr == S_OK || (work_item_hr == E_FAIL + && test_image[i].expected_info.ImageFileFormat == D3DX10_IFF_WMP), + "Got unexpected hr %#x.\n", work_item_hr); + if (work_item_hr == S_OK) + check_image_info(&image_info, test_image + i, __LINE__); + + hr = D3DX10CreateAsyncMemoryLoader(test_image[i].data, test_image[i].size, &loader); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + hr = D3DX10CreateAsyncTextureProcessor(device, NULL, &processor); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + hr = ID3DX10ThreadPump_AddWorkItem(pump, loader, processor, &work_item_hr, (void **)&resource); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + hr = ID3DX10ThreadPump_WaitForAllItems(pump); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + todo_wine_if(test_image[i].expected_info.MiscFlags & D3D10_RESOURCE_MISC_TEXTURECUBE) + ok(work_item_hr == S_OK || (work_item_hr == E_FAIL + && test_image[i].expected_info.ImageFileFormat == D3DX10_IFF_WMP), + "Got unexpected hr %#x.\n", work_item_hr); + if (work_item_hr == S_OK) + { + check_resource_info(resource, test_image + i, __LINE__); + check_resource_data(resource, test_image + i, __LINE__); + ID3D10Resource_Release(resource); + } + + winetest_pop_context(); + } + + ID3D10Device_Release(device); + + ret = ID3DX10ThreadPump_Release(pump); + ok(!ret, "Got unexpected refcount %u.\n", ret); +} + static void test_get_image_info(void) { static const WCHAR test_resource_name[] = L"resource.data"; @@ -3572,6 +3926,7 @@ START_TEST(d3dx10) test_D3DX10CreateAsyncResourceLoader(); test_D3DX10CreateAsyncTextureInfoProcessor(); test_D3DX10CreateAsyncTextureProcessor(); + test_D3DX10CreateThreadPump(); test_get_image_info(); test_create_texture(); test_font();
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=117237
Your paranoid android.
=== debian11 (32 bit report) ===
d3dx10_43: Unhandled exception: page fault on write access to 0x00000000 in 32-bit code (0x70bb0040).
=== debian11 (32 bit Arabic:Morocco report) ===
d3dx10_43: Unhandled exception: page fault on write access to 0x00000000 in 32-bit code (0x70bb0040).
=== debian11 (32 bit German report) ===
d3dx10_43: Unhandled exception: page fault on write access to 0x00000000 in 32-bit code (0x70bb0040).
=== debian11 (32 bit French report) ===
d3dx10_43: Unhandled exception: page fault on write access to 0x00000000 in 32-bit code (0x70bb0040).
=== debian11 (32 bit Hebrew:Israel report) ===
d3dx10_43: Unhandled exception: page fault on write access to 0x00000000 in 32-bit code (0x70bb0040).
=== debian11 (32 bit Hindi:India report) ===
d3dx10_43: Unhandled exception: page fault on write access to 0x00000000 in 32-bit code (0x70bb0040).
=== debian11 (32 bit Japanese:Japan report) ===
d3dx10_43: Unhandled exception: page fault on write access to 0x00000000 in 32-bit code (0x70bb0040).
=== debian11 (32 bit Chinese:China report) ===
d3dx10_43: Unhandled exception: page fault on write access to 0x00000000 in 32-bit code (0x70bb0040).
=== debian11 (32 bit WoW report) ===
d3dx10_43: Unhandled exception: page fault on write access to 0x00000000 in 32-bit code (0x70bb0040).
Could we use threadpool API for this?
On Sat Jun 18 16:12:19 2022 +0000, Nikolay Sivov wrote:
Could we use threadpool API for this?
While it would be possible it doesn't fit well this use case. The thread pump implementation uses permanent threads with dedicated threads for loading and processing tasks.