引言:
虽然GPU已经被用于很多通用计算当中了,但是还是有很多GPGPU的研究始于图形图像相关的,例如光线跟踪, 流体模拟等等。我们经常会需要通用计算的接口去访问DX或者OpenGL里面的一些资源。由于图形接口的纹理资源都是在显存里面存储的,如果仅仅为了让GPU的内核访问这些资源,就必须把图形接口的内存从显卡传到主存,然后在转换成相应的GPGPU接口形式传会显卡中,这样无疑繁琐了很多,而且做了很多不必要的工作。
为了方便GPU内核访问图形接口的资源,很多GPGPU接口都为程序员提供了Interoperability的功能。利用这个功能,程序员只需要简单的设置,就可以用相应的接口(CUDA C , Brook+ , OpenCL等)来访问这些显卡里面的资源。
本文介绍了CAL提供的Dx interoperability功能。实现了一个简单的Demo。
正文:
这已经是我第三次写关于interoperability的Demo了。前两次分别是CUDA和Brook+的interoperability (http://codeboycjy.blogbus.com/logs/40925084.html http://codeboycjy.blogbus.com/logs/39810508.html )。为了一致一些,我又实现了一个一摸一样的Demo。不过这回不同的是图形接口是Dx10,并且是基于CAL的。还是那张截图:
这次的Demo和以前两次也是一样的。用Pixel Shader写的话,会非常简单。但是这个Demo的目的不是Show怎么做像素处理的,而是展示一下怎样用GPGPU的Kernel访问Dx的纹理。
下面我们主要看两点:
1.怎样设置纹理与GPGPU资源的Mapping。
2.我会逐行解释一下第一个Kernel,第二个kernel留给读者自己分析了(里面应该有处漏洞,有兴趣可以试着找一下)。
首先,为了做interop,看看我们需要做些什么。
1. 首先我们需要检查一下当前设备是否支持interop,事实上,只有在vista系统下,CAL才支持Dx(9/10)的interop。
2. 然后我们需要找到mapping的两个函数指针,一个负责设备的对应,还有个负责资源的对应。
3. 利用这两个指针,我们需要把设备首先对应起来,注意,这里需要你把CAL和DX的设备都创建完毕后,才可以做Mapping。
4. 然后利用Map资源的函数,就可以把Dx资源与CAL资源对应起来。
第四点里面有些需要我们注意的地方。首先,对于那个CAL资源和Dx资源的时候,Dx的资源必须是创建完毕的,而CAL的资源是不用创建的。CAL会为我们自动对应。否则创建两份资源就是拷贝了。这里的对应就好像C++里面两个指针知道一处地址,谁都可以更改里面的内容。Dx 的资源并不一定仅仅是这个Demo里面利用的纹理,事实上,只要Dx认为是资源的东西,CAL都可以做对应,从而直接用内核访问,例如顶点缓冲,索引缓冲等等。
当资源对应后,我们就可以像对待正常的CAL资源一样对待和DX资源绑定的资源了。只不过这里我们修改的内存是有Dx系统为我们来存储的,而不是以CAL的资源存储格式存储的。但是其实对于GPU内核来说,这并没有什么影响。
做纹理interop基本就是这些内容,其实很easy的东西。下面我们来看看GPU的kernel怎么写的吧。
il_ps_2_0
dcl_input_interp(linear) v0.xy__
dcl_resource_id(0)_type(2d,unnorm)_fmtx(float)_fmtx(float)_fmtx(float)_fmtx(float)
dcl_cb cb0[1]
dcl_output_generic o0
dcl_literal l0 , 5.0 , 0.25 , 0.0 , 0.0
flr r0 , v0
mad r1.x , l0.y , r0.y , cb[0].z
cos r1.y , r1.x
mad r0.w , l0.x , r1.y , r0.x
sample_resource(0)_sampler(0) r2, r0.wyxx
mov o0 , r2
end上面就是这个Demo的第一个内核函数。比上次介绍的复杂一点点,但是其实仔细看,基本是一样的。我们还是照例提上其他类C语言的内核代码吧,方便对比一些。贴个Brook+的好了:
kernel void brook_wave_Texture( float t , float4 src[][] , out float4 desc<> )
{
//get the index
int2 index = instance().xy;
int srcX = (int) ( 5.0f * cos( t + 0.25f * (float) index.y ) ) + index.x ;
int srcY = index.y;
dest = src[srcY][srcX];
}Brook+的内核函数很简单,没什么好说的。主要来看一下上面CAL的代码是怎么work的好了。
前面声明与上一次介绍的东西基本是一样的,有不理解的朋友可以看下上次的解释。唯一不一样的就是有一个constant的变量。这个变量在程序中,每一帧都被CPU传入的数据更改,是根据时间变化的。
flr r0 , v0 : 把0.5开始的索引变为0.0开始。
mad r1.x , l0.y , cb[0].z : 这里其实就是Brook内核里面的 t + 0.25f * (float) index.y 。mad a , b , c , d的意思是 a = b * c + d
cos r1.y , r1.x : 把x分量的余弦值记录到x分量里面。
mad r0.w , l0.x , r1.y , r0.x : 这里就是对应上面的 srcX = (int) ( 5.0f * cos( t + 0.25f * (float) index.y ) ) + index.x。因为cos( t + 0.25f * (float) index.y )的值是r1.y,所以这个可以简化为 srcX = (int) ( 5.0f * r1.y ) + index.x;
这行指令执行完了以后,我们可以直接从资源里面去获取像素的值了。细心一点的朋友可能会注意到这里的索引是可能有越界现象的,但是CAL在访问Dx纹理的时候,越界的索引都被Clamp了,这一点和Brook+是一样的。所以我们不需要处理越界情况。
这次基本就是这些内容了,其实还是没有什么东西。想了解Dx interop,Dx10 初始化,以及CAL的kernel的朋友,可以把Demo下来看看。里面有这些相关的内容。
http://filer.blogbus.com/4730079/resource_473007912594659841.rar
用CAL直接访问Dx的纹理资源 (interoperability)
最新推荐文章于 2020-07-27 15:39:45 发布