Принудительное рендеринг оборудования
У меня есть библиотека 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()
до создания любого окна