CPU和GPU在显示计算机图形时如何交互?


58

在这里,您可以看到一个名为Triangle.exe的小型C ++程序的屏幕截图,该程序带有一个基于OpenGL API的旋转三角形。

在此处输入图片说明

诚然,这是一个非常基本的示例,但我认为它适用于其他图形卡操作。

我只是很好奇,想知道从双击Windows XP下的Triangle.exe直到看到三角形在监视器上旋转的整个过程。发生什么情况,CPU(首先处理.exe)和GPU(最终在屏幕上输出三角形)如何交互?

我猜想显示此旋转三角形所涉及的主要是以下硬件/软件:

硬件

  • 硬碟
  • 系统内存(RAM)
  • 中央处理器
  • 显存
  • 显卡
  • 液晶显示器

软件

  • 操作系统
  • DirectX / OpenGL API
  • Nvidia驱动程序

有人可以用某种流程图进行说明吗?

它不应该是涵盖每个步骤的复杂解释(可能超出范围的猜测),而应该是中级IT人士可以遵循的解释。

我敢肯定,很多甚至会自称为IT专业人员的人都无法正确描述此过程。


如果您仅考虑GPU是CPU的扩展,那么您的困境就过去了!
KawaiKx 2014年

Answers:


55

我决定写一些关于编程方面以及组件之间如何通信的文章。也许它将为某些领域提供一些启示。

简报

要在屏幕上绘制您在问题中张贴的单个图像,该怎么办?

有多种方法可以在屏幕上绘制三角形。为简单起见,我们假设不使用顶点缓冲区。(顶点缓冲区是您存储坐标的内存区域。)让我们假设程序只是简单地告诉图形处理管道有关一行中每个顶点的信息(一个顶点只是空间中的坐标)。

但是,在绘制任何东西之前,我们首先必须运行一些脚手架。稍后我们将说明原因

// Clear The Screen And The Depth Buffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 

// Reset The Current Modelview Matrix
glMatrixMode(GL_MODELVIEW); 
glLoadIdentity();

// Drawing Using Triangles
glBegin(GL_TRIANGLES);

  // Red
  glColor3f(1.0f,0.0f,0.0f);
  // Top Of Triangle (Front)
  glVertex3f( 0.0f, 1.0f, 0.0f);

  // Green
  glColor3f(0.0f,1.0f,0.0f);
  // Left Of Triangle (Front)
  glVertex3f(-1.0f,-1.0f, 1.0f);

  // Blue
  glColor3f(0.0f,0.0f,1.0f);
  // Right Of Triangle (Front)
  glVertex3f( 1.0f,-1.0f, 1.0f);

// Done Drawing
glEnd();

那是做什么的呢?

当您编写要使用图形卡的程序时,通常会选择某种与驱动程序的接口。驱动程序的一些众所周知的接口是:

  • 的OpenGL
  • Direct3D
  • 卡达

对于此示例,我们将坚持使用OpenGL。现在,你的界面司机是什么让你所有你需要让你的程序的工具谈话到图形卡(或驱动程序,然后会谈到卡)。

该界面一定会为您提供某些工具。这些工具采用可以从程序中调用的API的形式。

该API是我们在上面的示例中看到的。让我们仔细看看。

脚手架

在真正进行任何实际绘图之前,您必须执行设置。您必须定义您的视口(实际要渲染的区域),透视图(相机进入您的世界),要使用的抗锯齿(平滑三角形的边缘)...

但是我们不会考虑任何一个。我们只是看一眼每个框架要做的事情。喜欢:

清除画面

图形管道不会为您每一帧清除屏幕。您必须告诉它。为什么?这就是为什么:

在此处输入图片说明

如果您不清除屏幕,则只需每一帧上画图即可。这就是为什么我们称之为glClearGL_COLOR_BUFFER_BIT集。另一位(GL_DEPTH_BUFFER_BIT)告诉OpenGL清除深度缓冲区。该缓冲区用于确定哪些像素在其他像素之前(或之后)。

转型

在此处输入图片说明
图片来源

转换是我们获取所有输入坐标(三角形的顶点)并应用我们的ModelView矩阵的部分。这是解释我们的模型(顶点)如何旋转,缩放和平移(移动)的矩阵。

接下来,我们应用投影矩阵。这将移动所有坐标,以便它们正确面对我们的相机。

现在,我们使用视口矩阵再次进行变换。我们这样做是为了将模型缩放到显示器的大小。现在,我们有一组准备渲染的顶点!

我们待会再进行转换。

画画

要画一个三角形,我们可以简单地告诉OpenGL开始一个新的三角形的列表,通过调用glBeginGL_TRIANGLES不变。
您还可以绘制其他形式。像三角带三角扇子。这些主要是优化,因为它们需要较少的CPU和GPU之间的通信才能绘制相同数量的三角形。

之后,我们可以提供一组3个顶点的列表,这些顶点应组成每个三角形。每个三角形使用3个坐标(就像我们在3D空间中一样)。另外,我还通过在调用之前调用来为每个顶点提供颜色glColor3f glVertex3f

OpenGL 自动计算3个顶点之间的阴影(三角形的3个角)。它将在多边形的整个表面上插值颜色。

相互作用

现在,当您单击窗口时。该应用程序仅需捕获表示单击的窗口消息。然后,您可以在程序中运行所需的任何操作。

这得到了很多,一旦你要开始与你的3D场景互动更加困难。

您首先必须清楚地知道用户单击窗口的像素。然后,考虑到您的透视图,您可以从鼠标单击场景的角度来计算射线的方向。然后,您可以计算场景中的任何对象是否与该光线相交。现在您知道用户是否单击了对象。

那么,如何使其旋转呢?

转型

我知道通常会应用两种类型的转换:

  • 基于矩阵的转换
  • 基于骨骼的转换

不同之处在于骨骼会影响单个顶点。矩阵始终以相同的方式影响所有绘制的顶点。让我们看一个例子。

之前,我们在绘制三角形之前加载了单位矩阵。单位矩阵是根本不提供任何转换的矩阵。因此,无论我画什么,都只会受我的观点的影响。因此,三角形将完全不会旋转。

如果我现在要旋转它,我既可以自己算算自己(在CPU上)和简单的调用glVertex3f其他坐标(即旋转)。或者我可以通过glRotatef在绘制之前进行调用来让GPU完成所有工作:

// Rotate The Triangle On The Y axis
glRotatef(amount,0.0f,1.0f,0.0f);               

amount当然只是一个固定值 如果要设置动画,则必须跟踪amount并在每一帧增加它。

那么,等等,之前所有矩阵讨论发生了什么?

在这个简单的示例中,我们不必关心矩阵。我们简单地打电话glRotatef给我们,一切由我们来完成。

glRotate产生angle围绕向量xyz 的度数旋转。当前矩阵(请参阅glMatrixMode)乘以旋转矩阵,乘积替换当前矩阵,就好像使用以下矩阵作为参数调用了glMultMatrix一样:

x 2⁡1-c + cx y y⁡1-c-z⁢sx⁢z⁡1-c + y s s 0y⁢x⁡1-c + z⁢sy 2⁡1-c + cy⁢z⁡ 1-c-x s 0 x⁢1-c-y sy y z⁡1-c + x sz 2⁡1-c + c 0 0 0 0 1

好吧,谢谢!

结论

显而易见的是,关于 OpenGL 的讨论很多。但这并没有告诉我们任何事情。通讯在哪里?

在此示例中,OpenGL唯一要告诉我们的是何时完成。每次操作将花费一定的时间。有些操作花费了难以置信的时间,而另一些则非常快。

将顶点发送到GPU将是如此之快,我什至不知道如何表达它。从单个帧将数千个顶点从CPU发送到GPU根本没有问题。

清除屏幕可能要花费一毫秒或更长时间(请注意,通常每帧绘制时间只有16毫秒),具体取决于视口的大小。要清除它,OpenGL必须以要清除的颜色绘制每个像素,该像素可能是数百万个像素。

除此之外,我们几乎只能向OpenGL询问我们的图形适配器的功能(最大分辨率,最大抗锯齿,最大色深等)。

但是我们也可以用每个都有特定颜色的像素填充纹理。因此,每个像素都具有一个值,纹理是一个充满数据的巨型“文件”。我们可以将其加载到图形卡中(通过创建纹理缓冲区),然后加载着色器,告诉该着色器将纹理用作输入,并对“文件”进行一些非常繁琐的计算。

然后,我们可以将计算结果(以新颜色的形式)“渲染”为新的纹理。

这样便可以使GPU以其他方式为您工作。我认为CUDA的表现与该方面相似,但我从未有过使用它的机会。

我们真的只触及了整个主题。3D图形编程是一头野兽。

在此处输入图片说明
图片来源


38

很难确切了解您不了解的内容。

GPU具有BIOS映射的一系列寄存器。这些允许CPU访问GPU的内存并指示GPU执行操作。CPU将值插入这些寄存器中,以映射一些GPU的内存,以便CPU可以访问它。然后它将指令加载到该内存中。然后,它将一个值写入寄存器,该值告诉GPU执行CPU加载到其内存中的指令。

该信息包括GPU需要运行的软件。该软件与驱动程序捆绑在一起,然后驱动程序(通过在两个设备上运行其代码的一部分)来处理在CPU和GPU之间划分的责任。

然后,驱动程序将一系列“窗口”管理到CPU可以读取和写入的GPU内存中。通常,访问模式涉及CPU将指令或信息写入映射的GPU内存,然后通过寄存器指示GPU执行这些指令或处理该信息。该信息包括着色器逻辑,纹理等。


1
感谢您的解释。基本上,我不了解的是CPU指令集如何与GPU指令集通信,但是显然这是由驱动程序完成的。这就是我对抽象层的意思。
JohnnyFromBF 2012年

2
没有涉及的CPU指令集。驱动程序和运行时将您的CUDA,OpenGL,Direct3D等编译为本地GPU程序/内核,然后还上传到设备内存中。然后,命令缓冲区引用与其他任何资源一样的资源。
Axel Gneiting 2012年

2
我不确定您要指的是什么程序(在gpu上运行并包含在驱动程序中)。gpu在很大程度上是固定功能的硬件,它将运行的唯一程序是着色器,它们是由应用程序而非驱动程序提供的。驱动程序仅编译这些程序,然后将它们加载到GPU的内存中。
本·理查兹

1
@ sidran32:例如,在nVidia的Kepler体系结构中,内核,流和事件是由在GPU(而非(通常)在CPU上)运行的软件创建的。GPU端软件也管理RDMA。所有这些软件都由驱动程序加载到GPU内存中,并在处理CPU / GPU合作对的GPU端的GPU上作为“微型OS”运行。
大卫·史瓦兹

@DavidSchwartz我确实忘记了GPU计算任务。但是,无论如何,它们的行为仍然与着色器类似。不过,我不会将其称为“微型操作系统”,因为它没有通常与操作系统相关联的相同功能。它仍然是非常专业的软件,因为GPU的设计不像CPU(有充分的理由)。
本·理查兹

13

我只是很好奇,想知道从双击Windows XP下的Triangle.exe直到看到三角形在监视器上旋转的整个过程。发生什么情况,CPU(首先处理.exe)和GPU(最终在屏幕上输出三角形)如何交互?

让我们假设您实际上知道一个可执行文件如何在操作系统上运行以及该可执行文件如何从您的GPU发送到监视器,但是不知道它们之间发生了什么。因此,让我们从硬件方面看一下,并进一步扩展到程序员方面的答案...

CPU和GPU之间的接口是什么?

使用驱动程序,CPU可以将PCI等主板功能与图形卡进行对话,并向其发送命令以执行一些GPU指令,访问/更新GPU内存,加载要在GPU上执行的代码等。

但是,您不能真正通过代码直接与硬件或驱动程序对话;因此,这将必须通过OpenGL,Direct3D,CUDA,HLSL,Cg等API来实现。前者运行GPU指令和/或更新GPU内存时,后者实际上将在GPU上执行代码,因为它们是物理/着色器语言。

为什么要在GPU而不是CPU上运行代码?

尽管CPU擅长运行我们的日常工作站和服务器程序,但对于这些天在游戏中看到的所有闪亮图形并没有多加考虑。过去,有一些软件渲染器可以从某些2D和3D事物中获得成功,但它们非常有限。因此,这就是GPU发挥作用的地方。

GPU已针对图形中最重要的计算之一“ 矩阵操纵”进行了优化。尽管CPU必须按矩阵运算一次计算每个乘法(后来出现了3DNow!SSE之类的事情),但GPU可以一次完成所有这些乘法!并行性。

但是并行计算并不是唯一的原因,另一个原因是GPU离视频内存更近,这使其比必须通过CPU进行往返等要快得多。

这些GPU指令/内存/代码如何显示图形?

要使这一切正常进行,有一件遗漏的东西,我们需要可以写的东西然后可以阅读并发送到屏幕。我们可以通过创建一个framebuffer来做到这一点。无论您执行什么操作,最终都将更新帧缓冲区中的像素。除了位置以外,还保存有关颜色和深度的信息。

让我们举一个例子,您想在某处绘制一个血精灵(图像)。首先,将树纹理本身加载到GPU内存中,从而可以轻松地随意重绘它。接下来,要实际绘制精灵,我们可以使用顶点(将其放置在正确的位置)转换精灵,对其进行栅格化(将其从3D对象转换为像素)并更新帧缓冲区。为了获得更好的主意,以下是Wikipedia的OpenGL管道流程图:

这是整个图形思想的主要要点,更多的研究是读者的作业。


7

为了使事情简单,我们可以这样描述。某些内存地址(通过BIOS和/或操作系统)不是为RAM而是为视频卡保留的。以这些值(指针)写入的所有数据都将进入卡。因此,从理论上讲,任何程序只要知道地址范围就可以直接将其写入视频卡,而这正是过去的做法。实际上,在现代操作系统中,这是由顶部的视频驱动程序和/或图形库(DirectX,OpenGL等)管理的。


1
-1他问来自CPU的DirectX API调用如何与GPU通讯,您的答案是“由驱动程序和/或DirectX管理”?这也不能解释如何运行自定义代码(ala CUDA)。
BlueRaja-Danny Pflughoeft 2012年

3
请学习阅读。我说的是通过写入保留给GPU而不是RAM的特定内存地址。这说明了如何运行所有内容。为卡注册了存储范围。您在该范围内编写的所有内容都将交给运行顶点处理的GPU(CUDA)。
AZ。

5

GPU通常由DMA缓冲区驱动。也就是说,驱动程序将从用户空间程序接收的命令编译为指令流(切换状态,以这种方式绘制,切换上下文等),然后将其复制到设备内存中。然后,它指示GPU通过PCI寄存器或类似方法执行此命令缓冲区。

因此,在每次绘图调用等操作中,发生的情况是用户空间驱动程序将编译该命令,然后该命令通过中断调用内核空间驱动程序,最后将命令缓冲区提交给设备内存并指示GPU开始渲染。

在游戏机上,您甚至可以尽情享受自己做的所有事情,尤其是在PS3上。


0

我认为CPU通过总线将视频数据发送到GPU,然后GPU显示它。因此更快的GPU可以处理来自CPU的更多数据。这样,一些将cpuoffload处理到GPU上。因此,您在游戏中的速度更快。

这有点像CPU在其中存储内容的RAM,以便它可以快速加载和处理。两者都使游戏更快。

或声卡或网卡的工作原理相同,即获取数据并卸载CPU的某些工作。


这将重复另一个答案,并且不添加任何新内容。除非您确实有新贡献,否则请不要发布答案。
DavidPostill

0

我认为op不能确定CPU到底要告诉图形卡做什么,以及为什么图形相关命令(例如opengl或direct3d命令)没有直接发送到GPU。

CPU只是告诉GPU渲染什么。所有指令首先经过CPU,在此处它们被设置/初始化,以便GPU实际进行渲染。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.