我正在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;
}
有什么明显的我想念的地方吗?还是团结很擅长优化?
答案 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);