DDPF_FOURCC + DDSCAPS_SYSTEMMEMORY = DDERR_INVALIDPIXELFORMAT ?

Тема в разделе "WASM.DirectX", создана пользователем ginger_tigra, 4 фев 2017.

  1. ginger_tigra

    ginger_tigra New Member

    Публикаций:
    0
    Регистрация:
    8 мар 2009
    Сообщения:
    20
    Есть проблема. Нужно быстро выводить картинку в YUV-оверлей.
    Базовая идея: сделать YUV-surface в системной памяти, исходную картинку из RGB конвертировать в этот surface своими силами, затем Blt()'ом перегнать сконвертированную картинку в оверлейный surface в видеопамять - это должно быть, на глаз, где-то втрое быстрее, чем конвертировать сразу в видеопамять.
    Примерно вот так (полный код слишком громоздок, тут только выдержки):
    Код (Text):
    1.   DDPIXELFORMAT // dwSize, dwFlags,
    2. //  dwFourCC, dwRGBBitCount, dwRBitMask, dwGBitMask, dwBBitMask, dwRGBAlphaBitMask
    3.      yuy2_format  = { sizeof( DDPIXELFORMAT ), DDPF_FOURCC,
    4.        '2YUY', 0, 0, 0, 0, 0 };
    5.   DDSURFACEDESC yuv_desc = { sizeof( DDSURFACEDESC ) };
    6.   LPDIRECTDRAW dd; // полностью готовый к работе
    7.   IDirectDrawSurface *yuv_dds = NULL;
    8. // ..............
    9.   yuv_desc.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT;
    10.   yuv_desc.dwWidth = 1280; yuv_desc.dwHeight = 960;
    11.   yuv_desc.ddpfPixelFormat = yuy2_format;
    12.   yuv_desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY;
    13.   rc = dd->CreateSurface( yuv_desc, &yuv_dds, NULL );
    Наткнулся на грабли: CreateSurface() при попытке создания surface'а в системной памяти возвращает код ошибки "кривой формат пиксела". А в видеопамяти surface создаётся на ура.
    Проверено на двух машинах:
    - Intel Celeron-4 Nortwood, 2 ГГц, 1 ГБ ОЗУ, ATI RADEON 7500 AGP, Windows 2000, DirectX 9.0c;
    - Intel Pentium G850, 2.9 ГГц, 4 ГБ ОЗУ, Intel HD Graphics, Windows XP SP3 32-бит, DirectX 9.0c.
    В чём может быть проблема, куда копать дальше? Или плюнуть на попытку перегнать YUV'ную картинку Blt()'ом и искать способы быстрой передачи пикселов сквозь AGP, тсзать, вручную?
     
  2. Indy_

    Indy_ Well-Known Member

    Публикаций:
    4
    Регистрация:
    29 апр 2011
    Сообщения:
    4.775
    > DDPF_FOURCC + DDSCAPS

    Объясните, мне неучу, что это такое?
     
  3. T800

    T800 Member

    Публикаций:
    0
    Регистрация:
    7 дек 2006
    Сообщения:
    293
    Адрес:
    Moscow
    ginger_tigra,
    Думаю вам стоит обратиться на gamedev.ru
    Я хоть и использовал хуки DirectX, но уже совсем забыл что к чему там (давно было дело).
     
  4. TermoSINteZ

    TermoSINteZ Синоби даоса Команда форума

    Публикаций:
    2
    Регистрация:
    11 июн 2004
    Сообщения:
    3.552
    Адрес:
    Russia
    ginger_tigra, А не пробовали пиксель формат DDPF_FOURCC | DDPF_RGBTOYUV ?
    но это так - первая мысль. Я тож давно этим занимался, и оверлеями и тп. Года два назад.
    Скиньте сорец, чтоб мне не писать (или кусок собираемый, отображаемый) - я бы смог вспомнить и подсказать че к чему.
     
  5. ginger_tigra

    ginger_tigra New Member

    Публикаций:
    0
    Регистрация:
    8 мар 2009
    Сообщения:
    20
    А что это такое и как им пользоваться? Гугля только цитирует MSDN и ddraw.h :-(
     
  6. TermoSINteZ

    TermoSINteZ Синоби даоса Команда форума

    Публикаций:
    2
    Регистрация:
    11 июн 2004
    Сообщения:
    3.552
    Адрес:
    Russia
    ginger_tigra,
    на сколько я помню, этот флаг заставляет dx транслировать RGB в YUV автоматически
    Код (C):
    1. ...
    2. pf.dwFlags = DDPF_FOURCC | DDPF_RGBTOYUV;
    3. ...
    потом блит RGB и в оверлее получаем YUV

    Ну это, может и не работать. Надо проверить - повторюсь, давно это было ))
     
  7. ginger_tigra

    ginger_tigra New Member

    Публикаций:
    0
    Регистрация:
    8 мар 2009
    Сообщения:
    20
    Гы, сработало! :) Правда, не так, как ожидалось:
    Не-а, преобразования цвета не происходит - по крайней мере, на Blt() из системной памяти в YUV-оверлей ничего не перекодируется. Кроме того, Blt() завершается кодом "операция не поддерживается" в случае, если заказан ресайз (source и destination rect'ы разные) и/или входной формат не RGB16 (пробовалось при выходном YUY2 на AGP'шном Radeon'е).
    Но если совпали размер пиксела в байтах и размеры rect'ов- Blt() тупо переписывает байты из входного surface'а в выходной. Т.е. можно вручную перетранслировать картинку из RGB в YUY2, готовые YUY2'шные пикселы выложить в surface, созданный в системной памяти как RGB16, а затем Blt()'ом перегнать в оверлей в видеопамять втрое-вчетверо быстрее, чем если бы вручную конвертировать из RGB в YUY2-оверлей.
    Ура, первоначальная задача - скоростной вывод в YUV-оверлей - решена! TermoSINteZ, спасибо за подсказку!
    Ещё поэкспериментирую на машине с интел-графикой и попробую с неоверлейными surface'ами, расскажу чем кончилось.
     
    Последнее редактирование: 7 фев 2017
  8. TermoSINteZ

    TermoSINteZ Синоби даоса Команда форума

    Публикаций:
    2
    Регистрация:
    11 июн 2004
    Сообщения:
    3.552
    Адрес:
    Russia
    Всегда пожалуйста.
    Как я и говорил - уже забыл че и как было. Че помнил, то и подсказал .
     
  9. ginger_tigra

    ginger_tigra New Member

    Публикаций:
    0
    Регистрация:
    8 мар 2009
    Сообщения:
    20
    Ёпс! Как говорила одна крымчанка дочь офицера, не всё так однозначно. :-( На AGP'шной видеокарте трюк сработал, а на встроенной в процессор - ни в какую: "не поддерживается" - и точка. Пробую что-то скомбинировать...
     
  10. TermoSINteZ

    TermoSINteZ Синоби даоса Команда форума

    Публикаций:
    2
    Регистрация:
    11 июн 2004
    Сообщения:
    3.552
    Адрес:
    Russia
    ginger_tigra,
    проверьте еще на NVIDIA карточке.
    И еще можете проверить на AMD APU (встроенная графика, только не интел)
     
  11. ginger_tigra

    ginger_tigra New Member

    Публикаций:
    0
    Регистрация:
    8 мар 2009
    Сообщения:
    20
    В общем, нашёл приблизительное решение:
    Код (Text):
    1.   LPDIRECTDRAW dd; // полностью готовый к работе
    2.   DDPIXELFORMAT // dwSize, dwFlags,
    3. //  dwFourCC, dwRGBBitCount, dwRBitMask, dwGBitMask, dwBBitMask, dwRGBAlphaBitMask
    4.   rgb16_format = { sizeof( DDPIXELFORMAT ), DDPF_RGB,
    5.     0, 16, 0xf800, 0x07e0, 0x001F, 0 },
    6.   yuy2_format  = { sizeof( DDPIXELFORMAT ), DDPF_FOURCC | DDPF_YUV /* | DDPF_RGBTOYUV */,
    7.     FCC( 'YUY2' ), 16, 0x00ff00ff, 0x0000ff00, 0xff000000, 0 };
    8.   IDirectDrawSurface *yuv_dds = NULL, *base_dds = NULL, *ovly_dds = NULL;
    9.   DDSURFACEDESC yuv_desc = { sizeof( DDSURFACEDESC ) },
    10.     base_desc = { sizeof( DDSURFACEDESC ) }, ovly_desc = { sizeof( DDSURFACEDESC ) };
    11. // создаём первичный surface на весь экран:
    12.   base_desc.dwFlags = DDSD_CAPS;
    13.   base_desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_VIDEOMEMORY;
    14.   rc = dd->CreateSurface( base_desc, &base_dds, NULL );
    15. // создаём оверлейный surface:
    16.   ovly_desc.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT;
    17.   ovly_desc.dwWidth = 1280; ovly_desc.dwHeight = 960;
    18.   ovly_desc.ddpfPixelFormat = yuy2_format;
    19.   ovly_desc.ddsCaps.dwCaps = DDSCAPS_OVERLAY | DDSCAPS_VIDEOMEMORY;
    20.   rc = dd->CreateSurface( ovly_desc, &ovly_dds, NULL );
    21. // создаём промежуточный surface для пересчёта RGB в YVU:
    22.   yuv_desc.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT;
    23.   yuv_desc.dwWidth = 1280; yuv_desc.dwHeight = 960;
    24.   yuv_desc.ddpfPixelFormat = rgb16_format;
    25.   yuv_desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY;
    26.   rc = dd->CreateSurface( yuv_desc, &yuv_dds, NULL );
    27. // подключаем оверлей к первичному surface'у:
    28. RECT base_rect, ovly_rect; // отдельно рассчитаны и заполнены
    29.   rc = ovly_dds->UpdateOverlay( &ovly_rect, base_dds, &base_rect, DDOVER_SHOW, NULL );
    Дальше просто: yuv_dds заполняется картинкой в формате YUY2 и из него уже эта картинка переписывается в ovly_dds:
    Код (Text):
    1.   rc = ovly_dds->BltFast( ovly_rect.left, ovly_rect.top, yuv_dds, &ovly_rect, DDBLTFAST_WAIT );
    2.   if ( rc != DD_OK ) {
    3.     DDSURFACEDESC t_src_desc = { sizeof( DDSURFACEDESC ) }, t_dst_desc = { sizeof( DDSURFACEDESC ) };
    4.     HRESULT t_rc2;
    5.     if ( ( rc = (dst_dds)->Lock( NULL, &t_dst_desc, DDLOCK_WAIT, NULL ) ) == DD_OK ) {
    6.       if ( ( t_rc2 = (src_dds)->Lock( NULL, &t_src_desc, DDLOCK_WAIT | DDLOCK_READONLY, NULL ) ) == DD_OK ) {
    7.         int t_jx = (src_rect).bottom - (src_rect).top;
    8.         uint8_t *t_src = (uint8_t *) t_src_desc.lpSurface + t_src_desc.lPitch * (src_rect).top,
    9.         *t_dst = (uint8_t *) t_dst_desc.lpSurface + t_dst_desc.lPitch * (src_rect).top;
    10.         if ( t_jx > 0 ) do {
    11.           memcpy( t_dst, t_src, t_src_desc.lPitch ); t_src += t_src_desc.lPitch; t_dst += t_dst_desc.lPitch;
    12.         } while ( --t_jx > 0 );
    13.         t_rc2 = (src_dds)->Unlock( NULL );
    14.       }
    15.       if ( ( rc = (dst_dds)->Unlock( /* &src_rect */ NULL ) ) == DD_OK ) rc = t_rc2; \
    16.     }
    17.   }
    - т.е. либо срабатывает ovly_dds->BltFast( ... yuv_dds ... ) (на машине с AGP'шной видеокартой - занимает примерно 6 мс, т.е. чуть больше 300 МБ/с), либо не срабатывает (на встроенном графическом процессоре) - тогда содержимое yuv_dds'а переписывается "вручную" через memcpy() (за меньше миллисекунды, что вполне терпимо).
    Кстати, интересно: флаг DDPF_RGBTOYUV не нужен - BltFast() отлично работает и без него; надо только, чтобы ovly_dds имел флаг DDPF_YUV и заполненные цветовые маски, а yuv_dds и ovly_dds были с одинаковыми dwRGBBitCount'ами.

    Но вылезла ещё одна проблема. При отключении оверлея
    Код (Text):
    1.   ovly_dds->UpdateOverlay( NULL, base_dds, NULL, DDOVER_HIDE, NULL );
    на машине со встроенным графическим процессором наблюдаю бяку: в момент выполнения UpdateOverlay()'я экран либо коротко (не больше чем на кадр) вспыхивает ярко-зелёным, либо заполняется (опять же ненадолго - на один кадр от силы) широкими серыми вертикальными полосами. От чего зависит тип заполнения - пока не нашёл, попытки зачищать поверхности (как Blt()'ом, так и "вручную") результата не дали.
    А игра, к которой пытаюсь притачать работу через оверлей, то и дело закрывает DirectDraw и открывает его заново, поэтому мерцания очень раздражают.
     
  12. ginger_tigra

    ginger_tigra New Member

    Публикаций:
    0
    Регистрация:
    8 мар 2009
    Сообщения:
    20
    Увы, в пределах досягаемости пока нет ни того, ни другого. :-(