纹理一词来源于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. Linux中IRC通讯工具Pidgin的基本用法

    这是我发在南昌大学开源小组里的一篇教程 IRC IRC是个古老的产物,在图形界面流行之前就出现了.可是在现在非常多开源社区都是利用IRC来进行团队交流或对用户提供技术支持的,比方维基百科,所以还是有必 ...

  2. 全面解析Activity的生命周期

    欢迎Follow我的GitHub, 关注我的CSDN. 在Android应用中, Activity是最重要的组件, 其生命周期(Lifecycle)被大家所熟知. 可是, 大家须要注意一些细节, 才干 ...

  3. UVA 11461 - Square Numbers 数学水题

    http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&p ...

  4. 100万并发连接服务器笔记之Java Netty处理1M连接会怎么样

    前言 每一种该语言在某些极限情况下的表现一般都不太一样,那么我常用的Java语言,在达到100万个并发连接情况下,会怎么样呢,有些好奇,更有些期盼.这次使用经常使用的顺手的netty NIO框架(ne ...

  5. js进阶 12-7 pageY和screenY以及clientY的区别是什么

    js进阶 12-7 pageY和screenY以及clientY的区别是什么 一.总结 一句话总结:pageY是距文件,screenY是获取显示器屏幕位置的坐标,clientY是页面视口. 没有滚动条 ...

  6. ArcSDE:C#打开SDE数据库的几种方式总结

    转自原文 ArcSDE:C#打开SDE数据库的几种方式总结 1.通过指定连接属性参数打开数据库 /// <param name="server">数据库服务器名< ...

  7. Envelope

    IEnvelope Interface Provides access to methods and properties of envelopes. Note: the IEnvelope inte ...

  8. 【15.07%】【codeforces 625A】Guest From the Past

    time limit per test 1 second memory limit per test 256 megabytes input standard input output standar ...

  9. 实现拖拽上传文件的一款小控件——dropzone

    由于专注所以专业.非常多小巧的东西乍一看非常不起眼,却在特定的领域表现不俗,就是由于集中了热情. dropzone就是这样一款小控件,实现拖拽上传.它不依赖于其他像jquery等JS库.并且支持多方面 ...

  10. Java NIO详细介绍

    不错的文章,推荐一下. http://www.ibm.com/developerworks/cn/education/java/j-nio/j-nio.html JavaNIO非堵塞技术实际是采取Re ...