mirror of
git://source.winehq.org/git/wine.git
synced 2024-09-01 18:43:37 +00:00
1082 lines
31 KiB
C
1082 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;
|
|
}
|