15

我有一个用 C++ 编写的 OpenGL 库,它在使用 C++/CLI 适配器的 C# 应用程序中使用。我的问题是,如果在采用 Nvidia Optimus 技术的笔记本电脑上使用该应用程序,该应用程序将不会使用硬件加速并且会失败。

我曾尝试使用 Nvidias 文档http://developer.download.nvidia.com/devzone/devcenter/gamegraphics/files/OptimusRenderingPolicies.pdf中的信息 来了解将库链接到我的 C++-dll 并从我的 OpenGL 库中导出 NvOptimusEnablement但这失败了。我想我必须对 .exe 做一些事情,而不是对链接到 .exe 的 .dll 做一些事情

对我们来说,使用配置文件不是一个好的选择,因为我们需要确保使用 nvidia 硬件。

C# 应用程序是否可以通过某种方式强制 Optimus 使用 Nvidia 芯片组而不是集成的 Intel 芯片组?

4

5 回答 5

6

一个可行的解决方案。实际上所有这些都已经提到了,但是我花了一些时间来理解如何让它工作......

[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()在创建任何窗口之前调用

于 2017-10-14T20:37:36.227 回答
3

我从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. 无论哪种方式,尝试调用它并忽略此处的任何异常似乎都可以正常工作。

于 2015-07-31T12:15:30.470 回答
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 是一个静态库,会在非 NVIDIA 硬件上优雅地返回错误代码,因此您可以安全地使用它。

编辑:看起来有更简单的方法。来自 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;
于 2013-06-24T13:46:44.547 回答
1

我的解决方案适用于 NVidia 和 AMD,没有 DllExport(没有 NuGet 包 UnmanagedExports),基于导出的函数NvOptimusEnablementAmdPowerXpressRequestHighPerformance

在 VS2019 上,上述 UnmanagedExports 包似乎无法正常工作。我找不到另一个提供 DllExport 功能的 .NET 框架的工作包。这就是为什么我在在 Visual Studio和IL 支持上编写 IL 代码的帮助下,使用直接 MSIL 代码开发了自己的解决方案。

要遵循的步骤:

  1. 为您的 exe 选择 x86 或 x64 平台(您无法使用 AnyCPU 设置)
  2. 到 .csproj 文件(.csproj 用于 exe,而不用于任何 dll)添加以下部分,以便直接使用 MSIL 代码(您通常可以将其放在任何地方,即在 <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.目标” />)。对于 4.6.2 之后的 .net 框架,您可能需要更新 ildasm.exe 的路径:

    <PropertyGroup>
      <CoreCompileDependsOn>
        HideILFromCoreCompile;
        $(CoreCompileDependsOn);
      </CoreCompileDependsOn>
      <CompileDependsOn>
        HideILFromCompile;
        $(CompileDependsOn);
        InitializeIL;
        CoreDecompile;
        CoreCompileIL;
      </CompileDependsOn>
    </PropertyGroup>
    <Target Name="HideILFromCoreCompile">
      <ItemGroup>
        <Compile Remove="@(Compile)" Condition="'%(Extension)'=='.il'" />
      </ItemGroup>
    </Target>
    <Target Name="HideILFromCompile">
      <ItemGroup>
        <IL Include="@(Compile)" Condition="'%(Extension)'=='.il'" />
        <Compile Remove="@(Compile)" Condition="'%(Extension)'=='.il'" />
      </ItemGroup>
    </Target>
    <Target Name="InitializeIL">
      <PropertyGroup>
        <ILFile>@(IntermediateAssembly->'%(RootDir)%(Directory)%(Filename).il', ' ')</ILFile>
        <ILResourceFile>@(IntermediateAssembly->'%(RootDir)%(Directory)%(Filename).res', ' ')</ILResourceFile>
      </PropertyGroup>
    </Target>
    <Target Name="CoreDecompile" Inputs="@(IntermediateAssembly)" Outputs="$(ILFile)" Condition=" Exists ( @(IntermediateAssembly) ) ">
      <GetFrameworkSdkPath>
        <Output TaskParameter="Path" PropertyName="FrameworkSdkPath" />
      </GetFrameworkSdkPath>
      <PropertyGroup>
        <ILDasm>"$(FrameworkSdkPath)bin\ildasm.exe" /nobar /linenum /output:"$(ILFile)" @(IntermediateAssembly->'"%(FullPath)"', ' ')</ILDasm>
      </PropertyGroup>
      <PropertyGroup Condition=" Exists ( '$(FrameworkSdkPath)bin\NETFX 4.0 Tools\ildasm.exe' ) ">
        <ILDasm>"$(FrameworkSdkPath)bin\NETFX 4.0 Tools\ildasm.exe" /nobar /linenum /output:"$(ILFile)" @(IntermediateAssembly->'"%(FullPath)"', ' ')</ILDasm>
      </PropertyGroup>
      <PropertyGroup Condition=" Exists ( '$(FrameworkSdkPath)bin\NETFX 4.5.1 Tools\ildasm.exe' ) ">
        <ILDasm>"$(FrameworkSdkPath)bin\NETFX 4.5.1 Tools\ildasm.exe" /nobar /linenum /output:"$(ILFile)" @(IntermediateAssembly->'"%(FullPath)"', ' ')</ILDasm>
      </PropertyGroup>
      <PropertyGroup Condition=" Exists ( '$(FrameworkSdkPath)bin\NETFX 4.6 Tools\ildasm.exe' ) ">
        <ILDasm>"$(FrameworkSdkPath)bin\NETFX 4.6 Tools\ildasm.exe" /nobar /linenum /output:"$(ILFile)" @(IntermediateAssembly->'"%(FullPath)"', ' ')</ILDasm>
      </PropertyGroup>
      <PropertyGroup Condition=" Exists ( '$(FrameworkSdkPath)bin\NETFX 4.6.1 Tools\ildasm.exe' ) ">
        <ILDasm>"$(FrameworkSdkPath)bin\NETFX 4.6.1 Tools\ildasm.exe" /nobar /linenum /output:"$(ILFile)" @(IntermediateAssembly->'"%(FullPath)"', ' ')</ILDasm>
      </PropertyGroup>
      <PropertyGroup Condition=" Exists ( '$(FrameworkSdkPath)bin\NETFX 4.6.2 Tools\ildasm.exe' ) ">
        <ILDasm>"$(FrameworkSdkPath)bin\NETFX 4.6.2 Tools\ildasm.exe" /nobar /linenum /output:"$(ILFile)" @(IntermediateAssembly->'"%(FullPath)"', ' ')</ILDasm>
      </PropertyGroup>
      <Exec Command="$(ILDasm)" />
      <ItemGroup>
        <FileWrites Include="$(ILFile)" />
        <FileWrites Include="$(ILResourceFile)" />
      </ItemGroup>
      <PropertyGroup>
        <ILSource>$([System.IO.File]::ReadAllText($(ILFile)))</ILSource>
        <Replacement>// method ${method} forwardref removed for IL import</Replacement>
        <Pattern>\.method [^{}]+ cil managed forwardref[^}]+} // end of method (?&lt;method&gt;[^ \r\t\n]+)</Pattern>
        <ILSource>$([System.Text.RegularExpressions.Regex]::Replace($(ILSource), $(Pattern), $(Replacement)))</ILSource>
        <Pattern>\.method [^{}]+ cil managed[^\a]+"extern was not given a DllImport attribute"[^}]+} // end of method (?&lt;method&gt;[^ \r\t\n]+)</Pattern>
        <ILSource>$([System.Text.RegularExpressions.Regex]::Replace($(ILSource), $(Pattern), $(Replacement)))</ILSource>
      </PropertyGroup>
      <WriteLinesToFile File="$(ILFile)" Lines="$(ILSource)" Overwrite="true" />
      <PropertyGroup>
        <ILSource />
      </PropertyGroup>
      <Delete Files="@(IntermediateAssembly)" />
    </Target>
    <Target Name="CoreCompileIL" Inputs="@(IL)" Outputs="@(IntermediateAssembly)">
      <GetFrameworkPath>
        <Output TaskParameter="Path" PropertyName="FrameworkPath" />
      </GetFrameworkPath>
      <PropertyGroup>
        <ILAsm>"$(FrameworkPath)\ilasm.exe" /nologo /quiet /output:@(IntermediateAssembly->'"%(FullPath)"', ' ')</ILAsm>
      </PropertyGroup>
      <PropertyGroup Condition=" '$(FileAlignment)' != '' ">
        <ILAsm>$(ILAsm) /alignment=$(FileAlignment)</ILAsm>
      </PropertyGroup>
      <PropertyGroup Condition=" '$(BaseAddress)' != '' ">
        <ILAsm>$(ILAsm) /base=$(BaseAddress)</ILAsm>
      </PropertyGroup>
      <PropertyGroup Condition=" '$(OutputType)' == 'Library' ">
        <ILAsm>$(ILAsm) /dll</ILAsm>
      </PropertyGroup>
      <PropertyGroup Condition=" '$(DebugType)' == 'pdbonly' ">
        <ILAsm>$(ILAsm) /pdb</ILAsm>
      </PropertyGroup>
      <PropertyGroup Condition=" '$(DebugType)' == 'full' ">
        <ILAsm>$(ILAsm) /debug</ILAsm>
      </PropertyGroup>
      <PropertyGroup Condition=" '$(Optimize)' == 'true' ">
        <ILAsm>$(ILAsm) /optimize</ILAsm>
      </PropertyGroup>
      <PropertyGroup Condition=" '$(Platform)' == 'x64' ">
        <ILAsm>$(ILAsm) /pe64 /x64</ILAsm>
      </PropertyGroup>
      <PropertyGroup Condition=" '$(Platform)' == 'Itanium' ">
        <ILAsm>$(ILAsm) /pe64 /itanium</ILAsm>
      </PropertyGroup>
      <PropertyGroup Condition=" '$(AssemblyOriginatorKeyFile)' != '' ">
        <ILAsm>$(ILAsm) /key:"$(AssemblyOriginatorKeyFile)"</ILAsm>
      </PropertyGroup>
      <PropertyGroup Condition=" Exists ( '$(ILResourceFile)' ) ">
        <ILAsm>$(ILAsm) /resource:"$(ILResourceFile)"</ILAsm>
      </PropertyGroup>
      <PropertyGroup Condition=" Exists ( '$(ILFile)' ) ">
        <ILAsm>$(ILAsm) "$(ILFile)"</ILAsm>
      </PropertyGroup>
      <Exec Command="$(ILAsm) @(IL->'&quot;%(FullPath)&quot;', ' ')" />
      <ItemGroup>
        <FileWrites Include="@(IntermediateAssembly->'%(RootDir)%(Directory)DesignTimeResolveAssemblyReferencesInput.cache', ' ')" />
      </ItemGroup>
      <Touch Files="$(ILFile)" />
    </Target>
    
  3. 将扩展名为 .il 的文件添加到您的项目(例如 ForceDedicatedGraphicCard.il)
  4. 将下面的代码粘贴到此文件(而不是“WindowsApplication1”,您可以输入您的命名空间):

     .class public WindowsApplication1.ForceDedicatedGraphicCard
     {
         .method public static int32 NvOptimusEnablement() cil managed
         {
             .export [1]
             ldc.i4.1
             ret
         }
         .method public static uint32 AmdPowerXpressRequestHighPerformance() cil managed
         {
             .export [2]
             ldc.i4.1
             ret
         }
     }
    
  5. 构建项目
  6. 检查函数是否使用dumpbin.exe导出

    dumpbin.exe /exports your_project.exe
    
  7. 现在应该自动选择专用显卡。如果没有,请检查您是否有更新的驱动程序 - 旧驱动程序不支持这些导出的功能。
于 2020-05-13T14:05:27.633 回答
0

从文档来看,这似乎很简单。您有多种选择如何做到这一点。不幸的是,exe需要这样做,而不是dll。根据本教程,可能会执行以下操作:

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。甚至不需要调用导入的函数。

于 2014-05-20T11:26:06.023 回答