Эффективные технологии Direct2D многопоточность
Я пишу приложение для чтения электронных книг для Windows Store. Я использую цепочки подкачки Direct2D + DXGI для отображения страниц книг на экране.
мой контент книги иногда довольно сложен (геометрия, растровые изображения, маски и т. д.), Поэтому для его рендеринга может потребоваться до 100 мс. Поэтому я пытаюсь сделать внеэкранный рендеринг для растрового изображения в отдельном потоке, а затем просто показать это растровое изображение в основном потоке.
однако я не могу понять, как это сделать эффективно.
до сих пор я пробовал два подходы:
использовать один
ID2D1Factory
с флагом D2D1_FACTORY_TYPE_MULTI_THREADED создайтеID2D1BitmapRenderTarget
и использовать его в фоновом потоке для закадрового перевода. (Для этого дополнительно требуетсяID2D1Multithread::Enter/Leave
onIDXGISwapChain::Present
операции). Проблема в том,ID2D1RenderTarget::EndDraw
работа в фоновом потоке иногда занимает до 100 мс, и рендеринг основного потока блокируется на этот период из-за внутренней блокировки Direct2D.использовать отдельный
ID2D1Factory
в фоновом потоке (как описано в http://www.sdknews.com/ios/using-direct2d-for-server-side-rendering) и выключите внутреннюю синхронизацию Direct2D. В этом случае нет перекрестной блокировки между двумя потоками. К сожалению, в этом случае я не могу использовать результирующее растровое изображение в mainID2D1Factory
сразу, потому что оно принадлежит к различной фабрике. Я должен переместить растровые данные в память процессора, а затем скопировать их в память GPU mainID2D1Factory
. Эта операция также вводит значительные задержки (я считаю, что это из-за большого доступа к памяти, но я не уверен).
есть ли способ сделать это эффективно?
P. S. Все сроки приведены для Acer переключатель 10 таблетки. На обычном Core i7 PC оба подхода работают без видимого отставания.
2 ответов
хорошо, я нашел решение.
в принципе, все, что мне нужно, это изменить подход 2 для использования совместного использования ресурсов DXGI между двумя заводскими наборами DirectX. Я пропущу все кровавые детали (их можно найти здесь:http://xboxforums.create.msdn.com/forums/t/66208.aspx), но основные шаги:
- создайте два набора ресурсов DirectX: основной (который будет использоваться для экранного рендеринга) и дополнительный (для экрана передача.)
- используя
ID3D11Device2
из основного набора ресурсов создайте текстуру D3D 2D с помощьюCreateTexture2D
D3D11_BIND_RENDER_TARGET
,D3D11_BIND_SHADER_RESOURCE
,D3D11_RESOURCE_MISC_SHARED_NTHANDLE
иD3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX
флаги. - получить общий дескриптор от него, бросив его в
IDXGIResource1
, а вызовCreateSharedHandle
СXGI_SHARED_RESOURCE_READ
иDXGI_SHARED_RESOURCE_WRITE
. - откройте эту общую текстуру во вторичном наборе ресурсов в фоновом потоке, вызвав
ID3D11Device2::OpenSharedResource1
. - приобрести ключевой мьютекс этой текстуры (
IDXGIKeyedMutex::AcquireSync
), создайте цель рендеринга из него (ID2D1Factory2::CreateDxgiSurfaceRenderTarget
), нарисуйте на нем и отпустите мьютекс (IDXGIKeyedMutex::ReleaseSync
). - в основном потоке, в основном наборе ресурсов, приобретите мьютекс и создайте общее растровое изображение из текстуры, созданной на Шаге 2, Нарисуйте это растровое изображение, затем отпустите мьютекс.
обратите внимание, что материал блокировки мьютекса необходим. Не делать этого приводит к некоторым зашифрованным сообщениям об ошибках отладки DirectX и ошибочной операции или даже сбою.
tl; dr: визуализация растровых изображений в фоновом потоке в программном режиме. Нарисуйте из растровых изображений, чтобы отобразить цель в потоке пользовательского интерфейса в аппаратном режиме.
лучший подход, который я смог найти до сих пор, - использовать фоновые потоки с программа рендеринг (IWICImagingFactory::CreateBitmap
и ID2D1Factory::CreateWicBitmapRenderTarget
), а затем скопируйте его в аппаратное растровое изображение обратно в поток с аппаратной целью рендеринга через ID2D1RenderTarget::CreateBitmapFromWicBitmap
. А затем blit, используя ID2D1RenderTarget::DrawBitmap
.
вот как paint.net 4.0 делает выбор отрисовка. Когда вы рисуете выделение с помощью инструмента лассо, он будет использовать фоновый поток для асинхронного рисования контура выделения (поток пользовательского интерфейса не ждет завершения этого). Вы можете получить очень сложный многоугольник из-за стиля Штриха и анимации. Я визуализирую его 4 раза, где каждый кадр анимации имеет немного другое смещение для стиля штрихового штриха.
очевидно, что этот рендеринг может занять некоторое время, поскольку полигон становится более сложным (то есть, если вы продолжаете писать некоторое время). У меня есть несколько других специальных оптимизаций, когда вы используете инструмент выбора перемещения, который позволяет выполнять преобразования (поворот, перевод, масштаб): если фоновый поток еще не перерисовал текущий многоугольник с новым преобразованием, то я буду отображать старое растровое изображение (с текущим многоугольником и старым преобразованием) с новым преобразованием. Контур выделения может быть искажен (масштабирование) или обрезан (переведен за пределы видимой области), в то время как фоновый поток догоняет, но это небольшая цена за отзывчивость 60fps. Эта оптимизация работает очень хорошо, потому что вы не можете изменять полигон и преобразование выделения одновременно.