c# opengl nvidia optimus

c# - Forzar la representación acelerada por hardware



opengl nvidia (4)

Tengo una biblioteca OpenGL escrita en c ++ que se utiliza desde una aplicación C # utilizando adaptadores C ++ / CLI. Mi problema es que si la aplicación se usa en computadoras portátiles con tecnología Nvidia Optimus, la aplicación no utilizará la aceleración de hardware y fallará.

He intentado utilizar la información que se encuentra en el documento de Nvidias http://developer.download.nvidia.com/devzone/devcenter/gamegraphics/files/OptimusRenderingPolicies.pdf sobre cómo vincular libs a mi C ++ - dll y exportar NvOptimusEnablement de mi biblioteca OpenGL pero eso falla Supongo que tengo que hacer algo con el .exe no con los .dlls vinculados al .exe

Para nosotros no es una buena opción usar perfiles, ya que debemos asegurarnos de que se use el hardware nvidia.

¿Hay alguna forma en que una aplicación C # pueda obligar a Optimus a usar el chipset Nvidia en lugar del chipset Intel integrado?


Si su software falla en Intel, no podrá ejecutarlo en el 50% de las computadoras portátiles. Entonces sugiero arreglar esto en su lugar.

En vez de decirlo, puedes crear perfiles a la perfección por código. Solo usa NvAPI . Este código hace exactamente esto, pero tenga cuidado, probablemente no debería meterse con el perfil global y crear el suyo en su lugar:

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;

Al inicio, prueba si tu perfil existe. Si no, créelo (y probablemente también deba reiniciarse). NvAPI es una lib estática y devolverá graciosamente un código de error en un hardware que no sea de NVIDIA, por lo que puede enviarlo con seguridad.

EDITAR: Parece que hay una manera más fácil. Del código fuente 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;


Del documento parece ser bastante simple. Le dan múltiples opciones de cómo hacer eso. Desafortunadamente, el exe necesita hacer eso, no el dll. De acuerdo con este tutorial , podría ser posible hacer algo como:

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

Esto luego debe incluirse en su interfaz de biblioteca C ++ para que cualquier aplicación C # que la use se vea forzada a exportar esto. Alternativamente, puedes intentar enlazar con nvapi.dll :

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

De acuerdo con el documento, esto también debería ser suficiente para reconocer su aplicación como habilitada para NV. La función importada ni siquiera debería ser requerida para ser llamada.


Probé ambas opciones del cerdo , pero ninguno funcionó solo. Descubrí que necesitaba intentar llamar a la función importada.

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

luego en el inicio de mi aplicación:

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

En un sistema nVidia Optimus, System.EntryPointNotFoundException una excepción System.EntryPointNotFoundException , pero aún funciona para que la aplicación use el hardware nVidia. Probado en un sistema con una tarjeta ATI, obtuve System.DllNotFoundException . De cualquier manera, intentar llamar esto e ignorar cualquier excepción aquí parece funcionar bien.


Una solución de trabajo. En realidad, todos los que ya he mencionado, pero me llevó un tiempo entender cómo hacerlo funcionar ...

[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 }

Importante : llame a InitializeDedicatedGraphics() antes de crear cualquier ventana