您的位置:首页 > 编程语言 > C语言/C++

[C++]DirectX 12 3D游戏开发实战—第13章 学习笔记02 2019.5.11

2019-05-12 01:34 621 查看
版权声明:个人学习使用,请勿转载 https://blog.csdn.net/qq_36028073/article/details/90131266

个人学习使用,请勿转载

词汇

无序访问视图:Unordered Access View, UAV

内容

13.2 一个简单的计算着色器

cbuffer cbSettings
{
//计算着色器能访问的常量缓冲区数据
};
//数据源及着色器的输出
Texture2D gInputA;
Texture2D gInputB;
RWTexture2D<float4> gOutput;
//线程组中的线程数。组中的线程可以被设置为1D2D或者3D网格布局
[numthreads(16,16,1)]
void CS(int3 dispatchThreadID : SV_DispatchThreadID) //线程ID
{
//对两种源像素中纵横坐标分别为x,y出的纹素进行求和,并将结果保存到相应的gOutput
gOutput [dispatchThreadID.xy]=gInputA[dispatchThreadID.xy]+gInputB[dispatchThreadID.xy];
}

计算着色器由下列要素构成:

  1. 通过常量缓冲区访问的全局变量
  2. 输入与输出资源
  3. [numthreads(x,y,z)]属性,指定3D线程网格中的线程数量
  4. 每个线程都要进行的着色器指令
  5. 线程ID系统值参数
    我们能够根据需求定义出不同线程组布局。

计算流水线状态对象

为了开启计算着色器,还需要使用特定的计算流水线状态描述。此描述中的字段远少于D3D12_GRAPHICS_PIPELINE_STATE_DESC结构体,因为计算着色器位于图形流水线之外,因此所有图形流水线状态都不适用于计算着色器,示例如下:

D3D12_COMPUTE_PIPELINE_STATE_DESC wavesUpdatePSO = {};
wavesUpdatePSO.pRootSignature = mWaveRootSignature.Get();
wavesUpdatePSO.CS=
{
reinterpret_cast<BYTE*>(mShaders["wavesUpdateCS"]->GetBufferPointer()),
mShaders["wavespdateCS"]->GetBufferSize()
};
wavesUpdatePSO.Flags = D3D12_PIPELINE_STATE_FLAG_NONE;
ThrowIfFaild(md3dDevice->CreateComputePipelineState(
&wavesUpdatePSO,IID_PPV_ARGS(&mPSOs["wavesUpdate"])));

根签名定义了什么参数才是着色器所期望的输入,而cs字段就是所指定的计算着色器。
一个将着色器编译为字节码的示例:

mShaders["wavesUpdateCS"] = d3dUtil::CompileShader(
L"Shaders\\waveSim.hlsl",nullptr,"UpdateWavesCS","cs_5_0");

13.3数据的输入与输出资源

能与计算着色器绑定的资源类型有缓冲区与纹理两种。

13.3.1纹理输入

通过各输入纹理gInputA与gInputB分别创建SRV,再将他们作为参数传入根参数,就能令这两个纹理都绑定为着色器的输入资源。

cmdList->SetComputeRootDescriptorTable(1,mSrvA);
cmdList->SetComputeRootDescriptorTable(2,mSrvB);

13.3.2纹理输出与无序访问视图

对于输出资源:

RWTexture2D<float4> gOutput;

计算着色器的处理输出资源的方式比较特殊,它们的类型还有一个特别的前缀“RW”,意为读写,可以对计算着色器中的这类资源元素进行读写操作。gInputA和gInputB仅为只读,使用尖括号模板语法。来指定输出资源的类型与维数,若输出DXGI_FORMAT_R8G8_SINT类型的2D整形资源,则在HLSL中这样定义:

RWTexture2D<int2> gOutput;

输出资源与输入资源的绑定方法是截然不同的。为了绑定在计算着色器中要执行写操作的资源。需要将其与称为无序访问视图(Unorderd Access View)的新型视图关联在一起。代码中,用描述符句柄来标识无序访问视图,且通过结构体D3D12_UNORDERED_ACCESS_VIEW_DESC来对它进行描述。创建这种视图的整个过程与着色器资源视图很相似。这里给出一个为纹理资源创建UAV的示例:

D3D12_RESOURCE_DESC texDesc;
ZeroMemory(&texDesc, sizeof(D3D12_RESOURCE_DESC));
texDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
texDesc.Alignment = 0;
texDesc.Width=mWidth;
texDesc.Height = mHeight;
tex.DepthOrArraySize = 1;
texDesc.MipLevels = 1;
texDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
texDesc.SampleDesc.Count = 1;
texDesc.SampleDesc.Quality = 0;
texDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
texDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;

ThrowIfFaild(md3dDevice->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
D3D12_HEAP_FLAG_NONE,
&texDesc,
D3D12_RESOURCE_STATE_COMMON,
nullptr,
IID_PPV_ARGS(&mBlurMap0)));
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = ();
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
srvDesc.Format = mFormat;
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
srvDesc.Texture2D.MostDetailedMip=0;
srvDesc.Texture2D.MipLevels = 1;

D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc={};

uavDesc.Format = mFormat;
uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D;
uavDesc.Texture2D.MipSlice=0;

md3dDevice->CreateShaderResourceView(mBlurMap0.Get(),
&srvDesc,mBlur0CpuSrv);
md3dDevice->CreateUnorderedAccessView(mBlurMap.Get(),
nullptr,&uavDesc,mBlur0CpuUav);

如果一个纹理要与UAV绑定,则此纹理必须用标志D3D_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS来创建,示例中,我们将纹理分别绑定为一个UAV和一个SRV(不同时生效)。这是一种常见手段,通常会在计算着色器中对纹理执行某些操作,然后还可能用此纹理对几何体进行贴图,因此需要再将它与SRV绑定到顶点着色器或像素着色器。
类型为D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV的描述符堆可以混合存放CBV、SRV和UAV,因此,就能将UAV描述符置于这种堆中,将描述符句柄作为参数传至根参数,是资源绑定到流水线上以供分派调用。

void BlurApp::BuildPostProcessRootSignature()
{
CD3DX12_DESCRIPTOR_RANGE srvTable;
}
CD3DX12_DESCRIPTOR_RANGE srvTable;
srvTable.Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0);

CD3DX12_DESCRIPTOR_RANGE uavTable;
uavTable.Init(D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 1, 0);

// Root parameter can be a table, root descriptor or root constants.
CD3DX12_ROOT_PARAMETER slotRootParameter[3];

// Perfomance TIP: Order from most frequent to least frequent.
slotRootParameter[0].InitAsConstants(12, 0);
slotRootParameter[1].InitAsDescriptorTable(1, &srvTable);
slotRootParameter[2].InitAsDescriptorTable(1, &uavTable);

// A root signature is an array of root parameters.
CD3DX12_ROOT_SIGNATURE_DESC rootSigDesc(3, slotRootParameter,
0, nullptr,
D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);

// create a root signature with a single slot which points to a descriptor range consisting of a single constant buffer
ComPtr<ID3DBlob> serializedRootSig = nullptr;
ComPtr<ID3DBlob> errorBlob = nullptr;
HRESULT hr = D3D12SerializeRootSignature(&rootSigDesc, D3D_ROOT_SIGNATURE_VERSION_1,
serializedRootSig.GetAddressOf(), errorBlob.GetAddressOf());

if(errorBlob != nullptr)
{
::OutputDebugStringA((char*)errorBlob->GetBufferPointer());
}
ThrowIfFailed(hr);

ThrowIfFailed(md3dDevice->CreateRootSignature(
0,
serializedRootSig->GetBufferPointer(),
serializedRootSig->GetBufferSize(),
IID_PPV_ARGS(mPostProcessRootSignature.GetAddressOf())));
}

这个根签名的定义为:根参数槽0指向一个常量缓冲区,根参数槽1指向一个SRV,根参数槽2指向一个UAV,在分派调用开始之前,要先为计算着色器绑定常量数据与资源描述符以供使用。

cmdList->SetComputeRootSignature(rootSig);

cmdList->SetComputeRoot32BitConstants(0, 1, &blurRadius, 0);
cmdList->SetComputeRoot32BitConstants(0, (UINT)weights.size(), weights.data(), 1);

cmdList->SetComputeRootDescriptorTable(1, mBlur1GpuSrv);
cmdList->SetComputeRootDescriptorTable(2, mBlur0GpuUav);

UINT numGroupsY = (UINT)ceilf(mHeight / 256.0f);
cmdList->Dispatch(mWidth, numGroupsY, 1);

13.3.3利用索引对纹理进行采样

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐