DirectX 11, объединение пиксельных шейдеров для предотвращения узких мест

Я пытаюсь реализовать один сложный алгоритм с использованием графического процессора. Единственная проблема - это ограничения HW, а максимальный доступный уровень функций - 9_3.

Алгоритм - это, по сути, алгоритм, подобный «стерео сопоставлению» для двух изображений. Из-за упомянутых ограничений все вычисления должны выполняться только в вершинных / пиксельных шейдерах (API вычислений недоступен). Вершинные шейдеры здесь бесполезны, поэтому я считал их сквозными вершинными шейдерами.

Позвольте мне вкратце описать алгоритм:

  1. # P4 #
    # P5 #
  2. # P6 #
    # P7 #
  3. # P8 #
    # P9 #
  4. # P10 #
    # P11 #

Вот в основном ОЧЕНЬ простой график, показывающий мою текущую реализацию (исключая шаги 3 и 4).

Диаграмма проблем

Красные точки / круги / что угодно - это временные буферы (текстуры), в которых хранятся частичные результаты, и в каждой красной точке задействован ЦП.

Вопрос 1: Разве нельзя каким-то образом сообщить графическому процессору, как выполнять формирование каждой ветви до самого низа, не задействуя ЦП и не создавая узких мест? Т.е. чтобы запрограммировать последовательность графических конвейеров за один раз, а затем позволить графическому процессору выполнять свою работу.

Еще один вопрос о рендеринге в текстуру: все ли текстуры постоянно находятся в памяти графического процессора, даже между вызовами метода Draw () и переключением пиксельных / вершинных шейдеров? Или происходит какая-либо передача от графического процессора к процессору ... Потому что это может быть еще одна проблема, которая приводит к узкому месту.

Любая помощь будет оценена по достоинству!

Заранее спасибо.

С уважением, Лукаш


person Lukasz Spas    schedule 23.09.2013    source источник


Ответы (1)


Написание вычислительных алгоритмов в пиксельных шейдерах может быть очень сложным. Написание таких алгоритмов для 9_3 цели может быть невозможно. Слишком много ограничений. Но я думаю, что знаю, как решить твои проблемы.

1. Повторение шейдера

Во-первых, непонятно, что вы здесь называете «узким местом». Да, теоретически вызовы отрисовки в цикле for приводят к потере производительности. Но разве это узкое место? Ваше приложение действительно теряет здесь производительность? Сколько? Ответить могут только профилировщики (CPU и GPU). Но чтобы запустить его, вы должны сначала завершить свой алгоритм (этапы 3 и 4). Итак, мне лучше придерживаться текущего решения и начать реализацию всего алгоритма, затем профилировать и исправлять проблемы с производительностью.

Но, если вы чувствуете, что готовы к настройке ... Обычная технология "повторения" - это создание экземпляров. Вы можете создать еще один буфер вершин (называемый буфером экземпляра), который будет содержать параметры не для каждой вершины, а для одного экземпляра отрисовки. Затем вы делаете все одним DrawInstanced() вызовом.

На первом этапе буфер экземпляра может содержать ваше D значение и индекс целевого Texture3D слоя. Вы можете передать их из вершинного шейдера.

Как всегда, у вас есть компромисс: простота кода (возможно) производительности.

2. Многопроходный рендеринг

ЦП должен быть задействован для передачи результатов временной текстуры (результат прохода V) на второй проход (проход H)

Обычно вы делаете цепочку таким образом, поэтому процессор не задействован:

// Pass 1: from pTexture0 to pTexture1
// ...set up pipeline state for Pass1 here...
pContext->PSSetShaderResources(slot, 1, pTexture0); // source
pContext->OMSetRenderTargets(1, pTexture1, 0);      // target
pContext->Draw(...);

// Pass 2: from pTexture1 to pTexture2
// ...set up pipeline state for Pass1 here...
pContext->PSSetShaderResources(slot, 1, pTexture1); // previous target is now source
pContext->OMSetRenderTargets(1, pTexture2, 0);
pContext->Draw(...);
// Pass 3: ...

Обратите внимание, что pTexture1 должен иметь флаги D3D11_BIND_SHADER_RESOURCE и D3D11_BIND_RENDER_TARGET. У вас может быть несколько входных текстур и несколько целей рендеринга. Просто убедитесь, что каждый следующий проход знает, что выводит предыдущий проход. И если предыдущий проход использует больше ресурсов, чем текущий, не забудьте отвязать ненужные, чтобы предотвратить трудно обнаруживаемые ошибки:

pContext->PSSetShaderResources(2, 1, 0);
pContext->PSSetShaderResources(3, 1, 0);
pContext->PSSetShaderResources(4, 1, 0);
// Only 0 and 1 texture slots will be used

3. Расположение данных ресурса

Все ли текстуры постоянно находятся в памяти графического процессора, даже между вызовами метода Draw () и переключением пиксельных / вершинных шейдеров?

Мы никогда этого не узнаем. Водитель выбирает подходящее место для размещения ресурсов. Но если у вас есть ресурсы, созданные с DEFAULT использованием и 0 флагом доступа к ЦП, вы можете быть почти уверены, что они всегда будут в видеопамяти.

Надеюсь, это поможет. Удачного кодирования!

person Ivan Aksamentov - Drop    schedule 23.09.2013
comment
Спасибо за ваш ответ! - person Lukasz Spas; 23.09.2013
comment
Вы развеяли мои сомнения. Проблема с моей разработкой заключается в том, что я разрабатываю для мобильных устройств и не могу найти для этого подходящий профилировщик графического процессора. Возможно, я попробую запустить свой код на каком-нибудь старом ПК с сопоставимой производительностью, чтобы посмотреть, как он себя ведет. Спасибо еще раз! - person Lukasz Spas; 23.09.2013