纹理一词来源于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. ajax日期參数格式问题

    今天遇到ajax传输日期參数后台无法识别的问题,错误异常例如以下. 从异常中能够看出传输到后台的日期数据格式为Thu Aug 13 2015 19:45:20 GMT+0800 (中国标准时间),这样 ...

  2. Web安全之Cookie劫持

    1. Cookie是什么? 2. 窃取的原理是什么? 3. 系统如何防Cookie劫持呢? 看完这三个回答, 你就明白哪位传奇大侠是如何成功的!!! Cookie: HTTP天然是无状态的协议, 为了 ...

  3. [TypeScript] Define a function type

    type DigitValidator = (char) => boolean; -]{}/.test(char); export const digitValidators: {[key: s ...

  4. [慕课笔记]Node入口文件分析和目录初始化

    1:我们要在根目录下安装这些模块 2:然后来编写这些入口文件,这几行代码的大概意思是说,我引入一个express的模块,然后生成一个webview 的实例,将这个实例的监听端口设置成3000,然后就可 ...

  5. [Angular2 Form] patchValue, setValue and reset() for Form

    Learn how to update part of form model, full form model and reset whole form. We have form definetio ...

  6. ZOJ 3168 Sort ZOJ7 水

    再水一发,舍友肿么还在睡T T. ---------------------------------舍友还在睡觉的分割线--------------------------------- http:/ ...

  7. 从Lua调用C

    从Lua调用C: 方式:C函数从栈中获取函数參数(第一个參数总是局部栈的索引1),将结果压入栈中,C函数须要返回结果数量. 每一个函数都有自己的局部私有栈 样例: static int l_sin(l ...

  8. [Recompose] Add Lifecycle Hooks to a Functional Stateless Component using Recompose

    Learn how to use the 'lifecycle' higher-order component to conveniently use hooks without using a cl ...

  9. js进阶 11-20 弹出层如何制作

    js进阶 11-20 弹出层如何制作 一.总结 一句话总结:其实就是一个div,控制显示和隐藏即可.设置成绝对定位更好,就可以控制弹出层出现的位置.关闭的画质需要将display重新设置为none就好 ...

  10. 使用truss、strace或ltrace诊断软件的"疑难杂症"

    原文链接 简介 进程无法启动,软件运行速度突然变慢,程序的"Segment Fault"等等都是让每个Unix系统用户头痛的问题,本文通过三个实际案例演示如何使用truss.str ...