GPU 学习笔记(一)::CPU与GPU的数据互传

    技术2022-05-20  63

    开始接触GPU,读了一部分GPGPU::Basic Math Tutorial 整理了前一部分CPU与GPU互传数据的代码,记录如下:

     

    // 以下代码来自博客 http://blog.csdn.net/i53nd/archive/2008/05/30/2497336.aspx

     

      #include <stdio.h> #include <stdlib.h> #include <GL/glew.h> #include <GL/glut.h> int main(int argc, char **argv) { // 这里声明纹理的大小为:teSize;而数组的大小就必须是texSize*texSize*4 int texSize = 2; int i; // 生成测试数组的数据 float* data = (float*)malloc(4*texSize*texSize*sizeof(float)); float* result = (float*)malloc(4*texSize*texSize*sizeof(float)); for (i=0; i<texSize*texSize*4; i++) data[i] = (i+1.0)*0.01F; // 初始化OpenGL的环境 glutInit (&argc, argv); glutCreateWindow("TEST1"); glewInit(); /*在我们对纹理进行运算或存取的时候,为了能够正确地控制每一个数据元素,我 们得选择一个比较特殊的投影方式,把3D世界映射到2D屏幕上(从世界坐标空间到 屏幕设备坐标空间),另外屏幕像素与纹理元素也要一一对应。这种关系要成功, 关键是要采用正交投影及合适的视口。这样便能做到几何坐标(用于渲染)、纹理 坐标(用作数据输入)、像素坐标(用作数据输出)三者一一对应。有一个要提醒 大家的地方:如果使用texture2D,我们则须要对纹理坐标进行适当比例的缩放, 让坐标的值在0到1之间,前面有相关的说明。 为了建立一个一一对应的映射,我们把世界坐标中的Z坐标设为0,把下面这段代码 加入到initFBO()这个函数中 */ // 视口的比例是 1:1 pixel=texel=data 使得三者一一对应 glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0.0,texSize,0.0,texSize); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glViewport(0,0,texSize,texSize); /*首先,通过使用一些OpenGL的扩展函数,我们可以给GPU提供32位精度的浮点数。 另外有一个叫 EXT_framebuffer_object 的OpenGL的扩展, 该扩展允许我们把一个 离屏缓冲区作为我们渲染运算的目标,这个离屏缓冲区中的RGBA四个通道,每个都 是32位浮点的,这样一来, 要想GPU上实现四分量的向量运算就比较方便了,而且 得到的是一个全精度的浮点数,同时也消除了限定数值范围的问题。我们通常把这 一技术叫FBO,也就是Frame Buffer Object的缩写。 要使用该扩展,或者说要把传统的帧缓冲区关闭,使用一个离屏缓冲区作我们的渲 染运算区,只要以下很少的几行代码便可以实现了。有一点值得注意的是:当我用 使用数字0,来绑定一个FBO的时候,无论何时,它都会还原window系统的特殊帧缓 冲区,这一特性在一些高级应用中会很有用,但不是本教程的范围,有兴趣的朋友 可能自已研究一下。*/ // 生成并绑定一个FBO,也就是生成一个离屏渲染对像 GLuint fb; glGenFramebuffersEXT(1,&fb); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT,fb); // 生成两个纹理,一个是用来保存数据的纹理,一个是用作渲染对像的纹理 GLuint tex,fboTex; glGenTextures (1, &tex); glGenTextures (1, &fboTex); glBindTexture(GL_TEXTURE_RECTANGLE_ARB,fboTex); // 设定纹理参数 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP); // 这里在显卡上分配FBO纹理的贮存空间,每个元素的初始值是0; glTexImage2D(GL_TEXTURE_RECTANGLE_ARB,0,GL_RGBA32F_ARB, texSize,texSize,0,GL_RGBA,GL_FLOAT,0); // 分配数据纹理的显存空间 glBindTexture(GL_TEXTURE_RECTANGLE_ARB,tex); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP); glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_COLOR,GL_DECAL); glTexImage2D(GL_TEXTURE_RECTANGLE_ARB,0,GL_RGBA32F_ARB, texSize,texSize,0,GL_RGBA,GL_FLOAT,0); /*其实一个纹理,它不仅可以用来作数据输入对像,也还可以用作数据输出对 像。这也是提高GPU运算效率和关键所在。通过使用 framebuffer_object 这 个扩展,我们可以把数据直接渲染输出到一个纹理上。但是有一个缺点:一个 纹理对像不能同时被读写,也就是说,一个纹理,要么是只读的,要么就是只 写的。 FBO 扩展提供了一个简单的函数来实现把数据渲染到纹理。为了能够使用一个 纹理作为渲染对像,我们必须先把这个纹理与FBO绑定,这里假设离屏帧缓冲 已经被指定好了。 */ //把当前的FBO对像,与FBO纹理绑定在一起 glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB,fboTex,0); /*第一个参数的意思是很明显的。第二个参数是定义一个绑定点(每个FBO最大 可以支持四个不同的绑定点,当然,不同的显卡对这个最大绑定数的支持不一 样,可以用GL_MAX_COLOR_ATTACHMENTS_EXT来查询一下)。第三和第四个参数 应该清楚了吧,它们是实际纹理的标识。最后一个参数指的是使用多重映像纹 理,这里没有用到,因此设为0。 为了能成功绑定一纹理,在这之前必须先用glTexImage2D()来对它定义和分配 空间。但不须要包含任何数据。我们可以把FBO想像为一个数据结构的指针,为 了能够对一个指定的纹理直接进行渲染操作,我们须要做的就调用OpenGL来给 这些指针赋以特定的含义。*/ /*为了把数据传输到纹理中去,我们必须绑定一个纹理作为纹理目标,并通过一 个GL函数来发送要传输的数据。实际上就是把数据的首地址作为一个参数传递给 该涵数,并指定适当的纹理大小就可以了。如果用LUMINANCE格式,则意味着数 组中必须有texSize x texSize个元数。而RGBA格式,则是这个数字的4倍。注意 的是,在把数据从内存传到显卡的过程中,是全完不需要人为来干预的,由驱动 来自动完成。一但传输完成了,我们便可能对CPU上的数据作任意修改,这不会 影响到显卡中的纹理数据。 而且我们下次再访问该纹理的时候,它依然是可用 的。在NVIDIA的显卡中,以下的代码是得到硬件加速的。*/ // 把本地数据传输到显卡的纹理上。 glBindTexture(GL_TEXTURE_RECTANGLE_ARB,tex); glTexSubImage2D(GL_TEXTURE_RECTANGLE_ARB,0,0,0,texSize,texSize, GL_RGBA,GL_FLOAT,data); //这里三个值是0的参数,是用来定义多重映像纹理的,由于我们这里要求一次把 //整个数组传输一个纹理中,不会用到多重映像纹理,因此把它们都关闭掉。 /*CPU中的数据都是以行排列的方式映射到纹理中去的。更详细地说,就是:对于 RGBA格式,数组中的前四个数据,被传送到纹理的第一个元素的四个分量中,分别 与R,G,B,A分量一一对应,其它类推。而对于LUMINANCE 格式的纹理,纹理中第一 行的第一个元素,就对应数组中的第一个数据。其它纹理元素,也是与数组中的数 据一一对应的。*/ //--------------------begin------------------------- //以下代码是渲染一个大小为texSize * texSize矩形, //其作用就是把纹理中的数据,经过处理后,保存到帧缓冲中去, //由于用到了离屏渲染,这里的帧缓冲区指的就是FBO纹理。 //在这里,只是简单地把数据从纹理直接传送到帧缓冲中, //没有对这些流过GPU的数据作任何处理,但是如果我们会用CG、 //GLSL等高级着色语言,对显卡进行编程,便可以在GPU中 //截获这些数据,并对它们进行任何我们所想要的复杂运算。 //这就是GPGPU技术的精髓所在。问题讨论:www.physdev.com /*这是一个反方向的操作,那就是把数据从GPU传输回来,存放在CPU的数组上。同 样,有两种不同的方法可供我们选择。传统上,我们是使用OpenGL获取纹理的方法, 也就是绑定一个纹理目标,然后调用glGetTexImage()这个函数。这些函数的参数, 我们在前面都有见过。 glBindTexture(texture_target,texID); glGetTexImage(texture_target,0,texture_format,GL_FLOAT,data); */ glColor4f(1.00f,1.00f,1.00f,1.0f); glBindTexture(GL_TEXTURE_RECTANGLE_ARB,tex); glEnable(GL_TEXTURE_RECTANGLE_ARB); glBegin(GL_QUADS); glTexCoord2f(0.0, 0.0); glVertex2f(0.0, 0.0); glTexCoord2f(texSize, 0.0); glVertex2f(texSize, 0.0); glTexCoord2f(texSize, texSize); glVertex2f(texSize, texSize); glTexCoord2f(0.0, texSize); glVertex2f(0.0, texSize); glEnd(); //--------------------end------------------------ //但是这个我们将要读取的纹理,已经和一个FBO对像绑定的话,我们可以采用改变 //渲染指针方向的技术来实现。 // 从帧缓冲中读取数据,并把数据保存到result数组中。 glReadBuffer(GL_COLOR_ATTACHMENT0_EXT); glReadPixels(0, 0, texSize, texSize,GL_RGBA,GL_FLOAT,result); /*由于我们要读取GPU的整个纹理,因此这里前面两个参数是0,0。表示从0起始点开 始读取。该方法是被推荐使用的。 一个忠告:比起在GPU内部的传输来说,数据在主机内存与GPU内存之间相互传输,其 花费的时间是巨大的,因此要谨慎使用。由其是从CPU到GPU的逆向传输。 */ /* --------------CPU---------------- -------------GPU------------ | | | | | data arr: | | texture: | | [][][][][][][][][] --------------> [][][] | | | | [][][] | | | | [][][] | | | | // | | result: | | FBO: | | [][][][][][][][][] | | [][][] | | <----------------- [][][] | | | | [][][] | |-------------------------------| |--------------------------| */ // 显示最终的结果 printf("Data before roundtrip:/n"); for (i=0; i<texSize*texSize*4; i++) printf("%f/n",data[i]); printf("Data after roundtrip:/n"); for (i=0; i<texSize*texSize*4; i++) printf("%f/n",result[i]); // 释放本地内存 free(data); free(result); // 释放显卡内存 glDeleteFramebuffersEXT (1,&fb); glDeleteTextures (1,&tex); glDeleteTextures(1,&fboTex); system("pause"); return 0; }


    最新回复(0)