获取OpenGL版本号、双显卡切换技术代码实现(Optimus、NVAPI、CUDA)_opengl

“宠辱不惊,看庭前花开花落;去留无意,望天上云卷云舒。”

文章目录

  • ​​一、OpenGL支持库​​
  • ​​1、GLUT​​
  • ​​2、freeglut​​
  • ​​3、GLEW​​
  • ​​4、GLAD​​
  • ​​5、GLFW​​
  • ​​6、Mesa​​
  • ​​二、获取OpenGL版本号实现代码​​
  • ​​1、glut+glew​​
  • ​​2、win32+glew​​
  • ​​3、glfw+glad​​
  • ​​4、Android​​
  • ​​5、WebGL​​
  • ​​三、NVIDIA® Optimus™ 技术(双显卡切换技术)​​
  • ​​1、Optimus技术简介​​
  • ​​2、Optimus编程指南​​
  • ​​3、Application Profile Settings(上文第3种方法,NVAPI)​​
  • ​​4、Static Library Bindings(上文第4种方法,CUDA)​​
  • ​​5、Global Variable NvOptimusEnablement(上文第5种方法)​​
  • ​​6、设置垂直同步​​
  • ​​四、显卡驱动厂商​​
  • ​​1、NVIDIA​​
  • ​​2、AMD​​
  • ​​3、Intel​​
  • ​​五、显卡检测工具​​
  • ​​1、GPU Caps Viewer​​
  • ​​2、OpenGL Extensions Viewer​​
  • ​​3、dxdiag(系统自带)​​
  • ​​4、nvidia-smi(NVidia驱动)​​
  • ​​六、其他FAQ​​
  • ​​6.1 nvidiaopenglrdp.exe​​
  • ​​结语​​

一、OpenGL支持库

OpenGL依赖各平台提供用于渲染的context以及具体实现方式,而各平台提供的实现不尽相同。这些实现主要有:Windows平台下的WGL、Linux下的Mesa/GLX、Mac OS X下的Cocoa/NSGL,以及跨平台的GLUT、GLFW、SDL等等。

OpenGL函数库相关的API有核心库(gl),实用库(glu),辅助库(aux)、实用工具库(glut),窗口库(glx、agl、wgl)和扩展函数库等。

gl是核心,glu是对gl的部分封装。glx、agl、wgl 是针对不同窗口系统的函数。glut是为跨平台的OpenGL程序的工具包,比aux功能强大(aux很大程度上已经被glut库取代。)。扩展函数库是硬件厂商为实现硬件更新利用OpenGL的扩展机制开发的函数。

OpenGL的竞争对手是Direct3D;OpenGL对应的开源实现是mesa 3D。

获取OpenGL版本号、双显卡切换技术代码实现(Optimus、NVAPI、CUDA)_opengl_02

1、GLUT

官网地址:​ ​https://www.opengl.org/resources/libraries/glut/glut_downloads.php​​

获取OpenGL版本号、双显卡切换技术代码实现(Optimus、NVAPI、CUDA)_c++_03

GLUT是OpenGL工具库 OpenGL Utility Toolkit的缩写。
2000-03-22后,该库不再更新(最终版本3.7)。

GLUT (pronounced like the glut in gluttony) is the OpenGL Utility Toolkit, a window system independent toolkit for writing OpenGL programs. It implements a simple windowing application programming interface (API) for OpenGL. GLUT makes it considerably easier to learn about and explore OpenGL programming. GLUT provides a portable API so you can write a single OpenGL program that works on both Win32 PCs and X11 workstations.

2、freeglut

官网地址:​ ​http://freeglut.sourceforge.net/​​

GLUT或者FREEGLUT主要是1.0的基本函数功能;
2019-11-29后,该库不再更新(最终版本3.2.1)。

freeglut is a free-software/open-source alternative to the OpenGL Utility Toolkit (GLUT) library. GLUT was originally written by Mark Kilgard to support the sample programs in the second edition OpenGL ‘RedBook’. Since then, GLUT has been used in a wide variety of practical applications because it is simple, widely available and highly portable.

获取OpenGL版本号、双显卡切换技术代码实现(Optimus、NVAPI、CUDA)_c++_04

3、GLEW

官网地址:​ ​http://glew.sourceforge.net/​​

GLEW是使用OPENGL2.0之后的一个工具函数。
2017-7-31后,该库不再更新(最终版本2.1.0)。

The OpenGL Extension Wrangler Library (GLEW) is a cross-platform open-source C/C++ extension loading library. GLEW provides efficient run-time mechanisms for determining which OpenGL extensions are supported on the target platform. OpenGL core and extension functionality is exposed in a single header file. GLEW has been tested on a variety of operating systems, including Windows, Linux, Mac OS X, FreeBSD, Irix, and Solaris.

获取OpenGL版本号、双显卡切换技术代码实现(Optimus、NVAPI、CUDA)_opengl_05

4、GLAD

官网地址:
​​ ​https://glad.dav1d.de/​​​ ​https://github.com/Dav1dde/glad​​

GLAD是继GL3W,GLEW之后,当前最新的用来访问OpenGL规范接口的第三方库。官方网址为https://glad.dav1d.de/。

该库仍然在更新,加油!!!

获取OpenGL版本号、双显卡切换技术代码实现(Optimus、NVAPI、CUDA)_c++_06

5、GLFW

官网地址:​ ​https://www.glfw.org/​​

GLFW是一个轻量级的,开源的,跨平台的library。支持OpenGL及OpenGL ES,用来管理窗口,读取输入,处理事件等。

该库仍然在更新,加油!!!

GLFW is an Open Source, multi-platform library for OpenGL, OpenGL ES and Vulkan development on the desktop. It provides a simple API for creating windows, contexts and surfaces, receiving input and events.

GLFW is written in C and supports Windows, macOS, X11 and Wayland.

GLFW is licensed under the zlib/libpng license.

获取OpenGL版本号、双显卡切换技术代码实现(Optimus、NVAPI、CUDA)_opengl_07

6、Mesa

官网地址:​ ​https://mesa3d.org/​​

The Mesa 3D Graphics Library: Open source implementations of OpenGL, OpenGL ES, Vulkan, OpenCL, and more!

Mesa is primarily developed and used on Linux systems. But there’s also support for Windows, other flavors of Unix and other systems such as Haiku. We’re actively developing and maintaining several hardware and software drivers.

The primary API is OpenGL but there’s also support for OpenGL ES, Vulkan, EGL, OpenMAX, OpenCL, VDPAU, VA-API and XvMC.

Mesa3D provides an OpenGL implementation, just like NVidia’s, AMD’s, or Intel’s drivers provide an OpenGL implementation. They are all OpenGL implementations.

Mesa是Linux下的OpenGL实现。它提供了对AMD Radeon系列、Nvidia GPU、Intel i965, i945, i915以及VMWare虚拟GPU等多种硬件驱动的支持,同时也提供了对softpipe等多种软件驱动的支持。Mesa项目由Brian Paul于1993年8月创建,于1995年2月发布了第一个发行版,此后便受到越来越多的关注,如今Mesa已经是任何一个Linux版本首选的OpenGL实现。

GLX则是在Linux上用于提供GL与窗口交互、窗口管理等等的一组API。它的作用与Windows的WGL、Mac OS X的AGL以及针对OpenGL ES的EGL相似。在Linux上,窗口创建、管理等API遵循X Window接口,而GLX提供了OpenGL与X Window交互的办法。因此GLX也可以运用于其他使用X Window的平台,例如FreeBSD等。

获取OpenGL版本号、双显卡切换技术代码实现(Optimus、NVAPI、CUDA)_c++_08

二、获取OpenGL版本号实现代码

1、glut+glew

#include "GL\glew.h"
#include "GL\glut.h"
#include <Windows.h>
#pragma comment(lib,"glew32.lib")
#pragma comment(lib,"opengl32.lib")
#pragma comment(lib,"glu32.lib")

int main(int argc, char **argv)
{
glutInit(&argc, argv);
int wnd = glutCreateWindow("GLUT");

glewInit();
printf("OpenGL vendor string: %s\n", glGetString(GL_VENDOR));
printf("OpenGL renderer string: %s\n", glGetString(GL_RENDERER));
printf("OpenGL version string: %s\n", glGetString(GL_VERSION));

glutDestroyWindow(wnd);
return 0;
}

2、win32+glew

#include "GL\glew.h"
#include <Windows.h>

#pragma comment(lib,"opengl32.lib")
#pragma comment(lib,"glu32.lib")
#pragma comment(lib,"glew32.lib")

typedef struct GLContextStruct
{
HWND wnd;
HDC dc;
HGLRC rc;
} GLContext;

void InitContext(GLContext* ctx);
GLboolean CreateContext(GLContext* ctx);
void DestroyContext(GLContext* ctx);
void PrintExtensions(const char* s);

int main(int argc, char **argv)
{
GLenum err;
GLContext ctx;

/* ---------------------------------------------------------------------- */
/* create OpenGL rendering context */
InitContext(&ctx);
if (GL_TRUE == CreateContext(&ctx))
{
fprintf(stderr, "Error: CreateContext failed\n");
DestroyContext(&ctx);
return 1;
}

/* ---------------------------------------------------------------------- */
/* initialize GLEW */
glewExperimental = GL_TRUE;
err = glewInit();
if (GLEW_OK != err)
{
fprintf(stderr, "Error [main]: glewInit failed: %s\n", glewGetErrorString(err));
DestroyContext(&ctx);
return 1;
}

/* ---------------------------------------------------------------------- */
/* output header information */
/* OpenGL extensions */
printf("OpenGL vendor string: %s\n", glGetString(GL_VENDOR));
printf("OpenGL renderer string: %s\n", glGetString(GL_RENDERER));
printf("OpenGL version string: %s\n", glGetString(GL_VERSION));
printf("OpenGL extensions (GL_): \n");
PrintExtensions((const char*)glGetString(GL_EXTENSIONS));

/* GLU extensions */
printf("GLU version string: %s\n", gluGetString(GLU_VERSION));
printf("GLU extensions (GLU_): \n");
PrintExtensions((const char*)gluGetString(GLU_EXTENSIONS));

DestroyContext(&ctx);
return 0;
}


void InitContext(GLContext* ctx)
{
ctx->wnd = NULL;
ctx->dc = NULL;
ctx->rc = NULL;
}

GLboolean CreateContext(GLContext* ctx)
{
WNDCLASS wc;
PIXELFORMATDESCRIPTOR pfd;
/* check for input */
if (NULL == ctx) return GL_TRUE;
/* register window class */
ZeroMemory(&wc, sizeof(WNDCLASS));
wc.hInstance = GetModuleHandle(NULL);
wc.lpfnWndProc = DefWindowProc;
wc.lpszClassName = "GLTEST";
if (0 == RegisterClass(&wc)) return GL_TRUE;

/* create window */
ctx->wnd = CreateWindow("GLTEST", "GLTEST", 0, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL,
GetModuleHandle(NULL), NULL);
if (NULL == ctx->wnd) return GL_TRUE;
/* get the device context */
ctx->dc = GetDC(ctx->wnd);
if (NULL == ctx->dc) return GL_TRUE;
/* find pixel format */
ZeroMemory(&pfd, sizeof(PIXELFORMATDESCRIPTOR));
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL;
int visual = ChoosePixelFormat(ctx->dc, &pfd);
if (0 == visual) return GL_TRUE;

/* set the pixel format for the dc */
if (FALSE == SetPixelFormat(ctx->dc, visual, &pfd)) return GL_TRUE;
/* create rendering context */
ctx->rc = wglCreateContext(ctx->dc);
if (NULL == ctx->rc) return GL_TRUE;
if (FALSE == wglMakeCurrent(ctx->dc, ctx->rc)) return GL_TRUE;
return GL_FALSE;
}

void DestroyContext(GLContext* ctx)
{
if (NULL == ctx) return;
if (NULL != ctx->rc) wglMakeCurrent(NULL, NULL);
if (NULL != ctx->rc) wglDeleteContext(wglGetCurrentContext());
if (NULL != ctx->wnd && NULL != ctx->dc) ReleaseDC(ctx->wnd, ctx->dc);
if (NULL != ctx->wnd) DestroyWindow(ctx->wnd);
UnregisterClass("GLEW", GetModuleHandle(NULL));
}

/* do the magic to separate all extensions with comma's, except
for the last one that _may_ terminate in a space. */
void PrintExtensions(const char* s)
{
char t[80];
int i = 0;
char* p = 0;

t[79] = '\0';
while (*s)
{
t[i++] = *s;
if (*s == ' ')
{
if (*(s + 1) != '\0') {
t[i - 1] = ',';
t[i] = ' ';
p = &t[i++];
}
else /* zoinks! last one terminated in a space! */
{
t[i - 1] = '\0';
}
}
if (i > 80 - 5)
{
*p = t[i] = '\0';
printf(" %s\n", t);
p++;
i = (int)strlen(p);
#if defined(_MSC_VER) && (_MSC_VER >= 1400)
strcpy_s(t, sizeof(t), p);
#else
strcpy(t, p);
#endif
}
s++;
}
t[i] = '\0';
printf(" %s.\n", t);
}

3、glfw+glad

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#pragma comment(lib,"opengl32.lib")
#pragma comment(lib,"glu32.lib")
#pragma comment(lib,"glfw3.lib")

int main(int argc, char **argv)
{
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

glfwInit();
//glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
//glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
//glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "GLFW", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);

if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
printf("OpenGL vendor string: %s\n", glGetString(GL_VENDOR));
printf("OpenGL renderer string: %s\n", glGetString(GL_RENDERER));
printf("OpenGL version string: %s\n", glGetString(GL_VERSION));

glfwTerminate();
return 0;
}

4、Android

opengles 1.1: info.reqGlEsVersinotallow= 0x00010001
opengles 2.0: info.reqGlEsVersinotallow= 0x00020000

public class MainActivity extends AppCompatActivity {

private AppBarConfiguration appBarConfiguration;
private ActivityMainBinding binding;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
setSupportActionBar(binding.toolbar);

NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
appBarConfiguration = new AppBarConfiguration.Builder(navController.getGraph()).build();
NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);

binding.fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});

// 获取设备的OpenGL ES的版本号
ActivityManager am =(ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
ConfigurationInfo info = am.getDeviceConfigurationInfo();
String hex = Integer.toHexString (info.reqGlEsVersion);
System.out.println("OpenGL ES版本:" + hex + "(" + info.reqGlEsVersion+")");

}

5、WebGL

​ ​https://developer.mozilla.org/zh-CN/docs/Web/API/WebGL_API​​WebGL(Web图形库)是一个JavaScript API,可在任何兼容的Web浏览器中渲染高性能的交互式3D和2D图形,而无需使用插件。WebGL通过引入一个与OpenGL ES 2.0非常一致的API来做到这一点,该API可以在HTML5 元素中使用。 这种一致性使API可以利用用户设备提供的硬件图形加速。

目前支持 WebGL 的浏览器有:Firefox 4+, Google Chrome 9+, Opera 12+, Safari 5.1+, Internet Explorer 11+和Microsoft Edge build 10240+;然而, WebGL一些特性也需要用户的硬件设备支持。

WebGL 2 API引入了对大部分的OpenGL ES 3.0功能集的支持; 它是通过WebGL2RenderingContext界面提供的。

webgl_test1.html文件内容如下:

<!DOCTYPE html>
<html>
<head>
<meta lang="en">
<meta charset="UTF-8">
<title>WebGL Test</title>
<link href="style/style.css">
<script type="text/javascript" src="webgl_test1.js"></script>
</head>
<body>
<canvas id="canvas" width="200px" height="200px"></canvas>
</body>
</html>

webgl_test1.js文件内容如下:

/**
* Created by Tomcat on 2022-01-05
*/
window.onload = function () {

//获取canvas元素
var canvas = document.getElementById('canvas');

//获取绘制二维上下文
var gl = canvas.getContext('webgl');
if (!gl) {
console.log("Failed");
return;
}

//打印OpenGL信息
const gl1 = canvas.getContext("webgl");
if (!gl1) { console.log("WebGL1不支持!"); }

const gl2 = canvas.getContext("webgl2");
if (!gl2) { console.log("WebGL2不支持!"); }

var info1 = gl.getParameter(gl.VERSION);
if (info1) { console.log(info1); }

var info2 = gl.getParameter(gl.SHADING_LANGUAGE_VERSION);
if (info2) { console.log(info2); }

}

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>

<head>
<title>3D Simulation</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
#mycanvas,
#viewContainer,
body,
html {
width: 100%;
height: 100%
}

body,
html {
border: 0;
padding: 0;
margin: 0;
position: fixed
}
</style>
</head>

<body>
<div id="viewContainer">
<canvas id="mycanvas" width="1920" height="1080">Loading...</canvas>
</div>

<script type="text/javascript">
var gl;
var c = null;

function sleep(milliseconds) {
var start = new Date().getTime();
for (var i = 0; i < 1e7; i++) {
if ((new Date().getTime() - start) > milliseconds) {
break;
}
}
}

function initGL() {
if (!window.WebGLRenderingContext) {
alert("Your browser does not accept WebGL content");
window.location = "http://get.webgl.org";
} else {
try {
c = document.getElementById("mycanvas");
// c = = document.querySelector("#mycanvas");
gl = c.getContext("webgl");
if (!gl) {
gl = c.getContext("experimental-webgl");
}
if (!gl) {
alert("Could not initialise WebGL");
//alert("无法初始化WebGL,你的浏览器、操作系统或硬件等可能不支持WebGL。");
}
} catch (e) {
alert("Could not initialise WebGL: " + e);
}
}
}

window.onload = function () {
webGLStart();
}
function webGLStart() {
initGL();

// 使用完全不透明的黑色清除所有图像
gl.clearColor(0.0, 0.0, 0.0, 1.0);
// 用上面指定的颜色清除缓冲区
gl.clear(gl.COLOR_BUFFER_BIT);
}
</script>

</body>
</html>

三、NVIDIA® Optimus™ 技术(双显卡切换技术)

1、Optimus技术简介

官网地址:
​​ ​https://www.nvidia.cn/technologies/optimus/​​

NVIDIA Optimus 是NVIDIA 公司最新开发的双显卡,基于双显卡的智能切换技术,能够根据系统的负载自动切换独立显卡和集成显卡的运行。Optimus技术可完全自动地发挥作用,无需手动更改设置值,用户即可体验更长的电池续航时间以及惊人的视觉效果。

Optimus能够在后台运行,对用户运行的程序没有任何干扰,可无缝地计算出最大限度优化笔记本计算体验的方式。

Optimus(优驰)技术能对笔记本电脑进行智能优化,从而可在您需要时提供卓越的图形性能,同时还可延长电池续航时间,让您享受更长的欢乐时光。

Windows XP/Vista并不支持NVIDIA Optimus。也不能禁用集显。

获取OpenGL版本号、双显卡切换技术代码实现(Optimus、NVAPI、CUDA)_c++_09


获取OpenGL版本号、双显卡切换技术代码实现(Optimus、NVAPI、CUDA)_opengl_10

2、Optimus编程指南

官网相关资料地址:
​​ ​https://docs.nvidia.com/gameworks/content/technologies/desktop/optimus.htm​​​ ​https://developer.download.nvidia.cn/devzone/devcenter/gamegraphics/files/OptimusRenderingPolicies.pdf​​

(NVIDIA Optimus Programming Guide)Enabling NVIDIA High Performance Graphics Rendering on Optimus Systems。This feature is enabled using the following set of methods, listed in order from highest to lowest precedence:

  1. Forced Rendering Mode
  2. Right-Click Context Menu
  3. Application Profile Settings
  4. Static Library Bindings
  5. Global Variable NvOptimusEnablement (new in Driver Release 302)
  6. Global Profile Settings

总结:本文档描述了应用程序可用于启用和公开 NVIDIA 高性能图形处理器以在 Optimus 配置中进行渲染的6种方法。

3、Application Profile Settings(上文第3种方法,NVAPI)

上文第3种方法,通过程序集成nvapi库的功能进行设置,达到程序运行在独立高性能显卡上的效果。

NVAPI官网地址:​ ​https://developer.nvidia.com/nvapi​​

获取OpenGL版本号、双显卡切换技术代码实现(Optimus、NVAPI、CUDA)_opengl_11


获取OpenGL版本号、双显卡切换技术代码实现(Optimus、NVAPI、CUDA)_c++_12


NVAPI是NVIDIA®(英伟达™)的核心软件开发工具包,允许直接访问所有Windows平台上NVIDIA GPU(图形处理器)和驱动程序。NVAPI对那些超出人们所熟悉的DirectX和OpenGL图形API的操作类别提供支持。最初只开放给原始设备制造商和游戏开发工具,现在所有对在NVIDIA GPU上构建Windows应用程序感兴趣的开发人员都可以下载NVAPI。

官方nVidia API具有所需的NvAPI_DRS_ *函数。

这里有一个工作流: NvAPI_Initialize (如果失败,则表示用户没有nVidia GPU,这不是错误), NvAPI_DRS_CreateSession , NvAPI_DRS_LoadSettings , NvAPI_DRS_FindProfileByName 。

如果没有找到, NvAPI_DRS_CreateProfile和三个调用NvAPI_DRS_SetSetting ,设置三个DWORD属性:

  • SHIM_MCCOMPAT_ID = SHIM_MCCOMPAT_ENABLE
  • SHIM_RENDERING_MODE_ID = SHIM_RENDERING_MODE_ENABLE
  • SHIM_RENDERING_OPTIONS_ID = SHIM_RENDERING_OPTIONS_DEFAULT_RENDERING_MODE

然后,调用NvAPI_DRS_GetProfileInfo获取应用程序的数量,然后调用NvAPI_DRS_GetProfileInfo ,然后搜索EXE路径。

卸载时,通过调用NvAPI_DRS_FindProfileByName ,然后调用NvAPI_DRS_FindProfileByName删除完整的配置文件。

最后,调用NvAPI_DRS_SaveSettings 。

//NVAPI R470, 2021/07/13
//VC++2017
//Create by tomcat on 2022-01-09
#include "nvapi/nvapi.h"
#include "nvapi/NvApiDriverSettings.h"
int addExeToNvidiaGpu()
{
//
wchar_t szProfileName[256] = { 0 };
wchar_t szAppName[256] = { 0 };
GetModuleFileNameW(NULL, szAppName, 256);
wchar_t* pFileName = wcsrchr(szAppName, '\\');
if (pFileName) pFileName++;
if (pFileName) wcscpy(szProfileName, pFileName);

//
NvAPI_Status status = NVAPI_OK;
status = NvAPI_Initialize();
if (status != NVAPI_OK)
{
return -1;
}
NvDRSSessionHandle hSession = 0;
status = NvAPI_DRS_CreateSession(&hSession);
if (status != NVAPI_OK)
{
return -2;
}
status = NvAPI_DRS_LoadSettings(hSession);
if (status != NVAPI_OK)
{
return -3;
}

NvDRSProfileHandle hProfile = 0;
NvAPI_UnicodeString profile_name = {0};
memcpy_s(profile_name, sizeof(profile_name), szProfileName, wcslen(szProfileName) * sizeof(wchar_t));
status = NvAPI_DRS_FindProfileByName(hSession, profile_name, &hProfile);
if (status == NVAPI_OK) {
status = NvAPI_DRS_DeleteProfile(hSession, hProfile);
if (status != NVAPI_OK)
{
return -10;
}
status = NvAPI_DRS_SaveSettings(hSession);
if (status != NVAPI_OK)
{
return -11;
}
status = NvAPI_DRS_LoadSettings(hSession);
if (status != NVAPI_OK)
{
return -12;
}
}

NVDRS_PROFILE drsProfile = { 0 };
drsProfile.version = NVDRS_PROFILE_VER;
drsProfile.isPredefined = 0;
memcpy_s(drsProfile.profileName, sizeof(drsProfile.profileName), szProfileName, wcslen(szProfileName) * sizeof(wchar_t));
status = NvAPI_DRS_CreateProfile(hSession, &drsProfile, &hProfile);
if (status != NVAPI_OK)
{
return -13;
}

NVDRS_APPLICATION app;
memset(&app, 0, sizeof(NVDRS_APPLICATION));
app.version = NVDRS_APPLICATION_VER;
app.isPredefined = 0;
memcpy_s(app.appName, sizeof(app.appName), szAppName, 256 * sizeof(wchar_t));
memcpy_s(app.userFriendlyName, sizeof(app.appName), szAppName, 256 * sizeof(wchar_t));
status = NvAPI_DRS_CreateApplication(hSession, hProfile, &app);
if (status != NVAPI_OK)
{
return -14;
}

NVDRS_SETTING drsSetting = { 0 };
drsSetting.version = NVDRS_SETTING_VER;
drsSetting = NVDRS_SETTING();
drsSetting.version = NVDRS_SETTING_VER;
drsSetting.settingId = SHIM_MCCOMPAT_ID;
drsSetting.settingType = NVDRS_DWORD_TYPE;
drsSetting.u32CurrentValue = SHIM_MCCOMPAT_ENABLE;
status = NvAPI_DRS_SetSetting(hSession, hProfile, &drsSetting);
if (status != NVAPI_OK)
{
return -15;
}

drsSetting = NVDRS_SETTING();
drsSetting.version = NVDRS_SETTING_VER;
drsSetting.settingId = SHIM_RENDERING_MODE_ID;
drsSetting.settingType = NVDRS_DWORD_TYPE;
drsSetting.u32CurrentValue = SHIM_RENDERING_MODE_ENABLE;
status = NvAPI_DRS_SetSetting(hSession, hProfile, &drsSetting);
if (status != NVAPI_OK)
{
return -16;
}

drsSetting = NVDRS_SETTING();
drsSetting.version = NVDRS_SETTING_VER;
drsSetting.settingId = SHIM_RENDERING_OPTIONS_ID;
drsSetting.settingType = NVDRS_DWORD_TYPE;
drsSetting.u32CurrentValue = SHIM_RENDERING_OPTIONS_DEFAULT_RENDERING_MODE;
status = NvAPI_DRS_SetSetting(hSession, hProfile, &drsSetting);
if (status != NVAPI_OK)
{
return -17;
}
status = NvAPI_DRS_SaveSettings(hSession);
if (status != NVAPI_OK)
{
return -18;
}
status = NvAPI_DRS_DestroySession(hSession);
if (status != NVAPI_OK)
{
return -19;
}
hSession = 0;
NvAPI_Unload();
return 0;
}

4、Static Library Bindings(上文第4种方法,CUDA)

上文第4种方法,通过程序静态链接一些第三方库,达到程序运行在独立高性能显卡上的效果。

​ ​https://developer.download.nvidia.cn/compute/cuda/docs/CUDA_Developer_Guide_for_Optimus_Platforms.pdf​​

As of Release 302, the current list of libraries are vcamp110.dll, vcamp110d.dll, nvapi.dll, nvapi64.dll, opencl.dll, nvcuda.dll, and cudart*.*.

如果你的笔记本有两块显卡,一块是Intel的集成显卡(IGP),一块是Nvidia的独立GPU,你的笔记本一般采用了Optimus技术。在使用CUDA的时候,务必要理解Optimus,才能很好的使用你的笔记本来开发CUDA程序。

获取OpenGL版本号、双显卡切换技术代码实现(Optimus、NVAPI、CUDA)_opengl_13


另外关于cuda开发包的安装介绍如下:

​ ​https://developer.nvidia.com/cuda-toolkit-archive​​

获取OpenGL版本号、双显卡切换技术代码实现(Optimus、NVAPI、CUDA)_opengl_14


获取OpenGL版本号、双显卡切换技术代码实现(Optimus、NVAPI、CUDA)_opengl_15

CUDA开发包和NVIDIA显卡驱动的版本对应关系如下:

​ ​https://docs.nvidia.com/cuda/cuda-toolkit-release-notes/index.html​​

获取OpenGL版本号、双显卡切换技术代码实现(Optimus、NVAPI、CUDA)_opengl_16


cuda开发包安装完毕之后,在命令行输入:

nvcc -V

获取OpenGL版本号、双显卡切换技术代码实现(Optimus、NVAPI、CUDA)_opengl_17


在NVIDIA控制面板查看你的电脑显卡能支持的cuda最大版本,如下图所示:

获取OpenGL版本号、双显卡切换技术代码实现(Optimus、NVAPI、CUDA)_opengl_18


获取OpenGL版本号、双显卡切换技术代码实现(Optimus、NVAPI、CUDA)_opengl_19

5、Global Variable NvOptimusEnablement(上文第5种方法)

上文第5种方法,通过定义导出变量NvOptimusEnablement ,控制程序运行在独立高性能显卡上。

Global Variable NvOptimusEnablement (new in Driver Release 302):
Starting with the Release 302 drivers, application developers can direct the Optimus
driver at runtime to use the High Performance Graphics to render any application–even
those applications for which there is no existing application profile. They can do this by
exporting a global variable named NvOptimusEnablement. The Optimus driver looks for
the existence and value of the export. Only the LSB of the DWORD matters at this time. A value of 0x00000001 indicates that rendering should be performed using High
Performance Graphics. A value of 0x00000000 indicates that this method should be
ignored.

对于Intel + NVIDIA双GPU“Optimus”设置,应用程序NvOptimusEnablement可以按照OptimusRenderingPolicies.pdf中的说明进行导出。

在NV开发者网站上,有个关于Optimus的新文档,里面提到了在R302以上的驱动里,引入了一个启动NV卡的新方法。通过从exe中导出一个名为NvOptimusEnablement的全局变量,它为1的时候驱动就能切换到NV的显卡。通过这种方式,终于可以在程序中控制使用哪块显卡,并且没有性能下降问题。

为了使OpenGL能在移动平台上采用独显渲染,需要添加以下代码(主程序exe添加):

// http://developer.download.nvidia.com/devzone/devcenter/gamegraphics/files/OptimusRenderingPolicies.pdf
// The following line is to favor the high performance NVIDIA GPU if there are multiple GPUs
// Has to be .exe module to be correctly detected.
// N卡使用独显运行
extern "C" __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;

// And the AMD equivalent
// Also has to be .exe module to be correctly detected.
// A显卡使用独显运行
extern "C" __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 0x00000001;

6、设置垂直同步

N卡默认开启垂直同步,如果要让FPS突破显示器刷新率,需要禁用垂直同步,首先需要引入GLEW。

#include <GL/wglew.h>
...
//禁用垂直同步
wglSwapIntervalEXT(0);

//启用垂直同步
wglSwapIntervalEXT(1);

四、显卡驱动厂商

1、NVIDIA

NVIDIA 驱动程序下载:

​ ​https://www.nvidia.cn/Download/index.aspx?lang=cn​​​ ​https://www.nvidia.cn/geforce/drivers/​​

获取OpenGL版本号、双显卡切换技术代码实现(Optimus、NVAPI、CUDA)_c++_20

2、AMD

AMD 驱动程序与支持:

​ ​https://www.amd.com/zh-hans/support​​

获取OpenGL版本号、双显卡切换技术代码实现(Optimus、NVAPI、CUDA)_opengl_21

3、Intel

​ ​https://www.intel.cn/content/www/cn/zh/support/products/80939/graphics.html#support-product-selector​​

获取OpenGL版本号、双显卡切换技术代码实现(Optimus、NVAPI、CUDA)_c++_22


获取OpenGL版本号、双显卡切换技术代码实现(Optimus、NVAPI、CUDA)_opengl_23

五、显卡检测工具

1、GPU Caps Viewer

​ ​https://www.geeks3d.com/dl/show/657​​GPU Caps Viewer is a graphics card / GPU information and monitoring utility that quickly describes the essential capabilities of your GPU including GPU type, amount of VRAM , OpenGL, Vulkan, OpenCL and CUDA API support level. It also comes with OpenGL and Vulkan real time 3D demos.

获取OpenGL版本号、双显卡切换技术代码实现(Optimus、NVAPI、CUDA)_c++_24


获取OpenGL版本号、双显卡切换技术代码实现(Optimus、NVAPI、CUDA)_opengl_25

2、OpenGL Extensions Viewer

​ ​https://realtech-vr.com/home/glview​​

获取OpenGL版本号、双显卡切换技术代码实现(Optimus、NVAPI、CUDA)_opengl_26


经过一番分析:

(1)这个程序运行环境依赖.net 4.6.1

(2)使用了Amazon Web Services for .Net

(3)使用了WPF

获取OpenGL版本号、双显卡切换技术代码实现(Optimus、NVAPI、CUDA)_c++_27


获取OpenGL版本号、双显卡切换技术代码实现(Optimus、NVAPI、CUDA)_c++_28


获取OpenGL版本号、双显卡切换技术代码实现(Optimus、NVAPI、CUDA)_c++_29


获取OpenGL版本号、双显卡切换技术代码实现(Optimus、NVAPI、CUDA)_opengl_30


获取OpenGL版本号、双显卡切换技术代码实现(Optimus、NVAPI、CUDA)_opengl_31

3、dxdiag(系统自带)

(1)打开“运行”窗口。

(2)输入"dxdiag",进入 DirectX诊断工具,即列出显卡信息。

获取OpenGL版本号、双显卡切换技术代码实现(Optimus、NVAPI、CUDA)_opengl_32

4、nvidia-smi(NVidia驱动)

The NVIDIA System Management Interface (nvidia-smi) is a command line utility, based on top of the NVIDIA Management Library (NVML), intended to aid in the management and monitoring of NVIDIA GPU devices.

nvidia-smi
nvidia-smi -l 3
nvidia-smi --query-gpu=gpu_name,gpu_bus_id,vbios_version --format=csv
nvidia-smi --query-gpu=timestamp,memory.total,memory.free,memory.used,name,utilization.gpu,utilization.memory --format=csv -l 5

​ ​https://developer.nvidia.com/nvidia-system-management-interface​​

​ ​http://developer.download.nvidia.com/compute/cuda/6_0/rel/gdk/nvidia-smi.331.38.pdf​​

​ ​http://nvidia.custhelp.com/app/answers/detail/a_id/3751/~/useful-nvidia-smi-queries​​

Monitoring the framebuffer for NVIDIA GRID vGPU and GPU-passthrough

​ ​http://nvidia.custhelp.com/app/answers/detail/a_id/4108/~/monitoring-the-framebuffer-for-nvidia-grid-vgpu-and-gpu-passthrough​​

获取OpenGL版本号、双显卡切换技术代码实现(Optimus、NVAPI、CUDA)_c++_33

六、其他FAQ

6.1 nvidiaopenglrdp.exe

RDP远程桌面的OPENGL实在是太老了,必须下载英伟达它们提供的一个程序来驱动OpenGL:
nvidiaopenglrdp.exe。nvidia发布的 让windows的rdp远程桌面环境也可以使用 GrForce GPU来做终端桌面的OpenGL 加速补丁。windows远程桌面连接支持的问题,由于远程桌面仅提供通用的GDI+,可以运行OpenGL 1.1,但对高版本的程序需要安装客户端驱动支持。

来时无迹去无踪,去与来时事一同。何须更问浮生事,只此浮生在梦中。