Есть проблема. Нужно быстро выводить картинку в YUV-оверлей. Базовая идея: сделать YUV-surface в системной памяти, исходную картинку из RGB конвертировать в этот surface своими силами, затем Blt()'ом перегнать сконвертированную картинку в оверлейный surface в видеопамять - это должно быть, на глаз, где-то втрое быстрее, чем конвертировать сразу в видеопамять. Примерно вот так (полный код слишком громоздок, тут только выдержки): Код (Text): DDPIXELFORMAT // dwSize, dwFlags, // dwFourCC, dwRGBBitCount, dwRBitMask, dwGBitMask, dwBBitMask, dwRGBAlphaBitMask yuy2_format = { sizeof( DDPIXELFORMAT ), DDPF_FOURCC, '2YUY', 0, 0, 0, 0, 0 }; DDSURFACEDESC yuv_desc = { sizeof( DDSURFACEDESC ) }; LPDIRECTDRAW dd; // полностью готовый к работе IDirectDrawSurface *yuv_dds = NULL; // .............. yuv_desc.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT; yuv_desc.dwWidth = 1280; yuv_desc.dwHeight = 960; yuv_desc.ddpfPixelFormat = yuy2_format; yuv_desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY; 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, тсзать, вручную?
ginger_tigra, Думаю вам стоит обратиться на gamedev.ru Я хоть и использовал хуки DirectX, но уже совсем забыл что к чему там (давно было дело).
ginger_tigra, А не пробовали пиксель формат DDPF_FOURCC | DDPF_RGBTOYUV ? но это так - первая мысль. Я тож давно этим занимался, и оверлеями и тп. Года два назад. Скиньте сорец, чтоб мне не писать (или кусок собираемый, отображаемый) - я бы смог вспомнить и подсказать че к чему.
ginger_tigra, на сколько я помню, этот флаг заставляет dx транслировать RGB в YUV автоматически Код (C): ... pf.dwFlags = DDPF_FOURCC | DDPF_RGBTOYUV; ... потом блит RGB и в оверлее получаем YUV Ну это, может и не работать. Надо проверить - повторюсь, давно это было ))
Гы, сработало! Правда, не так, как ожидалось: Не-а, преобразования цвета не происходит - по крайней мере, на Blt() из системной памяти в YUV-оверлей ничего не перекодируется. Кроме того, Blt() завершается кодом "операция не поддерживается" в случае, если заказан ресайз (source и destination rect'ы разные) и/или входной формат не RGB16 (пробовалось при выходном YUY2 на AGP'шном Radeon'е). Но если совпали размер пиксела в байтах и размеры rect'ов- Blt() тупо переписывает байты из входного surface'а в выходной. Т.е. можно вручную перетранслировать картинку из RGB в YUY2, готовые YUY2'шные пикселы выложить в surface, созданный в системной памяти как RGB16, а затем Blt()'ом перегнать в оверлей в видеопамять втрое-вчетверо быстрее, чем если бы вручную конвертировать из RGB в YUY2-оверлей. Ура, первоначальная задача - скоростной вывод в YUV-оверлей - решена! TermoSINteZ, спасибо за подсказку! Ещё поэкспериментирую на машине с интел-графикой и попробую с неоверлейными surface'ами, расскажу чем кончилось.
Ёпс! Как говорила одна крымчанка дочь офицера, не всё так однозначно. :-( На AGP'шной видеокарте трюк сработал, а на встроенной в процессор - ни в какую: "не поддерживается" - и точка. Пробую что-то скомбинировать...
ginger_tigra, проверьте еще на NVIDIA карточке. И еще можете проверить на AMD APU (встроенная графика, только не интел)
В общем, нашёл приблизительное решение: Код (Text): LPDIRECTDRAW dd; // полностью готовый к работе DDPIXELFORMAT // dwSize, dwFlags, // dwFourCC, dwRGBBitCount, dwRBitMask, dwGBitMask, dwBBitMask, dwRGBAlphaBitMask rgb16_format = { sizeof( DDPIXELFORMAT ), DDPF_RGB, 0, 16, 0xf800, 0x07e0, 0x001F, 0 }, yuy2_format = { sizeof( DDPIXELFORMAT ), DDPF_FOURCC | DDPF_YUV /* | DDPF_RGBTOYUV */, FCC( 'YUY2' ), 16, 0x00ff00ff, 0x0000ff00, 0xff000000, 0 }; IDirectDrawSurface *yuv_dds = NULL, *base_dds = NULL, *ovly_dds = NULL; DDSURFACEDESC yuv_desc = { sizeof( DDSURFACEDESC ) }, base_desc = { sizeof( DDSURFACEDESC ) }, ovly_desc = { sizeof( DDSURFACEDESC ) }; // создаём первичный surface на весь экран: base_desc.dwFlags = DDSD_CAPS; base_desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_VIDEOMEMORY; rc = dd->CreateSurface( base_desc, &base_dds, NULL ); // создаём оверлейный surface: ovly_desc.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT; ovly_desc.dwWidth = 1280; ovly_desc.dwHeight = 960; ovly_desc.ddpfPixelFormat = yuy2_format; ovly_desc.ddsCaps.dwCaps = DDSCAPS_OVERLAY | DDSCAPS_VIDEOMEMORY; rc = dd->CreateSurface( ovly_desc, &ovly_dds, NULL ); // создаём промежуточный surface для пересчёта RGB в YVU: yuv_desc.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT; yuv_desc.dwWidth = 1280; yuv_desc.dwHeight = 960; yuv_desc.ddpfPixelFormat = rgb16_format; yuv_desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY; rc = dd->CreateSurface( yuv_desc, &yuv_dds, NULL ); // подключаем оверлей к первичному surface'у: RECT base_rect, ovly_rect; // отдельно рассчитаны и заполнены rc = ovly_dds->UpdateOverlay( &ovly_rect, base_dds, &base_rect, DDOVER_SHOW, NULL ); Дальше просто: yuv_dds заполняется картинкой в формате YUY2 и из него уже эта картинка переписывается в ovly_dds: Код (Text): rc = ovly_dds->BltFast( ovly_rect.left, ovly_rect.top, yuv_dds, &ovly_rect, DDBLTFAST_WAIT ); if ( rc != DD_OK ) { DDSURFACEDESC t_src_desc = { sizeof( DDSURFACEDESC ) }, t_dst_desc = { sizeof( DDSURFACEDESC ) }; HRESULT t_rc2; if ( ( rc = (dst_dds)->Lock( NULL, &t_dst_desc, DDLOCK_WAIT, NULL ) ) == DD_OK ) { if ( ( t_rc2 = (src_dds)->Lock( NULL, &t_src_desc, DDLOCK_WAIT | DDLOCK_READONLY, NULL ) ) == DD_OK ) { int t_jx = (src_rect).bottom - (src_rect).top; uint8_t *t_src = (uint8_t *) t_src_desc.lpSurface + t_src_desc.lPitch * (src_rect).top, *t_dst = (uint8_t *) t_dst_desc.lpSurface + t_dst_desc.lPitch * (src_rect).top; if ( t_jx > 0 ) do { memcpy( t_dst, t_src, t_src_desc.lPitch ); t_src += t_src_desc.lPitch; t_dst += t_dst_desc.lPitch; } while ( --t_jx > 0 ); t_rc2 = (src_dds)->Unlock( NULL ); } if ( ( rc = (dst_dds)->Unlock( /* &src_rect */ NULL ) ) == DD_OK ) rc = t_rc2; \ } } - т.е. либо срабатывает ovly_dds->BltFast( ... yuv_dds ... ) (на машине с AGP'шной видеокартой - занимает примерно 6 мс, т.е. чуть больше 300 МБ/с), либо не срабатывает (на встроенном графическом процессоре) - тогда содержимое yuv_dds'а переписывается "вручную" через memcpy() (за меньше миллисекунды, что вполне терпимо). Кстати, интересно: флаг DDPF_RGBTOYUV не нужен - BltFast() отлично работает и без него; надо только, чтобы ovly_dds имел флаг DDPF_YUV и заполненные цветовые маски, а yuv_dds и ovly_dds были с одинаковыми dwRGBBitCount'ами. Но вылезла ещё одна проблема. При отключении оверлея Код (Text): ovly_dds->UpdateOverlay( NULL, base_dds, NULL, DDOVER_HIDE, NULL ); на машине со встроенным графическим процессором наблюдаю бяку: в момент выполнения UpdateOverlay()'я экран либо коротко (не больше чем на кадр) вспыхивает ярко-зелёным, либо заполняется (опять же ненадолго - на один кадр от силы) широкими серыми вертикальными полосами. От чего зависит тип заполнения - пока не нашёл, попытки зачищать поверхности (как Blt()'ом, так и "вручную") результата не дали. А игра, к которой пытаюсь притачать работу через оверлей, то и дело закрывает DirectDraw и открывает его заново, поэтому мерцания очень раздражают.