さて、私はこの問題を数日間調査しているので、これまで私が知っていることを調べてみましょう。私のコードではなく、NVidiaのドライバーに問題があります。
基本的に、私のゲームは数秒実行した後に途切れ始めます(ランダムなフレームは、規則的なパターンで、16ミリ秒ではなく70ミリ秒かかります)。これは、「スレッド最適化」と呼ばれる設定がNvidiaコントロールパネル(最新のドライバー、Windows 10)で有効になっている場合にのみ発生します。残念ながら、この設定はデフォルトで有効になっているので、楽しい体験を得るために設定を微調整する必要はありません。
ゲームはCPUまたはGPUを集中的に使用しません(vsyncをオンにしないフレームで2ms)。データを同期する必要のあるopenGL関数を呼び出したり、バッファをストリーミングしたり、GPUなどからデータを読み取ったりすることはありません。最も単純なレンダラーについて。
問題は常にそこにあり、オーディオ用のfmodを追加したときにのみ目立つようになりました。 fmodはこれの原因ではありません(投稿の後半で詳しく説明します)
NVidia Nsightで問題をデバッグしようとすると、問題は解決しました。 「データ収集を開始」すると、すぐに吃音がなくなります。ここにサイコロはありません。
プロファイラーでは、「nvoglv32.dll」で多くのCPU時間が費やされます。このプロセスは、スレッド最適化がオンになっている場合にのみ生成されます。同期の問題だと思うので、Visual Studio ConcurrencyViewerでデバッグします。
NvidiaスレッドでこれらのCPU時間のブロックを調査すると、コールスタックで取得できる最も古い名前付き関数は " CreateToolhelp32Snapshot "であり、その後に Thread32Next で多くの時間が費やされます。以前にCPU時間を調べたときに、プロファイラーのThread32Nextに気づいたので、これは正しい方向に進んでいるように見えます。
それで、nvidiaドライバーが何らかの理由でプロセス全体のスナップショットを定期的に取得しているように見えますか?おそらくその理由は何でしょうか、なぜこれを行っているのですか、そしてどうすればそれを止めることができますか?
また、これは、fmodを追加すると、すべてのプロセススレッドの情報を取得し、fmodが多数のスレッドを生成するため、問題が顕著になり始めた理由を説明しています。
何か助けはありますか?これはnvidiaのドライバーの単なるバグですか、それともスレッド化された「最適化」を無効にするように他の人に指示することを修正するために私ができることがありますか?
編集1:同じ問題が私のラップトップ上の現在のnvidiaドライバーでも発生します。だから私は狂っていない
編集2:同じ問題がnvidiaのドライバーのバージョン362(以前のメジャーバージョン)で発生します
...または、スレッド化された「最適化」を無効にするように他の人に指示することを修正するために私ができることがありますか?
はい。
[〜#〜] nvapi [〜#〜] を使用してゲームのカスタム「アプリケーションプロファイル」を作成し、「スレッド最適化」設定を無効にすることができます。
NVIDIAサイトには 。PDFファイル があり、NVAPIの使用に関するヘルプとコード例がいくつかあります。
すべてのNVIDIAプロファイルを表示および管理するには、NVIDIAInspectorを使用することをお勧めします。デフォルトのNVIDIAコントロールパネルよりも便利です。
また、「スレッド最適化」を無効にして「アプリケーションプロファイル」を作成するコード例を次に示します。
#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;
}
最初にsubGlitchの回答に感謝します。その提案に基づいて、より安全なものを作成します。これにより、スレッドの最適化をキャッシュして変更し、後で復元することができます。
コードは次のとおりです。
#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);
}
上記の2つのインターフェース(Get/Set)に基づいて、元の設定を保存し、アプリケーションの終了時に復元することができます。つまり、スレッドの最適化を無効にする設定は、独自のアプリケーションにのみ影響します。
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);
};
当たり前のことを言うのは嫌いですが、言わなければならない気がします。
スレッド化された最適化は、マルチスレッドを利用するゲームであっても、多くのゲームでスタッターを引き起こすことで有名です。アプリケーションがスレッド化された最適化設定でうまく機能しない限り、唯一の論理的な答えは、ユーザーにそれを無効にするように指示することです。ユーザーが頑固でそれをしたくない場合、それは彼らのせいです。
私が考えることができる最近のメモリの唯一のバグは、古いバージョンのnvidiaドライバーが原因で、Wineで実行されているスレッド最適化を使用するアプリケーションがクラッシュしたことですが、これはあなたが説明する途切れの問題とは関係ありません。