[讀書筆記]Direct3D11基礎--緩衝區資源(Buffer Resource)


翻譯自:pratical rendering and computation with Direct3D 11

介紹

在Direct3D 11中 Buffer Resource提供了一維線性內存塊。可以使用許多不同的配置來更改緩衝區的行爲,但是它們都具有相同的基本線性佈局
在這裏插入圖片描述
緩衝區的大小以字節爲單位度量,組成緩衝區的元素可以有不同的大小,這取決於緩衝區的類型,以及每個特定緩衝區類型的特定配置。一些緩衝區類型將主要用於應用程序的c++端,而另一些緩衝區類型將主要用於HLSL着色器程序。本節將探討每種不同類型的緩衝區,描述它們提供的功能,並演示如何創建它們。此外,我們將討論緩衝區的常見用法,並提供基本的HLSL語法來聲明和在着色器程序中使用這些資源。

Vertex Buffers

頂點緩衝區的目的是存放所有最終將組裝成頂點並通過渲染管線發送的數據。最簡單的頂點緩衝區配置是頂點結構數組,其中每個頂點包含位置、法向量和紋理座標等元素。這些頂點元素必須符合可用的格式和類型規範。然而,在頂點中需要的任何通用信息也可以打包到頂點結構中,以允許爲特定的渲染算法定製輸入數據。
除了上面描述的簡單數組樣式的頂點緩衝區之外,這些緩衝區還允許其他一些更復雜的配置。例如,可以同時使用多個頂點緩衝區。這允許頂點數據分開存儲在不同的緩衝區中。例如,頂點位置可以存儲在一個緩衝區中,頂點法向量可以存儲在另一個緩衝區中。這允許應用程序根據需要有選擇地添加頂點數據,而不是爲所有渲染場景使用一個大的整體緩衝區。這種技術可以用來減少渲染操作所需的帶寬。
也可以執行實例渲染,其中一個或多個頂點緩衝區提供模型的每個頂點數據,另外一個頂點緩衝區提供每個實例數據,而不是每個頂點數據。通過第二個緩衝區的實例數據,將第一個緩衝區的模型數據渲染成一系列的實例模型。每個實例的數據可以包括一個世界座標轉換、顏色變化或用於區分模型的不同實例的任何其他內容。這種設置允許通過一個draw調用渲染許多對象,從而降低了渲染操作的總體CPU開銷。
在這裏插入圖片描述
從圖中可以看出,每種類型的頂點緩衝區都遵循我們的一般緩衝區佈局。每個緩衝區都是由相同大小的元素組成的一維數組。單個數據元素的大小總是與同一緩衝區中的其他元素的大小相同,儘管如果使用多個緩衝區,它們可能具有不同的元素大小。

Vertex Buffer使用

如上所述,頂點緩衝區的主要目的是爲渲染管線提供每個頂點的信息,可以是直接提供,也可以是通過實例提供。爲此,將頂點緩衝區綁定到管道的主要位置位於input assembler階段,該階段作爲渲染管線的入口點。除了input assembler階段,頂點緩衝區也可以附加到stream output階段,以允許渲染管線將頂點數據流到緩衝區中,以便在後續的渲染 passes中使用數據。

創建Vertex Buffer

在渲染管線中可以綁定資源的位置,每種資源都必須在創建時設置相應的綁定標誌,以便允許將資源綁定到那裏。記住這一點,頂點緩衝區必須始終設置d3d11_bind_vertex_buffer綁定標誌。如果該緩衝區還用於從渲染管線中流出頂點數據那麼還可以選擇性的包含D3D11_BIND_STREAM_OUTPUT綁定標誌。
除了綁定標誌的選擇之外,頂點緩衝區的另一個主要考慮因素是緩衝區將要使用的場景。取決於頂點緩衝區的內容是否會頻繁更改,以及這些更改是否來自CPU或GPU,將需要不同的使用標誌。例如,如果將加載到緩衝區的數據是靜態的,那麼應該使用d3d11_usage_constant usage標誌創建緩衝區資源。在這種情況下,當創建緩衝區時,它將通過傳遞給創建方法的D3D11_SUBRESURCE_DATA參數用頂點數據初始化,並且再也不會被修改。這種類型的頂點緩衝區的一個例子將用於保存靜態地形網格的內容。
但是,如果CPU經常更新緩衝區,則應該使用D3D11_USAGE_DYNAMIC創建緩衝區,併爲CPU訪問標誌參數使用CPU寫標誌。這種類型的頂點緩衝區使用的一個例子是,當頂點轉換在CPU上而不是GPU上執行時。然後,這些更新將被複制到每個幀的緩衝區資源中。這是一種常用的技術,通過將所有模型數據放入同一參照系(通常是world space或view space),將許多draw調用壓縮爲一個調用。第三種使用類型是創建一個緩衝區,該緩衝區將由GPU使用流輸出功能進行更新。在本例中,將使用D3D11_USAGE_DEFAULT usage標誌。
在這裏插入圖片描述

ID3DllBuffer* CreateVertexBuffer( UINT size,bool dynamic,bool streamout,D3D11_SUBRES0URCE_DATA* pData )
{
	D3D11_BUFFER_DESC desc;
	desc.ByteWidth = size;
	desc.MiscFlags = 0;
	desc.StructureByteStride = 0;
// Select the appropriate binding locations based on the passed in flags
	if ( streamout )
		desc.BindFlags = D3D11_BIND_VERTEX_BUFFER | D3D11_BIND_STREAM_0UTPUT;
	else
		desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
// Select the appropriate usage and CPU access flags based on the passed
// in flags
	if ( dynamic )
	{
		desc.Usage = D3D11_USAGE_DYNAMIC;
		desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	}
	else
	{
		desc.Usage = D3D11_USAGE_IMMUTABLE;
		desc.CPUAccessFlags = 0;
	}
// Create the buffer with the specified configuration
	ID3D11Buffer* pBuffer = 0;
	HRESULT hr = g_pDevice->CreateBuffer( &desc, pData, &pBuffer );
	if ( FAILED( hr ) )
	{
	// Handle the error here...
		return( 0 );
	}
	return( pBuffer );
}

Resource View

頂點緩衝區直接綁定到IA階段或Stream Output階段。因此,在使用資源視圖時不需要創建資源視圖。

Index Buffer

介紹

索引緩衝區提供了通過引用頂點緩衝區中存儲的頂點數據來定義基本類型的非常有用的功能。索引緩衝區或多或少提供指向頂點列表的索引列表。根據所需的基元類型(如點、線和三角形),將形成適當大小的索引組,以定義該基元由哪些頂點組成。
索引緩衝區的使用可能會顯著減少需要定義的頂點總數。由於每個相鄰基元定義可以引用與其相鄰基元相同的頂點數據,因此共享頂點不需要在頂點緩衝區中重複。此外,這種頂點共享允許多個原語使用來自頂點着色器的相同輸出頂點。

在這裏插入圖片描述

使用Index Buffer

由於索引緩衝區指定了要在基本設置操作中使用哪些頂點,所以您必須提前知道將要使用什麼基本拓撲—否則您將不知道將索引放入哪個順序。這通常是通過選擇渲染算法和幾何加載例程提前確定的。在準備執行draw函數的渲染管線配置過程中,所需的索引緩衝區被綁定到input assembler階段(IA),該階段將用於爲管道生成輸入圖元( input primitives)。由於這些緩衝區具有非常特殊的用途,所以它們通常不綁定到其他位置的管道。這個綁定位置如圖
在這裏插入圖片描述

創建Index Buffer

在創建索引緩衝區時,我們遵循標準的緩衝區創建過程,並填充D3D11_BUFFER_DESC結構。索引緩衝區的描述結構不會在緩衝區之間頻繁更改,因爲這類數據通常在內容創建程序中定義一次,然後按原樣導出。在應用程序啓動階段之後,索引緩衝區索引通常不會更改。但是,如果某些新算法確實需要動態更新索引緩衝區,也可以使用跟Vertext Buffers一樣的動態更新屬性來創建它。這可以在“Vertext Buffers”一節中討論的draw call reduction方案中使用,在該方案中,將多個幾何集合動態地分組到一起,形成一組頂點和索引緩衝區。

	ID3DllBuffer* CreateIndexBuffer( UINT size,bool dynamic,D3D11_SUBRES0URCE_DATA* pData )
	{
		D3D11_BUFFER_DESC desc;
		desc.ByteWidth = size;
		desc.MiscFlags = 0;
		desc.StructureByteStride = 0;
		deSC.BindFlagS = D3D11_BIND_INDEX_BUFFER;
		// Select the appropriate usage and CPU access flags based on the passed
		// in flags
		if ( dynamic )
		{
			desc.Usage = D3D11_USAGE_DYNAMIC;
			desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
		}
		else
		{
			desc.Usage = D3D11_USAGE_IMMUTABLE;
			desc.CPUAccessFlags = 0;
		}
		// Create the buffer with the specified configuration
		ID3DllBuffer* pBuffer = 0;
		HRESULT hr = g_pDevice->CreateBuffer( &desc, pData, &pBuffer );
		if ( FAILED( hr ) )
		{
		// Handle the error here...
			return( 0 );
		}
		return( pBuffer );
	}

要指定的第一項是緩衝區的大小(以字節爲單位)。從代碼清單中可以看到,索引緩衝區總是使用D3D11_BIND_INDEX_BUFFER綁定標誌創建的。當需要靜態索引緩衝區時,usage標誌指定爲D3D11_USAGE_IMMUTABLE,沒有任何CPU訪問標誌設置。在本例中,必須使用D3D11_SUBRES0URCE_DATA結構將緩衝區的預期內容提供給創建調用。如果需要動態緩衝區,我們將選擇D3D11_USAGE_DYNAMIC,以及D3D11_CPU_ACCESS_WRITE CPU訪問標誌。

Resource view

索引緩衝區直接綁定到IA程序階段,不需要資源視圖的幫助。因此,不需要創建資源視圖來使用索引緩衝區。

Constant Buffer

介紹

Constant Buffer它可以從可編程着色器階段訪問,並隨後在HLSL代碼中使用。一個Constant Buffer是用來提供constant信息給可編程着色程序正在執行的管道。術語constant是指在執行draw或dispatch調用期間,Buffer中的數據始終保持常量。任何只在管道調用之間更改的信息,例如世界轉換矩陣或對象顏色,都將在一個常量緩衝區中提供給着色器程序。這種機制是將數據從主機應用程序傳輸到每個可編程着色器階段的主要方法。常量緩衝區中包含的信息的類型和數量可能因緩衝區而異。這完全取決於每個特定着色器程序所需的數據,由着色器程序中的結構聲明定義。緩衝區可以或多或少地針對基本HLSL類型的任何組合以及由這些基本類型組成的結構進行定製。這些類型包括標量、向量、矩陣、這些類型的數組、類實例以及結構中每種類型的組合。如圖描述了這一類型的單一組合:
在這裏插入圖片描述
頂點緩衝區和索引緩衝區都定義一個基本的數據元素,然後以類似數組的方式多次重複該元素。而常量緩衝區不一樣,常量緩衝區定義了一個基本元素,但它沒有提供元素的一個以上實例。創建緩衝區時,緩衝區的大小足以容納所需的信息,但不是更大。這意味着常量緩衝區實現的是結構而不是數組。

使用常量緩衝區

每個可編程管道階段都可以接受一個或多個常量緩衝區。然後,它使緩衝區中的信息可以在着色器程序中使用。訪問數據時就像在着色器程序中全局聲明結構內容一樣。這意味着每個結構的元素必須在這個僞全局範圍內具有唯一的名稱。這種能力爲向特定渲染算法所需的着色器程序添加變化提供了很大的靈活性。需要注意的是,爲將緩衝區綁定爲常量緩衝區而創建的緩衝區可能不會綁定到管道上的任何其他類型的連接點。實際上,這不是一個問題,因爲一個常量緩衝區的內容已經可以用於所有可編程的管道階段。如圖所示突出顯示了用於綁定常量緩衝區的管道上的位置。
在這裏插入圖片描述
能夠使用相對較大的常量緩衝區並不意味着您應該爲着色器程序中所需的所有變量自動創建一個較大的緩衝區。由於CPU在每次使用之間都會更新常量緩衝區,所以在進行任何更改之後,必須將它們的內容上傳到GPU。讓我們假設一個情況。假設着色器程序需要十個參數,但是在每次渲染對象之間,只有兩個參數會發生變化。在本例中,我們將在每次draw調用之間將所有10個參數寫入緩衝區,只是爲了更新兩個不同的參數,因爲整個緩衝區總是完全更新的。根據要渲染的對象的數量,這些額外的參數更新可能會造成大量不必要的帶寬浪費。如果我們創建兩個常量緩衝區,其中一個保存八個靜態參數,另一個保存兩個動態參數,那麼我們只能更新不斷變化的參數,並顯著減少每個幀所需的數據更新量。但是,我們需要確保緩衝區只在真正需要時才更新!
更新常量緩衝區的另一個潛在的缺點是,這些緩衝區只支持從着色器程序讀取訪問。因爲它是隻讀的,所以可以同時將一個常量緩衝區綁定到管道中的多個位置,而不可能導致內存訪問衝突。

創建Constant Buffer

常量緩衝區還提供了許多不同的資源配置,以獲得最佳的性能。根據CPU更新常量緩衝區的頻率,選擇兩種典型配置。如果在應用程序的生命週期中多次更新常量緩衝區,則動態緩衝區資源最有意義。當然,如果一個常量緩衝區將包含一些不會在整個應用程序中更改的數據(例如固定的後臺緩衝區(back buffer)大小),那麼將它創建爲一個完全靜態的緩衝區會更有效,使用不可變的使用標誌。

	ID3DllBuffer* CreateConstantBuffer( UINT size,bool dynamic,bool CPUupdates,
	D3D11_SUBRES0URCE_DATA* pData )
	{
		D3D11_BUFFER_DESC desc;
		desc.ByteWidth = size;
		desc.HiscFlags = 0;
		desc.StructureByteStride = 0;
		desc.BindFlags = D3Dll_BIND_C0NSTANT_BUFFERj
	// Select the appropriate usage and CPU access flags based on the passed
	// in flags
		if ( dynamic && CPUupdates )
		{
			desc.Usage = D3D11_USAGE_DYNAMIC;
			desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
		}
		else if ( dynamic && !CPUupdates )
		{
			desc.Usage = D3D11_USAGE_DEFAULT;
			desc.CPUAccessFlags = 0;
		}
		else
		{
			desc.Usage = D3D11_USAGE_IMMUTABLE;
			desc.CPUAccessFlags = 0;
		}
		// Create the buffer with the specified configuration
		ID3D11Buffer* pBuffer = 0;
		HRESULT hr = g_pDevice->CreateBuffer( &desc, pData, &pBuffer );
		if ( FAILED( hr ) )
		{
		// Handle the error here...
			return( 0 );
		}
		return( pBuffer );
	}

與頂點緩衝區和索引緩衝區一樣,動態常量緩衝區的創建使用D3D11_USAGE_DYNAMIC usage標誌,並結合D3D11_CPU_ACCESS_WRITE標誌,以允許CPU在運行時更新資源。對於靜態內容,使用D3D11_ USAGE_IMMUTABLE usage且不使用任何CPU訪問標誌。另一種情況是,只有GPU在運行時才更新緩衝區,比如當使用ID3D11 DeviceContext:: copystructu()方法將Append/Consume緩衝區中的元素數量複製到常量緩衝區時。在本例中,我們需要一個默認的usage標誌,但是我們不設置任何CPU訪問標誌。這允許在GPU上使用時優化創建的資源。幾個需要注意的地方:第一,應用程序通常定義一個c++結構,該結構反映HLSL中常量緩衝區的所需內容。這允許將應用程序更新應用於此結構的系統內存實例,然後可以將該實例直接複製到緩衝區中。第二,必須使用字節寬度(ByteWidth是16字節的倍數)創建常量緩衝區。這一要求允許使用GPU的4元組寄存器類型高效地處理緩衝區,並且它只作爲常量緩衝區的要求出現。這必須在C/ c++的結構聲明中說明。第三,緩衝區描述不允許包含除D3D11_BIND_C0NSTAI\IT_BUFFER之外的任何其他綁定標誌。並不是一個很大的限制,因爲通常不需要將常量緩衝區綁定到管道中的另一個位置。

Resource view

儘管常量緩衝區綁定到可編程着色器階段,並且可以通過HLSL訪問,但是它們的內容不會以任何方式用資源視圖解釋。它們被精確地指定爲應該在HLSL中可用,因此不需要使用資源視圖。

HLSL常量緩衝資源對象

在HLSL中,常量緩衝區是用cbuffer關鍵字聲明的資源對象。

	cbuffer Transforms
	{
		matrix WorldMatrixj
		matrix ViewProjMatrix;
		matrix SkinMatrices[26];
	};
	cbuffer LightParameters
	{
		float3 LightPositionWS;
		float4 LightColor;
	};
	cbuffer ParticlelnsertParameters
	{
		float4 EmitterLocation;
		float4 RandomVector;
	};

在這個cbuffer結構中聲明的每個變量都可以在HLSL着色器程序中直接使用,就像在全局範圍中聲明一樣。主機應用程序使用常量緩衝區的名稱來按名稱標識緩衝區,並將適當的內容加載到緩衝區中。但是,在HLSL中不使用緩衝區名。與常量緩衝區名稱一樣,常量緩衝區的各個元素的名稱和類型也可以通過着色器反射API訪問。這一系列方法可用於確定每個子參數的名稱和類型,從而允許應用程序知道在運行時將哪些信息插入緩衝區。

Buffer/Structured Buffer

介紹

接下來的資源有兩個名稱,這取決於它包含的數據類型。standard buffer resource是指其元素是內置數據類型之一的緩衝區。通過這種方式,標準緩衝區資源類似於值數組。每個值都存儲在一個惟一的數組位置,並且可以由其位置的索引引用。這使得GPU上着色器程序的許多不同實例可以同時輕鬆地訪問資源。由於每個元素都是唯一標識的,開發人員可以輕鬆地構造程序以避免任何內存衝突。
在這裏插入圖片描述
與緩衝區資源非常相似的是結構化緩衝區資源。兩者之間的唯一區別是,結構化緩衝區允許用戶將結構定義爲基本元素,而不是內置的數據類型之一。這使得將特定處理問題的數據映射到資源相對簡單。如果開發人員可以在c++中定義一個合適的結構,那麼可以在HLSL中定義一個相應的結構,並且緩衝區將包含這些結構的數組,這些結構可以被可編程着色器階段用作資源對象。
結構化緩衝區旨在爲簡化自定義算法開發提供靈活的內存資源。由於結構中的數據格式可以使用HLSL中的任何可用類型,因此可以定製它來適應特定的場景。可用的結構形式與常量緩衝區非常相似,只是結構化緩衝區提供了所需結構的數組,而不是常量緩衝區這樣的單個實例。
在這裏插入圖片描述
這裏我們看到粒子結構使用多個變量,完整的粒子系統包含在結構化緩衝區中。這類緩衝區可以用於可編程管道階段的編寫和讀取。

使用buffers/structured buffers

由於能夠提供大量的結構化數據,對於可編程着色器階段要訪問的較大數據結構,緩衝區和結構化緩衝區是很好的選擇。這些緩衝區是附加到管道的第一個資源,帶有資源視圖,而不是直接綁定。這些緩衝區可用於對所有可編程管道階段的讀訪問,根據使用的資源視圖,還可以在像素和計算着色器階段進行讀/寫訪問。這意味着這些資源可能提供了不同管道階段之間以及同一管道階段內的各個處理元素之間通信的方法。
管道階段的資源訪問功能由用於將資源綁定到管道的資源視圖類型決定。與常量緩衝區不同,緩衝區/結構化緩衝區必須通過資源視圖綁定到管道,這意味着必須使用適當的綁定標誌創建它,以便與着色器資源視圖、無序訪問視圖或兩者都綁定。着色器資源視圖允許綁定到所有可編程管道階段,而無序訪問視圖只允許綁定到像素和計算着色器階段。由於執行對資源的寫訪問需要無序訪問視圖,因此UAV的可用綁定位置可以有效地確定這些寫訪問可以在哪裏執行。着色器資源視圖和無序訪問視圖的可用連接點如圖所示。與常量緩衝區一樣,當用於只讀用途時,緩衝區/結構化緩衝區可以綁定到多個位置。這是通過使用着色器資源視圖將其綁定到管道來實現的。然而,當使用無序訪問視圖時,它可能只綁定到一個位置。這是由運行時強制執行的,如果將資源綁定到多個位置進行寫入,運行時將打印錯誤消息。無序訪問視圖的使用還排除了同時使用着色器資源視圖,因爲它可能會帶來read-after-write的問題。
在這裏插入圖片描述

創建buffers/structured buffers

正如我們在其他緩衝區資源創建討論中看到的,必須確定緩衝區/結構化緩衝區將經歷哪種使用場景。這些資源通常有三種不同類型的使用模式。第一種情況是緩衝區中的數據在應用程序的整個生命週期內都是靜態的。例如,使用緩衝區/結構化緩衝區資源來保存預計算的數據,例如預計算 radiance transfer data或其他形式的查找表。第二種情況是CPU需要週期性地將數據加載到緩衝區中。在許多通用計算系統中,這是一種可能的場景,其中GPU被用作CPU的協處理器,並不斷向GPU提供要處理的新數據。在這兩種情況下,GPU只允許讀取緩衝區數據,而不能寫入資源。
最後一種情況是,當緩存內容以某種方式由GPU本身更新時。一個常見的例子是,GPU正在執行某種類型的物理模擬,稍後將用於渲染模擬結果的表示。在這種情況下,GPU將需要對緩衝區進行讀寫訪問,以便讀取內容、執行一些更新,然後將結果寫回緩衝區。這三個場景中的每一個都需要不同的設置,用於創建緩衝區時使用的使用、CPU訪問和綁定標誌。
除了這些標誌之外,我們還必須在使用結構化緩衝區時提供一些額外的大小信息。所有類型的緩衝區都要求指定ByteWidth參數,該參數以字節爲單位指示緩衝區的期望大小。結構化緩衝區還要求我們指定正在使用的結構元素的大小。當資源被可編程管道階段之一訪問時,此信息用作步驟大小。使用結構化緩衝區的另一個要求是表明緩衝區資源確實將用作結構化緩衝區。這是通過設置D3D11_RESOURCE_MISC_BUFFER_STRUCTURED雜項標誌實現的。

	ID3DllBuffer* CreateStructuredBuffer( UINT count, UINT structsize, bool CPUWritable, bool GPUWritable, D3D11_SUBRES0URCE_DATA* pData )
	{
		D3D11_BUFFER_DESC desc;
		desc.ByteWidth = count * structsize;
		desc.MiscFlagS = D3D11_RES0URCE_MISC_BUFFER_STRUCTURED;
		desc.StructureByteStride = structsize;
		// Select the appropriate usage and CPU access flags based on the passed in flags
		if ( !CPUWritable && IGPUWritable )
		{
			desc.BindFlagS = D3D11_BIND_SHADER_RES0URCE;
			desc.Usage = D3D11_USAGE_IMMUTABLE;
			desc.CPUAccessFlags = 0;
		}
		else if ( CPUWritable && IGPUWritable )
		{
			desc.BindFlagS = D3D11_BIND_SHADER_RES0URCE;
			desc.Usage = D3D11_USAGE_DYNAMIC;
			desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
		}
		else if ( ICPUWritable && GPUWritable )
		{
			desc.BindFlagS = D3D11_BIND_SHADER_RES0URCE |
			D3D11_BIND_UN0RDERED_ACCESS;
			desc.Usage = D3D11_USAGE_DEFAULT;
			desc.CPUAccessFlags = 0;
		}
		else if ( CPUWritable && GPUWritable )
		{
		// Handle the error here...
		// Resources can't be writable by both CPU and GPU simultaneously!
		}
		// Create the buffer with the specified configuration
		ID3DllBuffer* pBuffer = 0;
		HRESULT hr = g_pDevice->CreateBuffer( &desc, pData, &pBuffer );
		if ( FAILED( hr ) )
		{
		// Handle the error here...
			return( 0 );
		}
		return( pBuffer );
	}

我們將大小信息分割爲要包含在緩衝區中的結構數量的計數,並使用structsize參數以字節表示每個結構的大小。然後,這些參數用於計算緩衝區的總字節寬度。此外,上面討論的三個使用場景中的每一個都在代碼清單中表示;它們通過CPU訪問、使用和綁定標誌將它們區分開來。

Resource view

惟一可以與緩衝區或結構化緩衝區一起使用的資源視圖是着色器資源視圖和無序訪問視圖。實際上,這是將這些緩衝區綁定到管道的唯一方法。對於標準緩衝區類型,必須在SRV描述結構中直接提供格式。對於結構化緩衝區資源,應該將格式設置爲DXGI_F0RMAT_UNKNOWN,因爲沒有一種DXGI格式可以匹配所有可能的結構類型。格式是從着色器程序中的HLSL結構聲明派生出來的,元素的大小由應用程序在創建緩衝區資源時提供。
緩衝區資源的着色器資源視圖描述結構要求指定開始元素和視圖中包含的元素數量。這可以選擇緩衝區中的整個元素範圍,也可以用來選擇總資源的子集。當從HLSL查看時,這有效地將選擇的數據映射到[0,width-1]範圍,而所有其他訪問都被認爲超出界限的。這些值在D3D11_BUFFER_SRV結構中指定。

	ID3DllShaderResourceView* CreateBufferSRV( ID3DllResource* pResource )
	{
		D3D11_SHADER_RES0URCE_VIEW_DESC desc;
		// For structured buffers, DXGI_FORMAT_UNKNOWN must be used!
		// For standard buffers, utilize the appropriate format,
		desc.Format = DXGI_FORMAT_R32G32B32_FLOAT;
		desc.ViewDimension = D3D11_SRV_DIMENSI0N_BUFFER;
		desc.Buffer.ElementOffset = 0;
		desc.Buffer.ElementWidth = 100;
		ID3DllShaderResourceView* pView = 0;
		HRESULT hr = g_pDevice->CreateShaderResourceView( pResource, &desc,&pView );
		return( pView );
	}

通過調整ElementOffset和ElementWidth參數,可以使用一個大型緩衝區資源來包含一組較小的數據集,每個數據集都有自己的SRV,以提供對它們的訪問。通過使用着色器資源視圖來限制對資源的特定子集的訪問,並創建適當數量的着色器資源視圖,應用程序可以通過使用特定的着色器資源視圖有效地控制哪些數據對每次調用着色器程序都可見。也可以使用多個視圖同時選擇緩衝區的不同範圍。
無序訪問視圖使用相同的元素範圍選擇和格式規範,還提供了一組額外的標誌,可以爲特殊使用場景指定這些標誌。這些標誌允許無序訪問視圖用於 append/consume緩衝區和字節地址緩衝區,這兩者都是通過無序訪問視圖使用緩衝區資源的特殊方式。下面演示瞭如何創建無序訪問視圖。

	ID3D11UnorderedAccessView* CreateBufferUAV( ID3DllResource* pResource )
	{
		D3D11_UN0RDERED_ACCESS_VIEW_DESC desc;
		// For structured buffers, DXGI_FORMAT_UNKNOWN must be used!
		// For standard buffers, utilize the appropriate format,
		desc.Format = DXGI_FORMAT_R32G32B32_FLOAT;
		desc.ViewDimension = D3D11_UAV_DIMENSI0N_BUFFER;
		desc.Buffer.FirstElement = 0;
		desc.Buffer.NumElements = 100;
		desc.Buffer.Flags = D3D11_BUFFER_UAV_FLAG_C0UNTER;
		//desc.Buffer.Flags = D3D11_BUFFER_UAV_FLAG_APPEND;
		//desc.Buffer.Flags = D3D11_BUFFER_UAV_FLAG_RAW;
		ID3DllUnorderedAccessView* pView = 0;
		HRESULT hr = g_pDevice->CreateUnorderedAccessView( pResource, &desc,&pView );
		return( pView );
	}

清單顯示了D3D11_BUFFER_UAV_FLAG_C0UNTER標誌的設置,並註釋掉了附加標誌和原始標誌。此標誌用於爲可通過HLSL訪問的緩衝區資源提供計數器。這個計數器是通過使用HLSL緩衝資源對象的方法來操作的。

HLSL structured buffer resource

結構化緩衝區要求在着色器程序中定義相應的結構。一旦有了合適的結構,就使用類似模板的語法聲明結構化緩衝區。

	// Declare the structure that represents one fluid column's state
	struct GridPoint
	{
		float Height;
		float4 Flow;
	};
	// Declare the input and output resources
	RWStructuredBuffer<GridPoint> NewWaterState : register( u0 );
	StructuredBuffer<GridPoint> CurrentWaterState : register( t0 );

在這個代碼清單中,我們看到實際上有兩種不同的方法來聲明結構化緩衝區:StructuredBuffer<>和RWStructuredBuffero。這兩種形式分別代表了綁定到管道的結構化緩衝區(使用着色器資源視圖(用於只讀訪問)和無序訪問視圖(用於讀寫訪問)之間的差異。寄存器語句也指出了這一點,讀寫資源使用u#寄存器,只讀資源使用t#寄存器。聲明類型的選擇將決定應用程序必須使用哪種類型的資源視圖來將資源綁定到可編程着色器階段,以便在此着色器程序中使用。
一旦對象被聲明,我們可以看到有一種簡單的方法可以在HLSL中與結構化緩衝區交互。使用類似數組的語法訪問各個元素,使用方括號指示要使用哪個索引。這與c++的操作非常相似,可以使用點操作符訪問結構的各個成員。當資源與着色器資源視圖綁定時,只能讀取元素。但是,使用無序訪問視圖,也可以使用相同的語法將它們寫入。當使用resource視圖選擇緩衝區資源的一個子集時,它的元素似乎會被重新映射,這樣子範圍中的第一個元素就會被索引0訪問,隨後的每個元素都會被索引遞增的索引訪問。
這些HLSL資源對象還提供GetDimensions()方法,該方法返回緩衝區的大小,或者緩衝區的大小和結構化緩衝區的結構元素大小。這些可以被着色器程序用來實現範圍檢查。如果UAV使用D3D11_BUFFER_UAV_FLAG_C0UNTER標誌創建,也可以使用內置在RWStructuredBuffer中的計數器變量。如果使用這個標誌集創建UAV, HLSL程序可以使用IncrementCounter()和DecrementCounter()方法。通過提供存儲在結構化緩衝區中的元素的總數,可以使用這些元素實現定製的數據結構。使用此功能需要將UAV格式設置爲DXGI_FORMAT_R32_UNKNOWN。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章