用CAL直接訪問Dx的紋理資源 (interoperability)

引言:

      雖然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的。還是那張截圖:

dx interop on CAL1 dx interop on CAL2

      這次的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

發佈了25 篇原創文章 · 獲贊 7 · 訪問量 16萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章