Наиболее важной частью является чтение с некоторой поверхности, которая находится в видеопамяти («пул по умолчанию»). Это чаще всего рендеринг целей.
Давайте сначала разберемся с простыми деталями:
- чтение из текстуры такое же, как чтение с поверхности 0-уровня этой текстуры. Смотри ниже.
- то же самое для подмножества текстуры.
- чтение с поверхности, которая находится в пуле памяти не по умолчанию («системный» или «управляемый»), просто блокирует ее и читает байты.
- то же самое для подмножества поверхности. Просто заблокируйте соответствующую часть и прочитайте ее.
Итак, теперь у нас остались поверхности, которые находятся в видеопамяти («пул по умолчанию»). Это может быть любая поверхность / текстура, помеченная как цель рендеринга, или любая обычная поверхность / текстура, которую вы создали в пуле по умолчанию, или сам буфер. Сложная часть здесь в том, что вы не можете заблокировать это.
Краткий ответ: GetRenderTargetData метод на устройстве D3D.
Более длинный ответ (грубый набросок кода, который будет ниже):
- rt = получить целевую поверхность рендеринга (это может быть поверхность текстуры, или буфер, и т. Д.)
- , если rt является мультисэмплированным (GetDesc, проверьте D3DSURFACE_DESC.MultiSampleType), то: a) создайте другую целевую поверхность рендеринга того же размера, того же формата, но без мультисэмплинга; б) StretchRect от rt на эту новую поверхность; c) rt = эта новая поверхность (т.е. перейти на эту новую поверхность).
- выкл. = создать неэкранную плоскую поверхность (CreateOffscreenPlainSurface, пул D3DPOOL_SYSTEMMEM)
- device-> GetRenderTargetData ( rt , off )
- сейчас off содержит целевые данные рендеринга. LockRect (), чтение данных, UnlockRect () для них.
- 1052 * чистка *
Далее следует более длинный ответ (вставьте из кодовой базы, над которой я работаю) Этот не будет компилироваться из коробки, потому что он использует некоторые классы, функции, макросы и утилиты из остальной кодовой базы; но это должно помочь вам начать. Я также пропустил большую часть проверки ошибок (например, вне зависимости от того, находится ли ширина / высота вне границ). Я также пропустил часть, которая считывает реальные пиксели и, возможно, преобразует их в подходящий формат назначения (это довольно просто, но может занять много времени, в зависимости от количества преобразований формата, которые вы хотите поддерживать).
bool GfxDeviceD3D9::ReadbackImage( /* params */ )
{
HRESULT hr;
IDirect3DDevice9* dev = GetD3DDevice();
SurfacePointer renderTarget;
hr = dev->GetRenderTarget( 0, &renderTarget );
if( !renderTarget || FAILED(hr) )
return false;
D3DSURFACE_DESC rtDesc;
renderTarget->GetDesc( &rtDesc );
SurfacePointer resolvedSurface;
if( rtDesc.MultiSampleType != D3DMULTISAMPLE_NONE )
{
hr = dev->CreateRenderTarget( rtDesc.Width, rtDesc.Height, rtDesc.Format, D3DMULTISAMPLE_NONE, 0, FALSE, &resolvedSurface, NULL );
if( FAILED(hr) )
return false;
hr = dev->StretchRect( renderTarget, NULL, resolvedSurface, NULL, D3DTEXF_NONE );
if( FAILED(hr) )
return false;
renderTarget = resolvedSurface;
}
SurfacePointer offscreenSurface;
hr = dev->CreateOffscreenPlainSurface( rtDesc.Width, rtDesc.Height, rtDesc.Format, D3DPOOL_SYSTEMMEM, &offscreenSurface, NULL );
if( FAILED(hr) )
return false;
hr = dev->GetRenderTargetData( renderTarget, offscreenSurface );
bool ok = SUCCEEDED(hr);
if( ok )
{
// Here we have data in offscreenSurface.
D3DLOCKED_RECT lr;
RECT rect;
rect.left = 0;
rect.right = rtDesc.Width;
rect.top = 0;
rect.bottom = rtDesc.Height;
// Lock the surface to read pixels
hr = offscreenSurface->LockRect( &lr, &rect, D3DLOCK_READONLY );
if( SUCCEEDED(hr) )
{
// Pointer to data is lt.pBits, each row is
// lr.Pitch bytes apart (often it is the same as width*bpp, but
// can be larger if driver uses padding)
// Read the data here!
offscreenSurface->UnlockRect();
}
else
{
ok = false;
}
}
return ok;
}
SurfacePointer
в вышеприведенном коде - это умный указатель на COM-объект (он освобождает объект при назначении или деструкторе). Упрощает обработку ошибок. Это очень похоже на _comptr_t
вещи в Visual C ++.
Код выше читает всю поверхность. Если вы хотите эффективно прочитать только часть из них, то я считаю, что самый быстрый способ это примерно:
- создать поверхность пула по умолчанию необходимого размера.
- StretchRect от части исходной поверхности к той меньшей.
- действуйте как обычно с меньшим.
На самом деле это очень похоже на то, что код выше делает для обработки поверхностей с несколькими выборками. Если вы хотите получить только часть поверхности с несколькими выборками, вы можете сделать разрешение с несколькими выборками и получить его часть в одном StretchRect, я думаю.
Редактировать : удален фрагмент кода, который выполняет фактическое чтение пикселей и преобразование формата. Не было напрямую связано с вопросом, а код был длинным.
Редактировать : обновлено, чтобы соответствовать отредактированному вопросу.