Direct3D Compute Shader的运行速度比Unity Compute Shader慢15倍

时间:2018-09-26 14:40:51

标签: unity3d plugins direct3d hlsl

我正在Unity中开发一个VR应用程序,该应用程序使用本地插件进行视频解码,我想对解码后的视频帧进行一些处理。

我的第一步是使用从Unity应用程序中的C#脚本调度的Unity计算着色器。这行得通,我看到了预期的结果,但是我遇到了一个同步问题,即将参数从运行在渲染线程上的本机插件中拉出,需要将其馈送到运行在主线程上的计算着色器中。

我认为可以通过将Unity计算着色器转换为D3D11计算着色器并在本机插件中弹出解码器后对其进行处理,从而解决此问题。这也给了我预期的结果,但是却要付出巨大的性能代价。该应用程序丢弃帧,并且当使用RenderDoc配置单个帧时,我看到插件中的计算调度调用大约需要32毫秒,而使用Unity计算着色器需要3毫秒。

我找不到任何有关为何两者之间如此差异的信息。我曾尝试将D3D11着色器简化为只写零,并且探查器仍显示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;
}

有什么明显的我想念的地方吗?还是团结很擅长优化?

1 个答案:

答案 0 :(得分:0)

我设法通过在不同位置进行一些更改来解决此问题。

首先,我更改了着色器,将输出写入单个纹理对象:

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);