Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

基于WebGL的GPGPU指南 - 概述 #28

Open
llwanghong opened this issue Apr 28, 2023 · 0 comments
Open

基于WebGL的GPGPU指南 - 概述 #28

llwanghong opened this issue Apr 28, 2023 · 0 comments

Comments

@llwanghong
Copy link
Owner

llwanghong commented Apr 28, 2023

也是一直在思考前端到底能如何能发力突围的过程中发现了这系列基于WebGL的GPGPU指南的文章,网上大部分 WebGPU 并行计算的文章均引用到了这系列文章,看原文实际应用是Web中针对物理学公式的仿真模拟计算,目前只快速看完前五篇,会陆续整理出来,后续的章节看精力和实际需求斟酌整理。

整理译文的目录章节如下:

$GPU$ 提供了几乎无处不在的强大并行计算能力。但利用这些能力的知识却远未普及。围绕如何将像素渲染到屏幕,其编程模型和架构远超出大多数程序员的经验范畴。在接下来的内容中,我们将详细解释并举例说明在开发适用于利用 $GPU$ 进行通用计算 $(GPGPU)$ 的计算模型和程序的常见编程习惯。

超级速度

GPU性能远超CPU性能,并且差距越来越大!

上面所展示的性能是针对单精度、32位浮点数的。$GPU$ 能非常好地处理这些数值。然而,使用更高精度的数值,如64位浮点数,会大幅降低性能。实际上,这些更高精度的数值在 $WebGL$$OpenGL$ 中使用并不广泛。它们在诸如 OpenCLCUDA 这样的专用工具中的使用更为普遍。32位精度对于许多应用程序都非常适合的,尤其是在教学中。

等等,什么?WebGL 计算?

$WebGL$ 主要用于图形处理,当我们谈论 $GPGPU$ 时,我们到底在说什么?

现代计算机使用称为着色器 $(shaders)$ 的小程序来计算屏幕上每个像素的颜色。此外,计算机还有专用的硬件,即图形处理单元 $(GPUs)$,来并行大量地运行这些着色器。电脑游戏依赖这一特性,可以获得高帧率,并实现许多效果,如光照和阴影。但这种并行性和高性能都是有代价的。每个着色器的输出完全由其输入决定,在其执行过程中没有与其它着色器的通信。对于在给定的图形处理上下文中,每个像素的颜色不依赖于相邻像素,颜色仅取决于正在渲染的场景,这点确实说得通。然而,从利用 $GPU$ 进行通用计算的角度来看,这是一个严重的限制。

幸运的是,在科学、工程和数学中的大量问题都是可以解决的。

  • 在网格上。
  • 网格上一个点上的计算不依赖于其它点上正在进行的计算。
WebGL顶点构建出由片元填充的三角形
看起来就像一个求解数值微分方程的网格

着色器 $(shaders)$2001年早期被引入到主流图形领域,并且几乎立即被用于图形以外的领域。我们将看到着色器非常适合在网格 $(mesh)$ 或网格 $(grid)$ 上进行数值计算。许多其它的计算模型也已经被映射到图形硬件上,包括微分方程、金融建模、机器学习、图像处理,甚至数据库等领域。

我们如何做到这一点?

将问题映射到 $GPU$ 上的主要元素是什么?由于 $GPU$ 是为图形设计的,所以我们需要付出一些努力才能在更广泛的场景中使用它们。正如我们将要看到的,对于某些问题,这些付出的努力是非常值得的。

WebGL GPGPU实现的基本结构

$WebGL\ GPGPU$ 实现会使用一种通用的结构。这是将问题转化为易于使用图形硬件和 $WebGL$ 进行处理的结果。我们将从一个大概的描述开始,然后通过具体示例详细介绍如何使用 $GPU$ 进行计算。

画布(Canvas)

$WebGL$ 一样,我们首先创建一个画布。 $GPGPU$ 画布有几个显著特点。首先,画布的尺寸要适合问题规模。如果我们在一个 $128\times128$ 的网格上求解微分方程,那么就创建一个 $128\times128$$宽\times高$ )的画布。

其次,我们不需要将画布附加到 DOM 上,只需创建画布并用它获取一个 $WebGL$ 上下文。只有当我们用它来可视化结果时,才会将其附加到 $DOM$ 上。

几何形状

我们希望使用尽可能简单的几何形状来进行计算。顶点和几何形状的唯一目的就是针对画布中每个像素点生成对片段着色器的调用。几乎总是会使用两个能覆盖完整画布的三角形作为几何形状,正如图中所示。

片段

简单的几何形状被光栅化。它被划分成与原始画布上的像素相对应的片段。我们使用没有投影的简单几何形状,以便片段能直接映射到像素。

输入纹理

$GPU$ 上使用纹理来存储数据。对于 $GPGPU$ 实现来说,最简单的就是将它们视为二维数组,数组中的每个元素对应画布上的一个像素。

我们按照画布尺寸来创建一个纹理。所以,对于一个 $128\times128$ 的问题网格,我们会有一个 $128\times128$ 的画布和一个 $128\times128$ 的纹理。需要记住一点,纹理会使用归一化坐标。我们将 $(0,0)$ 点附着到几何形状的一个角上,将 $(1,1)$ 点附着到相对应的角上。这样将可以完全覆盖几何形状,进而覆盖整个画布以及纹理。

代码

现在事情变得非常有趣。当进行渲染时,针对几何形状中的每个片段都会调用片段着色器,并产生一个像素值 $gl\_FragColor$。这就是是网格上该点的计算结果。着色器从输入纹理中读值,并将 $gl\_FragColor$ 值装载到输出纹理中。

输出纹理

将片段着色器的结果绘制到屏幕上对于计算来说并不是非常有用。相反,我们将其写入另一个纹理。这种作为离屏渲染目标使用的纹理,被称为帧缓冲对象 $(Framebuffer Object, FBO)$

输出纹理的尺寸与画布和输入纹理相同。实际上,通过交换输入和输出纹理来进行另一次渲染是一种常见做法。

你可能会问“第一个纹理从哪里来?”,有两种可能性。第一种是直接从片段着色器创建纹理。考虑假如上面流程中没有输入纹理,片段着色器仅使用 $(x,y)$ 坐标作为输入来计算 $gl\_FragColor$。第二种方法是在 $OpenGL$ 之外计算值,并在创建纹理时在数据数组中指定它们。这个方法类似将图像数据加载到纹理中。我们将会展示两种方法的示例。

减速带(Speed Bumps)

这种技术对于大多数 $OpenGL ES$ 实现来说都非常直观,然而我们以远非 $OpenGL$ 设计目的的方式使用它,可以预期会有一些速度障碍。这在手机和平板电脑等移动设备上尤其明显,这些设备通常具有有限的纹理读写能力。 $GPGPU$ 仍然可用,但我们将介绍这些移动平台上一些额外可用的表示纹理数据的技术。

为了能够尽可能简单地阐明通用方法,我们首先将解释强劲桌面平台上的一个矩阵乘法示例,然后展示如何将代码适配到不太顺畅的移动平台。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant