2013-06-24 3 views
11

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

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

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

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

+1

Вы можете спросить * и * здесь : [http://gamedev.stackexchange.com/](http://gamedev.stackexchange.com/) – Tigran

+0

перейдите на панель nvidia и переключитесь на выделенный графический процессор –

+0

Использование панели nvidia - это не то, что я хочу сделать здесь. Я хотел бы сделать это в коде. – JohanR

ответ

1

Если ваше программное обеспечение не работает на 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 - это статическая библиотека и будет изящно возвращать код ошибки на оборудовании, отличном от 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; 
+0

У нас нет проблем с указанием того, что наше приложение не будет работать на определенном оборудовании, в данном случае графике Intel. Похоже, нам придется перейти с «настройками профиля» и использовать код NvAPI для создания профиля, либо непосредственно в приложении, либо как автономное приложение, которое будет запускаться из установщика при установке приложения. – JohanR

0

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

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

Это тогда должно быть включено в C++ библиотеки интерфейса, так что любое приложение C#, который использует его будет вынуждено экспортировать это. Кроме того, вы можете попробовать связыванние nvapi.dll:

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

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

+0

Для атрибута DllExport необходим пакет NuGet под названием UnmanagedExports. Это не поддерживается основной платформой .NET. – linleno

+0

Также появляется пакет UnmanagedExports NuGet поддерживает только методы экспорта, а не переменные, как показано в этом ответе. Попытка построить с помощью атрибута DllExport, показанного выше, не работает, поскольку он ожидает, что метод не является переменной. – Nerdtron

3

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

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. В любом случае, попытка вызвать это и игнорировать любое исключение здесь, похоже, работает нормально.

+1

Я не мог заставить это работать на моей машине. Проблема заключалась в том, что мое приложение работает как 64-битный процесс (я отключил «предпочитаю 32 бит» в настройках сборки приложения) и, следовательно, не смог найти и загрузить nvapi.dll. Мое решение создавало 2 отдельных класса, оба они обертывали метод NvAPI_Initialize, но из разных dll (nvapi.dll и nvapi64.dll) и выбирали во время выполнения, которое пытались использовать на основе свойства Environment.Is64BitProcess. Кажется, теперь все хорошо. – wiesener

1

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

[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() перед любым окно создается

Смежные вопросы