msvcrt: Save and restore rounding mode in fegetenv() / fesetenv().

Fixes Serious Sam 4 flickering in Vulkan mode.

Signed-off-by: Paul Gofman <pgofman@codeweavers.com>
Signed-off-by: Piotr Caban <piotr@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Paul Gofman 2020-11-17 23:07:49 +03:00 committed by Alexandre Julliard
parent c72e1b096d
commit 3acd68b241
2 changed files with 35 additions and 7 deletions

View File

@ -23,6 +23,7 @@
#include <stdio.h>
#include <float.h>
#include <math.h>
#include <fenv.h>
#include <limits.h>
#include <wctype.h>
@ -183,6 +184,9 @@ static float (CDECL *p_wcstof)(const wchar_t*, wchar_t**);
static double (CDECL *p_remainder)(double, double);
static int* (CDECL *p_errno)(void);
static int (CDECL *p_fegetenv)(fenv_t*);
static int (CDECL *p_fesetenv)(const fenv_t*);
static int (CDECL *p_fegetround)(void);
static int (CDECL *p_fesetround)(int);
static int (CDECL *p__clearfp)(void);
static _locale_t (__cdecl *p_wcreate_locale)(int, const wchar_t *);
static void (__cdecl *p_free_locale)(_locale_t);
@ -254,6 +258,10 @@ static BOOL init(void)
p_free_locale = (void*)GetProcAddress(module, "_free_locale");
SET(p_wctype, "wctype");
SET(p_fegetenv, "fegetenv");
SET(p_fesetenv, "fesetenv");
SET(p_fegetround, "fegetround");
SET(p_fesetround, "fesetround");
SET(p__clearfp, "_clearfp");
SET(p_vsscanf, "vsscanf");
SET(p__Cbuild, "_Cbuild");
@ -780,18 +788,27 @@ static void test_critical_section(void)
call_func1(p_critical_section_dtor, &cs);
}
static void test_fegetenv(void)
static void test_feenv(void)
{
fenv_t env, env2;
int ret;
fenv_t env;
p__clearfp();
ret = p_fegetenv(&env);
ok(!ret, "fegetenv returned %x\n", ret);
p_fesetround(FE_UPWARD);
ok(env.control == (_EM_INEXACT|_EM_UNDERFLOW|_EM_OVERFLOW|_EM_ZERODIVIDE|_EM_INVALID),
"env.control = %x\n", env.control);
ok(!env.status, "env.status = %x\n", env.status);
ret = p_fegetenv(&env2);
ok(!ret, "fegetenv returned %x\n", ret);
ok(env2.control == (_EM_INEXACT|_EM_UNDERFLOW|_EM_OVERFLOW|_EM_ZERODIVIDE|_EM_INVALID | FE_UPWARD),
"env2.control = %x\n", env2.control);
ret = p_fesetenv(&env);
ok(!ret, "fesetenv returned %x\n", ret);
ret = p_fegetround();
ok(ret == FE_TONEAREST, "Got unexpected round mode %#x.\n", ret);
}
static void test__wcreate_locale(void)
@ -1110,7 +1127,7 @@ START_TEST(msvcr120)
test__strtof();
test_remainder();
test_critical_section();
test_fegetenv();
test_feenv();
test__wcreate_locale();
test__Condition_variable();
test_wctype();

View File

@ -2074,7 +2074,7 @@ int CDECL _controlfp_s(unsigned int *cur, unsigned int newval, unsigned int mask
int CDECL MSVCRT_fegetenv(MSVCRT_fenv_t *env)
{
env->control = _controlfp(0, 0) & (MSVCRT__EM_INEXACT | MSVCRT__EM_UNDERFLOW |
MSVCRT__EM_OVERFLOW | MSVCRT__EM_ZERODIVIDE | MSVCRT__EM_INVALID);
MSVCRT__EM_OVERFLOW | MSVCRT__EM_ZERODIVIDE | MSVCRT__EM_INVALID | MSVCRT__RC_CHOP);
env->status = _statusfp();
return 0;
}
@ -2193,12 +2193,18 @@ int CDECL MSVCRT_fesetenv(const MSVCRT_fenv_t *env)
__asm__ __volatile__( "fnstenv %0" : "=m" (fenv) );
fenv.control_word &= ~0x3d;
fenv.control_word &= ~0xc3d;
if (env->control & MSVCRT__EM_INVALID) fenv.control_word |= 0x1;
if (env->control & MSVCRT__EM_ZERODIVIDE) fenv.control_word |= 0x4;
if (env->control & MSVCRT__EM_OVERFLOW) fenv.control_word |= 0x8;
if (env->control & MSVCRT__EM_UNDERFLOW) fenv.control_word |= 0x10;
if (env->control & MSVCRT__EM_INEXACT) fenv.control_word |= 0x20;
switch (env->control & MSVCRT__MCW_RC)
{
case MSVCRT__RC_UP|MSVCRT__RC_DOWN: fenv.control_word |= 0xc00; break;
case MSVCRT__RC_UP: fenv.control_word |= 0x800; break;
case MSVCRT__RC_DOWN: fenv.control_word |= 0x400; break;
}
fenv.status_word &= ~0x3d;
if (env->status & MSVCRT__SW_INVALID) fenv.status_word |= 0x1;
@ -2213,14 +2219,19 @@ int CDECL MSVCRT_fesetenv(const MSVCRT_fenv_t *env)
if (sse2_supported)
{
DWORD fpword;
__asm__ __volatile__( "stmxcsr %0" : "=m" (fpword) );
fpword &= ~0x1e80;
fpword &= ~0x7e80;
if (env->control & MSVCRT__EM_INVALID) fpword |= 0x80;
if (env->control & MSVCRT__EM_ZERODIVIDE) fpword |= 0x200;
if (env->control & MSVCRT__EM_OVERFLOW) fpword |= 0x400;
if (env->control & MSVCRT__EM_UNDERFLOW) fpword |= 0x800;
if (env->control & MSVCRT__EM_INEXACT) fpword |= 0x1000;
switch (env->control & MSVCRT__MCW_RC)
{
case MSVCRT__RC_CHOP: fpword |= 0x6000; break;
case MSVCRT__RC_UP: fpword |= 0x4000; break;
case MSVCRT__RC_DOWN: fpword |= 0x2000; break;
}
__asm__ __volatile__( "ldmxcsr %0" : : "m" (fpword) );
}