В этой статье содержатся некоторые часто задаваемые вопросы о Direct3D 10 с точки зрения разработчика, который переносит существующее приложение из Direct3D 9 (D3D9) в Direct3D 10 (D3D10).
Буферы констант
Как лучше всего обновлять буферы констант?
UpdateSubresource и Map with Discard должны иметь одинаковую скорость. Выберите один из них в зависимости от того, какой из них копирует наименьший объем памяти. Если данные уже хранятся в памяти в одном непрерывном блоке, используйте UpdateSubresource. Если вам нужно накапливать данные из других мест, используйте команду Сопоставление с отменой.
Каков худший способ организации буферов констант?
Наихудшая производительность реализуется путем помещения всех констант для определенного шейдера в один буфер констант. Хотя это часто самый простой способ переноса из D3D9 в D3D10, он может затруднить производительность. Например, рассмотрим сценарий, в котором используется следующий буфер констант:
cbuffer VSGlobalsCB
{
matrix ViewProj;
matrix Bones[100];
matrix World;
float SpecPower;
float4 BDRFCoefficients;
float AppTime;
uint2 RenderTargetSize;
};
Размер буфера составляет 6560 байт. Предположим, что есть приложение со 1000 объектами для отрисовки, 100 из которых являются сетчатыми, а 900 — статическими сетками. Кроме того, предположим, что это приложение использует сопоставление теней с одним источником света. Это означает, что существует два прохода: один для карты глубины, отображаемой из света, и один для прохода отрисовки вперед. Это приводит к 2000 вызовам draw. Хотя при каждом вызове draw не требуется обновлять каждую часть буфера констант, весь буфер констант по-прежнему обновляется и отправляется в карта. Это приводит к обновлению 13 МБ данных на каждый кадр (2000 вызовов draw раз 6560 КБ).
Как лучше всего организовать буферы констант?
Лучший способ — упорядочить буферы констант по частоте обновления. Константы, обновляемые с аналогичной частотой, должны находиться в одном буфере. Например, рассмотрим сценарий, представленный в разделе "Какой наихудший способ организовать буферы констант?", но с лучшим макетом констант:
cbuffer VSGlobalPerFrameCB
{
float AppTime;
};
cbuffer VSPerSkinnedCB
{
matrix Bones[100];
};
cbuffer VSPerStaticCB
{
matrix World;
};
cbuffer VSPerPassCB
{
matrix ViewProj;
uint2 RenderTargetSize;
};
cbuffer VSPerMaterialCB
{
float SpecPower;
float4 BDRFCoefficients;
};
Буферы констант разделяются по частоте обновления, но это только половина решения. Приложение должно правильно обновить буферы констант, чтобы в полной мере воспользоваться преимуществами разделения. Мы будем предполагать ту же сцену, что и выше: 900 статических сеток, 100 сеток с обложкой, один проход света и один проход вперед. Мы также предположим, что будут храниться некоторые буферы констант для каждого объекта. Это означает, что каждый объект будет содержать либо VSPerSkinnedCB, либо VSPerStaticCB в зависимости от того, является ли он кожаным или статическим. Мы делаем это, чтобы избежать удвоения количества матриц, отправленных через конвейер.
Мы разделим кадр на три этапа. Первый этап является началом кадра и включает в себя не отрисовку, а только постоянные обновления.
Начало кадра
- Обновление VSGlobalPerFrameCB для времени приложения (4 байта)
- Обновление 100 VSPerSkinnedCB для 100 скинированных объектов (640 000 байт)
- Обновление VSPerStaticCB для 900 статических объектов (57600 байт)
Далее проходит теневая карта. Обратите внимание, что единственным буфером констант, который фактически обновляется, является VSPerPassCB. Все остальные буферы констант были обновлены во время начального прохода кадра. Хотя нам по-прежнему нужно привязать эти буферы констант, объем информации, передаваемой в видео карта, является минимальным, так как буферы уже обновлены.
Теневой проход
- Обновление VSPerPassCB (72 байта)
- Нарисуйте 100 сеток с кожей (100 привязок, без обновлений)
- Рисование 900 статических сеток (100 привязок, без обновлений)
Аналогичным образом, проходу отрисовки вперед необходимо обновлять только данные для каждого материала, так как они не были сохранены для каждой сетки. Если предположить, что в сцене используется 500 материалов:
Переадресация
- Обновление VSPerPassCB (72 байта)
- Обновление 500 VSPerMaterialCB (10000 байт)
В результате всего 707 КБ. Хотя это очень надуманый сценарий, он показывает, насколько постоянные затраты на обновление можно уменьшить путем сортировки констант по частоте обновления.
Что делать, если у меня недостаточно места для хранения отдельных буферов констант для сеток, материала и т. д.?
Всегда можно использовать многоуровневую систему буферов констант. Создайте буферы констант переменного размера (16 байт, 32 байта, 64 байта и т. д.) до максимального необходимого размера буфера констант. Когда нужно привязать буфер констант к шейдеру, выберите наименьший буфер констант, который может содержать данные, необходимые шейдеру. Хотя этот подход немного менее эффективен, он является хорошим промежуточным шагом.
Я делюсь буферами констант между разными шейдерами. Один шейдер может использовать все константы, а другой — некоторые константы. Как лучше всего их обновить?
Один из подходов заключается в том, чтобы разделить буфер констант еще больше. Однако наступает момент, когда привязывается слишком много буферов констант. В этом случае переместите константы, которые, скорее всего, не будут использоваться несколькими шейдерами, в конец буфера констант. При получении данных переменных из шейдера используйте флаг D3D10_SVF_USED из D3D10_SHADER_VARIABLE_DESC, чтобы определить, используется ли переменная. Поместив неиспользуемые переменные в конец буфера констант, можно привязать меньший буфер к шейдеру, который не использует эти переменные, тем самым сэкономив затраты на обновление.
Насколько можно улучшить частоту кадров, если я отправляю кости моего персонажа только один раз на кадр, а не один раз за проход или рисование?
Вы можете повысить частоту кадров в диапазоне от 8 до 50 процентов в зависимости от объема избыточных данных. В худшем случае производительность не будет снижена.
Сколько буферов констант следует привязать одновременно?
Привяжите минимальное количество буферов констант, необходимое для получения всех данных в шейдер. В реалистичном сценарии рекомендуется использовать пять буферов констант. Совместное использование буферов констант между шейдерами (привязка одного и того же CB к VS и PS) также может повысить производительность.
Существуют ли затраты на привязку буферов констант без их использования?
Да, если вы не собираетесь использовать буфер, не вызывайте VSSetConsantBuffer или PSSetConstantBuffer. Эти дополнительные затраты на API могут сложиться в ходе нескольких вызовов draw.
Состояние
Как лучше всего управлять состоянием в D3D10?
Лучшее решение — знать все свое состояние и создавать объекты состояния. Это означает, что во время отрисовки привязка состояния является единственной операцией, которая должна произойти. D3D10 также отфильтровывает дубликаты.
Моя игра динамически загружается или содержит содержимое, созданное пользователем. Я не могу загрузить все объекты состояния впереди. Что следует делать?
Здесь есть два решения. Во-первых, просто создавать объекты состояния на лету и разрешать D3D10 фильтровать дубликаты. Однако это не рекомендуется для сценариев с большим количеством изменений объектов состояния на кадр. Лучшее решение — хэшировать объекты состояния самостоятельно и создавать объект состояния только в том случае, если тот, который соответствует требованиям, не найден в хэш-таблице. Причина использования пользовательской хэш-таблицы заключается в том, что приложение может выбрать быстрый хэш на основе сценария использования, конкретного для этого приложения. Например, если приложение изменяет только rendertargetwritemask в BlendState и сохраняет все остальные значения одинаковыми, приложение может создать хэш из rendertargetwritemask вместо всей структуры.
Состояние AlphaTest исчезло. Куда же он исчез?
Теперь AlphaTest должна иметь производительность в шейдере. См. пример FixedFuncEMU.
Что произошло с плоскостями клипа пользователя?
Плоскости клипа пользователя перемещены в шейдер. Это можно сделать двумя способами. Первый — вывод SV_ClipDistance из вершинного шейдера или геометрического шейдера. Другой вариант — использовать отмену в пиксельном шейдере на основе некоторого значения, передаваемого вершинным шейдером или геометрическим шейдером. Поэкспериментируйте с обоими способами, чтобы узнать, что быстрее для конкретного сценария. Использование SV_ClipDistance может привести к тому, что оборудование будет использовать подпрограмму отсечения на основе геометрии, которая может привести к замедлению выполнения вызовов рисования с привязкой к геометрии. Аналогичным образом, использование отмены перемещает работу на шейдер пикселей, что может привести к замедлению выполнения вызовов рисования с привязкой к пикселям.
Очистки не учитывают какие-либо параметры состояния, такие как параметры прямоугольника ножницы в моем состоянии растеризатора.
Очистки отделены от состояния конвейера. Чтобы получить поведение в стиле D3D9, эмуляция очищает, рисуя полноэкранный четырехэкранный квадрат.
Я возвращаю состояние по умолчанию, чтобы попытаться диагностировать ошибку отрисовки. Теперь мой экран просто показывает черный, хотя я знаю, что я рисую объекты на экране.
При возврате состояния к значениям по умолчанию (NULL) убедитесь, что sampleMask в вызове OMSetBlendState никогда не равен нулю. Если для SampleMask задано значение 0, то все примеры логически будут И с нулевым значением. В этом сценарии никакие примеры не проходят тест на наложение.
Куда попало состояние D3DSAMP\SRGBTEXTURE?
SRGB был удален как часть состояния выборки и теперь привязан к формату текстуры. Привязка текстуры SRGB приведет к той же выборке, что и при указании D3DSAMP_SRGBTEXTURE в Direct3D 9.
Форматы
Какой формат D3D9 соответствует формату D3D10?
Дополнительные сведения см. в разделе Рекомендации по Использованию Direct3D 9 в Direct3D 10.
Что случилось с A8R8G8B8 форматов текстур?
Они устарели в D3D10. Вы можете перезагрузить текстуры в R8G8B8A8, а также при загрузке или в шейдере.
Разделы справки использовать палетизированные текстуры?
Поместите цветовую палитру в буфер текстуры или констант и привяжите ее к конвейеру. В пиксельном шейдере выполните непрямый поиск, используя индекс в палетизированной текстуре.
Что представляют собой эти новые форматы SRGB?
SRGB был удален как часть состояния выборки и теперь привязан к формату текстуры. Привязка текстуры SRGB приведет к той же выборке, что и при указании D3DSAMP_SRGBTEXTURE в Direct3D 9.
Куда пошли поклонники треугольника?
Вентиляторы треугольников устарели в D3D10. Вентиляторы треугольников необходимо преобразовать либо в конвейере содержимого, либо при загрузке.
Компоновка шейдера
Мои шейдеры Direct3D 9 хорошо компилируются в модель шейдера 4.0, но когда я привязываю их к конвейеру, я получаю ошибки компоновки, отображаемые в выходных данных отладки со средой выполнения отладки.
Компоновка шейдеров в D3D10 гораздо строге. Элементы на последующем этапе должны считываться в том порядке, в каком они выводятся на предыдущем этапе. Пример:
Вершинный шейдер выводит следующее:
float4 Pos : SV_POSITION;
float3 Norm : NORMAL;
float2 Tex : TEXCOORD0;
Пиксельный шейдер считывает:
float3 Norm : NORMAL;
float2 Tex : TEXCOORD0;
Хотя положение в пиксельном шейдере не требуется, это приведет к ошибке компоновки, так как позиция выводится из вершинного шейдера, но не считывается шейдером пикселей. Более правильная версия будет выглядеть следующим образом:
Вершинный шейдер выводит следующее:
float3 Norm : NORMAL;
float2 Tex : TEXCOORD0;
float4 Pos : SV_POSITION;
Пиксельный шейдер считывает:
float3 Norm : NORMAL;
float2 Tex : TEXCOORD0;
В этом случае вершинный шейдер выводит те же сведения, но теперь пиксельный шейдер считывает данные в выходных данных порядка. Так как пиксельный шейдер ничего не считывает после Tex, нам не нужно беспокоиться о том, что VS выводит больше информации, чем считывает PS.
Мне нужна подпись шейдера, чтобы создать макет ввода, но я загружаю сетки и макеты перед созданием шейдеров. Что делать?
Одним из решений является переключение порядка и загрузка шейдеров перед загрузкой сеток. Тем не менее, это гораздо проще сказать, чем сделать. Вы всегда можете создать входные макеты по запросу, когда приложение потребуется. Вам придется сохранить версию подписи шейдера. Необходимо создать хэш на основе шейдера и макета буфера, а также создать макет ввода, только если соответствующий макет еще не существует.
Отрисовка вызовов
Какое ограничение на количество вызовов draw для D3D10 достигает 60 Гц? 30 Гц?
Direct3D 9 имеет ограничение на количество вызовов draw из-за затрат на ЦП за вызов draw. В Direct3D 10 стоимость каждого вызова draw была снижена. Однако между вызовами draw и частотой кадров больше нет определенной корреляции. Так как для вызовов draw часто требуется много вызовов поддержки (обновления буфера констант, привязки текстур, настройка состояния и т. д.), влияние частоты кадров API теперь в большей степени зависит от общего использования API, а не просто числа вызовов рисования.
Ресурсы
Какой тип использования ресурсов следует использовать для каких операций?
Используйте следующую памятку:
- ЦП обновляет ресурс несколько раз для каждого кадра: D3D10_USAGE_DYNAMIC
- ЦП обновляет ресурс меньше одного раза на кадр: D3D10_USAGE_DEFAULT
- ЦП не обновляет ресурс: D3D10_USAGE_IMMUTABLE
- ЦП должен считывать ресурс: D3D10_USAGE_STAGING
Так как буферы констант всегда часто обновляются, они не соответствуют "памятку". Сведения о типах ресурсов, используемых для буферов констант, см. в разделе Буферы констант .
Что произошло с DrawPrimitiveUP и DrawIndexedPrimitiveUP?
Они исчезли в D3D10. Для динамической геометрии используйте большой буфер D3D10_USAGE_DYNAMIC. В начале кадра сопоставьте его с D3D10_MAP_WRITE_DISCARD. Для каждого последующего вызова draw перемещайте указатель записи за позицию ранее нарисованных вершин и сопоставляйте буфер с D3D10_MAP_WRITE_NO_OVERWRITE. Если вы приближаетесь к концу буфера до конца кадра, обведите указатель записи в начало и сопоставить с D3D10_MAP_WRITE_DISCARD.
Можно ли записывать 16-разрядные и 32-разрядные индексы в один и тот же динамический геометрический буфер?
Да, можно, но это может повлечь за собой снижение производительности на определенном оборудовании. Безопаснее создавать отдельные буферы для динамических 16-разрядных и 32-разрядных данных индекса.
Разделы справки считывать данные из GPU в ЦП?
Необходимо использовать промежуточный ресурс. Скопируйте данные из ресурса GPU в промежуточный ресурс с помощью CopyResource. Сопоставьте промежуточный ресурс для чтения данных.
Мое приложение зависело от функций StretchRect.
Так как по сути это была оболочка для базовых функций Direct3D, она была удалена из API. Некоторые функции StretchRect были перемещены в D3DX10LoadTextureFromTexture. Для преобразования формата и копирования текстур может выполняться D3DX10LoadTextureFromTexture. Однако для таких операций, как преобразование одного размера в другой, скорее всего, потребуется операция отрисовки в текстуру в приложении.
В вызовах map для ресурсов нет смещений или размеров. Они были при вызовах блокировки в Direct3D 9; почему они изменились?
Смещения и размеры вызовов lock в Direct3D 9 были в основном беспорядок API и часто игнорирулись драйвером. Вместо этого смещения должны вычисляться приложением из указателя, возвращаемого в вызове Map.
Глубина как текстура
Что быстрее? Использование глубины в качестве текстуры или запись глубины в альфа и чтение?
Это зависит от приложения и оборудования. Используйте любой из них, чтобы сэкономить наибольшую пропускную способность. Если вы уже используете несколько целевых объектов отрисовки и имеете дополнительный канал, запись глубины из шейдера может оказаться лучшим решением. Кроме того, запись глубины в альфа-канал или другой целевой объект отрисовки позволяет записывать линейные значения глубины, которые могут ускорить вычисления, которым требуется доступ к буферу глубины.
Можно ли связать текстуру в качестве входных данных и как текстуру трафарета глубины, если отключить запись по глубине?
Не в D3D10.
MSAA
Можно ли разрешить текстуру элементов глубины MSAA?
Не в D3D10. Однако вы можете использовать отдельные примеры из текстуры MSAA. Дополнительные сведения см. в разделе HLSL .
Почему приложение аварийно завершает работу, как только я включаю MSAA?
Убедитесь, что вы включаете число примеров MSAA и номер качества, которые фактически перечисляются драйвером.
Сбои
Мое приложение аварийно завершает работу в D3D10 или в драйвере, и я не знаю, почему.
Первым шагом является включение среды выполнения отладки (D3D10_CREATE_DEVICE_DEBUG флаг передается в D3D10CreateDevice). Это приведет к наиболее распространенным ошибкам в качестве выходных данных отладки.
PIX аварийно завершает работу при попытке использовать приложение с ним.
Первым шагом является включение среды выполнения отладки (D3D10_CREATE_DEVICE_DEBUG флаг передается в D3D10CreateDevice). PIX имеет гораздо более высокую вероятность сбоя, если выходные данные отладки не являются чистыми.
В моей игре заканчивается виртуальное адресное пространство на 32-разрядной версии Vista под D3D10. На D3D9 проблем не возникает.
Возникли некоторые проблемы с D3D10 и виртуальным адресным пространством. Это исправлено в KB940105. Если проблема не устранена, убедитесь, что вы не создаете больше ресурсов, которые можно сопоставить (заблокировать) в D3D10, чем в D3D9. Также подумайте о переносе на 64-разрядную версию, так как в будущем это станет более распространенным.
Предикатная отрисовка
Я использовал предикатную отрисовку (на основе результатов запроса окклюзии). Почему мое приложение по-прежнему работает с той же скоростью?
Во-первых, убедитесь, что отрисовка, которую вы хотите пропустить, на самом деле является узким местом приложения. Если это не узкое место, то пропуск отрисовки не поможет частоте кадров.
Во-вторых, убедитесь, что между проблемой запроса и отрисовкой, которую вы хотите предикать, прошло достаточно времени. Если запрос не был завершен к моменту, когда вызов отрисовки достигнет GPU, отрисовка будет выполнена в любом случае.
В-третьих, предикация пропускает только определенные вызовы. Пропущенные вызовы: Draw, Clear, Copy, Update, ResolveSubresource и GenerateMips. Параметры состояния, настройка IA, сопоставление и создание вызовов не учитывают предикацию. При наличии большого количества вызовов настройки состояния вокруг вызова draw для предиката, эти состояния по-прежнему будут заданы.
Шейдер геометрии
Следует ли использовать геометрический шейдер для тесселировать мой (вставить что-нибудь сюда)?
Нет. Шейдер геометрии НЕ следует использовать для тесселяции.
Можно ли использовать геометрический шейдер для создания геометрии?
Да, в очень ограниченных сценариях. Геометрический шейдер в текущих частях D3D10 (2008) не оснащен для обработки большого расширения. В будущем это может измениться. Поставщики видео карта могут иметь специальный путь для одного-четырех расширений из-за существующего оборудования спрайта точки. Любое другое расширение должно быть очень ограниченным. Образцы ParticlesGS и PipesGS обеспечивают высокую частоту кадров, выполняя только ограниченное расширение. Для каждого кадра разворачивается только несколько точек.
Для чего следует использовать геометрический шейдер?
Все, что требует операций со всем примитивом, таких как обнаружение силуэта, барицентрические координаты и т. д. Кроме того, используйте его для выбора среза целевого массива отрисовки для отправки примитивов.
Можно ли вывести переменные объемы геометрии из геометрического шейдера?
Да, но это может привести к проблемам с производительностью. Возьмем пример вывода 1 точки для одного вызова и 4 балла для другого. В соответствии с рекомендациями по расширению это может привести к последовательному запуску потоков геометрического шейдера.
Как D3D10 знает, как создавать индексы смежности для моей сетки? Или, почему D3D10 не отображается правильно, если я указываю, что геометрическому шейдеру требуется информация о смежности.
Сведения о смежности создаются не D3D10, а приложением. Индексы соседства создаются приложением и должны содержать шесть индексов для каждого примитива; из шести нечетные нумерованные индексы являются пограничными смежными вершинами. Для создания этих данных можно использовать ID3DX10Mesh::GenerateAdjacencyAndPointsReps.
HLSL
Являются ли целочисленные и побитовые инструкции медленными?
Они могут быть. Различные карты D3D10 могут выдавать только целочисленные операции с подмножеством доступных единиц ALU. Это в значительной степени зависит от оборудования. Рекомендации по устранению целочисленных операций на этом конкретном оборудовании см. в статье о поставщике оборудования. Кроме того, будьте очень осторожны приведения между типами.
Что случилось с VPOS?
Если вы объявите входные данные в шейдер пикселей как SV_POSITION, вы получите то же поведение, что и объявление его как VPOS.
Разделы справки пример текстуры MSAA?
В шейдере объявите текстуру как Texture2DMS. Затем можно получить отдельные примеры с помощью методов Sample из объекта Texture2DMS.
Разделы справки определить, действительно ли используется переменная шейдера в буфере констант?
Просмотрите отраженную структуру D3D10_SHADER_VARIABLE_DESC для этой переменной. Для uFlags должен быть установлен флаг D3D10_SVF_USED.
Разделы справки определить, использует ли переменная шейдера в буфере констант FX10?
В настоящее время это невозможно с помощью FX10.
Я не могу контролировать буферы констант, создаваемые FX10. Как они создаются и обновляются?
Все буферы констант, управляемые FX10, создаются как D3D10_USAGE_DEFAULT ресурсы и обновляются с помощью UpdateSubresource. Так как FX10 хранит резервное хранилище всех постоянных данных, updateSubresource — лучший подход для их обновления.
Разделы справки эмулировать конвейер фиксированной функции с помощью шейдеров?
См. пример FixedFuncEMU.
Следует ли использовать новые указания компилятора \[unroll\], \[loop\], \[branch\] и т. д.?
Как правило, нет. Компилятор часто пытается использовать оба способа и выбирает самый быстрый. В некоторых случаях может потребоваться использовать [unroll], например, когда для получения текстуры внутри цикла требуется доступ к градиенту.
Влияет ли частичная точность на D3D10? Я могу указать частичную точность в D3D9 HLSL, но не в D3D10 HLSL.
Все операции D3D10 должны выполняться с 32-разрядной точностью с плавающей запятой. Поэтому частичная точность не должна иметь никакого значения в D3D10.
В D3D9 я мог бы выполнить фильтрацию теней HW PCF, привязав буфер глубины в качестве текстуры и используя обычные инструкции tex2d hlsl. Разделы справки сделать это на D3D10?
Необходимо использовать состояние средства сравнения и инструкции SampleCmp.
Как эта регистрация ключевое слово работает в D3D10?
Теперь ключевое слово регистра в D3D10 применяется к слоту, к которому привязан конкретный ресурс. В этом случае ресурс может быть буфером (константой или иным способом), текстурой или sampler.
- Для буферов констант используйте синтаксис register(bN), где N — входной слот (0–15).
- Для текстур используйте синтаксис register(tN), где N — входной слот (0–127).
- Для выборок используйте синтаксис register(sN), где N — входной слот (0–127).
Разделы справки поместить переменную в буфер констант, если регистр используется только для указания места привязки всего буфера?
Используйте ключевое слово packoffset. Аргумент packoffset имеет вид c[0-4095]. [x,y,z,w]. Пример:
cbuffer cbLotsOfEmptySpace
{
float IWaste2Floats : packoffset(c0.z);
float4 IWasteMore : packoffset(c13);
};
В этом буфере констант IWaste2Floats помещается в третье число с плавающей точкой (12-й байт) в буфере констант. IWasteMore помещается на 13-й float4-й или 52-й float в буфере констант.