wine/dlls/d3dx10_43/async.c

1081 lines
31 KiB
C

/*
* Copyright 2016 Andrey Gusev
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#define COBJMACROS
#include "d3d10_1.h"
#include "d3dx10.h"
#include "d3dcompiler.h"
#include "dxhelpers.h"
#include "winternl.h"
#include "wine/debug.h"
#include "wine/list.h"
WINE_DEFAULT_DEBUG_CHANNEL(d3dx);
struct asyncdataloader
{
ID3DX10DataLoader ID3DX10DataLoader_iface;
union
{
struct
{
WCHAR *path;
} file;
struct
{
HMODULE module;
HRSRC rsrc;
} resource;
} u;
void *data;
DWORD size;
};
static inline struct asyncdataloader *impl_from_ID3DX10DataLoader(ID3DX10DataLoader *iface)
{
return CONTAINING_RECORD(iface, struct asyncdataloader, ID3DX10DataLoader_iface);
}
static HRESULT WINAPI memorydataloader_Load(ID3DX10DataLoader *iface)
{
TRACE("iface %p.\n", iface);
return S_OK;
}
static HRESULT WINAPI memorydataloader_Decompress(ID3DX10DataLoader *iface, void **data, SIZE_T *size)
{
struct asyncdataloader *loader = impl_from_ID3DX10DataLoader(iface);
TRACE("iface %p, data %p, size %p.\n", iface, data, size);
*data = loader->data;
*size = loader->size;
return S_OK;
}
static HRESULT WINAPI memorydataloader_Destroy(ID3DX10DataLoader *iface)
{
struct asyncdataloader *loader = impl_from_ID3DX10DataLoader(iface);
TRACE("iface %p.\n", iface);
free(loader);
return S_OK;
}
static const ID3DX10DataLoaderVtbl memorydataloadervtbl =
{
memorydataloader_Load,
memorydataloader_Decompress,
memorydataloader_Destroy
};
HRESULT load_file(const WCHAR *path, void **data, DWORD *size)
{
DWORD read_len;
HANDLE file;
BOOL ret;
file = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (file == INVALID_HANDLE_VALUE)
return D3D10_ERROR_FILE_NOT_FOUND;
*size = GetFileSize(file, NULL);
*data = malloc(*size);
if (!*data)
{
CloseHandle(file);
return E_OUTOFMEMORY;
}
ret = ReadFile(file, *data, *size, &read_len, NULL);
CloseHandle(file);
if (!ret || read_len != *size)
{
WARN("Failed to read file contents.\n");
free(*data);
return E_FAIL;
}
return S_OK;
}
static HRESULT WINAPI filedataloader_Load(ID3DX10DataLoader *iface)
{
struct asyncdataloader *loader = impl_from_ID3DX10DataLoader(iface);
void *data;
DWORD size;
HRESULT hr;
TRACE("iface %p.\n", iface);
/* Always buffer file contents, even if Load() was already called. */
if (FAILED((hr = load_file(loader->u.file.path, &data, &size))))
return hr;
free(loader->data);
loader->data = data;
loader->size = size;
return S_OK;
}
static HRESULT WINAPI filedataloader_Decompress(ID3DX10DataLoader *iface, void **data, SIZE_T *size)
{
struct asyncdataloader *loader = impl_from_ID3DX10DataLoader(iface);
TRACE("iface %p, data %p, size %p.\n", iface, data, size);
if (!loader->data)
return E_FAIL;
*data = loader->data;
*size = loader->size;
return S_OK;
}
static HRESULT WINAPI filedataloader_Destroy(ID3DX10DataLoader *iface)
{
struct asyncdataloader *loader = impl_from_ID3DX10DataLoader(iface);
TRACE("iface %p.\n", iface);
free(loader->u.file.path);
free(loader->data);
free(loader);
return S_OK;
}
static const ID3DX10DataLoaderVtbl filedataloadervtbl =
{
filedataloader_Load,
filedataloader_Decompress,
filedataloader_Destroy
};
static HRESULT load_resource_initA(HMODULE module, const char *resource, HRSRC *rsrc)
{
if (!(*rsrc = FindResourceA(module, resource, (const char *)RT_RCDATA)))
*rsrc = FindResourceA(module, resource, (const char *)RT_BITMAP);
if (!*rsrc)
{
WARN("Failed to find resource.\n");
return D3DX10_ERR_INVALID_DATA;
}
return S_OK;
}
static HRESULT load_resource_initW(HMODULE module, const WCHAR *resource, HRSRC *rsrc)
{
if (!(*rsrc = FindResourceW(module, resource, (const WCHAR *)RT_RCDATA)))
*rsrc = FindResourceW(module, resource, (const WCHAR *)RT_BITMAP);
if (!*rsrc)
{
WARN("Failed to find resource.\n");
return D3DX10_ERR_INVALID_DATA;
}
return S_OK;
}
static HRESULT load_resource(HMODULE module, HRSRC rsrc, void **data, DWORD *size)
{
HGLOBAL hglobal;
if (!(*size = SizeofResource(module, rsrc)))
return D3DX10_ERR_INVALID_DATA;
if (!(hglobal = LoadResource(module, rsrc)))
return D3DX10_ERR_INVALID_DATA;
if (!(*data = LockResource(hglobal)))
return D3DX10_ERR_INVALID_DATA;
return S_OK;
}
HRESULT load_resourceA(HMODULE module, const char *resource, void **data, DWORD *size)
{
HRESULT hr;
HRSRC rsrc;
if (FAILED((hr = load_resource_initA(module, resource, &rsrc))))
return hr;
return load_resource(module, rsrc, data, size);
}
HRESULT load_resourceW(HMODULE module, const WCHAR *resource, void **data, DWORD *size)
{
HRESULT hr;
HRSRC rsrc;
if ((FAILED(hr = load_resource_initW(module, resource, &rsrc))))
return hr;
return load_resource(module, rsrc, data, size);
}
static HRESULT WINAPI resourcedataloader_Load(ID3DX10DataLoader *iface)
{
struct asyncdataloader *loader = impl_from_ID3DX10DataLoader(iface);
TRACE("iface %p.\n", iface);
if (loader->data)
return S_OK;
return load_resource(loader->u.resource.module, loader->u.resource.rsrc,
&loader->data, &loader->size);
}
static HRESULT WINAPI resourcedataloader_Decompress(ID3DX10DataLoader *iface, void **data, SIZE_T *size)
{
struct asyncdataloader *loader = impl_from_ID3DX10DataLoader(iface);
TRACE("iface %p, data %p, size %p.\n", iface, data, size);
if (!loader->data)
return E_FAIL;
*data = loader->data;
*size = loader->size;
return S_OK;
}
static HRESULT WINAPI resourcedataloader_Destroy(ID3DX10DataLoader *iface)
{
struct asyncdataloader *loader = impl_from_ID3DX10DataLoader(iface);
TRACE("iface %p.\n", iface);
free(loader);
return S_OK;
}
static const ID3DX10DataLoaderVtbl resourcedataloadervtbl =
{
resourcedataloader_Load,
resourcedataloader_Decompress,
resourcedataloader_Destroy
};
struct texture_info_processor
{
ID3DX10DataProcessor ID3DX10DataProcessor_iface;
D3DX10_IMAGE_INFO *info;
};
static inline struct texture_info_processor *impl_from_ID3DX10DataProcessor(ID3DX10DataProcessor *iface)
{
return CONTAINING_RECORD(iface, struct texture_info_processor, ID3DX10DataProcessor_iface);
}
static HRESULT WINAPI texture_info_processor_Process(ID3DX10DataProcessor *iface, void *data, SIZE_T size)
{
struct texture_info_processor *processor = impl_from_ID3DX10DataProcessor(iface);
TRACE("iface %p, data %p, size %Iu.\n", iface, data, size);
return get_image_info(data, size, processor->info);
}
static HRESULT WINAPI texture_info_processor_CreateDeviceObject(ID3DX10DataProcessor *iface, void **object)
{
TRACE("iface %p, object %p.\n", iface, object);
return S_OK;
}
static HRESULT WINAPI texture_info_processor_Destroy(ID3DX10DataProcessor *iface)
{
struct texture_info_processor *processor = impl_from_ID3DX10DataProcessor(iface);
TRACE("iface %p.\n", iface);
free(processor);
return S_OK;
}
static ID3DX10DataProcessorVtbl texture_info_processor_vtbl =
{
texture_info_processor_Process,
texture_info_processor_CreateDeviceObject,
texture_info_processor_Destroy
};
struct texture_processor
{
ID3DX10DataProcessor ID3DX10DataProcessor_iface;
ID3D10Device *device;
D3DX10_IMAGE_LOAD_INFO load_info;
D3D10_SUBRESOURCE_DATA *resource_data;
};
static inline struct texture_processor *texture_processor_from_ID3DX10DataProcessor(ID3DX10DataProcessor *iface)
{
return CONTAINING_RECORD(iface, struct texture_processor, ID3DX10DataProcessor_iface);
}
static HRESULT WINAPI texture_processor_Process(ID3DX10DataProcessor *iface, void *data, SIZE_T size)
{
struct texture_processor *processor = texture_processor_from_ID3DX10DataProcessor(iface);
TRACE("iface %p, data %p, size %Iu.\n", iface, data, size);
if (processor->resource_data)
{
WARN("Called multiple times.\n");
free(processor->resource_data);
processor->resource_data = NULL;
}
return load_texture_data(data, size, &processor->load_info, &processor->resource_data);
}
static HRESULT WINAPI texture_processor_CreateDeviceObject(ID3DX10DataProcessor *iface, void **object)
{
struct texture_processor *processor = texture_processor_from_ID3DX10DataProcessor(iface);
TRACE("iface %p, object %p.\n", iface, object);
if (!processor->resource_data)
return E_FAIL;
return create_d3d_texture(processor->device, &processor->load_info,
processor->resource_data, (ID3D10Resource **)object);
}
static HRESULT WINAPI texture_processor_Destroy(ID3DX10DataProcessor *iface)
{
struct texture_processor *processor = texture_processor_from_ID3DX10DataProcessor(iface);
TRACE("iface %p.\n", iface);
ID3D10Device_Release(processor->device);
free(processor->resource_data);
free(processor);
return S_OK;
}
static ID3DX10DataProcessorVtbl texture_processor_vtbl =
{
texture_processor_Process,
texture_processor_CreateDeviceObject,
texture_processor_Destroy
};
HRESULT WINAPI D3DX10CompileFromMemory(const char *data, SIZE_T data_size, const char *filename,
const D3D10_SHADER_MACRO *defines, ID3D10Include *include, const char *entry_point,
const char *target, UINT sflags, UINT eflags, ID3DX10ThreadPump *pump, ID3D10Blob **shader,
ID3D10Blob **error_messages, HRESULT *hresult)
{
TRACE("data %s, data_size %Iu, filename %s, defines %p, include %p, entry_point %s, target %s, "
"sflags %#x, eflags %#x, pump %p, shader %p, error_messages %p, hresult %p.\n",
debugstr_an(data, data_size), data_size, debugstr_a(filename), defines, include,
debugstr_a(entry_point), debugstr_a(target), sflags, eflags, pump, shader,
error_messages, hresult);
if (pump)
FIXME("Unimplemented ID3DX10ThreadPump handling.\n");
return D3DCompile(data, data_size, filename, defines, include, entry_point, target,
sflags, eflags, shader, error_messages);
}
HRESULT WINAPI D3DX10CreateEffectPoolFromFileA(const char *filename, const D3D10_SHADER_MACRO *defines,
ID3D10Include *include, const char *profile, UINT hlslflags, UINT fxflags, ID3D10Device *device,
ID3DX10ThreadPump *pump, ID3D10EffectPool **effectpool, ID3D10Blob **errors, HRESULT *hresult)
{
FIXME("filename %s, defines %p, include %p, profile %s, hlslflags %#x, fxflags %#x, device %p, "
"pump %p, effectpool %p, errors %p, hresult %p, stub!\n",
debugstr_a(filename), defines, include, debugstr_a(profile), hlslflags, fxflags, device,
pump, effectpool, errors, hresult);
return E_NOTIMPL;
}
HRESULT WINAPI D3DX10CreateEffectPoolFromFileW(const WCHAR *filename, const D3D10_SHADER_MACRO *defines,
ID3D10Include *include, const char *profile, UINT hlslflags, UINT fxflags, ID3D10Device *device,
ID3DX10ThreadPump *pump, ID3D10EffectPool **effectpool, ID3D10Blob **errors, HRESULT *hresult)
{
FIXME("filename %s, defines %p, include %p, profile %s, hlslflags %#x, fxflags %#x, device %p, "
"pump %p, effectpool %p, errors %p, hresult %p, stub!\n",
debugstr_w(filename), defines, include, debugstr_a(profile), hlslflags, fxflags, device,
pump, effectpool, errors, hresult);
return E_NOTIMPL;
}
HRESULT WINAPI D3DX10CreateAsyncMemoryLoader(const void *data, SIZE_T data_size, ID3DX10DataLoader **loader)
{
struct asyncdataloader *object;
TRACE("data %p, data_size %Iu, loader %p.\n", data, data_size, loader);
if (!data || !loader)
return E_FAIL;
object = calloc(1, sizeof(*object));
if (!object)
return E_OUTOFMEMORY;
object->ID3DX10DataLoader_iface.lpVtbl = &memorydataloadervtbl;
object->data = (void *)data;
object->size = data_size;
*loader = &object->ID3DX10DataLoader_iface;
return S_OK;
}
HRESULT WINAPI D3DX10CreateAsyncFileLoaderA(const char *filename, ID3DX10DataLoader **loader)
{
WCHAR *filename_w;
HRESULT hr;
int len;
TRACE("filename %s, loader %p.\n", debugstr_a(filename), loader);
if (!filename || !loader)
return E_FAIL;
len = MultiByteToWideChar(CP_ACP, 0, filename, -1, NULL, 0);
filename_w = malloc(len * sizeof(*filename_w));
MultiByteToWideChar(CP_ACP, 0, filename, -1, filename_w, len);
hr = D3DX10CreateAsyncFileLoaderW(filename_w, loader);
free(filename_w);
return hr;
}
HRESULT WINAPI D3DX10CreateAsyncFileLoaderW(const WCHAR *filename, ID3DX10DataLoader **loader)
{
struct asyncdataloader *object;
TRACE("filename %s, loader %p.\n", debugstr_w(filename), loader);
if (!filename || !loader)
return E_FAIL;
object = calloc(1, sizeof(*object));
if (!object)
return E_OUTOFMEMORY;
object->ID3DX10DataLoader_iface.lpVtbl = &filedataloadervtbl;
object->u.file.path = malloc((lstrlenW(filename) + 1) * sizeof(WCHAR));
if (!object->u.file.path)
{
free(object);
return E_OUTOFMEMORY;
}
lstrcpyW(object->u.file.path, filename);
object->data = NULL;
object->size = 0;
*loader = &object->ID3DX10DataLoader_iface;
return S_OK;
}
HRESULT WINAPI D3DX10CreateAsyncResourceLoaderA(HMODULE module, const char *resource, ID3DX10DataLoader **loader)
{
struct asyncdataloader *object;
HRSRC rsrc;
HRESULT hr;
TRACE("module %p, resource %s, loader %p.\n", module, debugstr_a(resource), loader);
if (!loader)
return E_FAIL;
object = calloc(1, sizeof(*object));
if (!object)
return E_OUTOFMEMORY;
if (FAILED((hr = load_resource_initA(module, resource, &rsrc))))
{
free(object);
return hr;
}
object->ID3DX10DataLoader_iface.lpVtbl = &resourcedataloadervtbl;
object->u.resource.module = module;
object->u.resource.rsrc = rsrc;
object->data = NULL;
object->size = 0;
*loader = &object->ID3DX10DataLoader_iface;
return S_OK;
}
HRESULT WINAPI D3DX10CreateAsyncResourceLoaderW(HMODULE module, const WCHAR *resource, ID3DX10DataLoader **loader)
{
struct asyncdataloader *object;
HRSRC rsrc;
HRESULT hr;
TRACE("module %p, resource %s, loader %p.\n", module, debugstr_w(resource), loader);
if (!loader)
return E_FAIL;
object = calloc(1, sizeof(*object));
if (!object)
return E_OUTOFMEMORY;
if (FAILED((hr = load_resource_initW(module, resource, &rsrc))))
{
free(object);
return hr;
}
object->ID3DX10DataLoader_iface.lpVtbl = &resourcedataloadervtbl;
object->u.resource.module = module;
object->u.resource.rsrc = rsrc;
object->data = NULL;
object->size = 0;
*loader = &object->ID3DX10DataLoader_iface;
return S_OK;
}
HRESULT WINAPI D3DX10CreateAsyncTextureInfoProcessor(D3DX10_IMAGE_INFO *info, ID3DX10DataProcessor **processor)
{
struct texture_info_processor *object;
TRACE("info %p, processor %p.\n", info, processor);
if (!processor)
return E_INVALIDARG;
object = malloc(sizeof(*object));
if (!object)
return E_OUTOFMEMORY;
object->ID3DX10DataProcessor_iface.lpVtbl = &texture_info_processor_vtbl;
object->info = info;
*processor = &object->ID3DX10DataProcessor_iface;
return S_OK;
}
HRESULT WINAPI D3DX10CreateAsyncTextureProcessor(ID3D10Device *device,
D3DX10_IMAGE_LOAD_INFO *load_info, ID3DX10DataProcessor **processor)
{
struct texture_processor *object;
TRACE("device %p, load_info %p, processor %p.\n", device, load_info, processor);
if (!device || !processor)
return E_INVALIDARG;
object = calloc(1, sizeof(*object));
if (!object)
return E_OUTOFMEMORY;
object->ID3DX10DataProcessor_iface.lpVtbl = &texture_processor_vtbl;
object->device = device;
ID3D10Device_AddRef(device);
init_load_info(load_info, &object->load_info);
*processor = &object->ID3DX10DataProcessor_iface;
return S_OK;
}
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;
LONG processing_count;
SRWLOCK io_lock;
CONDITION_VARIABLE io_cv;
unsigned int io_count;
struct list io_queue;
SRWLOCK proc_lock;
CONDITION_VARIABLE proc_cv;
unsigned int proc_count;
struct list proc_queue;
SRWLOCK device_lock;
unsigned int device_count;
struct list device_queue;
unsigned int thread_count;
HANDLE threads[1];
};
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);
struct work_item *item, *next;
struct list list;
unsigned 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);
WakeAllConditionVariable(&thread_pump->io_cv);
AcquireSRWLockExclusive(&thread_pump->proc_lock);
thread_pump->proc_count = THREAD_PUMP_EXITING;
ReleaseSRWLockExclusive(&thread_pump->proc_lock);
WakeAllConditionVariable(&thread_pump->proc_cv);
AcquireSRWLockExclusive(&thread_pump->device_lock);
thread_pump->device_count = THREAD_PUMP_EXITING;
ReleaseSRWLockExclusive(&thread_pump->device_lock);
for (i = 0; i < thread_pump->thread_count; ++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(item, next, &list, struct work_item, entry)
{
list_remove(&item->entry);
work_item_free(item, TRUE);
}
free(thread_pump);
}
return refcount;
}
static HRESULT WINAPI thread_pump_AddWorkItem(ID3DX10ThreadPump *iface, ID3DX10DataLoader *loader,
ID3DX10DataProcessor *processor, HRESULT *result, void **object)
{
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);
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;
InterlockedIncrement(&thread_pump->processing_count);
AcquireSRWLockExclusive(&thread_pump->io_lock);
++thread_pump->io_count;
list_add_tail(&thread_pump->io_queue, &work_item->entry);
ReleaseSRWLockExclusive(&thread_pump->io_lock);
WakeConditionVariable(&thread_pump->io_cv);
return S_OK;
}
static UINT WINAPI thread_pump_GetWorkItemCount(ID3DX10ThreadPump *iface)
{
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)
{
struct thread_pump *thread_pump = impl_from_ID3DX10ThreadPump(iface);
HRESULT hr;
LONG v;
TRACE("iface %p.\n", iface);
for (;;)
{
if (FAILED((hr = ID3DX10ThreadPump_ProcessDeviceWorkItems(iface, UINT_MAX))))
return hr;
AcquireSRWLockExclusive(&thread_pump->device_lock);
if (thread_pump->device_count)
{
ReleaseSRWLockExclusive(&thread_pump->device_lock);
continue;
}
v = thread_pump->processing_count;
ReleaseSRWLockExclusive(&thread_pump->device_lock);
if (!v)
break;
RtlWaitOnAddress(&thread_pump->processing_count, &v, sizeof(v), NULL);
}
return S_OK;
}
static HRESULT WINAPI thread_pump_ProcessDeviceWorkItems(ID3DX10ThreadPump *iface, UINT count)
{
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 void purge_list(struct list *list, LONG *count)
{
struct work_item *work_item;
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 (count && !InterlockedDecrement(count))
RtlWakeAddressAll(count);
}
}
static HRESULT WINAPI thread_pump_PurgeAllItems(ID3DX10ThreadPump *iface)
{
struct thread_pump *thread_pump = impl_from_ID3DX10ThreadPump(iface);
LONG v;
TRACE("iface %p.\n", iface);
for (;;)
{
AcquireSRWLockExclusive(&thread_pump->io_lock);
purge_list(&thread_pump->io_queue, &thread_pump->processing_count);
thread_pump->io_count = 0;
ReleaseSRWLockExclusive(&thread_pump->io_lock);
AcquireSRWLockExclusive(&thread_pump->proc_lock);
purge_list(&thread_pump->proc_queue, &thread_pump->processing_count);
thread_pump->proc_count = 0;
ReleaseSRWLockExclusive(&thread_pump->proc_lock);
AcquireSRWLockExclusive(&thread_pump->device_lock);
purge_list(&thread_pump->device_queue, NULL);
thread_pump->device_count = 0;
v = thread_pump->processing_count;
ReleaseSRWLockExclusive(&thread_pump->device_lock);
if (!v)
break;
RtlWaitOnAddress(&thread_pump->processing_count, &v, sizeof(v), NULL);
}
return S_OK;
}
static HRESULT WINAPI thread_pump_GetQueueStatus(ID3DX10ThreadPump *iface,
UINT *io_queue, UINT *process_queue, UINT *device_queue)
{
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);
*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 =
{
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
};
static DWORD WINAPI io_thread(void *arg)
{
struct thread_pump *thread_pump = arg;
struct work_item *work_item;
HRESULT hr;
TRACE("%p thread started.\n", thread_pump);
for (;;)
{
AcquireSRWLockExclusive(&thread_pump->io_lock);
while (!thread_pump->io_count)
SleepConditionVariableSRW(&thread_pump->io_cv, &thread_pump->io_lock, INFINITE, 0);
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);
if (!InterlockedDecrement(&thread_pump->processing_count))
RtlWakeAddressAll(&thread_pump->processing_count);
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);
WakeConditionVariable(&thread_pump->proc_cv);
}
return 0;
}
static DWORD WINAPI proc_thread(void *arg)
{
struct thread_pump *thread_pump = arg;
struct work_item *work_item;
SIZE_T size;
void *data;
HRESULT hr;
TRACE("%p thread started.\n", thread_pump);
for (;;)
{
AcquireSRWLockExclusive(&thread_pump->proc_lock);
while (!thread_pump->proc_count)
SleepConditionVariableSRW(&thread_pump->proc_cv, &thread_pump->proc_lock, INFINITE, 0);
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);
if (!InterlockedDecrement(&thread_pump->processing_count))
RtlWakeAddressAll(&thread_pump->processing_count);
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);
if (!InterlockedDecrement(&thread_pump->processing_count))
RtlWakeAddressAll(&thread_pump->processing_count);
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;
InterlockedDecrement(&thread_pump->processing_count);
RtlWakeAddressAll(&thread_pump->processing_count);
ReleaseSRWLockExclusive(&thread_pump->device_lock);
}
return 0;
}
HRESULT WINAPI D3DX10CreateThreadPump(UINT io_threads, UINT proc_threads, ID3DX10ThreadPump **pump)
{
struct thread_pump *object;
unsigned 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 (!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);
InitializeConditionVariable(&object->io_cv);
list_init(&object->io_queue);
InitializeSRWLock(&object->proc_lock);
InitializeConditionVariable(&object->proc_cv);
list_init(&object->proc_queue);
InitializeSRWLock(&object->device_lock);
list_init(&object->device_queue);
object->thread_count = io_threads + proc_threads;
for (i = 0; i < object->thread_count; ++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;
}