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

Joh*_*mBF 65 cpu graphics gpu graphics-card 3d-graphics

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

在此处输入图片说明

不可否认,这是一个非常基本的例子,但我认为它适用于其他显卡操作。

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

我猜想显示这个旋转三角形主要是以下硬件/软件等:

硬件

  • 硬盘
  • 系统内存 (RAM)
  • 中央处理器
  • 显存
  • 图形处理器
  • 液晶显示器

软件

  • 操作系统
  • DirectX/OpenGL API
  • 英伟达驱动

谁能解释这个过程,也许用某种流程图来说明?

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

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

Der*_*ler 59

我决定写一些关于编程方面以及组件如何相互通信的内容。也许它会对某些领域有所启发。

介绍

甚至将您在问题中发布的单个图像绘制在屏幕上需要什么?

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

但是,在我们可以绘制任何东西之前,我们首先必须运行一些脚手架。我们稍后会看到原因

// 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();
Run Code Online (Sandbox Code Playgroud)

那做了什么?

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

  • OpenGL
  • 直接3D
  • CUDA

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

这个界面势必会给你提供一定的工具。这些工具采用API 的形式,您可以从您的程序中调用它。

这个 API 就是我们在上面的例子中看到的。让我们仔细看看。

脚手架

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

但我们不会看任何这些。我们将看看你必须在每一帧中做的事情。喜欢:

清除屏幕

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

在此处输入图片说明

如果您不清除屏幕,您只需每一帧上绘制它。这就是我们glClearGL_COLOR_BUFFER_BIT集合跟注的原因。另一位 ( GL_DEPTH_BUFFER_BIT) 告诉 OpenGL 清除深度缓冲区。此缓冲区用于确定哪些像素位于其他像素的前面(或后面)。

转型

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

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

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

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

我们稍后会回到转型。

画画

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

之后,我们可以提供应构成每个三角形的 3 个顶点的集合列表。每个三角形使用 3 个坐标(因为我们在 3D 空间中)。此外,我公司还提供一个颜色为每个顶点,通过调用glColor3f 之前调用glVertex3f

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

相互作用

现在,当您单击窗口时。应用程序只需要捕获发出单击信号的窗口消息。然后你可以在你的程序中运行你想要的任何操作。

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

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

那么,如何让它旋转呢?

转型

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

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

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

例子

早些时候,我们在绘制三角形之前加载了我们的单位矩阵。单位矩阵是一种根本不提供任何转换的矩阵。所以,无论我画什么,只受我的观点影响。因此,三角形根本不会旋转。

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

// Rotate The Triangle On The Y axis
glRotatef(amount,0.0f,1.0f,0.0f);               
Run Code Online (Sandbox Code Playgroud)

amount当然,只是一个固定值。如果你想制作动画,你必须amount每帧跟踪并增加它。

那么,等等,之前所有的矩阵谈话发生了什么?

在这个简单的例子中,我们不必关心矩阵。我们只需打电话glRotatef,它就会为我们处理所有这些。

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

× 2 ? 1 - c + cx ?是吗?1-c-z ? sx ? ? 1 - c + y ? s 0 y ? X ?1 - c + z ? sy 2 ? 1 - c + cy ? ? 1-c-x ? s 0 x ? ? 1-c-y ? 是吗?? 1 - c + x ? s 2 ? 1 - c + c 0 0 0 0 1

嗯,谢谢!

结论

什么变得明显的是,有一个谈了很多OpenGL的。但这并没有告诉我们任何事情。沟通在哪里?

在这个例子中,OpenGL 告诉我们的唯一一件事就是什么时候完成。每个操作都需要一定的时间。有些操作需要非常长的时间,有些则非常快。

将顶点发送到 GPU 会如此之快,我什至不知道如何表达它。从 CPU 向 GPU 发送数千个顶点,每一帧,很可能根本没有问题。

清除屏幕可能需要一毫秒或更长时间(请记住,您通常只有大约 16 毫秒的时间来绘制每一帧),具体取决于您的视口有多大。要清除它,OpenGL 必须以您想要清除的颜色绘制每个像素,这可能是数百万个像素。

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

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

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

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

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

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


Dav*_*rtz 43

很难确切地理解您不理解的内容。

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

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

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

  • 不涉及 CPU 指令集。驱动程序和运行时将您的 CUDA、OpenGL、Direct3D 等编译为原生 GPU 程序/内核,然后这些程序/内核也会上传到设备内存。命令缓冲区然后像任何其他资源一样引用那些。 (2认同)
  • 我不确定您指的是什么程序(在 gpu 上运行并包含在驱动程序中)。gpu 主要是固定功能的硬件,它运行的唯一程序是着色器,它由应用程序提供,而不是驱动程序。驱动程序只编译这些程序,然后将它们加载到 gpu 的内存中。 (2认同)
  • @sidran32:例如,在 nVidia 的 Kepler 架构中,内核、流和事件是由在 GPU(而不是(通常)CPU)上运行的软件创建的。GPU 端软件也管理 RDMA。所有这些软件都由驱动程序加载到 GPU 内存中,并作为 GPU 上的“迷你操作系统”运行,处理 CPU/GPU 协作对的 GPU 端。 (2认同)

Tam*_*man 15

我只是好奇,想知道从 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 对象转换为像素)并更新帧缓冲区。为了获得更好的想法,这里是来自维基百科的 OpenGL 管道流程图:

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


AZ.*_*AZ. 8

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

  • 请学会阅读。我说的是写入为 GPU 而不是 RAM 保留的特定内存地址。这解释了如何运行所有内容。为卡注册了存储范围。您在该范围内编写的所有内容都将发送到运行顶点处理、CUDA 等的 GPU。 (3认同)

Axe*_*ing 7

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

因此,在每次绘制调用等时,用户空间驱动程序将编译命令,然后通过中断调用内核空间驱动程序,最后将命令缓冲区提交到设备内存并指示 GPU 开始渲染。

在游戏机上,您甚至可以享受自己完成所有操作的乐趣,尤其是在 PS3 上。