Принудительное рендеринг оборудования

У меня есть библиотека OpenGL, написанная на С++, которая используется из приложения С# с использованием адаптеров С++/CLI. Моя проблема заключается в том, что если приложение используется на ноутбуках с технологией Nvidia Optimus, приложение не будет использовать аппаратное ускорение и потерпеть неудачу.

Я попытался использовать информацию, содержащуюся в документе Nvidias http://developer.download.nvidia.com/devzone/devcenter/gamegraphics/files/OptimusRenderingPolicies.pdf о привязке библиотек к моей С++-dll и экспорте NvOptimusEnablement из моей OpenGL-библиотеки, но это не удается. Я думаю, что мне нужно что-то делать с .exe, а не с DLL файлами, связанными с .exe

Для нас неплохо использовать профили, так как мы должны убедиться, что используется оборудование nvidia.

Есть ли способ, которым приложение С# может заставить Optimus использовать набор микросхем Nvidia вместо интегрированного набора микросхем Intel?

Ответ 1

Я попробовал оба варианта свиньи, но не работал сам по себе. Я обнаружил, что мне нужно попытаться вызвать импортированную функцию.

using System.Runtime.InteropServices;

class OptimusEnabler
{
    [DllImport("nvapi.dll")]
    public static extern int NvAPI_Initialize();
};

затем в моем приложении:

try
{
    ///Ignore any System.EntryPointNotFoundException
    ///or System.DllNotFoundException exceptions here
    OptimusEnabler.NvAPI_Initialize();
}
catch
{ }

В системе nVidia Optimus я получаю System.EntryPointNotFoundException, но он все еще работает, чтобы приложение использовало аппаратное обеспечение nVidia. Протестировав систему с карточкой ATI, я получил System.DllNotFoundException. В любом случае, попытка вызвать это и игнорировать любое исключение здесь, похоже, работает нормально.

Ответ 2

Если ваше программное обеспечение терпит неудачу в Intel, то вы не сможете запустить его на 50% ноутбуков. Поэтому я предлагаю исправить это.

Чем сказано, вы можете создавать профили по коду. Просто используйте NvAPI. Этот код делает именно это, но будьте осторожны, вы, вероятно, не должны связываться с глобальным профилем и создавать свои собственные:

NvAPI_Status status;
// (0) Initialize NVAPI. This must be done first of all
status = NvAPI_Initialize();
if (status != NVAPI_OK) 
    PrintError(status, __LINE__);
// (1) Create the session handle to access driver settings
NvDRSSessionHandle hSession = 0;
status = NvAPI_DRS_CreateSession(&hSession);
if (status != NVAPI_OK) 
    PrintError(status, __LINE__);
// (2) load all the system settings into the session
status = NvAPI_DRS_LoadSettings(hSession);
if (status != NVAPI_OK) 
    PrintError(status, __LINE__);
// (3) Obtain the Base profile. Any setting needs to be inside
// a profile, putting a setting on the Base Profile enforces it
// for all the processes on the system
NvDRSProfileHandle hProfile = 0;
status = NvAPI_DRS_GetBaseProfile(hSession, &hProfile);
if (status != NVAPI_OK) 
    PrintError(status, __LINE__);


NVDRS_SETTING drsSetting1 = {0};
drsSetting1.version = NVDRS_SETTING_VER;
drsSetting1.settingId = SHIM_MCCOMPAT_ID;
drsSetting1.settingType = NVDRS_DWORD_TYPE;

NVDRS_SETTING drsSetting2 = {0};
drsSetting2.version = NVDRS_SETTING_VER;
drsSetting2.settingId = SHIM_RENDERING_MODE_ID;
drsSetting2.settingType = NVDRS_DWORD_TYPE;

NVDRS_SETTING drsSetting3 = {0};
drsSetting3.version = NVDRS_SETTING_VER;
drsSetting3.settingId = SHIM_RENDERING_OPTIONS_ID;
drsSetting3.settingType = NVDRS_DWORD_TYPE;

if( ForceIntegrated ){
    drsSetting1.u32CurrentValue = SHIM_MCCOMPAT_INTEGRATED;
    drsSetting2.u32CurrentValue = SHIM_RENDERING_MODE_INTEGRATED;
    drsSetting3.u32CurrentValue = SHIM_RENDERING_OPTIONS_DEFAULT_RENDERING_MODE | SHIM_RENDERING_OPTIONS_IGPU_TRANSCODING;
}else{
    drsSetting1.u32CurrentValue = SHIM_MCCOMPAT_ENABLE;
    drsSetting2.u32CurrentValue = SHIM_RENDERING_MODE_ENABLE;
    drsSetting3.u32CurrentValue = SHIM_RENDERING_OPTIONS_DEFAULT_RENDERING_MODE;
}



status = NvAPI_DRS_SetSetting(hSession, hProfile, &drsSetting1);
if (status != NVAPI_OK) 
    PrintError(status, __LINE__);

status = NvAPI_DRS_SetSetting(hSession, hProfile, &drsSetting2);
if (status != NVAPI_OK) 
    PrintError(status, __LINE__);

status = NvAPI_DRS_SetSetting(hSession, hProfile, &drsSetting3);
if (status != NVAPI_OK) 
    PrintError(status, __LINE__);

// (5) Now we apply (or save) our changes to the system
status = NvAPI_DRS_SaveSettings(hSession);
if (status != NVAPI_OK) 
    PrintError(status, __LINE__);
// (6) We clean up. This is analogous to doing a free()
NvAPI_DRS_DestroySession(hSession);
hSession = 0;

При запуске проверьте, существует ли ваш профиль. Если нет, создайте его (и вам, вероятно, придется перезапустить тоже). NvAPI является статическим lib и будет изящно возвращать код ошибки на оборудовании, отличном от NVIDIA, поэтому вы можете безопасно отправлять его.

EDIT: Похоже, что есть более простой способ. Из исходного кода GLFW 3:

// Applications exporting this symbol with this value will be automatically
// directed to the high-performance GPU on nVidia Optimus systems
//
GLFWAPI DWORD NvOptimusEnablement = 0x00000001;

Ответ 3

Из документа это выглядит довольно просто. Вам дается несколько вариантов, как это сделать. К сожалению, exe должен это сделать, а не dll. В соответствии с этот учебник можно сделать что-то вроде:

class OptimusEnabler {
    [DllExport("NvOptimusEnablement")]
    public static int NvOptimusEnablement = 1;
};

Затем это необходимо включить в ваш интерфейс библиотеки С++, чтобы любое приложение С#, которое его использовало, было вынуждено экспортировать это. В качестве альтернативы вы можете попробовать установить ссылку nvapi.dll:

class OptimusEnabler {
    [DllImport("nvapi.dll")]
    public static extern int NvAPI_Initialize();
};

В соответствии с документом этого также должно быть достаточно, чтобы распознать ваше приложение с поддержкой NV. Не нужно даже требовать, чтобы импортированная функция вызывалась.

Ответ 4

Рабочее решение. Фактически все, что уже упоминалось, но мне потребовалось время, чтобы понять, как заставить его работать...

[System.Runtime.InteropServices.DllImport("nvapi64.dll", EntryPoint = "fake")]
static extern int LoadNvApi64();

[System.Runtime.InteropServices.DllImport("nvapi.dll", EntryPoint = "fake")]
static extern int LoadNvApi32();

private void InitializeDedicatedGraphics()
{
    try
    {
        if (Environment.Is64BitProcess)
            LoadNvApi64();
        else
            LoadNvApi32();
    }
    catch { } // will always fail since 'fake' entry point doesn't exists
}

Важно - вызов InitializeDedicatedGraphics() до создания любого окна