Direct3D计算着色器运行速度比Unity计算着色器慢15倍

3
我正在使用Unity开发VR应用程序,使用本地插件进行视频解码,并希望对解码后的视频帧进行一些处理。我的第一步是使用Unity计算着色器,从Unity应用程序中的C#脚本触发调度。这个方法可以正常工作,我也看到了预期的结果,但是在渲染线程上运行的本地插件需要将一个参数提取出来,传递给主线程运行的计算着色器,这是一个同步问题。我认为可以通过将Unity计算着色器转换为D3D11计算着色器来解决这个问题,并且尽早在本地插件中处理已解码的帧。这样也会得到预期的结果,但性能损失非常大。应用程序的帧率下降,使用RenderDoc来分析单个帧时,计算调度调用在插件中的时间约为32毫秒,而使用Unity的计算着色器时只有3毫秒。
我找不到任何信息来解释两者之间存在如此大的差异。我尝试简化D3D11着色器,只输出零,但Profiler仍然显示大约32ms,这让我认为这与我在插件中设置着色器有关。我包含了一些代码来展示我的插件计算着色器的设置和执行过程。
原生C++插件中的计算着色器:
void process()
{

    ID3D11DeviceContext* ctx = NULL;
    device->GetImmediateContext(&ctx);

    ctx->UpdateSubresource(_pCB, 0, nullptr, &_bufferStruct, 0, 0);

    if (!_resourcesSet) {   

        // Set read texture 
        ID3D11ShaderResourceView * inY = nullptr;
        ID3D11ShaderResourceView * inU = nullptr;
        ID3D11ShaderResourceView * inV = nullptr;

        _inputTexture->getSRVs(&inY, &inU, &inV);

        // Set write texture
        ID3D11UnorderedAccessView * outY;
        ID3D11UnorderedAccessView * outU;
        ID3D11UnorderedAccessView * outV;

        _outputTexture->getUAVs(&outY, &outU, &outV);

        ctx->CSSetConstantBuffers(0, 1, &_pCB);
        ctx->CSSetShaderResources(0, 1, &inY);
        ctx->CSSetShaderResources(1, 1, &inU);
        ctx->CSSetShaderResources(2, 1, &inV);
        ctx->CSSetUnorderedAccessViews(0, 1, &outY, nullptr);
        ctx->CSSetUnorderedAccessViews(1, 1, &outU, nullptr);
        ctx->CSSetUnorderedAccessViews(2, 1, &outV, nullptr);
        ctx->CSSetShader(_computeShader, NULL, 0);
        _resourcesSet = true;
    }

    ctx->Dispatch(outputWidth / 8, outputHeight / 8, 1);

    ctx->Release();
}

简化后的计算着色器本身:
SamplerState TextureSampler
{
    Filter = MIN_MAG_MIP_LINEAR;
    AddressU = Wrap;
    AddressV = Wrap;
};

Texture2D<float> inY : register(t0);
Texture2D<float> inU : register(t1);
Texture2D<float> inV : register(t2);
RWTexture2D<float> outY : register(u0);
RWTexture2D<float> outU : register(u1);
RWTexture2D<float> outV : register(u2);

[numthreads(8,8,1)]
void CSMain (uint3 id : SV_DispatchThreadID)
{
    float3 col = float3(0.0, 0.0, 0.0);

    outY[id.xy] = col.r;
    outU[id.xy / 2] = col.g;
    outV[id.xy / 2] = col.b;
}

有什么明显的问题我忽略了吗,还是Unity在优化方面做得非常好?

尝试将 id.xy / 2 提取到一个本地的 unit2 变量中,并将其更改为 id.xy * 0.5。乘法通常比除法快3-4倍,只进行一次而不是两次可能会给您带来更高的收益。 - NextInLine
1个回答

2
我成功地通过在不同的地方进行一些更改来解决这个问题。
首先,我将着色器更改为将输出写入单个纹理对象:
RWTexture2D<float4> unpackedRGBA : register(u0);

我成功地创建了一个纹理,可以在着色器中写入并传递给Unity,这意味着我不需要制作纹理副本,我认为这是加速过程的真正关键:

D3D11_TEXTURE2D_DESC texDesc;
texDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
texDesc.Usage = D3D11_USAGE_DEFAULT;
texDesc.BindFlags = D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE;
texDesc.CPUAccessFlags = 0;
texDesc.MiscFlags = 0;

这里的关键是绑定标志的组合,意味着可以通过绑定UAV指针在着色器中写入纹理,同时也可以通过SRV指针将其交给Unity。

在Unity中,我使用SRV指针创建了一个纹理:

IntPtr nativeTexturePtr = new IntPtr();
nativeGetOutputTexture(ref nativeTexturePtr);
output = Texture2D.CreateExternalTexture(videoWidth, videoHeight, TextureFormat.RGBA32, false, false, nativeTexturePtr);

这导致我的初始实现使用Unity计算着色器的渲染时间相当,但我一直看到黑屏。最终的解决方法是在调度D3D11计算着色器后取消绑定输出纹理,这意味着当需要将其渲染到场景中时,它可以自由地绑定到Unity。

ctx->CSSetUnorderedAccessViews(0, 1, &gEmptyUav, nullptr);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接