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 --- dlls/mfplat/main.c | 168 +++++++++++++++++++++++++++++++++++++ dlls/mfplat/mfplat.spec | 2 +- dlls/mfplat/tests/mfplat.c | 39 +++++++++ include/mfapi.h | 1 + 4 files changed, 209 insertions(+), 1 deletion(-)
diff --git a/dlls/mfplat/main.c b/dlls/mfplat/main.c index 5a35350f5c5..f27cc272c30 100644 --- a/dlls/mfplat/main.c +++ b/dlls/mfplat/main.c @@ -19,6 +19,7 @@ #include <stdarg.h> #include <string.h> #include <math.h> +#include <limits.h>
#define COBJMACROS #define NONAMELESSUNION @@ -8928,3 +8929,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 f27cc272c30..1ce8673b14e 100644 --- a/dlls/mfplat/main.c +++ b/dlls/mfplat/main.c @@ -8553,6 +8553,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, @@ -8904,6 +8914,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; @@ -8930,6 +8943,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(); }
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=89778
Your paranoid android.
=== w7u_adm (32 bit report) ===
mfplat: 0a1c:mfplat: unhandled exception c0000005 at 6D5D7D1C
On 5/3/21 12:06 PM, Marvin wrote:
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=89778
Your paranoid android.
=== w7u_adm (32 bit report) ===
mfplat: 0a1c:mfplat: unhandled exception c0000005 at 6D5D7D1C
That's in earlier source tests, I don't think it's related.
On 5/3/21 3:53 AM, Nikolay Sivov wrote:
Implementation is based on similar gstreamer utility code, adjusted for signed arguments, and with platform optimizations removed.
Should it contain copyright information in that case?
On 5/3/21 7:00 PM, Zebediah Figura (she/her) wrote:
On 5/3/21 3:53 AM, Nikolay Sivov wrote:
Implementation is based on similar gstreamer utility code, adjusted for signed arguments, and with platform optimizations removed.
Should it contain copyright information in that case?
It probably should, sent v2 with that. Thanks.