Копирование и доступ к данным ресурсов (Direct3D 10)

Больше не нужно думать о ресурсах как о создании в видеопамяти или системной памяти. Или указывает, должна ли среда выполнения управлять памятью. Благодаря архитектуре новой модели WDDM (Windows Display Driver Model) приложения теперь создают ресурсы Direct3D 10 с различными флагами использования , чтобы указать, как приложение намерено использовать данные ресурсов. Новая модель драйвера виртуализирует память, используемую ресурсами; Затем он становится ответственностью диспетчера операционной системы, драйвера и памяти за размещение ресурсов в наиболее эффективной области памяти с учетом ожидаемого использования.

Сценарий по умолчанию подразумевает доступность ресурсов графическому процессору. Конечно, после этого бывают случаи, когда данные ресурсов должны быть доступны ЦП. Чтобы копировать данные ресурсов из разных расположений, чтобы соответствующий процессор мог осуществлять к ним доступ, не влияя на производительность, требует определенных знаний о принципах работы методов API.

Копирование данных ресурса

Ресурсы создаются в памяти, когда Direct3D вызывает метод Create. Они могут создаваться в видеопамяти, системной памяти и любой другой памяти. Поскольку модель драйвера WDDM виртуализирует эту память, приложениям больше не нужно отслеживать, в какой памяти создаются ресурсы.

В идеале все ресурсы должны быть размещены в видеопамяти и быть мгновенно доступны для графического процессора. Однако иногда нужно, чтобы ЦП считал данные ресурсов или графический процессор осуществил доступ к данным ресурсов, в которые был записан ЦП. Direct3D 10 обрабатывает эти различные сценарии, запрашивая в приложении указание использования, а затем предлагает несколько методов для копирования данных ресурсов при необходимости.

В зависимости от того как был создан ресурс, не всегда возможно осуществить прямой доступ к базовым данным. Это может подразумевать необходимость копирования данных ресурса из исходного ресурса в другой, доступный соответствующему процессору. Что касается Direct3D 10, доступ к ресурсам по умолчанию может осуществляться непосредственно с помощью GPU, а динамические и промежуточные ресурсы — напрямую через ЦП.

После создания ресурса его использование нельзя изменить. Вместо этого необходимо копировать содержимое одного ресурса в другой, созданный с другим использованием. Direct3D 10 предоставляет эту функцию тремя различными способами. Первые два метода ( ID3D10Device::CopyResource и ID3D10Device::CopySubresourceRegion) предназначены для копирования данных ресурсов из одного ресурса в другой. Третий метод (ID3D10Device::UpdateSubresource) предназначен для копирования данных из памяти в ресурс.

Существует два основных типа ресурсов: сопоставляемые и несопоставляемые. Ресурсы, созданные с динамическим или промежуточным использованием, сопоставимы, в то время как ресурсы, созданные с использованием по умолчанию или неизменяемым использованием, не сопоставляются.

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

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

Приложения, которые хотят копировать данные из ресурса с использованием по умолчанию в ресурс с промежуточным использованием (чтобы ЦП считывал данные, т. е. проблема обратного чтения GPU), должны делать это с осторожностью. Дополнительные сведения об этом последнем случае см. в разделе Доступ к данным ресурсов .

Доступ к данным ресурса

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

Производительность может упасть до нуля, если приложение попытается сопоставить ресурс в неподходящее время. Если приложение пытается осуществить доступ к результатам операции до того, как она будет завершена, произойдет зависание конвейера.

Выполнение операции сопоставления в неподходящее время может вызвать серьезное падение производительности, поскольку графический процессор и ЦП будут вынуждены синхронизироваться друг с другом. Синхронизация произойдет, если приложение хочет осуществлять доступ к ресурсу до того, как графический процессор завершит копирование ресурса в ресурс, доступный для сопоставления ЦП.

ЦП может считывать только ресурсы, созданные с флагом D3D10_USAGE_STAGING. Так как ресурсы, созданные с помощью этого флага, не могут быть заданы в качестве выходных данных конвейера, если ЦП хочет считывать данные в ресурсе, созданном gpu, данные должны быть скопированы в ресурс, созданный с флагом промежуточного хранения. Приложение может сделать это с помощью методов ID3D10Device::CopyResource или ID3D10Device::CopySubresourceRegion для копирования содержимого одного ресурса в другой. Затем приложение может получить доступ к этому ресурсу, вызвав соответствующий метод Map. Если доступ к ресурсу больше не требуется, приложение должно вызвать соответствующий метод Unmap. Например, ID3D10Texture2D::Map и ID3D10Texture2D::Unmap. Различные методы Map возвращают определенные значения в зависимости от входных флагов. Дополнительные сведения см. в разделе Map Примечания .

Примечание

Когда приложение вызывает метод Map, оно получает указатель на данные ресурса для доступа. Среда выполнения гарантирует, что указатель имеет определенное выравнивание в зависимости от уровня функций. Для D3D_FEATURE_LEVEL_10_0 и выше указатель выравнивается по 16 байтам. Для менее D3D_FEATURE_LEVEL_10_0 указатель выравнивается по 4 байтам. 16-байтовое выравнивание позволяет приложению выполнять операции, оптимизированные для SSE, с данными в собственном коде без перераспределения или копирования.

 

Вопросы производительности

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

В самом худшем сценарии параллелизма графического процессора и ЦП один процессор вынужден ждать результаты работы другого. Direct3D 10 пытается устранить эти затраты, делая методы ID3D10Device::CopyResource и ID3D10Device::CopySubresourceRegion асинхронными; копирование не обязательно выполняется к моменту, когда метод возвращает. Преимущество такого подхода в том, что производительность приложения не снижается из-за фактического копирования данных до тех пор, пока ЦП не осуществит доступ к данным, то есть во время вызова метода Map. Если метод Map вызывается после фактического копирования данных, потерь производительности не происходит. С другой стороны, если метод Map вызывается до копирования данных, произойдет зависание конвейера.

Асинхронные вызовы в Direct3D 10 (которые представляют собой подавляющее большинство методов, и особенно вызовы отрисовки) хранятся в так называемом буфере команд. Этот буфер является внутренним по отношению к графическому драйверу и используется для объединения в пакеты вызовов, адресованных базовому оборудованию, чтобы затратное переключение из режима пользователя в режим ядра в Microsoft Windows осуществлялось как можно реже.

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

  1. Вызывается метод Present .
  2. Вызывается ID3D10Device::Flush.
  3. Буфер команд заполнен; его размер является динамичным и контролируется операционной системой и графическим драйвером.
  4. ЦП требует доступа к результатам команды, дожидаясь выполнения в буфере команд.

Из четырех ситуаций последняя является наиболее неблагоприятной для производительности. Если приложение выполняет вызов ID3D10Device::CopyResource или ID3D10Device::CopySubresourceRegion , этот вызов помещается в очередь в буфер команд. Если приложение попытается сопоставить промежуточный ресурс, который был целевым объектом вызова копирования до очистки буфера команд, произойдет приостановка конвейера, так как необходимо выполнить не только вызов метода Copy, но и все другие буферизированные команды в буфере команд. Это вызовет синхронизацию графического процессора и ЦП, поскольку ЦП будет ждать доступа к промежуточному ресурсу, пока графический процессор очищает буфер команд и, наконец, заполняет необходимый ЦП ресурс. После того как графический процессор завершит копирование, ЦП начнет осуществлять доступ к промежуточному ресурсу, однако в это время графический процессор будет находиться в состоянии простоя.

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

Таким образом, если приложение хочет сопоставить ресурс, который создается в видеопамяти, и вызывает ID3D10Device::CopyResource или ID3D10Device::CopySubresourceRegion в кадре N, этот вызов фактически начнет выполняться в кадре N+1, когда приложение отправляет вызовы для следующего кадра. Копирование необходимо завершить, когда приложение обрабатывает кадр N+2.

Frame Состояние графического процессора/ЦП
Нет
  • ЦП создает вызовы отрисовки для текущего кадра.
N+1
  • Графический процессор выполняет вызовы, отправленные из ЦП во время кадра N.
  • ЦП создает вызовы отрисовки для текущего кадра.
N+2
  • Графический процессор завершает выполнение вызовов, отправленных из ЦП во время кадра N. Результаты готовы.
  • Графический процессор выполняет вызовы, отправленные из ЦП во время кадра N+1.
  • ЦП создает вызовы отрисовки для текущего кадра.
N+3
  • Графический процессор завершает выполнение вызовов, отправленных из ЦП во время кадра N+1. Результаты готовы.
  • Графический процессор выполняет вызовы, отправленные из ЦП во время кадра N+2.
  • ЦП создает вызовы отрисовки для текущего кадра.
N+4 ...

 

Ресурсы (Direct3D 10)