Implementation is based on similar gstreamer utility code, adjusted for signed arguments, and with platform optimizations removed.
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com ---
v2: added copyright notice for parts derived from gstreamer
dlls/mfplat/main.c | 179 +++++++++++++++++++++++++++++++++++++ dlls/mfplat/mfplat.spec | 2 +- dlls/mfplat/tests/mfplat.c | 39 ++++++++ include/mfapi.h | 1 + 4 files changed, 220 insertions(+), 1 deletion(-)
diff --git a/dlls/mfplat/main.c b/dlls/mfplat/main.c index 5a35350f5c5..2e5f17eb2af 100644 --- a/dlls/mfplat/main.c +++ b/dlls/mfplat/main.c @@ -14,11 +14,23 @@ * 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 + * + * + * MFllMulDiv implementation is derived from gstreamer utility functions code (gstutils.c), + * released under LGPL2. Full authors list follows. + * =================================================================================== + * Copyright (C) 1999,2000 Erik Walthinsen omega@cse.ogi.edu + * 2000 Wim Taymans wtay@chello.be + * 2002 Thomas Vander Stichele thomas@apestaart.org + * 2004 Wim Taymans wim@fluendo.com + * 2015 Jan Schmidt jan@centricular.com + * =================================================================================== */
#include <stdarg.h> #include <string.h> #include <math.h> +#include <limits.h>
#define COBJMACROS #define NONAMELESSUNION @@ -8928,3 +8940,170 @@ HRESULT WINAPI MFCreateDXGIDeviceManager(UINT *token, IMFDXGIDeviceManager **man
return S_OK; } + +static void llmult128(ULARGE_INTEGER *c1, ULARGE_INTEGER *c0, LONGLONG val, LONGLONG num) +{ + ULARGE_INTEGER a1, b0, v, n; + + v.QuadPart = llabs(val); + n.QuadPart = llabs(num); + + /* do 128 bits multiply + * nh nl + * * vh vl + * ---------- + * a0 = vl * nl + * a1 = vl * nh + * b0 = vh * nl + * b1 = + vh * nh + * ------------------- + * c1h c1l c0h c0l + * + * "a0" is optimized away, result is stored directly in c0. "b1" is + * optimized away, result is stored directly in c1. + */ + c0->QuadPart = (ULONGLONG)v.LowPart * n.LowPart; + a1.QuadPart = (ULONGLONG)v.LowPart * n.HighPart; + b0.QuadPart = (ULONGLONG)v.HighPart * n.LowPart; + + /* add the high word of a0 to the low words of a1 and b0 using c1 as + * scrach space to capture the carry. the low word of the result becomes + * the final high word of c0 */ + c1->QuadPart = (ULONGLONG)c0->HighPart + a1.LowPart + b0.LowPart; + c0->HighPart = c1->LowPart; + + /* add the carry from the result above (found in the high word of c1) and + * the high words of a1 and b0 to b1, the result is c1. */ + c1->QuadPart = (ULONGLONG)v.HighPart * n.HighPart + c1->HighPart + a1.HighPart + b0.HighPart; +} + +static ULONGLONG lldiv128(ULARGE_INTEGER c1, ULARGE_INTEGER c0, LONGLONG denom) +{ + ULARGE_INTEGER q1, q0, rhat; + ULARGE_INTEGER v, cmp1, cmp2; + unsigned int s = 0; + + v.QuadPart = llabs(denom); + + /* 64bit numerator */ + if (c1.QuadPart == 0) + return c0.QuadPart / v.QuadPart; + + /* 96bit numerator, 32bit denominator */ + if (v.HighPart == 0 && c1.HighPart == 0) + { + ULONGLONG low = c0.LowPart, high = c0.HighPart + ((ULONGLONG)c1.LowPart << 32); + low += (high % v.LowPart) << 32; + return ((high / v.LowPart) << 32) + (low / v.LowPart); + } + + /* 128bit numerator, 32bit denominator */ + if (v.HighPart == 0) + return UI64_MAX; + + /* count number of leading zeroes */ + BitScanReverse(&s, v.HighPart); + s = 31 - s; + + if (s) + { + /* normalize divisor and dividend */ + v.QuadPart <<= s; + c1.QuadPart = (c1.QuadPart << s) | (c0.HighPart >> (32 - s)); + c0.QuadPart <<= s; + } + + q1.QuadPart = c1.QuadPart / v.HighPart; + rhat.QuadPart = c1.QuadPart - q1.QuadPart * v.HighPart; + + cmp1.HighPart = rhat.LowPart; + cmp1.LowPart = c0.HighPart; + cmp2.QuadPart = q1.QuadPart * v.LowPart; + + while (q1.HighPart || cmp2.QuadPart > cmp1.QuadPart) + { + q1.QuadPart--; + rhat.QuadPart += v.HighPart; + if (rhat.HighPart) + break; + cmp1.HighPart = rhat.LowPart; + cmp2.QuadPart -= v.LowPart; + } + c1.HighPart = c1.LowPart; + c1.LowPart = c0.HighPart; + c1.QuadPart -= q1.QuadPart * v.QuadPart; + q0.QuadPart = c1.QuadPart / v.HighPart; + rhat.QuadPart = c1.QuadPart - q0.QuadPart * v.HighPart; + + cmp1.HighPart = rhat.LowPart; + cmp1.LowPart = c0.LowPart; + cmp2.QuadPart = q0.QuadPart * v.LowPart; + + while (q0.HighPart || cmp2.QuadPart > cmp1.QuadPart) + { + q0.QuadPart--; + rhat.QuadPart += v.HighPart; + if (rhat.HighPart) + break; + cmp1.HighPart = rhat.LowPart; + cmp2.QuadPart -= v.LowPart; + } + q0.HighPart += q1.LowPart; + + return q0.QuadPart; +} + +/*********************************************************************** + * MFllMulDiv (mfplat.@) + */ +LONGLONG WINAPI MFllMulDiv(LONGLONG val, LONGLONG num, LONGLONG denom, LONGLONG factor) +{ +#define LLOVERFLOW (sign ? I64_MIN : I64_MAX) + unsigned int sign, factor_sign, denom_sign; + ULARGE_INTEGER c1, c0; + ULONGLONG ret; + + TRACE("%s, %s, %s, %s.\n", wine_dbgstr_longlong(val), wine_dbgstr_longlong(num), + wine_dbgstr_longlong(denom), wine_dbgstr_longlong(factor)); + + /* compute 128-bit numerator product */ + llmult128(&c1, &c0, val, num); + + sign = (val < 0) ^ (num < 0); + factor_sign = factor < 0; + denom_sign = denom < 0; + + factor = llabs(factor); + if (sign == factor_sign) + { + if (UI64_MAX - c0.QuadPart < factor) + { + if (c1.QuadPart == UI64_MAX) return LLOVERFLOW; + c1.QuadPart++; + } + c0.QuadPart += factor; + } + else + { + if (c0.QuadPart >= factor) + c0.QuadPart -= factor; + else + { + if (c1.QuadPart) + c1.QuadPart--; + else + sign = !sign; + + c0.QuadPart = factor - c0.QuadPart; + } + } + + if (c1.QuadPart >= denom) return LLOVERFLOW; + + /* compute quotient, fits in 64 bits */ + ret = lldiv128(c1, c0, denom); + sign ^= denom_sign; + if (ret >= I64_MAX) return LLOVERFLOW; + return sign ? -(LONGLONG)ret : ret; +#undef LLOVERFLOW +} diff --git a/dlls/mfplat/mfplat.spec b/dlls/mfplat/mfplat.spec index cbda9404aa9..d8558cee0cb 100644 --- a/dlls/mfplat/mfplat.spec +++ b/dlls/mfplat/mfplat.spec @@ -176,6 +176,6 @@ @ stdcall MFUnwrapMediaType(ptr ptr) @ stub MFValidateMediaTypeSize @ stdcall MFWrapMediaType(ptr ptr ptr ptr) -@ stub MFllMulDiv +@ stdcall -ret64 MFllMulDiv(int64 int64 int64 int64) @ stub PropVariantFromStream @ stub PropVariantToStream diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index 89547be0a4f..b0c7298c28d 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -20,6 +20,7 @@
#include <stdarg.h> #include <string.h> +#include <limits.h>
#define COBJMACROS
@@ -7130,6 +7131,43 @@ static void test_MFLockSharedWorkQueue(void) ok(hr == S_OK, "Failed to shut down, hr %#x.\n", hr); }
+static void test_MFllMulDiv(void) +{ + /* (a * b + d) / c */ + static const struct muldivtest + { + LONGLONG a; + LONGLONG b; + LONGLONG c; + LONGLONG d; + LONGLONG result; + } + muldivtests[] = + { + { 0, 0, 0, 0, _I64_MAX }, + { 1000000, 1000000, 2, 0, 500000000000 }, + { _I64_MAX, 3, _I64_MAX, 0, 3 }, + { _I64_MAX, 3, _I64_MAX, 1, 3 }, + { -10000, 3, 100, 0, -300 }, + { 2, 0, 3, 5, 1 }, + { 2, 1, 1, -3, -1 }, + /* a * b product does not fit in uint64_t */ + { _I64_MAX, 4, 8, 0, _I64_MAX / 2 }, + /* Large a * b product, large denominator */ + { _I64_MAX, 4, 0x100000000, 0, 0x1ffffffff }, + }; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(muldivtests); ++i) + { + LONGLONG result; + + result = MFllMulDiv(muldivtests[i].a, muldivtests[i].b, muldivtests[i].c, muldivtests[i].d); + ok(result == muldivtests[i].result, "%u: unexpected result %s, expected %s.\n", i, + wine_dbgstr_longlong(result), wine_dbgstr_longlong(muldivtests[i].result)); + } +} + START_TEST(mfplat) { char **argv; @@ -7194,6 +7232,7 @@ START_TEST(mfplat) test_dxgi_surface_buffer(); test_sample_allocator(); test_MFMapDX9FormatToDXGIFormat(); + test_MFllMulDiv();
CoUninitialize(); } diff --git a/include/mfapi.h b/include/mfapi.h index c1cf766c005..986f5b9e3e4 100644 --- a/include/mfapi.h +++ b/include/mfapi.h @@ -552,6 +552,7 @@ HRESULT WINAPI MFTEnumEx(GUID category, UINT32 flags, const MFT_REGISTER_TYPE_IN HRESULT WINAPI MFInitAttributesFromBlob(IMFAttributes *attributes, const UINT8 *buffer, UINT size); HRESULT WINAPI MFInitMediaTypeFromWaveFormatEx(IMFMediaType *mediatype, const WAVEFORMATEX *format, UINT32 size); HRESULT WINAPI MFInvokeCallback(IMFAsyncResult *result); +LONGLONG WINAPI MFllMulDiv(LONGLONG val, LONGLONG num, LONGLONG denom, LONGLONG factor); HRESULT WINAPI MFLockPlatform(void); HRESULT WINAPI MFLockSharedWorkQueue(const WCHAR *name, LONG base_priority, DWORD *taskid, DWORD *queue); DXGI_FORMAT WINAPI MFMapDX9FormatToDXGIFormat(DWORD format);
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mfplat/main.c | 65 ++++++++++++++++++++++++++++++++++++++ dlls/mfplat/mfplat.spec | 2 ++ dlls/mfplat/tests/mfplat.c | 39 +++++++++++++++++++++++ 3 files changed, 106 insertions(+)
diff --git a/dlls/mfplat/main.c b/dlls/mfplat/main.c index 2e5f17eb2af..9d2842c7170 100644 --- a/dlls/mfplat/main.c +++ b/dlls/mfplat/main.c @@ -8564,6 +8564,16 @@ HRESULT WINAPI CreatePropertyStore(IPropertyStore **store) return S_OK; }
+struct shared_dxgi_manager +{ + IMFDXGIDeviceManager *manager; + unsigned int token; + unsigned int locks; +}; + +static struct shared_dxgi_manager shared_dm; +static CRITICAL_SECTION shared_dm_cs = { NULL, -1, 0, 0, 0, 0 }; + enum dxgi_device_handle_flags { DXGI_DEVICE_HANDLE_FLAG_OPEN = 0x1, @@ -8915,6 +8925,9 @@ static const IMFDXGIDeviceManagerVtbl dxgi_device_manager_vtbl = dxgi_device_manager_UnlockDevice, };
+/*********************************************************************** + * MFCreateDXGIDeviceManager (mfplat.@) + */ HRESULT WINAPI MFCreateDXGIDeviceManager(UINT *token, IMFDXGIDeviceManager **manager) { struct dxgi_device_manager *object; @@ -8941,6 +8954,58 @@ HRESULT WINAPI MFCreateDXGIDeviceManager(UINT *token, IMFDXGIDeviceManager **man return S_OK; }
+/*********************************************************************** + * MFLockDXGIDeviceManager (mfplat.@) + */ +HRESULT WINAPI MFLockDXGIDeviceManager(UINT *token, IMFDXGIDeviceManager **manager) +{ + HRESULT hr = S_OK; + + TRACE("%p, %p.\n", token, manager); + + EnterCriticalSection(&shared_dm_cs); + + if (!shared_dm.manager) + hr = MFCreateDXGIDeviceManager(&shared_dm.token, &shared_dm.manager); + + if (SUCCEEDED(hr)) + { + *manager = shared_dm.manager; + IMFDXGIDeviceManager_AddRef(*manager); + shared_dm.locks++; + + if (token) *token = shared_dm.token; + } + + LeaveCriticalSection(&shared_dm_cs); + + return hr; +} + +/*********************************************************************** + * MFUnlockDXGIDeviceManager (mfplat.@) + */ +HRESULT WINAPI MFUnlockDXGIDeviceManager(void) +{ + TRACE("\n"); + + EnterCriticalSection(&shared_dm_cs); + + if (shared_dm.manager) + { + IMFDXGIDeviceManager_Release(shared_dm.manager); + if (!--shared_dm.locks) + { + shared_dm.manager = NULL; + shared_dm.token = 0; + } + } + + LeaveCriticalSection(&shared_dm_cs); + + return S_OK; +} + static void llmult128(ULARGE_INTEGER *c1, ULARGE_INTEGER *c0, LONGLONG val, LONGLONG num) { ULARGE_INTEGER a1, b0, v, n; diff --git a/dlls/mfplat/mfplat.spec b/dlls/mfplat/mfplat.spec index d8558cee0cb..e8dc01c200c 100644 --- a/dlls/mfplat/mfplat.spec +++ b/dlls/mfplat/mfplat.spec @@ -131,6 +131,7 @@ @ stdcall MFInvokeCallback(ptr) @ stub MFJoinIoPort @ stdcall MFJoinWorkQueue(long long ptr) rtworkq.RtwqJoinWorkQueue +@ stdcall MFLockDXGIDeviceManager(ptr ptr) @ stdcall MFLockPlatform() rtworkq.RtwqLockPlatform @ stdcall MFLockSharedWorkQueue(wstr long ptr ptr) rtworkq.RtwqLockSharedWorkQueue @ stdcall MFLockWorkQueue(long) rtworkq.RtwqLockWorkQueue @@ -171,6 +172,7 @@ @ stub MFTraceFuncEnter @ stub MFUnblockThread @ stdcall MFUnjoinWorkQueue(long long) rtworkq.RtwqUnjoinWorkQueue +@ stdcall MFUnlockDXGIDeviceManager() @ stdcall MFUnlockPlatform() rtworkq.RtwqUnlockPlatform @ stdcall MFUnlockWorkQueue(long) rtworkq.RtwqUnlockWorkQueue @ stdcall MFUnwrapMediaType(ptr ptr) diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index b0c7298c28d..65ab72bd57b 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -262,6 +262,8 @@ static HRESULT (WINAPI *pMFCreateDXGISurfaceBuffer)(REFIID riid, IUnknown *surfa IMFMediaBuffer **buffer); static HRESULT (WINAPI *pMFCreateVideoMediaTypeFromSubtype)(const GUID *subtype, IMFVideoMediaType **media_type); static HRESULT (WINAPI *pMFLockSharedWorkQueue)(const WCHAR *name, LONG base_priority, DWORD *taskid, DWORD *queue); +static HRESULT (WINAPI *pMFLockDXGIDeviceManager)(UINT *token, IMFDXGIDeviceManager **manager); +static HRESULT (WINAPI *pMFUnlockDXGIDeviceManager)(void);
static HWND create_window(void) { @@ -935,6 +937,7 @@ static void init_functions(void) X(MFCreateVideoSampleAllocatorEx); X(MFGetPlaneSize); X(MFGetStrideForBitmapInfoHeader); + X(MFLockDXGIDeviceManager); X(MFLockSharedWorkQueue); X(MFMapDX9FormatToDXGIFormat); X(MFMapDXGIFormatToDX9Format); @@ -947,6 +950,7 @@ static void init_functions(void) X(MFTRegisterLocalByCLSID); X(MFTUnregisterLocal); X(MFTUnregisterLocalByCLSID); + X(MFUnlockDXGIDeviceManager);
if ((mod = LoadLibraryA("d3d11.dll"))) { @@ -7168,6 +7172,40 @@ static void test_MFllMulDiv(void) } }
+static void test_shared_dxgi_device_manager(void) +{ + IMFDXGIDeviceManager *manager; + HRESULT hr; + UINT token; + + if (!pMFLockDXGIDeviceManager) + { + win_skip("Shared DXGI device manager is not supported.\n"); + return; + } + + hr = pMFUnlockDXGIDeviceManager(); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + + manager = NULL; + hr = pMFLockDXGIDeviceManager(NULL, &manager); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + ok(!!manager, "Unexpected instance.\n"); + + hr = pMFLockDXGIDeviceManager(&token, &manager); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + + EXPECT_REF(manager, 3); + + hr = pMFUnlockDXGIDeviceManager(); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + + EXPECT_REF(manager, 2); + + hr = pMFUnlockDXGIDeviceManager(); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); +} + START_TEST(mfplat) { char **argv; @@ -7233,6 +7271,7 @@ START_TEST(mfplat) test_sample_allocator(); test_MFMapDX9FormatToDXGIFormat(); test_MFllMulDiv(); + test_shared_dxgi_device_manager();
CoUninitialize(); }