Direct3D Compute Shader работает в 15 раз медленнее, чем Unity Compute Shader - PullRequest
0 голосов
/ 26 сентября 2018

Я разрабатываю приложение для виртуальной реальности в Unity, которое использует собственный плагин для декодирования видео, и я хотел выполнить некоторую обработку декодированного видеокадра.

Первым моим шагом было использование вычислительного шейдера Unity, которыйбыл отправлен из скрипта C # в приложении Unity.Это сработало, и я увидел ожидаемые результаты, но у меня была проблема синхронизации с извлечением параметра из собственного плагина, работающего в потоке рендеринга, который необходимо было передать в вычислительный шейдер, работающий в основном потоке.

Я думал, что это можно решить путем преобразования вычислительного шейдера Unity в вычислительный шейдер D3D11 и обработки декодированного кадра в собственном плагине, как только он выскочит из декодера.Это также дало мне ожидаемые результаты, но с огромными затратами на производительность.Приложение удаляет кадры, и при использовании RenderDoc для профилирования одного кадра я вижу около 32 мс для вызова диспетчеризации вычислений в плагине по сравнению с 3 мс при использовании вычислительного шейдера Unity.

Я не могу найти никакой информации о том, почемуесть такое расхождение между тогда двумя.Я попытался упростить шейдер D3D11 до простой записи нулей, а профилировщик все еще показывает около 32 мс, что заставляет меня думать, что это связано с моей настройкой шейдера в плагине.

Я включил некоторый кодчтобы показать мою настройку и выполнение вычислительного шейдера плагина.

Вычислить шейдер в родном плагине 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;
}

Isесть что-то очевидное, чего мне не хватает, или единство просто очень хорошо в оптимизации?

1 Ответ

0 голосов
/ 16 октября 2018

Мне удалось решить эту проблему, сделав пару изменений в разных местах.

Сначала я изменил шейдер, чтобы записать вывод в один объект текстуры:

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, но также передана в Unity с помощью указателя SRV.

В 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);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...