Впрочем, зачем ждать до вечера? Вот слайды моего доклада про SPU Render, в этой ветке я могу ответить на все вопросы, возникшие при прослушивании доклада, прочтении слайдов, или на те которые возникнут после того как выложат аудио/видео :)
Как раз недавно закончил похожую работу. Примерно те же проблемы были и те же решения.
Основная беда была с поинтер-чейсингом. Пришлось включать более мелкие структуры данных в большие, укладывать все линейно в памяти и расставлять указатели после ДМА. Перед посылкой объекта на СПУ я собираю 4-5 указателей, которые все-таки требуют индерекшена. Виртуальные функции были побеждены после анализа их использования (оказалось, что виртуальность никто не использует).
Вот с организацией команд бафера (КБ) получилось по-другому (пока). Показалось хорошей идеей иметь большой КБ для ГПУ и вставлять все туда (включая и те куски КБ, которые создает ППУ). Не делая никаких КОЛЛ-РЕТов. Аргументы за были следующие.
Один: проще аллокация памяти под КБ отдельных объектов. С кольцевым буфером всегда понятно, когда что свободно и никакой фрагментации. Кольцевых буфером получилось два. Один для ГПУ - в него пишет СПУ, и второй кольцевой буфер - для ППУ, но он маленький, потому что намного бОльшее количество объектов идет прямо на СПУ.
Два: ну так быстрее же должно работать без КОЛЛ-РЕТов (хотя реально это вряд ли сказывается, разве что шедоу-пасс да зет-припасс).
В итоге получилась схема, которая не параллелится (то есть чисто на генерацию КБ работает только один СПУ, остальные занимаются эджем и шейдер патчингом). Хочется переделать. Вопрос такой: как выделяется память под КБ для индивидуальных объектов (или групп) и как мы узнаем о том, что она освободилась?
Удивился высокому выигрышу от оптимизации кода на СПУ. Какое из изменений было САМЫМ полезным? Что именно соптимизировалось так хорошо (патчинг, создание КБ, остальное)?
Про call/ret/cb - пояснение. У меня на сцену call/ret штук 5. Не на каждый батч, на каждую группу. Соответственно на каждую группу батчей (примерно - opaque сцена, transparent сцена, reflection, refraction, shadows) есть один CB. Там сейчас тупо двойная буферизация, потом наверное будет возможность кольцевой буфер сделать.
Про выигрыш от оптимизации кода - ну, хм, порядок вроде обычный :) а вот с самым полезным сложно. Вот список который велся по ходу разработки (от начала до конца, тут не только оптимизация кода): http://www.everfall.com/paste/id.php?o64o1utg8z3n
shader patch optimization - это в основном всякие loop unroll, apply auto params это много чего (loop unroll, switch -> table lookup, подтягивание пары математических функций, выравнивание входных данных, оптимизация setvertexshaderconstant, etc.).
Про колл-рет все еще не понятно. Если есть всего 4-5 групп, то как объекты распределяются по СПУ? Типа одна группа - один СПУ? Или внутри группы объекты могут пойти на разные СПУ? Если последнее, то как мы поддерживаем порядок объектов (по стейтам-шейдерам, от ближних к дальним, от дальних к ближним, приорететы, и т.п.)?
С оптимизацией порядок был бы обычный если речь шла бы о какой-нибудь потоковой обработке с кучей математики. Типа постпроцессинга. Там, конечно, да. Даблбафер сделал - в два раза быстрее. Векторизовал - в три раза быстрее. А обычный код типа генерации КБ соптимизировать в 6 раз это почетно. Если будет время попробую тоже.
Как раз недавно закончил похожую работу. Примерно те же проблемы были и те же решения.
Основная беда была с поинтер-чейсингом. Пришлось включать более мелкие структуры данных в большие, укладывать все линейно в памяти и расставлять указатели после ДМА. Перед посылкой объекта на СПУ я собираю 4-5 указателей, которые все-таки требуют индерекшена. Виртуальные функции были побеждены после анализа их использования (оказалось, что виртуальность никто не использует).
Вот с организацией команд бафера (КБ) получилось по-другому (пока). Показалось хорошей идеей иметь большой КБ для ГПУ и вставлять все туда (включая и те куски КБ, которые создает ППУ). Не делая никаких КОЛЛ-РЕТов. Аргументы за были следующие.
Один: проще аллокация памяти под КБ отдельных объектов. С кольцевым буфером всегда понятно, когда что свободно и никакой фрагментации. Кольцевых буфером получилось два. Один для ГПУ - в него пишет СПУ, и второй кольцевой буфер - для ППУ, но он маленький, потому что намного бОльшее количество объектов идет прямо на СПУ.
Два: ну так быстрее же должно работать без КОЛЛ-РЕТов (хотя реально это вряд ли сказывается, разве что шедоу-пасс да зет-припасс).
В итоге получилась схема, которая не параллелится (то есть чисто на генерацию КБ работает только один СПУ, остальные занимаются эджем и шейдер патчингом). Хочется переделать. Вопрос такой: как выделяется память под КБ для индивидуальных объектов (или групп) и как мы узнаем о том, что она освободилась?
Удивился высокому выигрышу от оптимизации кода на СПУ. Какое из изменений было САМЫМ полезным? Что именно соптимизировалось так хорошо (патчинг, создание КБ, остальное)?
Reply
Про выигрыш от оптимизации кода - ну, хм, порядок вроде обычный :) а вот с самым полезным сложно. Вот список который велся по ходу разработки (от начала до конца, тут не только оптимизация кода): http://www.everfall.com/paste/id.php?o64o1utg8z3n
shader patch optimization - это в основном всякие loop unroll, apply auto params это много чего (loop unroll, switch -> table lookup, подтягивание пары математических функций, выравнивание входных данных, оптимизация setvertexshaderconstant, etc.).
Reply
С оптимизацией порядок был бы обычный если речь шла бы о какой-нибудь потоковой обработке с кучей математики. Типа постпроцессинга. Там, конечно, да. Даблбафер сделал - в два раза быстрее. Векторизовал - в три раза быстрее. А обычный код типа генерации КБ соптимизировать в 6 раз это почетно. Если будет время попробую тоже.
Reply
С оптимизацией - там не в 6, там в 4 раза. Но вообще я сам удивился, да ;)
Reply
Leave a comment