Подтвердить что ты не робот

Драйвер графической подсистемы Nvidia, вызывающий заметное застревание кадров

Хорошо. Я изучил эту проблему в течение нескольких дней, поэтому позвольте мне перейти то, что я знаю до сих пор, что заставляет меня думать, что это может быть проблемой с драйвером NVidia, а не с моим кодом.

В основном моя игра начинает заикаться после запуска нескольких секунд (случайные кадры занимают 70 мс вместо 16 мс, на регулярной основе). Это ТОЛЬКО случается, если на панели управления Nvidia (последние версии драйверов, окна 10) включен параметр "Резьбовая оптимизация". К сожалению, этот параметр включен по умолчанию, и мне бы не хотелось, чтобы люди настраивали свои настройки, чтобы получить приятный опыт.

  • В игре нет интенсивного процессора или GPU (2 мс без использования vsync). Он не вызывает какие-либо функции openGL, которые должны синхронизировать данные, а также не передавать потоки каких-либо буферов или считывать данные с графического процессора или чего-либо еще. О простейшем возможном рендерере.

  • Проблема всегда заключалась в том, что она только начинала становиться заметной, когда я добавлял в fmod для аудио. fmod не является причиной этого (более поздний пост)

  • Попытка отладить проблему с NVidia Nsight заставила проблему уйти. "Начать сбор данных" мгновенно заставляет заикаться. Здесь нет кубиков.

  • В профилировщике много времени процессора затрачивается на "nvoglv32.dll". Этот процесс запускается только в том случае, если включена оптимизация потоков. Я подозреваю, что это проблема синхронизации, поэтому я отлаживаю визуальную студию Concurrency Viewer.

  • A-HA! vsyncs

  • Исследуя эти блоки времени процессора в потоке nvidia, самая ранняя именованная функция, которую я могу получить в своем стеке, - " CreateToolhelp32Snapshot" за которым следует много времени, проведенного в Thread32Next. Я заметил Thread32Next в профилировщике, когда смотрел на процессор раньше, так что это похоже на то, что я на правильном пути.

  • Итак, похоже, что периодически драйвер nvidia захватывает моментальный снимок всего процесса по какой-то причине? Что может быть причиной, почему она это делает, и как мне ее остановить?

  • Также это объясняет, почему проблема стала заметной, когда я добавил в fmod, потому что ее захватывающая информация для всех потоков процессов, а fmod порождает много потоков.

  • Любая помощь? Это просто ошибка в драйвере nvidia или есть что-то, что я могу сделать, чтобы исправить это, говоря другим людям, чтобы отключить Threaded "Оптимизация"?

edit 1: та же проблема возникает и с текущими драйверами nvidia на моем ноутбуке. Так что я не сумасшедший

edit 2: эта же проблема возникает в версии 362 (предыдущая основная версия) драйвера nvidia

4b9b3361

Ответ 1

... или есть что-то Я могу сделать, чтобы исправить это, говоря другим людям, чтобы отключить Threaded "Оптимизация"?

Да.

Вы можете создать собственный "профиль приложения" для своей игры, используя NVAPI и отключить в нем настройку "Threaded Optimization".

Существует файл

#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;
}

Ответ 2

Спасибо за ответ 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);
}

На основе двух интерфейсов (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);
};

Ответ 3

Ненависть заявить очевидное, но я чувствую, что нужно сказать.

Решаемая оптимизация известна тем, что заставляет заикаться во многих играх, даже тех, которые используют многопоточность. Если ваше приложение не работает с параметром оптимизации потоковой передачи, единственным логическим ответом является указание пользователям отключить его. Если пользователи упрямы и не хотят этого делать, это их вина.

Единственная ошибка в последней памяти, о которой я могу думать, это то, что более старые версии драйвера nvidia привели к сбоям приложений с оптимизацией потоков в Wine, но это не связано с проблемой заикания, которую вы описываете.