Стоимость вызовов состояния ID3D11DeviceContext с одним и тем же вводом подряд? - PullRequest
0 голосов
/ 01 ноября 2019

Это относится к любому вызову состояния DX11, будь то XXSetConstantBuffers или IASetVertexBuffers и т. Д ... По существу, какова стоимость выполнения следующих действий:

ctx->VSSetConstantBuffers(0, 2, constBuffers);
ctx->VSSetConstantBuffers(0, 2, constBuffers);
ctx->VSSetConstantBuffers(0, 2, constBuffers);

По сути, это то же самое, что иследующее?

ctx->VSSetConstantBuffers(0, 2, constBuffers);

Возьмите следующий веб-сайт здесь В таблице 1 указано, что XXSetConstantBuffers стоит 114 циклов ЦП. Мне кажется, что при проверке текущего установленного значения указателя было бы мало или совсем не требовалось дополнительных затрат, и эти операции выполнялись только в том случае, если значения отличаются. Кажется, это также очень важная функциональность, например, если я устанавливаю буфер вершин при инициализации программы, я могу затем переустановить этот буфер вершин в начале каждого кадра на , убедитесь, что , но насколькоGPU обеспокоен тем, что вершинный буфер фактически устанавливается только при инициализации программы.

Кроме того, я могу сделать:

//for the sake of example, assuming only one vertex buffer is ever set.
ID3D11Buffer* vBuffer;
UINT stride, offset;
ctx->IAGetVertexBuffers(0, 1, &vBuffer, &stride, &offset);
if (vBuffer != pointerToBufferIWantToSet)
{
    UINT newStride = 8 * sizeof(FLOAT), newOffset = 0;
    ctx->IASetVertexBuffers(0, 1, &pointerToBufferIWantToSet, &newStride, &newOffset);
}//dont set vertex buffer if already set

Но это кажется подозрительным, если это заставляет избегать циклов ЦПЯ чувствую, что весь код DX должен делать это, но никто не делает. Поэтому я чувствую, что важно спросить:

  • Управляет ли драйвер этим?
  • Есть ли издержки при вызове методов getter контекста устройства?

Если ответом на оба вышеупомянутых вопроса является «нет», то, похоже, наилучшим способом является создание моей собственной обертки объекта состояния, что кажется ненужным объемом работы.

Ответы [ 2 ]

1 голос
/ 01 ноября 2019

Единственный общий ответ, который я могу дать на ваш первый вопрос: «Это зависит». Насколько мне известно, Direct3D11 не отслеживает состояние своего конвейера, чтобы избежать избыточных изменений состояния.

Некоторые поставщики могут решить реализовать такую ​​функцию в своих драйверах, но я бы не стал рассчитывать наэто. Лучше предположить, что трижды вызов одного и того же метода будет стоить в три раза дороже, чем вызов его один раз.

Относительно вашего второго вопроса: Да, есть издержки *. Всякий раз, когда вы вызываете метод DirectX, при условии, что происходит переключение контекста (переключение с контекста приложений на контекст драйверов), что требует времени.

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


(*) Это основано на моих знаниях из лекций об операционных системах и компьютерной графике. Возможно, он не применим к Direct3D11 и / или устарел.


РЕДАКТИРОВАТЬ: решение вопроса в комментарии, поскольку оно не вписывается в комментарий.

Q: Можете ли вы подробнее рассказать о "сортировке вызовов рендеринга"?

Изменение состояния часто приводит к его штрафу. Поэтому мы хотим уменьшить количество необходимых изменений состояния.

Давайте немного подумаем о более широкой картине. Перед выполнением любого вызова отрисовки вы обычно связываете свои буферы, шейдеры и так далее. Таким образом, для каждого вызова отрисовки у нас есть назначенное ему состояние (или конфигурация). Мы можем представить это состояние с помощью кортежа объектов состояния, например:

vertex_buffer a;
index_buffer b;
shader c;
shader d;
render_target e;

pipeline_state state{a, b, c, d, e};

// Then later use...
set_pipeline_state(state);
render_amazing_stuff();

Теперь давайте предположим, что мы хотим нарисовать 3 объекта с немного другой конфигурацией.

vertex_buffer a, b, c; // a stores the first model, b the second, ...
index_buffer f, g;

shader c;
shader d;
render_target e;

pipeline_state state1{a, f, c, d, e};
pipeline_state state2{b, f, c, d, e};
pipeline_state state3{b, g, c, d, e};

У нас есть 3 состояния конвейера, которые отличаются незначительно. Мы можем либо повысить нашу производительность, переупорядочив вызовы отрисовки по их состоянию конвейера, чтобы уменьшить разницу между ними.

set_pipeline_state(state1); // Set initial state, with all buffers..
render();
set_pipeline_state(state2); // Binds only the new vertex buffer; since it's the only difference.
render();
set_pipeline_state(state3); // Binds only the new index buffer; since it's the only difference.
render();

Разница между всеми состояниями минимальна, сокращая количество изменений состояния, эффективно даваяувеличение кадров в секунду.

0 голосов
/ 01 ноября 2019

Итак, я откопал старый вопрос об обмене стека GameDev, который можно найти здесь это очень похожий вопрос. Вот выдержка, это принятый ответ:

PSGetShader (и вообще все методы получения состояний в D3D) не предназначены для вызова на высокой частоте. Вы должны сделать отслеживание грязного состояния, и вы должны реализовать это самостоятельно. Использование PSGetShader как минимум создаст дополнительные издержки AddRef, которые вам не нужны для отслеживания грязного состояния.

Я думаю, что это в сочетании с ответом Джулиана приводит к довольно четкому сообщению, чторазработчик приложения должен не предполагать, что отслеживание состояния выполняется драйвером.

...