Почему D3D Present() такой медленный?
Он не медленный! Сам по себе Present всего-лишь говорит GPU, что текущий кадр закончен и его можно выводить на экран. Он также может делать несколько копирований (blit) и очищений, но это очень быстрые операции на современных видеокартах.
Причина по которой вы замечаете Present в вашем профайлере, это то, что Present ждет синхронизации СPU и GPU. Это два отдельных устройства работающие асинхронно и параллельно (ну, в идеале :-), и вы должны быть осведомлены, что в некоторое конкретное время, одно из них, может ждать другое. Понятно, что GPU не может идти впереди CPU (потому что CPU генерирует команды для GPU), но достаточно легко CPU может дать видеокарточке столько работы, что GPU не закончит ее в необходимое время.
Предположим, что GPU рендерит кадр каждые 30ms. Но что будет, если CPU закончит кадр только за 10ms? Понятно, что вы можете запоминать ("буферизировать") кадры, чтобы обеспечить более плавный рендеринг (и DirectX делает это за вас, если только вы его не прервете), но в некоторой точке CPU все равно будет создавать столько кадров, что GPU не сможет их отрендерить достаточно быстро.
Вы конечно можете позволить CPU и видеокарточке работать независимо, но может получиться так, что данные от СPU будут рендериться не то что в течении секунд - в течении минут! Так, что когда вы подвинете мышь влево, только через секунду курсор действительно сместится влево. Понятно, что это неприемлемо для игр и других графических приложений реального времени. Поэтому DirectX ограничивает CPU двумя кадрами вперед GPU (отсюда когда вы видите изображение на экране, на самом деле это может быть 3 кадра назад). Это гарантирует низкое время отклика на действия пользователя и высокий, без рывков, FPS, т.е. чем больше буферезированна система, тем меньше GPU и CPU ждут друг друга.
Например, CPU опережает GPU на два кадра и зовет Present. В этой точке, DirectX говорит что CPU ушел слишком далеко вперед, и сознательно ждет пока видеокарта не закончит рендеринг текущего фрейма - что будет занимать 20 ms (30ms - 10 ms). Вот поэтому то вы и видите, что Present занимает столько времени, хотя сам по себе его вызов очень простой.
Если вы хотите не хотите тратить драгоценное CPU-такты на простой, вы можете использовать D3DPRESENT_DONOTWAIT флаг. Что означает, что при переполнении буфера кадров в GPU, Present вместо того чтобы остановиться, вернет D3DERR_WASSTILLDRAWING, как бы говоря "GPU еще занят". После чего вы можете сделать какую-нибудь работу и вызвать Present позже.
Примечание: в общем случае, достаточно трудно выполнять некоторую полезную работу в это время. Вы должны помнить, что на некоторых системах с медленным CPU и быстрыми видеокартами, процессор будет постоянно занят, а GPU наоборот, так что вы никогда не получите D3DERR_WASSTILLDRAWING - GPU будет все время "ждать" CPU. Что означает, что любое время выигранное этим методом очень ненадежно - вы не сможете ничего с помощью него посчитать. Так что будьте очень осторожны с этой функциональностью!
Остается только добавить, что по умолчанию в оконном режиме стоит флаг D3DPRESENT_INTERVAL_DEFAULT, что эквивалентно D3DPRESENT_INTERVAL_ONE. В результате получается, что fps оконных приложений ограничен разверткой монитора (напр 75Hz). Установка его в D3DPRESENT_INTERVAL_IMMEDIATE позволяет выводить изображение сразу, а про D3DPRESENT_DONOTWAIT сказано уже выше.
18 февраля 2007 (Обновление: 4 дек 2017)