You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
// Tests, terminate on first failure.success=initializer.test(0,0)&&initializer.test(10,12)&&initializer.test(100,100);if(!success){renderToUnsignedBytes(texture);}
/** * Accepts a passed in texture, which is assumed to contain single floating point * values, and packs each texture element in the corresponding RGBA element of the * newly created texture. * * @param {WebGLTexture} A texture previously populated with floating point values. */functionrenderToUnsignedBytes(texture){unsignedByteTexture=gpgpUtility.makeTexture(WebGLRenderingContext.UNSIGNED_BYTE,null);unsignedByteFramebuffer=gpgpUtility.attachFrameBuffer(unsignedByteTexture);bufferStatus=gpgpUtility.frameBufferIsComplete();if(bufferStatus.isComplete){unsignedByteConverter=newToUnsignedBytes(gpgpUtility);unsignedByteConverter.convert(matrixColumns,matrixRows,texture);// Delete resources no longer in use.unsignedByteConverter.done();// Tests, terminate on first failure.success=unsignedByteConverter.test(0,0)&&unsignedByteConverter.test(10,12)&&unsignedByteConverter.test(100,100);}else{alert(bufferStatus.message);}}
测试方法从GPU读回这些数据,比较宽慰的是,这个过程非常简单。
// One each for RGBA component of a pixelbuffer=newUint8Array(4);// Read a 1x1 block of pixels, a single pixelgl.readPixels(i,// x-coord of lower left cornerj,// y-coord of lower left corner1,// width of the block1,// height of the blockgl.RGBA,// Format of pixel data.gl.UNSIGNED_BYTE,// Data type of the pixel data, must match makeTexturebuffer);// Load pixel data into bufferfloatingPoint=newFloat32Array(buffer.buffer);
整理译文的目录章节如下:
减速带
浮点纹理
使用浮点纹理进行通用GPU计算(GPGPU)的主要问题集中在以下几方面:
这些问题在意料之中,因为浮点纹理在GPGPU处理中至关重要,但在计算机图形学中却不属于主流。幸运的是,由于这些问题彼此关联密切,因此它们的解决方案也密切相关。如果我们无法使用浮点纹理解决某个步骤,我们则可以使用纹理储存浮点数的方式来解决。
许多情况下,运行模拟及显示结果都完全在GPU上完成。对于这些情况,无法从GPU读取浮点纹理的问题将无关紧要。事实上,我预计有些人会认为这一节内容是多余的。如果你只对数值技术感兴趣,或者你的目标受众始终使用更强大的系统,那么这一节内容确实不是必需的。然而,对于希望触及广泛受众的教学设计人员或开发人员来说,这些材料是必不可少的。
下面这个图表涵盖了决定是否需要使用无符号字节纹理的主要要素,以及将它们纳入项目的过程。
幸运的是1 过程中的每个决策点都已经建立对应的测试。这意味着这个过程可以自动化,并且根据测试结果加载不同的程序。
我们已经开发了始终使用浮点纹理决策点的代码示例。下一步将解决即使能够从片段着色器读写浮点值,但ReadPixels无法从GPU读回浮点像素的情况。顺便说一下,这正是我手机的情况,配备了Adreno 220 GPU。这要求我们将输出纹理中的每个浮点数打包到一个UNSIGNED_BYTE RGBA纹理元素(texel)中。然后将这些纹理元素读回到CPU,并解构成浮点数。
最终情况是涉及无法使用浮点纹理的场景。可能是因为OES_texture_float扩展不可用,进而无法创建浮点纹理,或者是因为CheckFramebufferStatus状态未完成,我们无法将计算结果写入(渲染到)浮点纹理。在任何一种情况下,我们都会将中间结果存储在UNSIGNED_BYTE RGBA纹理中。在每一步计算结尾,我们将浮点结果转换为无符号字节,然后在下一步计算开始时,我们再将无符号字节转换为浮点数。这些每次计算中的多次转换将会导致在性能方面要付出很高的代价。
当我们使用无符号字节纹理时,我们希望当浮点格式和RGBA无符号字节相互数据转换时,不会丢失信息,即尽可能少地损失精度。
你可能已经注意到字节顺序是ABGR而不是RGBA。这是因为Intel、ARM和大多数目标平台是小端序(little endian),在这种情况下字节顺序是反向的。
一旦理解了浮点数中各个位的含义后,提取这些位并不困难。IEEE 754浮点表示将一个数映射为
如果WebGL支持位运算,分解这个公式可能会更容易。我们仍然可以通过纯粹的数学操作来分解它。
我们从一段提取浮点数值符号位的代码片段开始。
GLSL中的step函数,当第二个参数小于第一个参数时会返回0,否则返回1。但真正有趣的是:”为什么使用函数而不是if或三元运算符,或者if-then-else?”,通常情况下,GPU被设计为在多个着色器上以完全相同的方式并行运行相同的代码。因此,if语句和分支结构并不能被很好地处理,在合理情况下应避免使用。GLSL有许多类似的函数,如step和clamp,可以用来实现条件执行的效果2。
下一步是提取指数。数字通常是$2^\left(exponent-127\right)\times \text{some factor less than 2}$ 。所以,如果我们做对数运算并向下取整 $\lfloor log_{2}\left(value\right) \rfloor$ ,得到的就是 $exponent - 127$ 。
我们可以用$\frac{value}{2^{exponent}} - 1$ 来获取尾数位
现在我们已经恢复了尾数,可以从指数中去除偏移量或偏置。
我们还可以将结果的第一个 ($r$ ) 字节设置为符号位和指数的前7位。符号位乘以 $2^7 = 128$ 将其左移7位,然后指数除以 $2$ 实现右移1位。
第二个 ($g$ ) 字节是由指数的最后1位以及尾数的前7位组合而成。
蓝色 ($b$ ) 字节是接下来的8位。
截至目前,应该意识到了我们重复计算了多个表达式,意味着有必要进行重构。
基于这块代码,我们可以构建一个将浮点数转换为无符号字节纹理的程序 ToUnsignedBytes。查看前面的过程图,我们可以当帧缓冲状态检查通过时来调用这个程序,但在计算后我们暂无法将像素读回到CPU。
测试方法从GPU读回这些数据,比较宽慰的是,这个过程非常简单。
Uint8Array(4) 是一个由4个无符号8位整数组成的数组。这跟我们加载到纹理中的4个无符号字节完全匹配。要从这些字节重新得到浮点数,只需使用$new Float32Array(buffer.buffer)$ 。在大端序(big endian)架构上,可以此时重新排序这些字节。
为了在计算中使用无符号字节纹理,我们还需要在着色器内部读回数据。这意味着需要逆转一下用来将数字打包到RGBA字节中的过程。
这次我们从纹素中解包各个RGBA字节到一个浮点数中。
我们再次从符号开始,这是alpha字节中最左边的位。
Next we pull out the exponent. Starting with the last bit to make it easy to trim the bit from the blue byte.
接下来提取指数。从蓝色字节的最后一位开始,以便于剪除该位。
剩下未处理的位都是尾数部分。
最后,将所有部分组合成完整的结果,参考之前使用的相同公式。
通用使用模式将是读取纹理数据,解包数据,执行计算,然后将结果打包成可以存储在纹理中的形式。
精度
几乎同样常见的一个问题就是GPU所能支持的精度,即用多少位来存储浮点数。在片段着色器开头的代码块已经隐含地传达了这一点。
较低精度的表示方法简单地使用更少的比特位来表示一个数字。这个例子展示了一个16位IEEE浮点数。可以将其与上面的32位浮点数进行比较。
最明显的区别之一是指数偏移量变为15,变小了很多。
看起来大多数情况下可以通过上面ifdef定义浮点数精度的方式来解决这些差异。然而,重要的是要考虑降低的精度是否能为模拟提供所需的准确性。
注释
The text was updated successfully, but these errors were encountered: