纹理一词来源于GPU图形世界,GPU通用并行计算“盗用”了纹理一词,定义了一个纹理内存的概念。纹理内存缓存在 设备上,在某些情况下能减少对内存的请求并降低内存带宽的使用,是专门为那些在内存访问模式中存在大量空间局部性的图形应用而设计,意味着一个线程读取的位置可能与邻近线程读取的位置“非常接近”。对于GPU内核而言,纹理内存是只读内存,并且只有通过特殊的纹理API才能对其访问

纹理内存分为一维纹理内存和二维纹理内存,理解纹理内存最好的方式是丢掉“纹理”两个字,纹理内存本质上是一块内存,是GPU在特定应用中对一维、二维变量的特殊声明定义以及特殊使用,这种特殊使用能够减少内存流量,提升运算性能。

纹理变量(引用)必须声明为文件作用域内的全局变量,这里先探讨一下一维纹理内存的使用方法。一维纹理内存的关键操作如下:

  • 1、用texture<类型>类型声明。

如声明一个unsigned char 型的一维纹理tex1,格式为:

texture<unsigned char,1,cudaReadmodeElementType> tex1;

  • 2、通过cudaBindTexture()绑定到纹理内存中,并关联到对应的数据上。

如将unsigned char类型的dev_A绑定到一维纹理tex1上,格式为:

cudaBindTexture(0,tex1,dev_A);

注意一旦将数据绑定到一个纹理内存上,该数据就已经传输到了设备缓存上,在核函数中就可以直接访问,不再需要额外传入。

  • 3、 通过tex1Dfetch()来读取纹理内存中的数据。

纹理内存是一种特殊的内存,需要使用特定的纹理API来访问其中的数据。如访问tex1数组的第3个元素,格式为:

tex1Dfetch(tex1,2);

  • 4、 通过cudaUnbindTexture()取消绑定纹理内存。

纹理内存使用完之后需要取消绑定,释放空间,如解除纹理tex1的绑定,格式为:

cudaUnbindTexture(tex1);

考虑一个简单的应用,把一个长度是100的向量A中的数据拷贝到一个向量B中,使用普通CPU编程实现如下:

#include <iostream>

using namespace std;

#define _length 100

//CPU函数实现复制一个数组
void Copy_CPU(unsigned int * listSource, unsigned int * listTarget, int length)
{
for (int i = 0; i < length; i++)
{
listTarget[i] = listSource[i];
}
} int main()
{
unsigned int * listSource = new unsigned int[_length];
unsigned int * listTarget = new unsigned int[_length]; //赋值
for (int i = 0; i < _length; i++)
{
listSource[i] = i;
} //调用CPU复制函数
Copy_CPU(listSource, listTarget, _length); cout << "原始数据: ";
for (int i = 0; i < _length; i++)
{
cout << listSource[i] << " ";
}
cout << endl << endl << "通过CPU拷贝的数据: ";
for (int i = 0; i < _length; i++)
{
cout << listTarget[i] << " ";
}
getchar();
}

运行结果:

使用GPU编程,普通变量编程实现

#include"cuda_runtime.h"
#include"device_launch_parameters.h"
#include<iostream> #define _length 100 using namespace std; //声明要调用的Copy_GPU函数
extern "C" void Copy_GPU(unsigned int* listSource, unsigned int* listTarget, int length); void main(int argc, char** argv)
{
unsigned int *listSource = new unsigned int[_length];
unsigned int *listTarget = new unsigned int[_length]; //赋值
for (int i = 0; i < _length; i++)
{
listSource[i] = i;
} // 调用Copy_GPU函数,Copy_GPU中会调用gpu端的kernel函数
Copy_GPU(listSource, listTarget, _length); cout << "原始数据: ";
for (int i = 0; i < _length; i++)
{
cout << listSource[i] << " ";
}
cout << endl << endl << "通过GPU普通内存拷贝的数据: ";
for (int i = 0; i < _length; i++)
{
cout << listTarget[i] << " ";
}
getchar();
} //核心代码,在gpu端执行的kernel,
__global__ void Blending_Texture(unsigned int* listSource, unsigned int* listTarget, int size)
{
//通过线程ID得到数组下标
int index = blockIdx.x * blockDim.x + threadIdx.x; if (index < size)
listTarget[index] = listSource[index];
} void Copy_GPU(unsigned int* listSource, unsigned int* listTarget, int length)
{
int data_size = length * sizeof(unsigned int); unsigned int *dev_Source;
unsigned int *dev_Target; //在设备上申请显存空间
cudaMalloc((void**)&dev_Source, data_size);
cudaMalloc((void**)&dev_Target, data_size); //将host端的数据拷贝到device端
cudaMemcpy(dev_Source, listSource, data_size, cudaMemcpyHostToDevice); //调用kernel
Blending_Texture << < ceil(_length / 10), 10 >> > (dev_Source, dev_Target, _length); //将结果拷贝到host端 ☆host就是CPU
cudaMemcpy(listTarget, dev_Target, data_size, cudaMemcpyDeviceToHost); //释放内存空间
cudaFree(dev_Source);
cudaFree(dev_Target);
}

运行结果:

使用GPU编程,一维纹理变量编程实现

#include"cuda_runtime.h"
#include"device_launch_parameters.h"
#include<iostream> #define _length 100 using namespace std; //声明纹理,用来绑定纹理,其实也就是个纹理标识
texture<unsigned int, 1, cudaReadModeElementType> rT1; //声明要调用的Copy_GPU函数
extern "C" void Copy_GPU(unsigned int* listSource, unsigned int* listTarget, int length); void main(int argc, char** argv)
{
unsigned int *listSource = new unsigned int[_length];
unsigned int *listTarget = new unsigned int[_length]; //赋值
for (int i = 0; i < _length; i++)
{
listSource[i] = i;
} // 调用Copy_GPU函数,Copy_GPU中会调用gpu端的kernel函数
Copy_GPU(listSource, listTarget, _length); cout << "原始数据: ";
for (int i = 0; i < _length; i++)
{
cout << listSource[i] << " ";
}
cout << endl << endl << "通过GPU纹理内存拷贝的数据: ";
for (int i = 0; i < _length; i++)
{
cout << listTarget[i] << " ";
}
getchar();
} //核心代码,在gpu端执行的kernel,
__global__ void Blending_Texture(unsigned int* listTarget, int size)
{
//通过线程ID得到数组下标
int index = blockIdx.x * blockDim.x + threadIdx.x; //通过纹理获取函数得到数据再运算
if (index < size)
listTarget[index] = tex1Dfetch(rT1, index);
} void Copy_GPU(unsigned int* listSource, unsigned int* listTarget, int length)
{
int data_size = length * sizeof(unsigned int); unsigned int *dev_Source;
unsigned int *dev_Target; //在设备上申请显存空间
cudaMalloc((void**)&dev_Source, data_size);
cudaMalloc((void**)&dev_Target, data_size); //将host端的数据拷贝到device端
cudaMemcpy(dev_Source, listSource, data_size, cudaMemcpyHostToDevice); //绑定纹理,绑定的纹理标识对应的数据
cudaBindTexture(0, rT1, dev_Source); //调用kernel
Blending_Texture << < ceil(_length / 10), 10 >> > (dev_Target, _length); //将结果拷贝到host端 ☆host就是CPU
cudaMemcpy(listTarget, dev_Target, data_size, cudaMemcpyDeviceToHost); //取消绑定
cudaUnbindTexture(rT1); //释放内存空间
cudaFree(dev_Source);
cudaFree(dev_Target);
}

运行结果:

再举一个使用CUDA+OpenCv编程,实现复制一幅图像的例子:

#include"cuda_runtime.h"
#include<iostream>
#include<highgui/highgui.hpp>
#include<imgproc/imgproc.hpp> #define DIM 512 //图像尺寸 using namespace std;
using namespace cv; //一维纹理声明
texture<unsigned char, 1, cudaReadModeElementType> rT1; __global__ void Kernel_Copy(unsigned char* imageTarget)
{
int x = threadIdx.x + blockIdx.x*blockDim.x;
int y = threadIdx.y + blockIdx.y*blockDim.y;
int offset = x + y*blockDim.x*gridDim.x; //复制图像
imageTarget[offset * 3 + 2] = tex1Dfetch(rT1, offset * 3 + 2);
imageTarget[offset * 3 + 1] = tex1Dfetch(rT1, offset * 3 + 1);
imageTarget[offset * 3 + 0] = tex1Dfetch(rT1, offset * 3 + 0);
} void main(int argc, char** argv)
{
Mat image = imread("D:\\lena.jpg");
Mat imageSource;
resize(image, imageSource, Size(DIM, DIM)); //调整图像大小 Mat imageTarget = Mat(Size(DIM, DIM), CV_8UC3, Scalar::all(0)); //分配空间
unsigned char *dev_imageSource;
unsigned char *dev_imageTarget;
cudaMalloc((void**)&dev_imageSource, 3 * imageSource.rows*imageSource.cols);
cudaMalloc((void**)&dev_imageTarget, 3 * imageSource.rows*imageSource.cols); cudaMemcpy(dev_imageSource, imageSource.data, 3 * imageSource.cols*imageSource.rows, cudaMemcpyHostToDevice);
cudaMemcpy(dev_imageTarget, imageTarget.data, 3 * imageSource.cols*imageSource.rows, cudaMemcpyHostToDevice); //绑定纹理
cudaBindTexture(0, rT1, dev_imageSource); dim3 grids(DIM / 16, DIM / 16);
dim3 threads(16, 16);
//调用kernel
Kernel_Copy << < grids, threads >> > (dev_imageTarget); //将结果拷贝到host端 ☆host就是CPU
cudaMemcpy(imageTarget.data, dev_imageTarget, 3 * imageSource.cols*imageSource.rows, cudaMemcpyDeviceToHost); imshow("CUDA纹理内存使用示例", imageTarget);
waitKey(); //解除纹理绑定
cudaUnbindTexture(rT1); cudaFree(dev_imageSource);
cudaFree(dev_imageSource);
}

运行结果:

CUDA一维纹理内存的更多相关文章

  1. CUDA:纹理内存

    纹理内存: 与常量内存类似,纹理内存是另一种形式的只读内存,并且同样缓存在芯片上.因此某些情况下能够减少对内存的请求并提供高效的内存带宽.纹理内存是专门为那些在内存访问模式中存在大量空间局部性的图形应 ...

  2. 《GPU高性能编程CUDA实战》第七章 纹理内存

    ▶ 本章介绍了纹理内存的使用,并给出了热传导的两个个例子.分别使用了一维和二维纹理单元. ● 热传导(使用一维纹理) #include <stdio.h> #include "c ...

  3. CUDA二维纹理内存+OpenCV图像滤波

    CUDA和OpenCV混合编程,使用CUDA的纹理内存,实现图像的二值化以及滤波功能. #include <cuda_runtime.h> #include <highgui/hig ...

  4. CUDA Texture纹理存储器 示例程序

    原文链接 /* * Copyright 徐洪志(西北农林科技大学.信息工程学院). All rights reserved. * Data: 2012-4-20 */ // // 此程序是演示了1D和 ...

  5. CUDA中多维数组以及多维纹理内存的使用

    纹理存储器(texture memory)是一种只读存储器,由GPU用于纹理渲染的图形专用单元发展而来,因此也提供了一些特殊功能.纹理存储器中的数据位于显存,但可以通过纹理缓存加速读取.在纹理存储器中 ...

  6. cuda纹理内存的使用

    CUDA纹理内存的访问速度比全局内存要快,因此处理图像数据时,使用纹理内存是一个提升性能的好方法. 贴一段自己写的简单的实现两幅图像加权和的代码,使用纹理内存实现. 输入:两幅图 lena, moon ...

  7. 基于纹理内存的CUDA热传导模拟

    原文链接 项目中有三个,第一个是全局内存,其余两个分别是基于1d和2d纹理内存.项目打包下载. 纹理内存是只读内存,与常量内存相同的是,纹理内存也缓存在芯片中,因此某些情况下,它能减少对内存的请求并提 ...

  8. CUDA C 纹理提取Texture Fetching

    CUDA C 纹理提取Texture Fetching 一.参数曲面的纹理  使用纹理指定参数曲面属性. 二.CUDA C 纹理获取开发 用于计算纹理函数,根据纹理引用的各种属性返回的值的公式(请参见 ...

  9. CUDA 纹理内存

    原文链接 1.概述 纹理存储器中的数据以一维.二维或者三维数组的形式存储在显存中,可以通过缓存加速访问,并且可以声明大小比常数存储器要大的多. 在kernel中访问纹理存储器的操作称为纹理拾取(tex ...

随机推荐

  1. error app/styles/components/iconfont.scss (Line 12: Invalid GBK character "\xE5")

    因为要用到iconfont,引入iconfont到sass文件后,出现编译sass文件错误,如下截图: 解决方法:在顶部设置编码格式 @charset "utf-8"; 编译成功!

  2. HDU 1996汉诺塔VI

    题目: n个盘子的汉诺塔问题的最少移动次数是2^n-1,即在移动过程中会产生2^n个系列.由于 发生错移产生的系列就增加了,这种错误是放错了柱子,并不会把大盘放到小盘上,即各柱 子从下往上的大小仍保持 ...

  3. 大数据(十四) - Storm

    storm是一个分布式实时计算引擎 storm/Jstorm的安装.配置.启动差点儿一模一样 storm是twitter开源的 storm的特点 storm支持热部署,即时上限或下线app 能够在st ...

  4. [Ionic2] Device Interaction in an Ionic App with Cordova Plugins

    In this lesson, we are going to learn how to interact with native components through Cordova plugins ...

  5. POJ 1751 Highways (ZOJ 2048 ) MST

    http://poj.org/problem?id=1751 http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=2048 题目大 ...

  6. LinearLayout的一些注意事项 分类: H1_ANDROID 2013-10-26 23:01 856人阅读 评论(0) 收藏

    1.orientation的默认值为horizontal,即从左向右排列.由于一般从上向下排列,所以必须指定orientation属性. 2.layout_gravity与gravity的区别: (1 ...

  7. 二、Reids基础命令--字符串

    11.一个字符串类型的KEY同意存储的数据的最大容量是 512MB 12.INCR 使key加1,key不存在时默认是0 . 返回递增后的值. 127.0.0.1:6379> incr num ...

  8. Android的事件分发

    1. Touch事件和绘制事件的异同之处 Touch事件和绘制事件非常相似,都是由ViewRoot派发下来的,可是不同之处在绘制事件是由应用中的某个View发起请求,一层一层上传到ViewRoot.再 ...

  9. struts2基本的配置代码

    <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "- ...

  10. ios开发总结:Utils常用方法等收集,添加扩展类,工具类方法,拥有很多方便快捷功能(不断更新中。。。)

    BOBUtils 工具大全 本人github开源和收集功能地址:https://github.com/niexiaobo [对ios新手或者工作一年以内开发人员很有用处] 常用方法等收集.添加扩展类. ...