Выполнение считывания текстур и поверхностей Direct3D

StackOverflow https://stackoverflow.com/questions/120066

Вопрос

Мне нужно выяснить, как вернуть данные из текстур и поверхностей D3D обратно в системную память.Какой самый быстрый способ сделать такие вещи и как?

Кроме того, если мне нужен только один подзаголовок, как можно прочитать только эту часть без необходимости считывать все в системную память?

Короче говоря, я ищу краткое описание того, как скопировать следующее в системная память:

  1. а текстура
  2. а подмножество из текстура
  3. а поверхность
  4. а подмножество из поверхность
  5. а Текстура D3DUSAGE_RENDERTARGET
  6. а подмножество из Текстура D3DUSAGE_RENDERTARGET

Это Direct3D 9, но ответы о более новых версиях D3D также будут признательны.

Это было полезно?

Решение

Самая сложная часть — чтение с некоторой поверхности, находящейся в видеопамяти («пул по умолчанию»).Чаще всего это цели рендеринга.

Давайте сначала разберемся с простыми частями:

  1. чтение из текстуры аналогично чтению с поверхности нулевого уровня этой текстуры.См. ниже.
  2. то же самое для подмножества текстуры.
  3. чтение с поверхности, которая находится в пуле памяти, отличном от стандартного («системного» или «управляемого»), означает просто ее блокировку и чтение байтов.
  4. то же самое для подмножества поверхности.Просто заблокируйте соответствующую часть и прочитайте ее.

Итак, теперь у нас остались поверхности, находящиеся в видеопамяти («пул по умолчанию»).Это может быть любая поверхность/текстура, помеченная как цель рендеринга, или любая обычная поверхность/текстура, созданная вами в пуле по умолчанию, или сам обратный буфер.Сложность здесь в том, что вы не можете его заблокировать.

Короткий ответ: GetRenderTargetData метод на устройстве D3D.

Более длинный ответ (приблизительный вариант кода, который будет ниже):

  1. рт = получить целевую поверхность рендеринга (это может быть поверхность текстуры, задний буфер и т. д.)
  2. если рт мультисэмплируется (GetDesc, проверьте D3DSURFACE_DESC.MultiSampleType), тогда:а) создать другую целевую поверхность рендеринга того же размера, того же формата, но без мультисэмплинг;б) StretchRect из рт в эту новую поверхность;в) рт = эта новая поверхность (т.е.продолжить движение по этой новой поверхности).
  3. выключенный = создать закадровую плоскую поверхность (CreateOffscreenPlainSurface, пул D3DPOOL_SYSTEMMEM)
  4. устройство->GetRenderTargetData( рт, выключенный )
  5. сейчас выключенный содержит целевые данные рендеринга.LockRect(), читаем данные, UnlockRect() на них.
  6. очистка

Далее следует еще более длинный ответ (вставка из кодовой базы, над которой я работаю).Этот не будет компилировать «из коробки», поскольку он использует некоторые классы, функции, макросы и утилиты из остальной части кодовой базы;но это должно помочь вам начать.Я также опустил большую часть проверки ошибок (например,выходит ли заданная ширина/высота за пределы).Я также опустил ту часть, которая считывает реальные пиксели и, возможно, преобразует их в подходящий целевой формат (это довольно просто, но может занять много времени, в зависимости от количества преобразований формата, которые вы хотите поддерживать).

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++.

Код выше считывает всю поверхность.Если вы хотите эффективно прочитать только часть, я считаю, что самый быстрый способ примерно такой:

  1. создайте поверхность бассейна по умолчанию необходимого размера.
  2. StretchRect от части исходной поверхности к меньшей.
  3. действуйте как обычно с меньшим.

Фактически это очень похоже на то, что код выше делает для обработки поверхностей с несколькими выборками.Я думаю, если вы хотите получить только часть поверхности с несколькими выборками, вы можете выполнить разрешение нескольких выборок и получить ее часть в одном StretchRect.

Редактировать:удален фрагмент кода, который выполняет фактическое чтение пикселей и преобразование формата.Не имело прямого отношения к вопросу, а код был длинным.

Редактировать:обновлено в соответствии с отредактированным вопросом.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top