如何有效地计算在整个像素着色器阶段中使用颜色的次数

How to efficiently count the times that a color was used during whole Pixel Shader Stage?

本文关键字:段中使 颜色 计算 有效地 像素      更新时间:2023-10-16

我正在考虑在像素着色器中实现它。这是我的部分代码:

首先,我创建了一个Texture1D作为颜色表

D3D11_TEXTURE1D_DESC t1d;
t1d.Width = ModelInfo::ColorCount;
t1d.ArraySize = 1;
t1d.MipLevels = 1;
t1d.CPUAccessFlags = 0;
t1d.MiscFlags = 0;
t1d.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
t1d.Usage = D3D11_USAGE_DEFAULT;
t1d.BindFlags = D3D11_BIND_SHADER_RESOURCE;
ZeroMemory(&InitData, sizeof(InitData));
InitData.pSysMem = ModelInfo::Colors;
hr = m_D3DDevice->CreateTexture1D(&t1d, &InitData, &m_ColorTable);
if (FAILED(hr))
return hr;
D3D11_SHADER_RESOURCE_VIEW_DESC viewDesc;
viewDesc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE1D;
viewDesc.Texture1D.MostDetailedMip = 0;
viewDesc.Texture1D.MipLevels = 1;
hr = m_D3DDevice->CreateShaderResourceView(m_ColorTable, &viewDesc, &m_ColorResView);
if (FAILED(hr))
return hr;

然后我把它传给像素着色器

m_ImmediateContext->PSSetShaderResources(0, 1, &m_ColorResView);

在像素着色器中,我使用这样的颜色表:

Texture1D RandomTex : register(t0);
float4 PS(VS_OUTPUT Input, uint Primitive : SV_PrimitiveID) : SV_Target
{
uint Index = Primitive % ColorCount.x;
return RandomTex[Index];
}

我想使用这个颜色表中每种颜色的alpha通道来计算在整个像素着色器阶段中使用的颜色的次数。。。

我想修改像素着色器中的颜色表,就像下面的代码一样。但这似乎是不可行的。

RandomTex[Index].a = RandomTex[Index].a + 1;

我一直在寻找一种方法来有效地计算颜色,而不是在纹理上渲染,并使用c++在cpu上计算。

我想的所有方法都必须在cpu上做一些额外的计数工作,因为我发现很难做像x++这样的操作(可能是gpu上的一些并行问题),此外,我认为需要渲染纹理两次的方法可能比在cpu上直接计数要慢。

我已经研究了很长时间了。但没有用。请帮助或尝试给出一些如何实现这一目标的想法。

您可以将一个小的写入缓冲区附加到像素着色器(使用无序视图),并在像素着色器中使用原子操作。

这是修改后的HLSL代码

Texture1D RandomTex : register(t0);
RWStructuredBuffer<uint> RWColorCountData : register(u1);
float4 PS(VS_OUTPUT Input, uint Primitive : SV_PrimitiveID) : SV_Target
{
uint Index = Primitive % ColorCount.x;
InterlockedAdd(RWColorCountData[Index], 1);
return RandomTex[Index];
}

这里我使用StructuredBuffer,但如果您愿意,也可以使用ByteAddressBuffer。还要注意,资源附加到寄存器u1,因为第一个插槽仍然由渲染目标占用。

写入缓冲区应具有与1d纹理相同的元素计数,并且需要连接到管道(在渲染目标的顶部),使用OMSetRenderTargetsAndUnorderedAccessViews

每帧,您还需要将缓冲区清除回0(如果需要),否则,值将随着时间的推移而增加,为此您可以使用ClearUnorderedAccessViewUint

请注意,在您的情况下,由于您使用的是uint缓冲区,并且函数需要uint Values[4],因此只有Values[0]将用作清除值。