Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Содержимое Direct3D9 можно включить в приложение Windows Presentation Foundation (WPF). В этом разделе описывается, как создать содержимое Direct3D9, чтобы эффективно взаимодействовать с WPF.
Замечание
При использовании содержимого Direct3D9 в WPF также необходимо думать о производительности. Дополнительные сведения о том, как оптимизировать производительность, см. в разделе "Рекомендации по повышению производительности" для Direct3D9 и взаимодействия WPF.
Отображение буферов
Класс D3DImage управляет двумя буферами отображения, которые называются задним буфером и передним буфером. Задний буфер — это поверхность Direct3D9. При вызове метода изменения в обратном буфере копируются вперед в передний буфер Unlock.
На следующем рисунке показана связь между задним буфером и передним буфером.
Создание устройства Direct3D9
Чтобы отобразить содержимое Direct3D9, необходимо создать устройство Direct3D9. Существует два объекта Direct3D9, которые можно использовать для создания устройства: объект IDirect3D9
и объект IDirect3D9Ex
. Используйте эти объекты для создания IDirect3DDevice9
и IDirect3DDevice9Ex
устройств соответственно.
Создайте устройство, вызвав один из следующих методов.
IDirect3D9 * Direct3DCreate9(UINT SDKVersion);
HRESULT Direct3DCreate9Ex(UINT SDKVersion, IDirect3D9Ex **ppD3D);
В операционной системе Windows Vista или более поздней версии используйте Direct3DCreate9Ex
метод с дисплеем, настроенным для использования модели драйвера Windows (WDDM).
Direct3DCreate9
Используйте метод на любой другой платформе.
Доступность метода Direct3DCreate9Ex
d3d9.dll имеет метод Direct3DCreate9Ex
только в операционных системах, начиная с Windows Vista. Если вы напрямую связываете функцию в Windows XP, приложение не загружается. Чтобы определить, поддерживается ли Direct3DCreate9Ex
метод, загрузите библиотеку DLL и найдите адрес proc. В следующем коде показано, как протестировать Direct3DCreate9Ex
метод. Полный пример кода см. в пошаговом руководстве по созданию содержимого Direct3D9 для размещения в WPF.
HRESULT
CRendererManager::EnsureD3DObjects()
{
HRESULT hr = S_OK;
HMODULE hD3D = NULL;
if (!m_pD3D)
{
hD3D = LoadLibrary(TEXT("d3d9.dll"));
DIRECT3DCREATE9EXFUNCTION pfnCreate9Ex = (DIRECT3DCREATE9EXFUNCTION)GetProcAddress(hD3D, "Direct3DCreate9Ex");
if (pfnCreate9Ex)
{
IFC((*pfnCreate9Ex)(D3D_SDK_VERSION, &m_pD3DEx));
IFC(m_pD3DEx->QueryInterface(__uuidof(IDirect3D9), reinterpret_cast<void **>(&m_pD3D)));
}
else
{
m_pD3D = Direct3DCreate9(D3D_SDK_VERSION);
if (!m_pD3D)
{
IFC(E_FAIL);
}
}
m_cAdapters = m_pD3D->GetAdapterCount();
}
Cleanup:
if (hD3D)
{
FreeLibrary(hD3D);
}
return hr;
}
Создание HWND
Для создания устройства требуется HWND. Как правило, вы создаете фиктивный HWND для использования Direct3D9. В следующем примере кода показано, как создать фиктивный HWND.
HRESULT
CRendererManager::EnsureHWND()
{
HRESULT hr = S_OK;
if (!m_hwnd)
{
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = DefWindowProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = NULL;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass))
{
IFC(E_FAIL);
}
m_hwnd = CreateWindow(szAppName,
TEXT("D3DImageSample"),
WS_OVERLAPPEDWINDOW,
0, // Initial X
0, // Initial Y
0, // Width
0, // Height
NULL,
NULL,
NULL,
NULL);
}
Cleanup:
return hr;
}
Параметры представления
Для создания устройства также требуется структура D3DPRESENT_PARAMETERS
, но важны только несколько параметров. Эти параметры выбраны для минимизации объема памяти.
Задайте для BackBufferHeight
полей BackBufferWidth
значение 1. Установка значения 0 приводит к тому, что параметры принимают размеры HWND.
Всегда устанавливайте D3DCREATE_MULTITHREADED
и D3DCREATE_FPU_PRESERVE
флаги, чтобы предотвратить повреждение памяти, используемой Direct3D9, и предотвратить изменение параметров FPU Direct3D9.
В следующем коде показано, как инициализировать структуру D3DPRESENT_PARAMETERS
.
HRESULT
CRenderer::Init(IDirect3D9 *pD3D, IDirect3D9Ex *pD3DEx, HWND hwnd, UINT uAdapter)
{
HRESULT hr = S_OK;
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = TRUE;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
d3dpp.BackBufferHeight = 1;
d3dpp.BackBufferWidth = 1;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
D3DCAPS9 caps;
DWORD dwVertexProcessing;
IFC(pD3D->GetDeviceCaps(uAdapter, D3DDEVTYPE_HAL, &caps));
if ((caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) == D3DDEVCAPS_HWTRANSFORMANDLIGHT)
{
dwVertexProcessing = D3DCREATE_HARDWARE_VERTEXPROCESSING;
}
else
{
dwVertexProcessing = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
}
if (pD3DEx)
{
IDirect3DDevice9Ex *pd3dDevice = NULL;
IFC(pD3DEx->CreateDeviceEx(
uAdapter,
D3DDEVTYPE_HAL,
hwnd,
dwVertexProcessing | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE,
&d3dpp,
NULL,
&m_pd3dDeviceEx
));
IFC(m_pd3dDeviceEx->QueryInterface(__uuidof(IDirect3DDevice9), reinterpret_cast<void**>(&m_pd3dDevice)));
}
else
{
assert(pD3D);
IFC(pD3D->CreateDevice(
uAdapter,
D3DDEVTYPE_HAL,
hwnd,
dwVertexProcessing | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE,
&d3dpp,
&m_pd3dDevice
));
}
Cleanup:
return hr;
}
Создание целевого объекта отрисовки обратного буфера
Чтобы отобразить содержимое Direct3D9 в объекте D3DImage, создайте поверхность Direct3D9 и назначьте её с помощью метода SetBackBuffer.
Проверка поддержки адаптера
Перед созданием поверхности убедитесь, что все адаптеры поддерживают необходимые свойства поверхности. Даже если вы выполняете рендеринг только на один адаптер, окно WPF может отображаться на любом адаптере в системе. Всегда следует писать код Direct3D9, который обрабатывает конфигурации с несколькими адаптерами, и необходимо проверить все адаптеры для поддержки, так как WPF может перемещать поверхность между доступными адаптерами.
В следующем примере кода показано, как проверить все адаптеры в системе для поддержки Direct3D9.
HRESULT
CRendererManager::TestSurfaceSettings()
{
HRESULT hr = S_OK;
D3DFORMAT fmt = m_fUseAlpha ? D3DFMT_A8R8G8B8 : D3DFMT_X8R8G8B8;
//
// We test all adapters because because we potentially use all adapters.
// But even if this sample only rendered to the default adapter, you
// should check all adapters because WPF may move your surface to
// another adapter for you!
//
for (UINT i = 0; i < m_cAdapters; ++i)
{
// Can we get HW rendering?
IFC(m_pD3D->CheckDeviceType(
i,
D3DDEVTYPE_HAL,
D3DFMT_X8R8G8B8,
fmt,
TRUE
));
// Is the format okay?
IFC(m_pD3D->CheckDeviceFormat(
i,
D3DDEVTYPE_HAL,
D3DFMT_X8R8G8B8,
D3DUSAGE_RENDERTARGET | D3DUSAGE_DYNAMIC, // We'll use dynamic when on XP
D3DRTYPE_SURFACE,
fmt
));
// D3DImage only allows multisampling on 9Ex devices. If we can't
// multisample, overwrite the desired number of samples with 0.
if (m_pD3DEx && m_uNumSamples > 1)
{
assert(m_uNumSamples <= 16);
if (FAILED(m_pD3D->CheckDeviceMultiSampleType(
i,
D3DDEVTYPE_HAL,
fmt,
TRUE,
static_cast<D3DMULTISAMPLE_TYPE>(m_uNumSamples),
NULL
)))
{
m_uNumSamples = 0;
}
}
else
{
m_uNumSamples = 0;
}
}
Cleanup:
return hr;
}
Создание поверхности
Перед созданием поверхности убедитесь, что возможности устройства поддерживают хорошую производительность в целевой операционной системе. Дополнительные сведения см. раздел Соображения по производительности для интеграции Direct3D9 и WPF.
После того как вы проверили возможности устройства, можно создать поверхность. В следующем примере кода показано, как создать целевой объект отрисовки.
HRESULT
CRenderer::CreateSurface(UINT uWidth, UINT uHeight, bool fUseAlpha, UINT m_uNumSamples)
{
HRESULT hr = S_OK;
SAFE_RELEASE(m_pd3dRTS);
IFC(m_pd3dDevice->CreateRenderTarget(
uWidth,
uHeight,
fUseAlpha ? D3DFMT_A8R8G8B8 : D3DFMT_X8R8G8B8,
static_cast<D3DMULTISAMPLE_TYPE>(m_uNumSamples),
0,
m_pd3dDeviceEx ? FALSE : TRUE, // Lockable RT required for good XP perf
&m_pd3dRTS,
NULL
));
IFC(m_pd3dDevice->SetRenderTarget(0, m_pd3dRTS));
Cleanup:
return hr;
}
WDDM
В операционных системах Windows Vista и более поздних версиях, настроенных для использования WDDM, можно создать целевую текстуру отрисовки и передать поверхность уровня 0 в SetBackBuffer метод. Этот подход не рекомендуется использовать в Windows XP, так как не удается создать блокируемую отрисовку целевой текстуры и производительность будет снижена.
Обработка состояния устройства
Класс D3DImage управляет двумя буферами отображения, которые называются задним буфером и передним буфером. Задний буфер — это поверхность Direct3D. Изменения обратного буфера копируются в передний буфер при вызове Unlock метода, где он отображается на оборудовании. Иногда передний буфер становится недоступным. Это отсутствие доступности может быть вызвано блокировкой экрана, полноэкранными эксклюзивными приложениями Direct3D, переключением пользователей или другими системными действиями. При возникновении этого, приложение WPF уведомляется о событии IsFrontBufferAvailableChanged. Как ваше приложение реагирует на недоступность переднего буфера, зависит от того, включен ли WPF для возврата к программной отрисовке. Метод SetBackBuffer имеет перегрузку, которая принимает параметр, указывающий, возвращается ли WPF к отрисовке программного обеспечения.
При вызове SetBackBuffer(D3DResourceType, IntPtr) перегрузки или вызове SetBackBuffer(D3DResourceType, IntPtr, Boolean) перегрузки с enableSoftwareFallback
заданным параметром false
система отрисовки освобождает ссылку на задний буфер, когда передний буфер становится недоступным, и ничего не отображается. Когда фронтальный буфер снова доступен, система отрисовки вызывает IsFrontBufferAvailableChanged событие для уведомления вашего приложения WPF. Вы можете создать обработчик событий для события, IsFrontBufferAvailableChanged чтобы повторно перезапустить отрисовку с допустимой поверхностью Direct3D. Чтобы перезапустить отрисовку, необходимо вызвать SetBackBuffer.
При вызове SetBackBuffer(D3DResourceType, IntPtr, Boolean) перегрузки с enableSoftwareFallback
набором true
параметров система отрисовки сохраняет свою ссылку на буфер обратной части, когда передний буфер становится недоступным, поэтому при повторной доступности переднего буфера не требуется вызывать SetBackBuffer .
Если включена отрисовка программного обеспечения, могут возникнуть ситуации, когда устройство пользователя становится недоступным, но система отрисовки сохраняет ссылку на поверхность Direct3D. Чтобы проверить, недоступен ли устройство Direct3D9, вызовите TestCooperativeLevel
метод. Чтобы проверить устройства Direct3D9Ex, вызовите CheckDeviceState
метод, так как TestCooperativeLevel
метод не рекомендуется и всегда возвращает успешный результат. Если устройство пользователя стало недоступным, вызовите SetBackBuffer ссылку WPF на обратный буфер. Если необходимо сбросить устройство, вызовите SetBackBuffer, установив параметр backBuffer
в null
, а затем снова вызовите SetBackBuffer, установив backBuffer
на допустимую поверхность Direct3D.
Reset
Вызовите метод для восстановления с недопустимого устройства, только если вы реализуете поддержку нескольких адаптеров. В противном случае выпустите все интерфейсы Direct3D9 и повторно создайте их полностью. Если макет адаптера изменился, объекты Direct3D9, созданные до изменения, не обновляются.
Обработка изменения размеров
Если D3DImage отображается в разрешении, отличном от собственного размера, он масштабируется в соответствии с текущим BitmapScalingMode, при этом Fant заменяется на Bilinear.
Если требуется более высокая точность, необходимо создать новую поверхность при изменении размера контейнера D3DImage .
Существует три возможных подхода к регулировке изменения размера.
Участвуйте в системе макета и создавайте новую поверхность при изменении размера. Не создавайте слишком много поверхностей, так как вы можете исчерпать или фрагментировать память видео.
Дождитесь, пока событие изменения размера не произошло в течение фиксированного периода времени, чтобы создать новую поверхность.
Создайте объект DispatcherTimer , который проверяет размеры контейнера несколько раз в секунду.
Оптимизация для нескольких мониторов
Чрезмерно сниженная производительность может возникнуть, если система отрисовки перемещается на другой монитор.
В WDDM, если мониторы находятся на одной и той же видеокарте и используются Direct3DCreate9Ex
, производительность не снижается. Если мониторы находятся на отдельных видеокартках, производительность снижается. В Windows XP производительность всегда уменьшается.
D3DImage При переходе на другой монитор можно создать новую поверхность на соответствующем адаптере, чтобы восстановить хорошую производительность.
Чтобы избежать штрафа за производительность, напишите код специально для дела с несколькими мониторами. В следующем списке показан один из способов разработки кода для нескольких мониторов.
Найдите точку D3DImage в пространстве экрана с
Visual.ProjectToScreen
помощью метода.MonitorFromPoint
Используйте метод GDI для поиска монитора, отображающего точку.Используйте метод
IDirect3D9::GetAdapterMonitor
для определения, какой адаптер Direct3D9 используется монитором.Если адаптер не совпадает с адаптером с задним буфером, создайте новый буфер обратно на новом мониторе и назначьте его обратному буферу D3DImage .
Замечание
D3DImage Если D3DImage охватывает мониторы, производительность будет медленной, за исключением случаев WDDM и IDirect3D9Ex
на том же адаптере. В этой ситуации нет способа повысить производительность.
В следующем примере кода показано, как найти текущий монитор.
void
CRendererManager::SetAdapter(POINT screenSpacePoint)
{
CleanupInvalidDevices();
//
// After CleanupInvalidDevices, we may not have any D3D objects. Rather than
// recreate them here, ignore the adapter update and wait for render to recreate.
//
if (m_pD3D && m_rgRenderers)
{
HMONITOR hMon = MonitorFromPoint(screenSpacePoint, MONITOR_DEFAULTTONULL);
for (UINT i = 0; i < m_cAdapters; ++i)
{
if (hMon == m_pD3D->GetAdapterMonitor(i))
{
m_pCurrentRenderer = m_rgRenderers[i];
break;
}
}
}
}
Обновите монитор при изменении размера или положения контейнера D3DImage, или обновите монитор с помощью DispatcherTimer
, который обновляется несколько раз в секунду.
Программная отрисовка в WPF
WPF синхронно отображается в потоке пользовательского интерфейса в программном обеспечении в следующих ситуациях.
Печатание
При возникновении одной из этих ситуаций система отрисовки вызывает метод CopyBackBuffer для копирования аппаратного буфера в программный буфер. Реализация по умолчанию использует метод с вашей поверхностью GetRenderTargetData
. Так как этот вызов возникает вне шаблона блокировки и разблокировки, он может завершиться ошибкой. В этом случае CopyBackBuffer
метод возвращается null
и изображение не отображается.
Можно переопределить метод CopyBackBuffer, вызвать базовую реализацию, и если она возвращает null
, то можно вернуть заполнитель BitmapSource.
Вы также можете реализовать собственную отрисовку программного обеспечения вместо вызова базовой реализации.
Замечание
Если WPF полностью отрисовывается программно, D3DImage не отображается, так как WPF не имеет заглавного буфера.
См. также
.NET Desktop feedback