Поделиться через


Совместное использование surface между API графики Windows

В этом разделе представлен технический обзор взаимодействия с использованием общего доступа к поверхностям между графическими API Windows, включая Direct3D 11, Direct2D, DirectWrite, Direct3D 10 и Direct3D 9Ex. Если у вас уже есть рабочие знания об этих API, этот документ поможет вам использовать несколько API для отрисовки на одной поверхности в приложении, предназначенном для операционных систем Windows 7 или Windows Vista. В этом разделе также приведены рекомендации и указатели на дополнительные ресурсы.

Примечание.

Для взаимодействия Direct2D и DirectWrite в среде выполнения DirectX 11.1 можно использовать устройства Direct2D и контексты устройств для отображения непосредственно на устройствах Direct3D 11.

 

Эта тема описана в следующих разделах.

Введение

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

В Windows 7 (и Windows Vista SP2 с пакетом взаимодействия Windows 7, Vista 7IP) интерфейсы API отрисовки графики — Direct3D 11, Direct2D, Direct3D 10.1, Direct3D 10.0, Direct3D 9Ex, Direct3D 9c и более ранних API Direct3D, а также GDI и GDI+. Компонент образов Windows (WIC) и DirectWrite связаны с технологиями обработки изображений, а Direct2D выполняет отрисовку текста. API ускорения видео DirectX (DXVA), основанный на Direct3D 9c и Direct3D 9Ex, используется для обработки видео.

По мере развития графических API Windows на основе Direct3D корпорация Майкрософт инвестирует больше усилий в обеспечение взаимодействия между API. Недавно разработанные API Direct3D и API более высокого уровня на основе API Direct3D также обеспечивают поддержку, необходимую для обеспечения совместимости со старыми API. Для иллюстрации приложения Direct2D могут использовать Direct3D 10.1, предоставляя общий доступ к устройству Direct3D 10.1. Кроме того, API Direct3D 11, Direct2D и Direct3D 10.1 могут воспользоваться преимуществами инфраструктуры графики DirectX (DXGI) 1.1, что позволяет синхронизированным общим поверхностям, которые полностью поддерживают взаимодействие между этими API. ИНТЕРФЕЙСы API на основе DXGI 1.1 взаимодействуют с GDI и GDI+ по ассоциации, получая контекст устройства GDI из поверхности DXGI 1.1.

Несинхронизированный общий доступ к поверхностям поддерживается средой выполнения Direct3D 9Ex. Приложения видео на основе DXVA могут использовать вспомогательный помощник по взаимодействию Direct3D 9Ex и DXGI для взаимодействия DXVA на основе Direct3D 9Ex с Direct3D 11 для вычислений шейдера или может взаимодействовать с Direct2D для элементов управления 2D или отрисовки текста. WIC и DirectWrite также взаимодействуют с GDI, Direct2D и другими API Direct3D.

Direct3D 10.0, Direct3D 9c и более старые среды выполнения Direct3D не поддерживают общие поверхности. Копии системной памяти будут по-прежнему использоваться для взаимодействия с API на основе GDI или DXGI.

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

Обзор взаимодействия API

Взаимодействие с графическими ИНТЕРФЕЙСами Windows surface можно описать с точки зрения сценариев API в API и соответствующих функциональных возможностей взаимодействия. Начиная с Windows 7 и начиная с Windows Vista с пакетом обновления 2 (SP2) с 7IP, новые API и связанные среды выполнения включают Direct2D и связанные технологии: Direct3D 11 и DXGI 1.1. Производительность GDI также была улучшена в Windows 7. Direct3D 10.1 появился в Windows Vista с пакетом обновления 1 (SP1). На следующей схеме показана поддержка взаимодействия между API.

схема поддержки взаимодействия между графическими API Windows

На этой схеме стрелки показывают сценарии взаимодействия, в которых одна и та же поверхность может быть доступна подключенными API. Синие стрелки указывают на механизмы взаимодействия, представленные в Windows Vista. Зеленые стрелки указывают на поддержку взаимодействия для новых API или улучшений, которые помогают старым API взаимодействовать с новыми API. Например, зеленые стрелки представляют общий доступ к устройствам, поддержку синхронизированной общей поверхности, вспомогательный инструмент синхронизации Direct3D 9Ex/DXGI и получение контекста устройства GDI из совместимой поверхности.

Сценарии взаимодействия

Начиная с Windows 7 и Windows Vista 7IP, основные предложения из графических API Windows поддерживают отрисовку нескольких API в одной поверхности DXGI 1.1.

Direct3D 11, Direct3D 10.1, Direct2D — взаимодействие друг с другом

Api Direct3D 11, Direct3D 10.1 и Direct2D (и связанные с ним API, такие как DirectWrite и WIC), могут взаимодействовать друг с другом с помощью общего доступа к устройствам Direct3D 10.1 или синхронизированных общих поверхностей.

Совместное использование устройств Direct3D 10.1 с Direct2D

Совместное использование устройств между Direct2D и Direct3D 10.1 позволяет приложению использовать оба API для эффективной и эффективной отрисовки на одной поверхности DXGI 1.1, используя один и тот же базовый объект устройства Direct3D. Direct2D позволяет вызывать API Direct2D с помощью существующего устройства Direct3D 10.1, используя тот факт, что Direct2D построен на основе среды выполнения Direct3D 10.1 и DXGI 1.1. В следующих фрагментах кода показано, как Direct2D получает целевой объект отрисовки устройства Direct3D 10.1 из поверхности DXGI 1.1, связанной с устройством. Целевой объект отрисовки устройства Direct3D 10.1 может выполнять вызовы рисования Direct2D между API BeginDraw и EndDraw.

// Direct3D 10.1 Device and Swapchain creation
HRESULT hr = D3D10CreateDeviceandSwapChain1(
                pAdapter,
                DriverType,
                Software,
                D3D10_CREATE_DEVICE_BGRA_SUPPORT,
                featureLevel,
                D3D10_1_SDK_VERSION,
                pSwapChainDesc,
                &pSwapChain,
                &pDevice
                );

hr = pSwapChain->GetBuffer(
        0,
        __uuidof(IDXGISurface),
        (void **)&pDXGIBackBuffer
        ));

// Direct3D 10.1 API rendering calls
...

hr = D2D1CreateFactory(
        D2D1_FACTORY_TYPE_SINGLE_THREADED,
        &m_spD2DFactory
        ));

pD2DFactory->CreateDxgiSurfaceRenderTarget(
        pDXGIBackBuffer,
        &renderTargetProperties,
        &pD2DBackBufferRenderTarget
        ));
...

pD2DBackBufferRenderTarget->BeginDraw();
//Direct2D API rendering calls
...

pD2DBackBufferRenderTarget->EndDraw();

pSwapChain->Present(0, 0);

Замечания

  • Связанное устройство Direct3D 10.1 должно поддерживать формат BGRA. Это устройство было создано путем вызова D3D10CreateDevice1 с параметром D3D10_CREATE_DEVICE_BGRA_SUPPORT. Формат BGRA поддерживается начиная с уровня компонентов Direct3D 10 9.1.
  • Приложение не должно создавать несколько идентификаторов ID2D1RenderTargets, связываясь с тем же устройством Direct3D10.1.
  • Для оптимальной производительности всегда сохраняйте по крайней мере один ресурс, например текстуры или поверхности, связанные с устройством.

Общий доступ к устройствам подходит для однопоточного использования одного устройства отрисовки, совместно используемого как Direct3D 10.1, так и API-интерфейсов отрисовки Direct2D. Синхронизированные общие поверхности обеспечивают многопоточное, внутрипроцессное и внепроцессное использование нескольких устройств отрисовки, используемых API Direct3D 10.1, Direct2D и Direct3D 11.

Другим методом взаимодействия Direct3D 10.1 и Direct2D является использование ID3D1RenderTarget::CreateSharedBitmap, который создает объект ID2D1Bitmap из IDXGISurface. Вы можете написать сцену Direct3D10.1 на растровое изображение и отобразить ее с помощью Direct2D. Дополнительные сведения см. в разделе ID2D1RenderTarget::CreateSharedBitmap Method.

Растеризация программного обеспечения Direct2D

Совместное использование устройств с Direct3D 10.1 не поддерживается при использовании отрисовщика программного обеспечения Direct2D, например путем указания D2D1_RENDER_TARGET_USAGE_FORCE_SOFTWARE_RENDERING в D2D1_RENDER_TARGET_USAGE при создании целевого объекта отрисовки Direct2D.

Direct2D может использовать программный растризатор WARP10 для совместного использования устройства с Direct3D 10 или Direct3D 11, но производительность значительно снижается.

Синхронизированные общие поверхности DXGI 1.1

API Direct3D 11, Direct3D 10.1 и Direct2D используют DXGI 1.1, что обеспечивает функциональность для синхронизации чтения с одной и той же поверхности памяти видео (DXGISurface1) двумя или более устройствами Direct3D. Устройства отрисовки, использующие синхронизированные общие поверхности, могут быть устройства Direct3D 10.1 или Direct3D 11, работающие в одном процессе или межпроцессных процессах.

Приложения могут использовать синхронизированные общие поверхности для взаимодействия между любыми устройствами на основе DXGI 1.1, например Direct3D 11 и Direct3D 10.1 или между Direct3D 11 и Direct2D, путем получения устройства Direct3D 10.1 из целевого объекта отрисовки Direct2D.

В API Direct3D 10.1 и более поздних версий для использования DXGI 1.1 убедитесь, что устройство Direct3D создается с помощью объекта адаптера DXGI 1.1, который перечисляется из объекта фабрики DXGI 1.1. Вызовите CreateDXGIFactory1, чтобы создать объект IDXGIFactory1 и EnumAdapters1, чтобы перечислить объект IDXGIAdapter1. Объект IDXGIAdapter1 необходимо передать в рамках вызова D3D10CreateDevice или D3D10CreateDeviceAndSwapChain. Дополнительные сведения об API DXGI 1.1 см. в руководстве по программированию для DXGI.

Программные интерфейсы

D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX
При создании синхронизированного общего ресурса задайте D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX в D3D10_RESOURCE_MISC_FLAG.

typedef enum D3D10_RESOURCE_MISC_FLAG {
    D3D10_RESOURCE_MISC_GENERATE_MIPS      = 0x1L,
    D3D10_RESOURCE_MISC_SHARED             = 0x2L,
    D3D10_RESOURCE_MISC_TEXTURECUBE        = 0x4L,
    D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX  = 0x10L,
    D3D10_RESOURCE_MISC_GDI_COMPATIBLE     = 0x20L,
}   D3D10_RESOURCE_MISC_FLAG;

D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX
Позволяет синхронизировать созданный ресурс с помощью API IDXGIKeyedMutex::AcquireSync и ReleaseSync. Следующие API-интерфейсы создания ресурсов Direct3D 10.1, которые все принимают параметр D3D10_RESOURCE_MISC_FLAG, были расширены для поддержки нового флага.

  • ID3D10Device1::CreateTexture1D
  • ID3D10Device1::CreateTexture2D
  • ID3D10Device1::CreateTexture3D
  • ID3D10Device1::CreateBuffer

Если какие-либо из перечисленных функций вызываются с набором флагов D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX, интерфейс, возвращенный, можно запросить интерфейс IDXGIKeyedMutex, который реализует API AcquireSync и ReleaseSync для синхронизации доступа к поверхности. Устройство, создающее поверхность и любое другое устройство, открывающее поверхность (с помощью OpenSharedResource), требуется для вызова IDXGIKeyedMutex::AcquireSync перед любыми командами отрисовки на поверхность и IDXGIKeyedMutex::ReleaseSync при завершении отрисовки.
Устройства WARP и REF не поддерживают общие ресурсы. При попытке создать ресурс с этим флагом на устройстве WARP или REF метод создания возвращает код ошибки E_OUTOFMEMORY.
ИНТЕРФЕЙС IDXGIKEYEDMUTEX
Новый интерфейс в DXGI 1.1, IDXGIKeyedMutex, представляет ключ мьютекс, который обеспечивает монопольный доступ к общему ресурсу, используемому несколькими устройствами. Справочная документация об этом интерфейсе и двух методах AcquireSync и ReleaseSync см. в разделе IDXGIKeyedMutex.

Пример. Синхронизация общего доступа к поверхности между двумя устройствами Direct3D 10.1

В приведенном ниже примере показано совместное использование поверхности между двумя устройствами Direct3D 10.1. Синхронизированная общая поверхность создается устройством Direct3D10.1.

// Create Sync Shared Surface using Direct3D10.1 Device 1.
D3D10_TEXTURE2D_DESC desc;
ZeroMemory( &desc, sizeof(desc) );
desc.Width = width;
desc.Height = height;
desc.MipLevels = 1;
desc.ArraySize = 1;
// must match swapchain format in order to CopySubresourceRegion.
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
desc.SampleDesc.Count = 1;
desc.Usage = D3D10_USAGE_DEFAULT;
// creates 2D texture as a Synchronized Shared Surface.
desc.MiscFlags = D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX;
desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE;
ID3D10Texture2D* g_pShared = NULL;
g_pd3dDevice1->CreateTexture2D( &desc, NULL, &g_pShared );

// QI IDXGIResource interface to synchronized shared surface.
IDXGIResource* pDXGIResource = NULL;
g_pShared->QueryInterface(__uuidof(IDXGIResource), (LPVOID*) &pDXGIResource);

// obtain handle to IDXGIResource object.
pDXGIResource->GetSharedHandle(&g_hsharedHandle);
pDXGIResource->Release();
if ( !g_hsharedHandle )
    return E_FAIL;

// QI IDXGIKeyedMutex interface of synchronized shared surface's resource handle.
hr = g_pShared->QueryInterface( __uuidof(IDXGIKeyedMutex),
    (LPVOID*)&g_pDXGIKeyedMutex_dev1 );
If ( FAILED( hr ) || ( g_pDXGIKeyedMutex_dev1 == NULL ) )
    return E_FAIL;

То же устройство Direct3D10.1 может получить синхронизированную общую поверхность для отрисовки путем вызова AcquireSync, а затем освобождения поверхности для отрисовки другого устройства путем вызова ReleaseSync. Если не предоставлять общий доступ к синхронизированной общей поверхности с любым другим устройством Direct3D, создатель может получить и освободить синхронизированную общую поверхность (для запуска и завершения отрисовки) путем получения и выпуска с помощью того же значения ключа.

// Obtain handle to Sync Shared Surface created by Direct3D10.1 Device 1.
hr = g_pd3dDevice2->OpenSharedResource( g_hsharedHandle,__uuidof(ID3D10Texture2D),
                                        (LPVOID*) &g_pdev2Shared);
if (FAILED (hr))
    return hr;
hr = g_pdev2Shared->QueryInterface( __uuidof(IDXGIKeyedMutex),
                                    (LPVOID*) &g_pDXGIKeyedMutex_dev2);
if( FAILED( hr ) || ( g_pDXGIKeyedMutex_dev2 == NULL ) )
    return E_FAIL;

// Rendering onto Sync Shared Surface from D3D10.1 Device 1 using D3D10.1 Device 2.
UINT acqKey = 1;
UINT relKey = 0;
DWORD timeOut = 5;
DWORD result = g_pDXGIKeyedMutex_dev2->AcquireSync(acqKey, timeOut);
if ( result == WAIT_OBJECT_0 )
    // Rendering calls using Device 2.
else
    // Handle unable to acquire shared surface error.
result = g_pDXGIKeyedMutex_dev2->ReleaseSync(relKey));
if (result == WAIT_OBJECT_0)
    return S_OK;

Второе устройство Direct3D10.1 может получить синхронизированную общую поверхность для отрисовки путем вызова AcquireSync, а затем освобождение поверхности для отрисовки первого устройства путем вызова ReleaseSync. Обратите внимание, что устройство 2 может получить синхронизированную общую поверхность, используя то же значение ключа, что и в вызове ReleaseSync устройства 1.

// Rendering onto Sync Shared Surface from D3D10.1 Device 1 using D3D10.1 Device 1.
UINT acqKey = 0;
UINT relKey = 1;
DWORD timeOut = 5;
DWORD result = g_pDXGIKeyedMutex_dev1->AcquireSync(acqKey, timeOut);
if (result == WAIT_OBJECT_0)
    // Rendering calls using Device 1.
else
    // Handle unable to acquire shared surface error.
result = g_pDXGIKeyedMutex_dev1->ReleaseSync(relKey));
if ( result == WAIT_OBJECT_0 )
    return S_OK;

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

// Within Device 1's process/thread:
// Rendering onto Sync Shared Surface from D3D10.1 Device 1 using D3D10.1 Device 1
result = g_pDXGIKeyedMutex_dev1->AcquireSync(0, timeOut);
// Rendering calls using Device 1
...
result = g_pDXGIKeyedMutex_dev1->ReleaseSync(1);
...
////////////////////////////////////////////////////////////////////////////
// Within Device 2's process/thread:
// Rendering onto Sync Shared Surface from D3D10.1 Device 1 using D3D10.1 Device 2
result = g_pDXGIKeyedMutex_dev2->AcquireSync(1, timeOut);
// Rendering calls using Device 2
...
result = g_pDXGIKeyedMutex_dev1->ReleaseSync(2);

////////////////////////////////////////////////////////////////////////////
// Within Device 3's process/thread:
// Rendering onto Sync Shared Surface from D3D10.1 Device 1 using D3D10.1 Device 3
result = g_pDXGIKeyedMutex_dev1->AcquireSync(2, timeOut);
// Rendering calls using Device 3
...
result = g_pDXGIKeyedMutex_dev1->ReleaseSync(0);
...

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

Использование синхронизированных общих поверхностей с Direct2D и Direct3D 11

Аналогичным образом, для совместного использования между API Direct3D 11 и Direct3D 10.1 можно создать синхронизированную общую поверхность на устройстве API и предоставить доступ к другим устройствам API в процессе или вне процесса.

Приложения, использующие Direct2D, могут совместно использовать устройство Direct3D 10.1 и использовать синхронизированную общую поверхность для взаимодействия с Direct3D 11 или другими устройствами Direct3D 10.1, независимо от того, принадлежат ли они к одному процессу или разным процессам. Однако для однопроцессных приложений с одним потоком совместное использование устройств — это наиболее высокопроизводительный и эффективный способ взаимодействия между Direct2D и Direct3D 10 или Direct3D 11.

Программный растризатор

Синхронизированные общие поверхности не поддерживаются, если приложения используют программные растризаторы Direct3D или Direct2D, включая эталонный растризатор и WARP, а не использование аппаратного ускорения графического оборудования.

Взаимодействие между API на основе Direct3D 9Ex и DXGI

API Direct3D 9Ex включали понятие общего доступа к поверхности, чтобы разрешить другим API читать из общей поверхности. Чтобы предоставить общий доступ к общей поверхности Direct3D 9Ex для чтения и записи, необходимо добавить ручную синхронизацию в само приложение.

Помощник по синхронизации Direct3D 9Ex Shared Surfaces плюс вспомогательный помощник по синхронизации вручную

Самая основная задача в Direct3D 9Ex и Direct3D 10 или 11 заключается в передаче одной поверхности от первого устройства (устройства A) ко второму (устройству B), таким образом, когда устройство B получает дескриптор на поверхности, отрисовка устройства A гарантированно завершена. Поэтому устройство B может использовать эту поверхность без беспокойства. Это очень похоже на классическую проблему производителя-потребителя, и эта дискуссия моделирует проблему таким образом. Первое устройство, использующее поверхность, а затем относит его от производителя (Device A), а устройство, которое изначально ожидает, является потребителем (Устройство B). Любое реальное приложение является более сложным, чем это, и будет объединять несколько стандартных блоков производителя-потребителя, чтобы создать нужную функциональность.

Стандартные блоки производителя-потребителя реализуются в вспомогательном приложении с помощью очереди поверхностей. Поверхности заквещаются производителем и удаляются потребителем. Вспомогательный интерфейс представляет три COM-интерфейса: ISurfaceQueue, ISurfaceProducer и ISurfaceConsumer.

Общие сведения о вспомогательном помощнике

Объект ISurfaceQueue — это стандартный блок для использования общих поверхностей. Он создается с инициализированным устройством Direct3D и описанием для создания фиксированного количества общих поверхностей. Объект очереди управляет созданием ресурсов и открытием кода. Число и тип поверхностей фиксированы; После создания поверхностей приложение не может добавить или удалить их.

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

Время существования создания или объекта
Существует два способа создания объекта очереди: с помощью CreateSurfaceQueue или с помощью метода Clone iSurfaceQueue. Так как интерфейсы являются COM-объектами, применяется стандартное управление временем существования COM.
Модель производителя или потребителя
Enqueue (): Производитель вызывает эту функцию, чтобы указать, что она выполняется с поверхностью, которая теперь может стать доступной другому устройству. После возвращения из этой функции устройство-производитель больше не имеет прав на поверхность и небезопасно продолжать использовать его.
Dequeue (): потребляющее устройство вызывает эту функцию для получения общей поверхности. API гарантирует, что все отложенные поверхности готовы к использованию.
Метаданные
API поддерживает связывание метаданных с общими поверхностями.
В enqueue() можно указать дополнительные метаданные, которые будут передаваться на используемое устройство. Метаданные должны быть меньше максимального числа известных во время создания.
Dequeue() может при необходимости передавать буфер и указатель на размер буфера. Очередь заполняет буфер метаданными из соответствующего вызова Enqueue.
Клонирование
Каждый объект ISurfaceQueue решает односторонней синхронизации. Предполагается, что подавляющее большинство приложений, использующих этот API, будет использовать закрытую систему. Простейшая закрытая система с двумя устройствами, отправляя поверхности назад и вперед, требует двух очередей. Объект ISurfaceQueue имеет метод Clone(), чтобы создать несколько очередей, которые являются частью одного большого конвейера.
Клон создает новый объект ISurfaceQueue из существующего объекта и предоставляет общий доступ ко всем открытым ресурсам между ними. Результирующий объект имеет точно те же поверхности, что и исходная очередь. Клонированные очереди могут иметь разные размеры метаданных друг от друга.
Surfaces
ISurfaceQueue отвечает за создание и управление ее поверхностями. Недопустимо, чтобы заквещать произвольные поверхности. Кроме того, поверхность должна иметь только одного активного "владельца". Он должен находиться в определенной очереди или использоваться определенным устройством. Недопустимо иметь его в нескольких очередях или для устройств, которые будут продолжать использовать поверхность после его выполнения.

Сведения об API

IsurfaceQueue

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

Очередь предоставляет следующие API:

API Description
CreateSurfaceQueue Создает объект ISurfaceQueue (очередь "root").
ISurfaceQueue::OpenConsumer Возвращает интерфейс для отложенного устройства.
ISurfaceQueue::OpenProducer Возвращает интерфейс для создания устройства, которое необходимо заставить.
ISurfaceQueue::Clone Создает объект ISurfaceQueue, который использует поверхности с объектом корневой очереди.

 

CreateSurfaceQueue

typedef struct SURFACE_QUEUE_DESC {
  UINT            Width;
  UINT            Height;
  DXGI_FORMAT     Format;
  UINT            NumSurfaces;
  UINT            MetaDataSize;
  DWORD           Flags;
} SURFACE_QUEUE_DESC;

Участники

Ширина, высота измерений общих поверхностей. Все общие поверхности должны иметь одинаковые размеры.
Формат общих поверхностей. Все общие поверхности должны иметь одинаковый формат. Допустимые форматы зависят от используемых устройств, так как разные пары устройств могут совместно использовать разные типы форматов.
NumSurfaces Количество поверхностей, входящих в очередь. Это фиксированное число.
MetaDataSize Максимальный размер буфера метаданных.
Флаги флагов для управления поведением очереди. См. заметки.

HRESULT CreateSurfaceQueue(
  [in]   SURFACE_QUEUE_DESC *pDesc,
  [in]   IUnknown *pDevice,
  [out]  IDXGIXSurfaceQueue **ppQueue
);

Параметры

pDesc [in] Описание создаваемой общей очереди поверхности.

pDevice [in] Устройство, которое должно использоваться для создания общих поверхностей. Это явный параметр из-за функции в Windows Vista. Для общих поверхностей между Direct3D 9 и Direct3D 10 поверхности должны создаваться с помощью Direct3D 9.

ppQueue [out] При возврате содержит указатель на объект ISurfaceQueue.

Возвращаемые значения

Если pDevice не может совместно использовать ресурсы, эта функция возвращает DXGI_ERROR_INVALID_CALL. Эта функция создает ресурсы. Если ошибка завершается ошибкой, возвращается ошибка. В случае успешного выполнения возвращается S_OK.

Замечания

Создание объекта очереди также создает все поверхности. Предполагается, что все поверхности представляют собой 2D-целевые объекты отрисовки и будут созданы с помощью набора флагов D3D10_BIND_RENDER_TARGET и D3D10_BIND_SHADER_RESOURCE (или эквивалентных флагов для разных сред выполнения).

Разработчик может указать флаг, указывающий, будет ли очередь обращаться к нескольким потокам. Если флаги не заданы (Flags == 0), очередь будет использоваться несколькими потоками. Разработчик может указать единый потоковый доступ, который отключает код синхронизации и обеспечивает улучшение производительности для этих случаев. Каждая клонированная очередь имеет собственный флаг, поэтому для разных очередей в системе могут быть разные элементы управления синхронизацией.

Открытие производителя

HRESULT OpenProducer(
  [in]   IUnknown *pDevice,
  [out]  IDXGIXSurfaceProducer **ppProducer
);

Параметры

pDevice [in]

Устройство производителя, которое помещает поверхности в очередь поверхности.

ppProducer [out] Возвращает объект в интерфейс производителя.

Возвращаемые значения

Если устройство не поддерживает общий доступ к поверхностям, возвращает DXGI_ERROR_INVALID_CALL.

Открытие потребителя

HRESULT OpenConsumer(
  [in]   IUnknown *pDevice,
  [out]  IDXGIXSurfaceConsumer **ppConsumer
);

Параметры
pDevice [in]
Устройство-получатель, которое удаляет поверхности из очереди surface. ppConsumer [out] Возвращает объект в интерфейс потребителя.

Возвращаемые значения

Если устройство не поддерживает общий доступ к поверхностям, возвращает DXGI_ERROR_INVALID_CALL.

Замечания

Эта функция открывает все поверхности в очереди для устройства ввода и кэширует их. Последующие вызовы Dequeue просто пойдут в кэш и не должны повторно открывать поверхности каждый раз.

Клонирование IDXGIXSurfaceQueue

typedef struct SHARED_SURFACE_QUEUE_CLONE_DESC {
  UINT         MetaDataSize;
  DWORD        Flags;
} SHARED_SURFACE_QUEUE_CLONE_DESC;

Элементы MetaDataSize и Flags имеют то же поведение, что и для CreateSurfaceQueue.

HRESULT Clone(
  [in]   SHARED_SURFACE_QUEUE_CLONE_DESC *pDesc,
  [out]  IDXGIXSurfaceQueue **ppQueue
);

Параметры

pDesc [in] — структуру, которая содержит описание создаваемого объекта Clone. Этот параметр следует инициализировать.
ppQueue [out] Возвращает инициализированный объект.

Замечания

Клонировать можно из любого существующего объекта очереди, даже если он не является корневым.

IDXGIXSurfaceConsumer

HRESULT Dequeue(
  [in]      REFIID    id,
  [out]     void      **ppSurface,
  [in,out]  void      *pBuffer,
  [in,out]  UINT      *pBufferSize,
  [in]      DWORD     dwTimeout
);

Параметры
id [in]
REFIID 2D-поверхности потребляющего устройства.

  • Для IDirect3Device9 идентификатор REFIID должен быть __uuidof(IDirect3DTexture9).
  • Для ID3D10Device reFIID должно быть __uuidof(ID3D10Texture2D).
  • Для id3D11Device reFIID должен быть __uuidof(ID3D11Texture2D).

ppSurface [out] Возвращает указатель на поверхность.
pBuffer [in, out] Необязательный параметр и, если не NULL, при возврате, содержит метаданные, переданные в соответствующем вызове очереди.
pBufferSize [in, out] Размер pBuffer в байтах. Возвращает количество байтов, возвращаемых в pBuffer. Если вызов очереди не предоставил метаданные, pBuffer имеет значение 0.
dwTimeout [in] Указывает значение времени ожидания. Дополнительные сведения см. в примечаниях.

Возвращаемые значения

Эта функция может возвращать WAIT_TIMEOUT, если задано значение времени ожидания, а функция не возвращается до значения времени ожидания. См. заметки. Если поверхности недоступны, функция возвращает значение NULL, pBufferSize имеет значение 0, а возвращаемое значение — 0x80070120 (WIN32_TO_HRESULT(WAIT_TIMEOUT)).

Замечания

Этот API может блокироваться, если очередь пуста. Параметр dwTimeout работает идентично API синхронизации Windows, например WaitForSingleObject. Для неблокирующего поведения используйте время ожидания 0.

ISurfaceProducer

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

Метод Description
ISurfaceProducer::Enqueue Помещает поверхность в объект очереди. После завершения этого вызова производитель будет выполнен с поверхностью и поверхность готова к другому устройству.
ISurfaceProducer::Flush Используется, если приложения должны иметь неблокирующее поведение. Дополнительные сведения см. в примечаниях.

 

Enqueue

HRESULT Enqueue(
  [in]  IUnknown *pSurface,
  [in]  void *pBuffer,
  [in]  UINT BufferSize,
  [in]  DWORD Flags
);

Параметры
pSurface [in]
Поверхность производства устройства, которое должно быть вложено. Эта поверхность должна быть отложенной поверхностью из той же сети очередей. pBuffer [in] Необязательный параметр, который используется для передачи метаданных. Он должен указывать на данные, которые будут переданы в вызов dequeue.
BufferSize [in] Размер pBuffer в байтах.
Флаги [in] Необязательный параметр, который управляет поведением этой функции. Единственным флагом является SURFACE_QUEUE_FLAG_ DO_NOT_WAIT. См. примечания для Flush. Если флаг не передается (Flags == 0), используется поведение блокировки по умолчанию.

Возвращаемые значения

Эта функция может возвращать DXGI_ERROR_WAS_STILL_DRAWING, если используется флаг SURFACE_QUEUE_FLAG_DO_NOT_WAIT.

Замечания

  • Эта функция помещает поверхность в очередь. Если приложение не указывает SURFACE_QUEUE_FLAG_DO_NOT_WAIT, эта функция блокируется и будет выполнять синхронизацию GPU-ЦП, чтобы убедиться, что все отрисовки на поверхности, вложенной в очередь, завершены. Если эта функция выполнена успешно, поверхность будет доступна для отмены. Если требуется неблокирующее поведение, используйте флаг DO_NOT_WAIT. Дополнительные сведения см. в разделе Flush().
  • В зависимости от правил подсчета ссылок COM поверхность, возвращаемая Dequeue, будет AddRef(), поэтому приложению не нужно делать это. После вызова Enqueue приложение должно освободить поверхность, так как они больше не используют его.

Краска

HRESULT Flush(
  [in]  DWORD Flags,
  [out] UINT *nSurfaces
);

Параметры
Флаги [in]
Единственным флагом является SURFACE_QUEUE_FLAG_ DO_NOT_WAIT. См. заметки. nSurfaces [out] Возвращает количество поверхностей, которые по-прежнему ожидаются и не смыты.

Возвращаемые значения

Эта функция может возвращать DXGI_ERROR_WAS_STILL_DRAWING, если используется флаг SURFACE_QUEUE_FLAG_DO_NOT_WAIT. Эта функция возвращает S_OK, если все поверхности были успешно удалены. Эта функция возвращает DXGI_ERROR_WAS_STILL_DRAWING только в том случае, если поверхности не были промыты. Вместе возвращаемое значение и nSurfaces указывают приложению, что было сделано, и если какие-либо действия остаются.

Замечания

Flush имеет смысл только в том случае, если предыдущий вызов в очередь использовал флаг DO_NOT_WAIT; в противном случае это будет no-op. Если вызов в очередь использовал флаг DO_NOT_WAIT, запрос возвращается немедленно, а синхронизация GPU-ЦП не гарантируется. Поверхность по-прежнему считается заквеченной, производство устройства не может продолжать использовать его, но оно недоступно для отмены. Чтобы попытаться зафиксировать поверхность для отмены, необходимо вызвать Flush. Очистка пытается зафиксировать все поверхности, которые в настоящее время заключены в очередь. Если флаг не передается в Flush, он будет блокировать и очищать всю очередь, готовя все поверхности в нем для отмены. Если используется флаг DO_NOT_WAIT, очередь проверит поверхности, чтобы узнать, готова ли какая-либо из них; Этот шаг не блокируется. Surfaces, завершив синхронизацию GPU-ЦП, будут готовы к работе с потребительским устройством. Поверхности, которые по-прежнему ожидаются, будут не затронуты. Функция возвращает количество поверхностей, которые по-прежнему должны быть промыты.

Примечание.

Очистка не будет нарушать семантику очереди. API гарантирует, что поверхности, помещенные в очередь, будут зафиксированы до того, как поверхности, закручив их позже, независимо от того, когда происходит синхронизация GPU-ЦП.

 

Вспомогательный помощник взаимодействия Direct3D 9Ex и DXGI: практическое руководство

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

Два устройства

Пример приложения, использующего этот вспомогательный сервер, может совместно использовать Direct3D 9Ex и Direct3D 11. Приложение может обрабатывать содержимое с обоими устройствами и представлять содержимое с помощью Direct3D 9. Обработка может означать отрисовку содержимого, декодирование видео, выполнение вычислительных шейдеров и т. д. Для каждого кадра приложение сначала обрабатывается с Direct3D 11, а затем обрабатывается с Direct3D 9 и, наконец, присутствует с Direct3D 9. Кроме того, обработка с помощью Direct3D 11 создаст некоторые метаданные, которые потребуется использовать Direct3D 9. В этом разделе рассматривается вспомогательное использование в трех частях, соответствующих этой последовательности: инициализация, основной цикл и очистка.

Инициализация
Инициализация включает следующие действия.

  1. Инициализация обоих устройств.
  2. Создайте корневую очередь: m_11to9Queue.
  3. Клонирование из корневой очереди: m_9to11Queue.
  4. Вызов OpenProducer/OpenConsumer в обеих очередях.

Имена очередей используют цифры 9 и 11, чтобы указать, какой API является производителем и который является потребителем: m_продуцердляочереди потребителей. Соответственно, m_11to9Queue указывает очередь, для которой устройство Direct3D 11 создает поверхности, которые использует устройство Direct3D 9. Аналогичным образом, m_9to11Queue указывает очередь, для которой Direct3D 9 создает поверхности, которые использует Direct3D 11.
Корневая очередь изначально заполнена, и все клонированные очереди изначально пусты. Это не должно быть проблемой для приложения, за исключением первого цикла enqueues и Dequeues и доступности метаданных. Если деквею запрашивает метаданные, но ни один не был задан (либо потому, что изначально ничего не существует, либо запрос не задан ничего), dequeue видит, что метаданные не были получены.

  1. Инициализация обоих устройств.

    m_pD3D9Device = InitializeD3D9ExDevice();
    m_pD3D11Device = InitializeD3D11Device();
    
  2. Создайте корневую очередь.
    Этот шаг также создает поверхности. Ограничения размера и формата идентичны созданию общего ресурса. Размер буфера метаданных фиксируется во время создания, и в этом случае мы просто передаваем UINT.
    Очередь должна быть создана с фиксированным числом поверхностей. Производительность зависит от сценария. Наличие нескольких поверхностей повышает вероятность того, что устройства заняты. Например, если существует только одна поверхность, то между двумя устройствами не будет параллелизации. С другой стороны, увеличение числа поверхностей увеличивает объем памяти, что может снизить производительность. В этом примере используется две поверхности.

    SURFACE_QUEUE_DESC Desc;
    Desc.Width        = 640;
    Desc.Height       = 480;
    Desc.Format       = DXGI_FORMAT_R16G16B16A16_FLOAT;
    Desc.NumSurfaces  = 2;
    Desc.MetaDataSize = sizeof(UINT);
    Desc.Flags        = 0;
    
    CreateSurfaceQueue(&Desc, m_pD3D9Device, &m_11to9Queue);
    
  3. Клонируйте корневую очередь.
    Каждая клонированная очередь должна использовать одни и те же поверхности, но может иметь разные размеры буфера метаданных и разные флаги. В этом случае метаданные Direct3D 9 до Direct3D 11 отсутствуют.

    SURFACE_QUEUE_CLONE_DESC Desc;
    Desc.MetaDataSize = 0;
    Desc.Flags        = 0;
    
    m_11to9Queue->Clone(&Desc, &m_9to11Queue);
    
  4. Откройте устройства производителя и потребителя.
    Приложение должно выполнить этот шаг перед вызовом Enqueue и Dequeue. Открытие интерфейса производителя и потребителя возвращает интерфейсы, содержащие API-интерфейсы enqueue/dequeue.

    // Open for m_p9to11Queue.
    m_p9to11Queue->OpenProducer(m_pD3D9Device, &m_pD3D9Producer);
    m_p9to11Queue->OpenConsumer(m_pD3D11Device, &m_pD3D11Consumer);
    
    // Open for m_p11to9Queue.
    m_p11to9Queue->OpenProducer(m_pD3D11Device, &m_pD3D11Producer);
    m_p11to9Queue->OpenConsumer(m_pD3D9Device, &m_pD3D9Consumer);
    

Основной цикл
Использование очереди моделиируется после классической проблемы производителя или потребителя. Подумайте об этом с точки зрения каждого устройства. Каждое устройство должно выполнить следующие действия: вывести поверхность из своей очереди использования, обрабатывать на поверхности, а затем вставлять в очередь производства. Для устройства Direct3D 11 использование Direct3D 9 почти идентично.

// Direct3D 9 Device.
IDirect3DTexture9* pTexture9 = NULL;
REFIID             surfaceID9 = _uuidof(IDirect3DTexture9);
UINT               metaData;
UINT               metaDataSize;
while (!done)
{
    // Dequeue surface.
    m_pD3D9Consumer->Dequeue(surfaceID9, (void**)&pSurface9,
                             &metaData, &metaDataSize, INFINITE);

    // Process the surface.
    ProcessD3D9(pSurface9);

    // Present the surface using the meta data.
    PresentD3D9(pSurface9, metaData, metaDataSize);

    // Enqueue surface.
    m_pD3D9Producer->Enqueue(pSurface9, NULL, 0, 0);
}

Очистка
Этот шаг очень прост. Помимо обычных шагов по очистке API Direct3D, приложение должно освободить возвращаемые COM-интерфейсы.

m_pD3D9Producer->Release();
m_pD3D9Consumer->Release();
m_pD3D11Producer->Release();
m_pD3D11Consumer->Release();
m_p9to11Queue->Release();
m_p11to9Queue->Release();

Неблокирующее использование

Предыдущий пример имеет смысл для многопоточного варианта использования, в котором каждое устройство имеет свой собственный поток. В этом примере используются блокирующие версии API: INFINITE для времени ожидания, и флаг не требуется. Если вы хотите использовать вспомогательный метод без блокировки, необходимо внести только несколько изменений. В этом разделе показано неблокирующее использование обоих устройств в одном потоке.

Инициализация
Инициализация идентична, за исключением флагов. Так как приложение является однопоточным, используйте этот флаг для создания. Это отключает некоторый код синхронизации, который потенциально повышает производительность.

SURFACE_QUEUE_DESC Desc;
Desc.Width        = 640;
Desc.Height       = 480;
Desc.Format       = DXGI_FORMAT_R16G16B16A16_FLOAT;
Desc.NumSurfaces  = 2;
Desc.MetaDataSize = sizeof(UINT);
Desc.Flags        = SURFACE_QUEUE_FLAG_SINGLE_THREADED;

CreateSurfaceQueue(&Desc, m_pD3D9Device, &m_11to9Queue);
SURFACE_QUEUE_CLONE_DESC Desc;
Desc.MetaDataSize = 0;
Desc.Flags        = SURFACE_QUEUE_FLAG_SINGLE_THREADED;

m_11to9Queue->Clone(&Desc, &m_9to11Queue);

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

// Direct3D 11 Device.
ID3D11Texture2D* pSurface11 = NULL;
REFIID           surfaceID11 = __uuidof(ID3D11Texture2D);
UINT             metaData;
while (!done)
{
    //
    // D3D11 Portion.
    //

    // Dequeue surface.
    hr = m_pD3D11Consumer->Dequeue(surfaceID11,
                                   (void**)&pSurface11,
                                   NULL, 0, 0);
    // Only continue if we got a surface.
    if (SUCCEEDED(hr))
    {
        // Process the surface and return some meta data.
        ProcessD3D11(pSurface11, &metaData);

        // Enqueue surface.
        m_pD3D11Producer->Enqueue(pSurface11, &metaData,
                                  sizeof(UINT),
                                  SURFACE_QUEUE_FLAG_DO_NOT_WAIT);
    }
    // Flush the queue to check if any surfaces completed.
    m_pD3D11Producer->Flush(NULL,SURFACE_QUEUE_FLAG_DO_NOT_WAIT);

    //
    // Do the same with the Direct3D 9 Device.
    //

    // Dequeue surface.
    hr = m_pD3D9Consumer->Dequeue(surfaceID9,
                                  (void**)&pSurface9,
                                  &metaData,
                                  &metaDataSize, 0);
    // Only continue if we got a surface.
    if (SUCCEEDED(hr)))
    {
        // Process the surface.
        ProcessD3D9(pSurface9);

        // Present the surface using the meta data.
        PresentD3D9(pSurface9, metaData, metaDataSize);

        // Enqueue surface.
        m_pD3D9Producer->Enqueue(pSurface9, NULL, 0,
                                 SURFACE_QUEUE_FLAG_DO_NOT_WAIT);
    }
    // Flush the queue to check if any surfaces completed.
    m_pD3D9Producer->Flush(NULL,SURFACE_QUEUE_FLAG_DO_NOT_WAIT);
}

Более сложное решение может проверить возвращаемое значение из очереди и от очистки, чтобы определить, требуется ли очистка.

Три устройства

Расширение предыдущих примеров для покрытия нескольких устройств является простым. Следующий код выполняет инициализацию. После создания объектов производителя или потребителя код для их использования совпадает. В этом примере есть три устройства и, следовательно, три очереди. Поток Surfaces из Direct3D 9 в Direct3D 10 в Direct3D 11.

SURFACE_QUEUE_DESC Desc;
Desc.Width        = 640;
Desc.Height       = 480;
Desc.Format       = DXGI_FORMAT_R16G16B16A16_FLOAT;
Desc.NumSurfaces  = 2;
Desc.MetaDataSize = sizeof(UINT);
Desc.Flags        = 0;

SURFACE_QUEUE_CLONE_DESC Desc;
Desc.MetaDataSize = 0;
Desc.Flags        = 0;

CreateSurfaceQueue(&Desc, m_pD3D9Device, &m_11to9Queue);
m_11to9Queue->Clone(&Desc, &m_9to10Queue);
m_11to9Queue->Clone(&Desc, &m_10to11Queue);

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

// Open for m_p9to10Queue.
m_p9to10Queue->OpenProducer(m_pD3D9Device, &m_pD3D9Producer);
m_p9to10Queue->OpenConsumer(m_pD3D10Device, &m_pD3D10Consumer);

// Open for m_p10to11Queue.
m_p10to11Queue->OpenProducer(m_pD3D10Device, &m_pD3D10Producer);
m_p10to11Queue->OpenConsumer(m_pD3D11Device, &m_pD3D11Consumer);

// Open for m_p11to9Queue.
m_p11to9Queue->OpenProducer(m_pD3D11Device, &m_pD3D11Producer);
m_p11to9Queue->OpenConsumer(m_pD3D9Device, &m_pD3D9Consumer);

Заключение

Вы можете создавать решения, использующие взаимодействие для использования нескольких API DirectX. Взаимодействие с графическим API Windows теперь предлагает общую среду выполнения управления surface DXGI 1.1. Эта среда выполнения обеспечивает синхронизированную поддержку общего доступа к поверхности в недавно разработанных API, таких как Direct3D 11, Direct3D 10.1 и Direct2D. Улучшения взаимодействия между новыми API и существующими API-интерфейсами помогают миграции приложений и обратной совместимости. API-интерфейсы потребителей Direct3D 9Ex и DXGI 1.1 могут взаимодействовать, как показано с механизмом синхронизации, доступным в более ранних примерах приложений Win32, которые можно найти в архивной коллекции кодов MSDN в репозитории Microsoft Samples .