【原创】Linux环境下的图形系统和AMD R600显卡编程(11)——R600指令集
1 低級著色語言tgsi
OpenGL程序使用GLSL語言對可編程圖形處理器進(jìn)行編程,GLSL語言(以下高級著色語言就是指GLSL)是語法類似C的高級語言,在GLSL規(guī)范中,GLSL語言被先翻譯成教低級的類匯編語言,然后被翻譯成硬件特定的指令集。OpenGL體系管理委員會于2002年6月和2002年9月分別通過了兩個官方擴(kuò)展:ARB_VERTEX_PROGRAM與ARB_FRAGMENT_PROGRAM來統(tǒng)一對低級著色語言的支持,GLSL語言被編譯成針對這兩個擴(kuò)展的低級著色語言(因此這兩個擴(kuò)展可以看成是GLSL運(yùn)行的虛擬機(jī)),顯卡廠商的驅(qū)動將低級著色語言翻譯成GPU指令。這兩個擴(kuò)展的1.0版本分別是arbvpl0和arbfpl0,這兩個擴(kuò)展的2.0版本分別是arbvp20和arbfp2。
圖1
目前,在Mesa上,GLSL首先被編譯器翻譯成tgsi中間語言,然后顯卡特定的驅(qū)動將這些tgsi語言的代碼編譯成GPU指令,這個過程如圖1示(不考慮geometry shader和tesselation shader)。這里有tgsi的詳細(xì)描述。這篇碩士論文“高級著色語言及其優(yōu)化編譯”對GLSL和低級著色語言有比較詳細(xì)的論述。
2 R600指令
在CPU上運(yùn)行的程序,所有的訪存指令和運(yùn)算指令按照代碼的堆疊順序執(zhí)行(不考慮指令集并行),如果有跳轉(zhuǎn)指令則跳轉(zhuǎn)到相應(yīng)位置。
GPU主要用于運(yùn)算,早期的GPU沒有包含復(fù)雜的控制程序,R600 Shader程序和CPU程序比較顯得比較簡陋,必須有專門的代碼指示程序的執(zhí)行順序,并且指示程序運(yùn)行順序的指令(Control Flow 指令,后面稱為CF 指令)、運(yùn)算指令(后面稱ALU指令)和訪存指令(在R600 GPU 中稱為Fetch 指令)必須按照類別存放,同一種類型的指令放在一起,不同類型的指令按照某種順序存放,同一類型的指令(不包括CF指令)構(gòu)成一個Clause。圖2顯示了R600 GPU Shader程序在顯存中存放的形式。
圖2
R600的指令包含Control Flow(后面簡稱CF)指令,ALU(運(yùn)算)指令,Vertex Fecth (取頂點(diǎn))指令和Texture Fetch(取紋理)指令。指令的格式稱為Microde Format。
每一個Shader程序(Pixel Shader或者Vertex Shader)包含兩部分,一部分是CF指令,另一部分是Clause。 這些Clause由CF 指令初始化(或者不恰當(dāng)?shù)睦斫獬蒀lause 由CF指令調(diào)用)。R600的每一條指令的格式(為了保持和手冊上術(shù)語的一致,后面將使用Microcode Format這個詞)都包含了2 個或者4個DWORD(CF 和ALU為2個DWORD,Vertex Fetch 和Texture Fetch 為4個DWORD,后面在說地址的時候都是以DWORD為單位的),這些Microcode Format可以在“R600 Family Instruction Set Architecture”手冊上查閱到。
下面將使用兩個實(shí)例來說明R600的指令集。和下面這兩個實(shí)例等價的GLSL程序大概是這個樣子的:
// vertex shader
attribute vec4 a_position;
attribute vec3 a_texture;
varying vec2 v_texCoord;
void main()
{
gl_Position = a_position;
v_texCoord = a_texture;
}
?// pixel shader
uniform sample2D sampler;
varying vec2 v_texCoord;
void main ()
{
gl_FragColor = texture2D(sampler, v_texCoord);
}
3 Vertex Shader示例
下面使用一個具體的實(shí)例來說明,下面的程序來自我們的R600 EXA驅(qū)動的Copy過程的Vertex Shader(請參考后續(xù)章節(jié)),按照圖2的要求,這段程序被分成了兩部分,第一部分是CF指令,共四條指令,指令0~指令3(指令3為空指令,用于對齊),第二部分為取頂點(diǎn)指令,共兩條指令,指令4~ 指令5,分別用于取頂點(diǎn)位置坐標(biāo)和紋理坐標(biāo)。
int R600_copy_vs(RADEONChipFamily ChipSet, uint32_t* shader)
{
int i = 0;
/* 0 ? 指令0 */
shader[i++] = CF_DWORD0(ADDR(4));
shader[i++] = CF_DWORD1(POP_COUNT(0),?CF_CONST(0),
COND(SQ_CF_COND_ACTIVE),?I_COUNT(2),?CALL_COUNT(0),
END_OF_PROGRAM(0),?VALID_PIXEL_MODE(0),?CF_INST(SQ_CF_INST_VTX),
WHOLE_QUAD_MODE(0),?BARRIER(1));
/* 1 ?指令1 */
shader[i++] = CF_ALLOC_IMP_EXP_DWORD0(ARRAY_BASE(CF_POS0),?TYPE(SQ_EXPORT_POS),?RW_GPR(1),
RW_REL(ABSOLUTE),?INDEX_GPR(0),?ELEM_SIZE(0));
shader[i++] = CF_ALLOC_IMP_EXP_DWORD1_SWIZ(SRC_SEL_X(SQ_SEL_X),?SRC_SEL_Y(SQ_SEL_Y),?SRC_SEL_Z(SQ_SEL_Z),?
SRC_SEL_W(SQ_SEL_W),?R6xx_ELEM_LOOP(0),?BURST_COUNT(0),?END_OF_PROGRAM(0),
VALID_PIXEL_MODE(0),?CF_INST(SQ_CF_INST_EXPORT_DONE),?WHOLE_QUAD_MODE(0),
BARRIER(1));
/* 2 ?指令2 */
shader[i++] = CF_ALLOC_IMP_EXP_DWORD0(ARRAY_BASE(0),?TYPE(SQ_EXPORT_PARAM),?RW_GPR(0),
RW_REL(ABSOLUTE),?INDEX_GPR(0),?ELEM_SIZE(0));
shader[i++] = CF_ALLOC_IMP_EXP_DWORD1_SWIZ(SRC_SEL_X(SQ_SEL_X),?SRC_SEL_Y(SQ_SEL_Y),
SRC_SEL_Z(SQ_SEL_Z),?SRC_SEL_W(SQ_SEL_W),?R6xx_ELEM_LOOP(0),
BURST_COUNT(0),?END_OF_PROGRAM(1),?VALID_PIXEL_MODE(0),
CF_INST(SQ_CF_INST_EXPORT_DONE),?WHOLE_QUAD_MODE(0),?BARRIER(0));
/* 3 ?指令3*/
shader[i++] = 0x00000000;
shader[i++] = 0x00000000;
/* 4/5 指令4 */
shader[i++] = VTX_DWORD0(VTX_INST(SQ_VTX_INST_FETCH),?FETCH_TYPE(SQ_VTX_FETCH_VERTEX_DATA),
FETCH_WHOLE_QUAD(0),?BUFFER_ID(0),?SRC_GPR(0),?SRC_REL(ABSOLUTE),
SRC_SEL_X(SQ_SEL_X),?MEGA_FETCH_COUNT(16));
shader[i++] = VTX_DWORD1_GPR(DST_GPR(1),?DST_REL(0),?DST_SEL_X(SQ_SEL_X),?DST_SEL_Y(SQ_SEL_Y),
DST_SEL_Z(SQ_SEL_0),?DST_SEL_W(SQ_SEL_1),?USE_CONST_FIELDS(0),
DATA_FORMAT(FMT_32_32_FLOAT),?NUM_FORMAT_ALL(SQ_NUM_FORMAT_SCALED),
FORMAT_COMP_ALL(SQ_FORMAT_COMP_SIGNED),?SRF_MODE_ALL(SRF_MODE_ZERO_CLAMP_MINUS_ONE));
shader[i++] = VTX_DWORD2(OFFSET(0),
#if X_BYTE_ORDER == X_BIG_ENDIAN
ENDIAN_SWAP(SQ_ENDIAN_8IN32),
#else
ENDIAN_SWAP(SQ_ENDIAN_NONE),
#endif
CONST_BUF_NO_STRIDE(0),?MEGA_FETCH(1));
shader[i++] = VTX_DWORD_PAD;
/* 6/7 指令5 */
shader[i++] = VTX_DWORD0(VTX_INST(SQ_VTX_INST_FETCH),?FETCH_TYPE(SQ_VTX_FETCH_VERTEX_DATA),
FETCH_WHOLE_QUAD(0),?BUFFER_ID(0),?SRC_GPR(0),?SRC_REL(ABSOLUTE),
SRC_SEL_X(SQ_SEL_X),?MEGA_FETCH_COUNT(8));
shader[i++] = VTX_DWORD1_GPR(DST_GPR(0),?DST_REL(0),?DST_SEL_X(SQ_SEL_X),?DST_SEL_Y(SQ_SEL_Y),
DST_SEL_Z(SQ_SEL_0),?DST_SEL_W(SQ_SEL_1),?USE_CONST_FIELDS(0),?
DATA_FORMAT(FMT_32_32_FLOAT),?NUM_FORMAT_ALL(SQ_NUM_FORMAT_SCALED),
FORMAT_COMP_ALL(SQ_FORMAT_COMP_SIGNED),
SRF_MODE_ALL(SRF_MODE_ZERO_CLAMP_MINUS_ONE));
shader[i++] = VTX_DWORD2(OFFSET(8),
#if X_BYTE_ORDER == X_BIG_ENDIAN
ENDIAN_SWAP(SQ_ENDIAN_8IN32),
#else
ENDIAN_SWAP(SQ_ENDIAN_NONE),
#endif
CONST_BUF_NO_STRIDE(0),?MEGA_FETCH(0));
shader[i++] = VTX_DWORD_PAD;
return i;
}
上面程序的運(yùn)行過程如
結(jié)合下面這幾張圖詳細(xì)描述程序運(yùn)行的過程。
圖3
圖4
圖5
圖3示,初始狀態(tài),圖中有兩個線程,此刻兩個線程正在要兩個頂點(diǎn)數(shù)據(jù)進(jìn)行處理。
- CF指令0,?指令0的ADDR位指示程序從地址4處的指令(指令4)開始運(yùn)行,I_COUNT位指示共執(zhí)行2條指令(指令4和指令5),執(zhí)行完后回到指令0,指令0的END_OF_PROGRAM位表明程序還沒有結(jié)束,繼續(xù)執(zhí)行CF指令1。
- Vertex Fetch Clause,CF的指令0指明程序會從第指令4處開始執(zhí)行,指令4和指令5構(gòu)成一個Vertex Fetch Clause,兩條指令一起完成取頂點(diǎn)數(shù)據(jù),這里是要進(jìn)行Copy操作,頂點(diǎn)數(shù)據(jù)包括頂點(diǎn)的位置坐標(biāo)和紋理坐標(biāo)。由于是2D操作,因此這里的坐標(biāo)的有效分量只有兩個。 指令4的VTX_INST位表明改指令是一條取數(shù)據(jù)的指令,從BUFFER_ID為0 的內(nèi)存處取頂點(diǎn)(FETCH_TYPE)數(shù)據(jù),SRC_GPR為索引號所在的源寄存器位置,一次取的數(shù)據(jù)量為16字節(jié)(一個四元向量的大小),取出來的數(shù)據(jù)被放置在編號為1的寄存器中(DST_GPR),DST_SEL_X(SQ_SEL_X)表明取出來的向量的X分量放置到目的寄存器第一個DWORD位置處,Y分量放置到第二個DWORD(DST_SEL_Y(SQ_SEL_Y)),目的寄存器的第三個DWORD 處被置為0,第四個DWORD 處被置為1(可以使用0,1或者0.5)。指令5和指令4類似,由于所有頂點(diǎn)屬性數(shù)據(jù)已經(jīng)取完,因此原來存在于GPR0 的地址不再需要,可以覆蓋掉。圖4。
- CF指令1和指令2,這是兩條輸出指令,指令所做的工作如圖5示,指令1用于輸出頂點(diǎn)的位置坐標(biāo)(TYPE(SQ_EXPORT_POS)),這條指令從GPR1(RW_GPR(1))中讀取數(shù)據(jù),將數(shù)據(jù)輸出到Position Buffer 0 中(ARRAY_BASE(CF_POS0))。輸出的時候還有一個Swizzle操作,這條指令的Swizzle操作沒有變換向量各個分量。指令1的END_OF_PROGRAM標(biāo)志表明程序還沒有結(jié)束,因此繼續(xù)執(zhí)行指令2,指令2的END_OF_PROGRAM位表明程序至此結(jié)束(后面如果還寫有指令將不會被執(zhí)行)。
4 Pixel Shader示例
/* copy ps --------------------------------------- */
int R600_copy_ps(RADEONChipFamily ChipSet, uint32_t* shader)
{
int i=0;
/* CF INST 指令 0 */
shader[i++] = CF_DWORD0(ADDR(2));
shader[i++] = CF_DWORD1(POP_COUNT(0),?CF_CONST(0),?COND(SQ_CF_COND_ACTIVE),?I_COUNT(1),
CALL_COUNT(0),?END_OF_PROGRAM(0),?VALID_PIXEL_MODE(0),?CF_INST(SQ_CF_INST_TEX),
WHOLE_QUAD_MODE(0),?BARRIER(1));
/* CF INST 指令 1 */
shader[i++] = CF_ALLOC_IMP_EXP_DWORD0(ARRAY_BASE(CF_PIXEL_MRT0),?TYPE(SQ_EXPORT_PIXEL),?RW_GPR(0),
RW_REL(ABSOLUTE),?INDEX_GPR(0),?ELEM_SIZE(1));
shader[i++] = CF_ALLOC_IMP_EXP_DWORD1_SWIZ(SRC_SEL_X(SQ_SEL_X),?SRC_SEL_Y(SQ_SEL_Y),?
SRC_SEL_Z(SQ_SEL_Z),?SRC_SEL_W(SQ_SEL_W),?R6xx_ELEM_LOOP(0),?BURST_COUNT(1),
END_OF_PROGRAM(1),?VALID_PIXEL_MODE(0),?CF_INST(SQ_CF_INST_EXPORT_DONE),
WHOLE_QUAD_MODE(0),?BARRIER(1));
/* TEX INST 指令 2 */
shader[i++] = TEX_DWORD0(TEX_INST(SQ_TEX_INST_SAMPLE),?BC_FRAC_MODE(0),?FETCH_WHOLE_QUAD(0),
RESOURCE_ID(0),?SRC_GPR(0),?SRC_REL(ABSOLUTE),?R7xx_ALT_CONST(0));
shader[i++] = TEX_DWORD1(DST_GPR(0),?DST_REL(ABSOLUTE),?DST_SEL_X(SQ_SEL_X), /* R */?
DST_SEL_Y(SQ_SEL_Y), /* G */
DST_SEL_Z(SQ_SEL_Z), /* B */
DST_SEL_W(SQ_SEL_W), /* A */
LOD_BIAS(0),
COORD_TYPE_X(TEX_UNNORMALIZED),?COORD_TYPE_Y(TEX_UNNORMALIZED),
COORD_TYPE_Z(TEX_UNNORMALIZED),?COORD_TYPE_W(TEX_UNNORMALIZED));
shader[i++] = TEX_DWORD2(OFFSET_X(0),?OFFSET_Y(0),?OFFSET_Z(0),?SAMPLER_ID(0),?SRC_SEL_X(SQ_SEL_X),
SRC_SEL_Y(SQ_SEL_Y),?SRC_SEL_Z(SQ_SEL_0),?SRC_SEL_W(SQ_SEL_1));
shader[i++] = TEX_DWORD_PAD;
return i;
}
這里總共三條指令,其中CF指令0和CF指令1是兩條CF指令,TEX指令2是一條取紋理的指令。
指令0表明程序?qū)腶ddr為2的指令2處開始執(zhí)行,指令2是一條texture fetch 指令,這條指令根據(jù)GPR0中給出的紋理坐標(biāo)(SRC_GPR(0),根據(jù)前面semantic的配置,被插值的紋理坐標(biāo)存放在GPR0中)從id號為0的紋理資源中取出紋理值,放入到GPR0中(DST_GPR(0x0))。
取紋理操作完成后,執(zhí)行指令1,指令1是一條輸出指令,將取到的紋理直接放到Render target 0(ARRAY_BASE(CF_PIXEL_MRT0))上去。
R600顯卡的指令遠(yuǎn)不止以上這些,讀者在理解上面的內(nèi)容之后,閱讀R600指令集手冊將不會有太大困難,感興趣的可以深入進(jìn)去了解更多的指令。
?
轉(zhuǎn)載于:https://www.cnblogs.com/shoemaker/p/linux_graphics11.html
總結(jié)
以上是生活随笔為你收集整理的【原创】Linux环境下的图形系统和AMD R600显卡编程(11)——R600指令集的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 烘干机多少钱一台啊?
- 下一篇: 《BI项目笔记》用Excel2013连接