Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nvidia graphics driver causing noticeable frame stuttering

Ok I've been researching this issue for a few days now so let me go over what I know so far which leads me to believe this might be an issue with NVidia's driver and not my code.

Basically my game starts stuttering after running a few seconds (random frames take 70ms instead of 16ms, on a regularish pattern). This ONLY happens if a setting called "Threaded Optimization" is enabled in the Nvidia control panel (latest drivers, windows 10). Unfortunately this setting is enabled by default and I'd rather not have to have people tweak their settings to get an enjoyable experience.

  • The game is not CPU or GPU intensive (2ms a frame without vsync on). It's not calling any openGL functions that need to synchronize data, and it's not streaming any buffers or reading data back from the GPU or anything. About the simplest possible renderer.

  • The problem was always there it just only started becoming noticeable when I added in fmod for audio. fmod is not the cause of this (more later in the post)

  • Trying to debug the problem with NVidia Nsight made the problem go away. "Start Collecting Data" instantly causes stuttering to go away. No dice here.

  • In the Profiler, a lot of cpu time is spent in "nvoglv32.dll". This process only spawns if Threaded Optimization is on. I suspect it's a synchronization issue then, so I debug with visual studio Concurrency Viewer.

  • A-HA! vsyncs

  • Investigating these blocks of CPU time on the nvidia thread, the earliest named function I can get in their callstack is "CreateToolhelp32Snapshot" followed by a lot of time spent in Thread32Next. I noticed Thread32Next in the profiler when looking at CPU times earlier so this does seem like I'm on the right track.

  • So it looks like periodically the nvidia driver is grabbing a snapshot of the whole process for some reason? What could possibly be the reason, why is it doing this, and how do I stop it?

  • Also this explains why the problem started becoming noticeable once I added in fmod, because its grabbing info for all the processes threads, and fmod spawns a lot of threads.

  • Any help? Is this just a bug in nvidia's driver or is there something I can do to fix it other telling people to disable Threaded "Optimization"?

edit 1: The same issue occurs with current nvidia drivers on my laptop too. So I'm not crazy

edit 2: the same issue occurs on version 362 (previous major version) of nvidia's driver

like image 374
TylerGlaiel Avatar asked Apr 30 '16 19:04

TylerGlaiel


People also ask

Can GPU drivers cause stuttering?

Fix 1 Update your graphics driverOutdated or faulty graphics drivers tend to cause a stuttering screen. Keeping your drivers up-to-date should always be your go-to option especially when you play graphics-intensive games. There are 2 options to update your graphics driver: manually and automatically.

Why is my PC skipping frames?

The most common reason for reduced FPS is graphics settings that create a larger workload than your hardware can handle. So how do you achieve better FPS? Getting a faster CPU, more RAM, or a newer graphics card is one solution.

What does stuttering in games look like?

Game stuttering is the irregular delays between GPU frames that can have a notable impact on gameplay, and depending on the intensity, even make a game unplayable. A game that stutters will feel slow, or laggy, and momentarily delay the player's actions.


Video Answer


2 Answers

... or is there something I can do to fix it other telling people to disable Threaded "Optimization"?

Yes.

You can create custom "Application Profile" for your game using NVAPI and disable "Threaded Optimization" setting in it.

There is a .PDF file on NVIDIA site with some help and code examples regarding NVAPI usage.

In order to see and manage all your NVIDIA profiles I recommend using NVIDIA Inspector. It is more convenient than the default NVIDIA Control Panel.

Also, here is my code example which creates "Application Profile" with "Threaded Optimization" disabled:

#include <stdlib.h> #include <stdio.h>  #include <nvapi.h> #include <NvApiDriverSettings.h>   const wchar_t*  profileName             = L"Your Profile Name"; const wchar_t*  appName                 = L"YourGame.exe"; const wchar_t*  appFriendlyName         = L"Your Game Casual Name"; const bool      threadedOptimization    = false;   void CheckError(NvAPI_Status status) {     if (status == NVAPI_OK)         return;      NvAPI_ShortString szDesc = {0};     NvAPI_GetErrorMessage(status, szDesc);     printf("NVAPI error: %s\n", szDesc);     exit(-1); }   void SetNVUstring(NvAPI_UnicodeString& nvStr, const wchar_t* wcStr) {     for (int i = 0; i < NVAPI_UNICODE_STRING_MAX; i++)         nvStr[i] = 0;      int i = 0;     while (wcStr[i] != 0)     {         nvStr[i] = wcStr[i];         i++;     } }   int main(int argc, char* argv[]) {     NvAPI_Status status;     NvDRSSessionHandle hSession;      status = NvAPI_Initialize();     CheckError(status);      status = NvAPI_DRS_CreateSession(&hSession);     CheckError(status);      status = NvAPI_DRS_LoadSettings(hSession);     CheckError(status);       // Fill Profile Info     NVDRS_PROFILE profileInfo;     profileInfo.version             = NVDRS_PROFILE_VER;     profileInfo.isPredefined        = 0;     SetNVUstring(profileInfo.profileName, profileName);      // Create Profile     NvDRSProfileHandle hProfile;     status = NvAPI_DRS_CreateProfile(hSession, &profileInfo, &hProfile);     CheckError(status);       // Fill Application Info     NVDRS_APPLICATION app;     app.version                     = NVDRS_APPLICATION_VER_V1;     app.isPredefined                = 0;     SetNVUstring(app.appName, appName);     SetNVUstring(app.userFriendlyName, appFriendlyName);     SetNVUstring(app.launcher, L"");     SetNVUstring(app.fileInFolder, L"");      // Create Application     status = NvAPI_DRS_CreateApplication(hSession, hProfile, &app);     CheckError(status);       // Fill Setting Info     NVDRS_SETTING setting;     setting.version                 = NVDRS_SETTING_VER;     setting.settingId               = OGL_THREAD_CONTROL_ID;     setting.settingType             = NVDRS_DWORD_TYPE;     setting.settingLocation         = NVDRS_CURRENT_PROFILE_LOCATION;     setting.isCurrentPredefined     = 0;     setting.isPredefinedValid       = 0;     setting.u32CurrentValue         = threadedOptimization ? OGL_THREAD_CONTROL_ENABLE : OGL_THREAD_CONTROL_DISABLE;     setting.u32PredefinedValue      = threadedOptimization ? OGL_THREAD_CONTROL_ENABLE : OGL_THREAD_CONTROL_DISABLE;      // Set Setting     status = NvAPI_DRS_SetSetting(hSession, hProfile, &setting);     CheckError(status);       // Apply (or save) our changes to the system     status = NvAPI_DRS_SaveSettings(hSession);     CheckError(status);       printf("Success.\n");      NvAPI_DRS_DestroySession(hSession);      return 0; } 
like image 132
subGlitch Avatar answered Sep 21 '22 19:09

subGlitch


Thanks for subGlitch's answer first, based on that proposal, I just make a safer one, which would enable you to cache and change the thread optimization, then restore it afterward.

Code is like below:

#include <stdlib.h> #include <stdio.h> #include <nvapi.h> #include <NvApiDriverSettings.h>  enum NvThreadOptimization {     NV_THREAD_OPTIMIZATION_AUTO         = 0,     NV_THREAD_OPTIMIZATION_ENABLE       = 1,     NV_THREAD_OPTIMIZATION_DISABLE      = 2,     NV_THREAD_OPTIMIZATION_NO_SUPPORT   = 3 };  bool NvAPI_OK_Verify(NvAPI_Status status) {     if (status == NVAPI_OK)         return true;      NvAPI_ShortString szDesc = {0};     NvAPI_GetErrorMessage(status, szDesc);      char szResult[255];     sprintf(szResult, "NVAPI error: %s\n\0", szDesc);     printf(szResult);      return false; }  NvThreadOptimization GetNVidiaThreadOptimization() {     NvAPI_Status status;     NvDRSSessionHandle hSession;     NvThreadOptimization threadOptimization = NV_THREAD_OPTIMIZATION_NO_SUPPORT;      status = NvAPI_Initialize();     if(!NvAPI_OK_Verify(status))         return threadOptimization;      status = NvAPI_DRS_CreateSession(&hSession);     if(!NvAPI_OK_Verify(status))         return threadOptimization;      status = NvAPI_DRS_LoadSettings(hSession);     if(!NvAPI_OK_Verify(status))     {         NvAPI_DRS_DestroySession(hSession);         return threadOptimization;;     }       NvDRSProfileHandle hProfile;     status = NvAPI_DRS_GetBaseProfile(hSession, &hProfile);     if(!NvAPI_OK_Verify(status))     {         NvAPI_DRS_DestroySession(hSession);         return threadOptimization;;     }      NVDRS_SETTING originalSetting;     originalSetting.version = NVDRS_SETTING_VER;     status = NvAPI_DRS_GetSetting(hSession, hProfile, OGL_THREAD_CONTROL_ID, &originalSetting);     if(NvAPI_OK_Verify(status))     {         threadOptimization = (NvThreadOptimization)originalSetting.u32CurrentValue;     }      NvAPI_DRS_DestroySession(hSession);      return threadOptimization; }  void SetNVidiaThreadOptimization(NvThreadOptimization threadedOptimization) {     NvAPI_Status status;     NvDRSSessionHandle hSession;      if(threadedOptimization == NV_THREAD_OPTIMIZATION_NO_SUPPORT)         return;      status = NvAPI_Initialize();     if(!NvAPI_OK_Verify(status))         return;      status = NvAPI_DRS_CreateSession(&hSession);     if(!NvAPI_OK_Verify(status))         return;      status = NvAPI_DRS_LoadSettings(hSession);     if(!NvAPI_OK_Verify(status))     {         NvAPI_DRS_DestroySession(hSession);         return;     }      NvDRSProfileHandle hProfile;     status = NvAPI_DRS_GetBaseProfile(hSession, &hProfile);     if(!NvAPI_OK_Verify(status))     {         NvAPI_DRS_DestroySession(hSession);         return;     }      NVDRS_SETTING setting;     setting.version                 = NVDRS_SETTING_VER;     setting.settingId               = OGL_THREAD_CONTROL_ID;     setting.settingType             = NVDRS_DWORD_TYPE;     setting.u32CurrentValue         = (EValues_OGL_THREAD_CONTROL)threadedOptimization;      status = NvAPI_DRS_SetSetting(hSession, hProfile, &setting);     if(!NvAPI_OK_Verify(status))     {         NvAPI_DRS_DestroySession(hSession);         return;     }      status = NvAPI_DRS_SaveSettings(hSession);     NvAPI_OK_Verify(status);      NvAPI_DRS_DestroySession(hSession); } 

Based on the two interfaces (Get/Set) above, you may well save the original setting and restore it when your application exits. That means your setting to disable thread optimization only impact your own application.

static NvThreadOptimization s_OriginalNVidiaThreadOptimization = NV_THREAD_OPTIMIZATION_NO_SUPPORT;  // Set s_OriginalNVidiaThreadOptimization =  GetNVidiaThreadOptimization(); if(    s_OriginalNVidiaThreadOptimization != NV_THREAD_OPTIMIZATION_NO_SUPPORT     && s_OriginalNVidiaThreadOptimization != NV_THREAD_OPTIMIZATION_DISABLE) {     SetNVidiaThreadOptimization(NV_THREAD_OPTIMIZATION_DISABLE); }  //Restore if(    s_OriginalNVidiaThreadOptimization != NV_THREAD_OPTIMIZATION_NO_SUPPORT     && s_OriginalNVidiaThreadOptimization != NV_THREAD_OPTIMIZATION_DISABLE) {     SetNVidiaThreadOptimization(s_OriginalNVidiaThreadOptimization); }; 
like image 25
Alex Avatar answered Sep 21 '22 19:09

Alex