Страницы

Поиск по вопросам

Показаны сообщения с ярлыком gpu. Показать все сообщения
Показаны сообщения с ярлыком gpu. Показать все сообщения

понедельник, 2 марта 2020 г.

Какую лучше архитектуру использовать для вычислений на GPU

#cpp #cuda #gpu #opencl

Необходимо перенести вычисления с CPU на GPU. Начал гуглить про это всё, в итоге
есть 2 похожие архитектуры - CUDA от Nvidia и OpenCL от AMD, второе можно и на Nvidia
использовать. Вроде бы пишут, что видеокарты на AMD больше заточены под вычисления
и быстрее на 30-40%.

У кого был опыт программирования на подобном. Можете прояснить пожалуйста, что лучше
выбрать для себя?

Программу пишу на C#, но как понял, класс вычислений придется писать на C++ и подключать
библиотеку к своей программе.
    


Ответы

Ответ 1



Занимаюсь сложными гидродинамическими расчётами на GPU уже давно - OpenCL. По производительности вычислений float карты NVidia всегда опережают AMD с аналогичным количеством и частотой потоковых процессоров. Процентов на 30-50. От использования double лучше по возможности уходить.

вторник, 24 декабря 2019 г.

Воспроизведение большого видео с деформацией - DirectX

#mediaplayer #directx #gpu #directshow

Приветствую всех!

Ситуация следующая: есть видео разрешением 1920x2400, закодированное hevc (h265),
спрятанное в .mp4, подготовленное с частотой 30 фпс; аудиопотока нет, только видео

Есть задача - воспроизводить это видео на двух мониторах 1920x1080 (так называемая
вертикальная стерео-пара)
После поиска методов для реализации данного алгоритма, было принято решение использовать
DirectShow для получения кадров и DirectX для вывода текстуры. Получилась следующая
цепочка графов:

FileSourceFilter -> Lav Splitter -> Lav Video Decoder -> SampleGrabber -> NullRenderer

В callback-функции SampleGrabber'а беру и создаю Texture2D, из которой создаю ShaderResourceView.
Ну, и соответственно, есть два плоских меша, на которых накладывается текстура. Отрисовка
происходит вот так:

const float bg_clear_color[4] = { 1.0f, 1.0f, 1.0f, 1.0f };

pD3DDC->ClearRenderTargetView(pD3DRenderTarget, bg_clear_color);
pD3DDC->ClearDepthStencilView(pD3DDepthStencilView, D3D11_CLEAR_DEPTH, 1.0f, 0);

objects[0]->Render();
objects[1]->Render();

pD3DSwapChain->Present(0, 0);


И картинка идет, но происходят подтормаживания. Бьюсь над этим вопросом уже несколько
месяцев, не знаю в каком направлении рыть, пробовал всякое, и аппаратное ускорение
и снижение битрейта видео до 10-12 мбит/c. Ситуация немного улучшилась, но все равно
картинка дергается.

Прошу экспертов и знающих проконсультировать по данному вопросу, может быть сам алгоритм
неправильно продуман. Есть чувство, что я что-то упускаю. Буду рад любой помощи, спасибо.

EDIT:

В данный момент по совету @Roman совершаю миграцию своего движка на Media Foundation
с DirectShow. Так как мы перешли на MF, начали использовать h264 для надежности, потому
что как я понял, h265 сложнее использовать и по-умолчанию драйвера не установлены,
придется подключать их дополнительно, как самописные фильтры DS (возможно, я ошибаюсь,
но это пока не важно).

Задача немного изменилась и теперь произносится так: есть четыре видео 1920x1080
h264 20 mbps 30 fps. Разрешение было выбрано именно такое, потому что в документации
на MSDN мной было замечено, что максимальное разрешение видео, при котором возможно
использовать аппаратное ускорение, - 1920x1088. Задача требует максимально быстро получать
кадры из каждого видео и писать их в текстуры ID3D11Texture2D, чтобы потом их выводить
в сцене Direct3D 11.

Сейчас на рабочей машине стоит две видеокарты NVIDIA GTX 950, к каждой видеокарте
подключено по два монитора, и еще один монитор. Все мониторы 1920x1080.

После того как я провел неделю кряду над поиском информации по MF, я был удивлен,
как мало информации можно найти на тему видео текстур с поддержкой HWA, проштрудировал
практически все мануалы по MF на MSDN, но все равно многое осталось мне непонятным.
Сейчас расскажу как у меня сейчас все работает и чего хотелось бы добиться. Выражаю
огромную благодарность товарищу @Roman, который многое подсказал и направил меня в
нужном направлении.

Direct3D устройство создается у меня следующим кодом:

D3D_FEATURE_LEVEL fl[] = {D3D_FEATURE_LEVEL_11_1};
CH(D3D11CreateDevice(0, D3D_DRIVER_TYPE_HARDWARE, 0, D3D11_CREATE_DEVICE_VIDEO_SUPPORT,
fl, ARRAYSIZE(fl), D3D11_SDK_VERSION, &pD3DDevice, 0, &pD3DDC));


При перечислении доступных устройств у меня в списке отображается четыре, Intel(R)
HD Graphics, два NDIVIA GTX 950 и Microsoft Basic Render Driver.
Хотелось бы использовать два видео на первом NVIDIA устройстве, а остальные два на
другом, но вот в чем проблема. Если я создаю устройство с указанием одного из них,
мне приходится передавать параметр D3D_DRIVER_TYPE_UNKNOWN, что снижает фпс и одно
видео на NV выдает ~25 фпс. Если я инициализирую устройство как в коде выше - по-умолчанию
выбирается Intel, но зато фпс выдает ~50 (я подозреваю, что почему-то не включается
аппаратное ускорение), что странно.

Моя затея следующая: я хочу максимально задействовать силы NVIDIA карты с HWA, чтобы
все кадры видео сразу рендерелись в текстуру, без лишнего копирования в CPU, а потом
обратно в GPU, как у меня было реализовано.

CComPtr sample;
CH(reader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0, 0, &flags, 0, &sample.p));

CComPtr buffer;
BYTE* data;
CH(sample->ConvertToContiguousBuffer(&buffer));
CH(buffer->Lock(&data, NULL_PTR, NULL_PTR));

DWORD dwSize;
CH(buffer->GetCurrentLength(&dwSize));
UpdateVideoTexture(data, dwSize);

buffer->Unlock();


Этот код был создан на основе вот этого, но там все также есть затык, получается
мы вытаскиваем текстуру из GPU и потом засовываем ее обратно. Этот код медленный. Поэтому
я начал копать дальше, и вот что получилось, как я инициализирую SourceReader.

//Инициализируем COM библиотеку для нашего приложения и MF библиотеку
CH(CoInitializeEx(NULL_PTR, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE));
CH(MFStartup(MF_VERSION));

//Говорим библиотеке D3D что мы используем мультипоточные вызовы MF, чтобы избежать
undefined behavior
const CComQIPtr pMultithread = pD3DDevice;
pMultithread->SetMultithreadProtected(TRUE);

//Создание Менеджера Устройств MF
UINT token;
CComPtr manager;
CH(MFCreateDXGIDeviceManager(&token, &manager));

//Привязываем наше D3D Устройство к Менеджеру Устройств
CH(manager->ResetDevice(pD3DDevice, token));

//Создаем свойства SourceReader
CComPtr attributes;
CH(MFCreateAttributes(&attributes, 2));
CH(attributes->SetUnknown(MF_SOURCE_READER_D3D_MANAGER, manager));  //указываем менеджер
CH(attributes->SetUINT32(MF_SOURCE_READER_DISABLE_DXVA, FALSE));
CH(attributes->SetUINT32(MF_SOURCE_READER_ENABLE_ADVANCED_VIDEO_PROCESSING, TRUE));

//Создаем SourceReader и загружаем видео, зажатое h264 кодеком
CH(MFCreateSourceReaderFromURL(L"D:\\Work\\WorkFiles\\1.mp4", attributes, &reader));

//Настраиваем выходной медиатип декодера в SourceReader
CComPtr output_type;
CH(MFCreateMediaType(&output_type));
CH(output_type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video));
CH(output_type->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_NV12));
CH(reader->SetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM, NULL_PTR, output_type));

//Создаем Видео Устройство
CComPtr pVideoDevice;
CH(pD3DDevice->QueryInterface(IID_PPV_ARGS(&pVideoDevice)));

//Создаем видеотекстуру
CH(D3DHelper_Create2DTexture(pD3DDevice, &pD3DVideoTexture, CORE_WIDTH, CORE_HEIGHT,
DXGI_FORMAT_NV12, D3D11_BIND_DECODER, 0, D3D11_USAGE_DEFAULT, 0));

D3D11_VIDEO_DECODER_OUTPUT_VIEW_DESC dc;
dc.DecodeProfile = D3D11_DECODER_PROFILE_H264_VLD_NOFGT;
dc.Texture2D.ArraySlice = 0;
dc.ViewDimension = D3D11_VDOV_DIMENSION_TEXTURE2D;

//И описываем D3D View текстуры
CH(pVideoDevice->CreateVideoDecoderOutputView((ID3D11Resource*)pD3DVideoTexture,
&dc, &pVideoDecoderOutputView));
CH(pD3DDC->QueryInterface(IID_PPV_ARGS(&pVideoContext)));

//Описываем енумератор Видео Процессора
D3D11_VIDEO_PROCESSOR_CONTENT_DESC ed;
CLEAR(ed);

ed.InputFrameFormat = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE;
ed.InputFrameRate.Numerator = 60;
ed.InputFrameRate.Denominator = 1;
ed.InputWidth = CORE_WIDTH;
ed.InputHeight = CORE_HEIGHT;
ed.OutputFrameRate.Numerator = 60;
ed.OutputFrameRate.Denominator = 1;
ed.OutputWidth = CORE_WIDTH;
ed.OutputHeight = CORE_HEIGHT;
ed.Usage = D3D11_VIDEO_USAGE_OPTIMAL_SPEED;

CComPtr pVideoEnum;
CH(pVideoDevice->CreateVideoProcessorEnumerator(&ed, &pVideoEnum));

//Создаем Видео Процессор
CH(pVideoDevice->CreateVideoProcessor(pVideoEnum, 0, &pVideoProcessor));


В последствии я планирую вызывать ReadSample, а что делать дальше не знаю, прошу
помощи, спасибо. Ради поддержки вышестоящего кода пришлось даже пересесть с Win7 на
Win10. Благодарю за любую помощь.

EDIT:

P.S.: Вот код, которым сейчас инициализируется текстура:

D3D11_MAPPED_SUBRESOURCE resource;
CH(pD3DDC->Map(pD3DVideoTexture, 0, D3D11_MAP_WRITE_DISCARD, 0, &resource));

memcpy(resource.pData, data, dwSize);
pD3DDC->Unmap(pD3DVideoTexture, 0);

SAFE_COM_RELEASE(pD3DVideoTextureRV);

CH(pD3DDevice->CreateShaderResourceView(pD3DVideoTexture, 0, &pD3DVideoTextureRV));
pD3DDC->PSSetShaderResources(0, 1, &pD3DVideoTextureRV);

    


Ответы

Ответ 1



В этой схеме есть сразу несколько потенциальных заторов, один или несколько из которых ограничивают общую работу. Они таковы: Неизвестна производительность декодера, справляется ли он нормально с задачей или он и тормозит Подключение Sample Grabber таково, что возможно между ним и декодером происходит дополнительное преобразование данных, либо как дополнительный фильтр, либо в рамках LAV декодера - это может сказываться на производительности Sample Grabber выгребает данные в системной памяти независимо от режима аппаратного ускорения Sample Grabber может оперировать ограниченным числом буферов, что в свою очередь может ограничивать возможность декодера своевременно пополнять очередь; само присутствие Sample Grabber как правило является свидетельством неспособности потреблять данные более эффективным способом Не указан тип текстуры заполнение staging текстур в реальном времени имеет слабую производительность Так, в общем, хорошо бы декодировать аппаратно прямиком в текстуру и дальше уже данными в ресурсе графического адаптера манипулировать рисуя сцену. Такой подход, к сожалению, нереализуем с Sample Grabber. Media Foundation API, к примеру, такие фокусы позволяет.

Ответ 2



С помощью этого примера получилось разобраться с проблемой, выводится минимум по 50 фпс на всех четырех мониторах, спасибо за помощь! EDIT: Подробно расскажу как это получилось. Добавляю подробную информацию, поиск которой занял у меня около недели, но результат того стоит, надеюсь, этот код поможет быстрее понять принцип. Выкладываю кусок кода, отвечающий за получение видеотекстуры и ее запись в шейдер для дальнейшего отображения на моделях в сцене. Код максимально сжатый, с опущенными проверками для наглядности алгоритма. Следующая часть работает с инициализацией MF как в первом посте. Суть алгоритма в следующем: CComPtr pSample; CH(pSourceReader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0, 0, &flags, 0, &pSample)); CComPtr pBuffer; CH(pSample->GetBufferByIndex(0, &pBuffer)); CComPtr pDXGIBuffer; CH(pBuffer->QueryInterface(IID_PPV_ARGS(&pDXGIBuffer))); CComPtr pTexture2D; CH(pDXGIBuffer->GetResource(IID_PPV_ARGS(&pTexture2D))); D3D11_TEXTURE2D_DESC desc; pTexture2D->GetDesc(&desc); D3D11_VIDEO_PROCESSOR_CONTENT_DESC cd; CL(cd); cd.InputFrameFormat = D3D11_VIDEO_FRAME_FORMAT_INTERLACED_TOP_FIELD_FIRST; cd.InputWidth = desc.Width; cd.InputHeight = desc.Height; cd.OutputWidth = desc.Width; cd.OutputHeight = desc.Height; cd.Usage = D3D11_VIDEO_USAGE_PLAYBACK_NORMAL; CH(pVideoDevice->CreateVideoProcessorEnumerator(&cd, &pVideoEnum)); CH(pVideoEnum->CheckVideoProcessorFormat(DXGI_FORMAT_B8G8R8A8_UNORM, &uiFlags)); CH(pVideoDevice->CreateVideoProcessor(pVideoEnum, 0, &pVideoProcessor)); pSample.Release(); pBuffer.Release(); CH(MFCreateSample(&pSample)); CH(MFCreateDXGISurfaceBuffer(__uuidof(ID3D11Texture2D), pD3DVideoTexture, 0, FALSE, &pBuffer)); CH(pSample->AddBuffer(pBuffer)); D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC OutputViewDesc; CL(OutputViewDesc); OutputViewDesc.ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D; OutputViewDesc.Texture2D.MipSlice = 0; OutputViewDesc.Texture2DArray.MipSlice = 0; OutputViewDesc.Texture2DArray.FirstArraySlice = 0; CComPtr pOutputView; CH(pVideoDevice->CreateVideoProcessorOutputView(pD3DVideoTexture, pVideoEnum, &OutputViewDesc, &pOutputView)); D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC ivd; CL(ivd); ivd.FourCC = 0; ivd.ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D; ivd.Texture2D.MipSlice = 0; ivd.Texture2D.ArraySlice = 0; CComPtr pInputView; CH(pVideoDevice->CreateVideoProcessorInputView(pTexture2D, pVideoEnum, &ivd, &pInputView)); D3D11_VIDEO_PROCESSOR_STREAM sd; CL(sd); sd.Enable = TRUE; sd.OutputIndex = 0; sd.InputFrameOrField = 0; sd.PastFrames = 0; sd.FutureFrames = 0; sd.ppPastSurfaces = NULL; sd.ppFutureSurfaces = NULL; sd.pInputSurface = pInputView; sd.ppPastSurfacesRight = NULL; sd.ppFutureSurfacesRight = NULL; CH(pVideoContext->VideoProcessorBlt(pVideoProcessor, pOutputView, 0, 1, &sd)); SAFE_COM_RELEASE(pD3DVideoTextureRV); CH(pD3DDevice->CreateShaderResourceView(pD3DVideoTexture, 0, &pD3DVideoTextureRV)); pD3DDC->PSSetShaderResources(0, 1, &pD3DVideoTextureRV); А теперь объясню что здесь происходит. Как ранее подсказывал @Roman, мы при правильной инициализации, мы получаем с помощью IMFSourceReader указатель на наши сэмплы (IMFSample). Далее мы получаем указатель на IMFDXGIBuffer интерфейс, из которого в свою очередь вытягиваем указатель на нужный интерфейс ID3D11Texture2D. В этой текстуре уже хранится информация кадра видео, но как я понял, ее нельзя сразу использовать для рендеринга, нужно пропустить ее через некоторый видеопроцессинг, что мы и делаем далее. Стоит заметить, что ту текстуру, которую я использую для шейдера я создаю ранее, в другой части кода с BIND-флагами D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET, как это описано здесь. Далее по коду мы создаем нужные интерфейсы для того, чтобы указать что конечный результат будет записан в нашу текстуру (она объявлена в коде как pD3DVideoTexture). После всех операций, на основе этой текстуры, я создаю ID3D11ShaderResourceView, который связывает нашу текстуру с шейдером. При правильных действиях, программа выдает от 125 до 175 фпс на NVIDIA GTX 950 (для одного видео 1920x1080 h264 20 mbps). Хочу подчеркнуть, что это не максимум, MF поистине сильная вещь, хорошо оптимизированная под Windows 10. Если не использовать дополнительное копирование в pD3DVideoTexture, а указывать везде текстуру задника окна, которую мы можем получить из интерфейса IDXGISwapChain с помощью метода GetBuffer(), то фпс у меня доходил от 300 до 350, при том, что CPU был загружен на ~20%, а GPU на ~50-60%. Под словом "фпс", здесь я имею в виду максимально возможное количество кадров, которое может вытащить движок в секунду, надеюсь, понятно, что для многих видеоплееров не нужно больше 30-50. Надеюсь, этой информацией я кому-то помогу, и люди быстрее найдут решение! Еще раз спасибо за помощь.

вторник, 10 декабря 2019 г.

Важна ли видеокарта для сервера?

#windows #сервер #rdp #gpu

Есть обычный компьютер (года 2008 выпуска) в качестве сервера на базе Windows 7 x64.
Воткнут в розетку и с интернетом. Все подключения к нему осуществляются через RDP если
необходимо что-то запустить/настроить. В остальное время молотит без участия пользователя.
Хочется максимально снизить шум и тепловыделение. Встроенного видео нет. Другие матери,
из имеющихся, оборудованы меньшим количеством разъёмов под оперативку.

Отсюда встал вопрос:


  Какие минимальные требования к видеокарте для RDP подключений
  (максимум 2 подключения)?


Что имею:


  PCI-E GeForce 7600GT (стоит в данный момент и шумит), какая-то PCI
  плата с 1-2МБ памяти из конца 90-х (используется для восстановления BIOS
  видеокарт). Что-то типа такого S3 Trio.

    


Ответы

Ответ 1



RDP не использует ресурсы видео карты, подключается через собственный виртуальный драйвер. Но для запуска самого сервера видео карта нужна. (При определенных условиях компьютер/сервер можно запустить и без видео-карты). ... На сервере RDP использует собственный видеодрайвер для визуализации вывода, создавая информацию рендеринга в сетевых пакетах с использованием протокола RDP и отправки их по сети клиенту. На клиенте RDP получает данные рендеринга и интерпретирует пакеты в соответствии с вызовами API интерфейса графического устройства Microsoft Windows (GDI). Для входного пути события клиентской мыши и клавиатуры перенаправляются от клиента к серверу. На сервере RDP использует свой собственный драйвер клавиатуры и мыши для приема этих событий клавиатуры и мыши. ... о протоколе RDP

Ответ 2



Нужно понимать что RDP - это протокол. Ваш сервер в общем случае может быть вообще без видеокарты, но тогда не запуститься ваша операционная система. Чтобы быть точнее - когда вы подключаетесь по RDP, то отрисовка происходит у вас на компьютере, но не на сервере. Сервер вам передает лишь данные. Так что требований к видеокарте на сервере нет вообще ни каких.

воскресенье, 8 декабря 2019 г.

Как узнать об использовании видеокарты приложением?

#cpp #windows #3d #gpu

Необходимо при запуске 3д приложения выполнять код. Думал ставить хуки на создание
окон d3d и opengl, но потом увидел в диспетчере вот такое 


  


Подскажите функцию или пример, как можно вынуть данную информацию.
    


Ответы

Ответ 1



Функциональность просмотра информации о работе GPU в диспетчере - новинка Windows 10 (подробнее). Для того, чтобы она работала, необходим графический адаптер с поддержкой WDDM 2.0. Информация берется, по-видимому, из специального счетчика производительности "GPU Engine", который имеет для каждого процесса, использующего графику, несколько экземпляров с именем вида: pid_5036_luid_0x00000000_0x00009F54_phys_0_eng_3_engtype_3D Из этой строки можно извлечь информацию о том, к какому процессу и графическому движку он относится. У счетчика два значения, Utilization Percentage и Running time. Utilization Percentage у меня почему-то всегда нулевое, наверное, самый простой способ найти процессы, использующие 3D-графику - это отфильтровать те, у которых Running time больше нуля. Для работы со счетчиками производительности можно использовать WMI или PDH-функции. Пример вывода процессов, использующих 3D Engine, с помощью PDH-функций: #define WIN32_LEAN_AND_MEAN 1 #include #include #include #include #include //для работы со счетчиками производительности: #include #include #pragma comment(lib, "pdh.lib") //для получения информации о процессах: #include "psapi.h" #include "Shlwapi.h" #pragma comment(lib, "Shlwapi.lib") const PWSTR COUNTER_OBJECT = L"GPU Engine"; //Имя счетчика производительности /*Вывод имени процесса по его PID*/ void PrintProcessName(int pid){ HANDLE hProcess; DWORD charsCarried=0; TCHAR PrName[MAX_PATH]=L""; hProcess= OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,FALSE,pid); if (hProcess) { charsCarried=MAX_PATH; if (QueryFullProcessImageName(hProcess,0,PrName,&charsCarried)==0){ StringCchCopy(PrName,MAX_PATH,L"Unknown process"); } CloseHandle(hProcess); } else StringCchCopy(PrName,MAX_PATH,L"Unknown process"); PathStripPath(PrName); wprintf(L"%s",PrName); } void main(void) { PDH_STATUS status = ERROR_SUCCESS; LPWSTR pwsCounterListBuffer = NULL; DWORD dwCounterListSize = 0; LPWSTR pwsInstanceListBuffer = NULL; DWORD dwInstanceListSize = 0; LPWSTR pTemp = NULL; HQUERY hQuery = NULL; HCOUNTER hCounter = NULL; DWORD dwFormat = PDH_FMT_LONG; PDH_FMT_COUNTERVALUE ItemBuffer; wchar_t buffer[1000]; wchar_t * p; // Determine the required buffer size for the data. status = PdhEnumObjectItems( NULL, // real-time source NULL, // local machine COUNTER_OBJECT, // object to enumerate pwsCounterListBuffer, // pass NULL and 0 &dwCounterListSize, // to get required buffer size pwsInstanceListBuffer, &dwInstanceListSize, PERF_DETAIL_WIZARD, // counter detail level 0); if (status == PDH_MORE_DATA) { // Allocate the buffers and try the call again. pwsCounterListBuffer = (LPWSTR)malloc(dwCounterListSize * sizeof(WCHAR)); pwsInstanceListBuffer = (LPWSTR)malloc(dwInstanceListSize * sizeof(WCHAR)); if (NULL != pwsCounterListBuffer && NULL != pwsInstanceListBuffer) { status = PdhEnumObjectItems( NULL, // real-time source NULL, // local machine COUNTER_OBJECT, // object to enumerate pwsCounterListBuffer, &dwCounterListSize, pwsInstanceListBuffer, &dwInstanceListSize, PERF_DETAIL_WIZARD, // counter detail level 0); if (status == ERROR_SUCCESS) { wprintf(L"\nProcesses using 3D engine:\n\n"); // Walk the instance list. The list can contain one // or more null-terminated strings. The list is terminated // using two null-terminator characters. for (pTemp = pwsInstanceListBuffer; *pTemp != 0; pTemp += wcslen(pTemp) + 1) { p = wcsstr(pTemp,L"engtype_3D"); if(p==NULL)continue;//отфильтровать записи, не относящиеся к 3D Engine //построим путь к запрашиваемому значению счетчика... StringCchCopy(buffer,1000,L"\\GPU Engine("); StringCchCat(buffer,1000,pTemp); StringCchCat(buffer,1000,L")\\Running time"); //создадим запрос... status = PdhOpenQuery(NULL, 0, &hQuery); if (ERROR_SUCCESS != status) { wprintf(L"PdhOpenQuery failed with 0x%x\n", status); continue; } //добавим счетчик в запрос... status = PdhAddCounter(hQuery, buffer, 0, &hCounter); if (ERROR_SUCCESS != status) { wprintf(L"PdhAddCounter failed with 0x%x\n", status); continue; } //получаем данные... status = PdhCollectQueryData(hQuery); if (ERROR_SUCCESS != status) { wprintf(L"PdhCollectQueryData failed with 0x%x\n", status); continue; } status = PdhCollectQueryData(hQuery); if (ERROR_SUCCESS != status) { wprintf(L"PdhCollectQueryData failed with 0x%x\n", status); continue; } // Format the performance data record. status = PdhGetFormattedCounterValue(hCounter, dwFormat,(LPDWORD)NULL,&ItemBuffer); if (ERROR_SUCCESS != status) { wprintf(L"PdhGetFormattedCounterValue failed with 0x%x\n", status); continue; } int pid=0; //выведем информацию о процессе, если Running time > 0 if(ItemBuffer.longValue > 0){ swscanf(pTemp,L"pid_%d",&pid); //парсим PID из имени счетчика PrintProcessName(pid); wprintf(L", PID: %d - ", pid); wprintf(L"Usage time: %d\n",(int)ItemBuffer.longValue); } PdhCloseQuery(hQuery); } } else { wprintf(L"Second PdhEnumObjectItems failed with %0x%x.\n", status); } } else { wprintf(L"Unable to allocate buffers.\n"); status = ERROR_OUTOFMEMORY; } } else { wprintf(L"\nPdhEnumObjectItems failed with 0x%x.\n", status); } if (pwsCounterListBuffer != NULL) free (pwsCounterListBuffer); if (pwsInstanceListBuffer != NULL) free (pwsInstanceListBuffer); system("PAUSE"); } На системах до Windows 10 (или если нет поддержки данной функциональности у адаптера, и, как следствие - нет этого счетчика), можно использовать функцию EnumProcessModules для получения загруженных DLL процесса. Если среди них есть библиотеки Direct 3D - d3d9.dll, d3d10.dll, d3d11.dll и т.п., можно считать, что процесс использует 3D-графику.

пятница, 17 мая 2019 г.

Какую лучше архитектуру использовать для вычислений на GPU

Необходимо перенести вычисления с CPU на GPU. Начал гуглить про это всё, в итоге есть 2 похожие архитектуры - CUDA от Nvidia и OpenCL от AMD, второе можно и на Nvidia использовать. Вроде бы пишут, что видеокарты на AMD больше заточены под вычисления и быстрее на 30-40%.
У кого был опыт программирования на подобном. Можете прояснить пожалуйста, что лучше выбрать для себя?
Программу пишу на C#, но как понял, класс вычислений придется писать на C++ и подключать библиотеку к своей программе.


Ответ

Занимаюсь сложными гидродинамическими расчётами на GPU уже давно - OpenCL. По производительности вычислений float карты NVidia всегда опережают AMD с аналогичным количеством и частотой потоковых процессоров. Процентов на 30-50. От использования double лучше по возможности уходить.

вторник, 27 ноября 2018 г.

Воспроизведение большого видео с деформацией - DirectX

Приветствую всех!
Ситуация следующая: есть видео разрешением 1920x2400, закодированное hevc (h265), спрятанное в .mp4, подготовленное с частотой 30 фпс; аудиопотока нет, только видео
Есть задача - воспроизводить это видео на двух мониторах 1920x1080 (так называемая вертикальная стерео-пара) После поиска методов для реализации данного алгоритма, было принято решение использовать DirectShow для получения кадров и DirectX для вывода текстуры. Получилась следующая цепочка графов:
FileSourceFilter -> Lav Splitter -> Lav Video Decoder -> SampleGrabber -> NullRenderer
В callback-функции SampleGrabber'а беру и создаю Texture2D, из которой создаю ShaderResourceView. Ну, и соответственно, есть два плоских меша, на которых накладывается текстура. Отрисовка происходит вот так:
const float bg_clear_color[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
pD3DDC->ClearRenderTargetView(pD3DRenderTarget, bg_clear_color); pD3DDC->ClearDepthStencilView(pD3DDepthStencilView, D3D11_CLEAR_DEPTH, 1.0f, 0);
objects[0]->Render(); objects[1]->Render();
pD3DSwapChain->Present(0, 0);
И картинка идет, но происходят подтормаживания. Бьюсь над этим вопросом уже несколько месяцев, не знаю в каком направлении рыть, пробовал всякое, и аппаратное ускорение и снижение битрейта видео до 10-12 мбит/c. Ситуация немного улучшилась, но все равно картинка дергается.
Прошу экспертов и знающих проконсультировать по данному вопросу, может быть сам алгоритм неправильно продуман. Есть чувство, что я что-то упускаю. Буду рад любой помощи, спасибо.
EDIT:
В данный момент по совету @Roman совершаю миграцию своего движка на Media Foundation с DirectShow. Так как мы перешли на MF, начали использовать h264 для надежности, потому что как я понял, h265 сложнее использовать и по-умолчанию драйвера не установлены, придется подключать их дополнительно, как самописные фильтры DS (возможно, я ошибаюсь, но это пока не важно).
Задача немного изменилась и теперь произносится так: есть четыре видео 1920x1080 h264 20 mbps 30 fps. Разрешение было выбрано именно такое, потому что в документации на MSDN мной было замечено, что максимальное разрешение видео, при котором возможно использовать аппаратное ускорение, - 1920x1088. Задача требует максимально быстро получать кадры из каждого видео и писать их в текстуры ID3D11Texture2D, чтобы потом их выводить в сцене Direct3D 11.
Сейчас на рабочей машине стоит две видеокарты NVIDIA GTX 950, к каждой видеокарте подключено по два монитора, и еще один монитор. Все мониторы 1920x1080.
После того как я провел неделю кряду над поиском информации по MF, я был удивлен, как мало информации можно найти на тему видео текстур с поддержкой HWA, проштрудировал практически все мануалы по MF на MSDN, но все равно многое осталось мне непонятным. Сейчас расскажу как у меня сейчас все работает и чего хотелось бы добиться. Выражаю огромную благодарность товарищу @Roman, который многое подсказал и направил меня в нужном направлении.
Direct3D устройство создается у меня следующим кодом:
D3D_FEATURE_LEVEL fl[] = {D3D_FEATURE_LEVEL_11_1}; CH(D3D11CreateDevice(0, D3D_DRIVER_TYPE_HARDWARE, 0, D3D11_CREATE_DEVICE_VIDEO_SUPPORT, fl, ARRAYSIZE(fl), D3D11_SDK_VERSION, &pD3DDevice, 0, &pD3DDC));
При перечислении доступных устройств у меня в списке отображается четыре, Intel(R) HD Graphics, два NDIVIA GTX 950 и Microsoft Basic Render Driver. Хотелось бы использовать два видео на первом NVIDIA устройстве, а остальные два на другом, но вот в чем проблема. Если я создаю устройство с указанием одного из них, мне приходится передавать параметр D3D_DRIVER_TYPE_UNKNOWN, что снижает фпс и одно видео на NV выдает ~25 фпс. Если я инициализирую устройство как в коде выше - по-умолчанию выбирается Intel, но зато фпс выдает ~50 (я подозреваю, что почему-то не включается аппаратное ускорение), что странно.
Моя затея следующая: я хочу максимально задействовать силы NVIDIA карты с HWA, чтобы все кадры видео сразу рендерелись в текстуру, без лишнего копирования в CPU, а потом обратно в GPU, как у меня было реализовано.
CComPtr sample; CH(reader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0, 0, &flags, 0, &sample.p));
CComPtr buffer; BYTE* data; CH(sample->ConvertToContiguousBuffer(&buffer)); CH(buffer->Lock(&data, NULL_PTR, NULL_PTR));
DWORD dwSize; CH(buffer->GetCurrentLength(&dwSize)); UpdateVideoTexture(data, dwSize);
buffer->Unlock();
Этот код был создан на основе вот этого, но там все также есть затык, получается мы вытаскиваем текстуру из GPU и потом засовываем ее обратно. Этот код медленный. Поэтому я начал копать дальше, и вот что получилось, как я инициализирую SourceReader.
//Инициализируем COM библиотеку для нашего приложения и MF библиотеку CH(CoInitializeEx(NULL_PTR, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE)); CH(MFStartup(MF_VERSION));
//Говорим библиотеке D3D что мы используем мультипоточные вызовы MF, чтобы избежать undefined behavior const CComQIPtr pMultithread = pD3DDevice; pMultithread->SetMultithreadProtected(TRUE);
//Создание Менеджера Устройств MF UINT token; CComPtr manager; CH(MFCreateDXGIDeviceManager(&token, &manager));
//Привязываем наше D3D Устройство к Менеджеру Устройств CH(manager->ResetDevice(pD3DDevice, token));
//Создаем свойства SourceReader CComPtr attributes; CH(MFCreateAttributes(&attributes, 2)); CH(attributes->SetUnknown(MF_SOURCE_READER_D3D_MANAGER, manager)); //указываем менеджер CH(attributes->SetUINT32(MF_SOURCE_READER_DISABLE_DXVA, FALSE)); CH(attributes->SetUINT32(MF_SOURCE_READER_ENABLE_ADVANCED_VIDEO_PROCESSING, TRUE));
//Создаем SourceReader и загружаем видео, зажатое h264 кодеком CH(MFCreateSourceReaderFromURL(L"D:\\Work\\WorkFiles\\1.mp4", attributes, &reader));
//Настраиваем выходной медиатип декодера в SourceReader CComPtr output_type; CH(MFCreateMediaType(&output_type)); CH(output_type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video)); CH(output_type->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_NV12)); CH(reader->SetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM, NULL_PTR, output_type));
//Создаем Видео Устройство CComPtr pVideoDevice; CH(pD3DDevice->QueryInterface(IID_PPV_ARGS(&pVideoDevice)));
//Создаем видеотекстуру CH(D3DHelper_Create2DTexture(pD3DDevice, &pD3DVideoTexture, CORE_WIDTH, CORE_HEIGHT, DXGI_FORMAT_NV12, D3D11_BIND_DECODER, 0, D3D11_USAGE_DEFAULT, 0));
D3D11_VIDEO_DECODER_OUTPUT_VIEW_DESC dc; dc.DecodeProfile = D3D11_DECODER_PROFILE_H264_VLD_NOFGT; dc.Texture2D.ArraySlice = 0; dc.ViewDimension = D3D11_VDOV_DIMENSION_TEXTURE2D;
//И описываем D3D View текстуры CH(pVideoDevice->CreateVideoDecoderOutputView((ID3D11Resource*)pD3DVideoTexture, &dc, &pVideoDecoderOutputView)); CH(pD3DDC->QueryInterface(IID_PPV_ARGS(&pVideoContext)));
//Описываем енумератор Видео Процессора D3D11_VIDEO_PROCESSOR_CONTENT_DESC ed; CLEAR(ed);
ed.InputFrameFormat = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE; ed.InputFrameRate.Numerator = 60; ed.InputFrameRate.Denominator = 1; ed.InputWidth = CORE_WIDTH; ed.InputHeight = CORE_HEIGHT; ed.OutputFrameRate.Numerator = 60; ed.OutputFrameRate.Denominator = 1; ed.OutputWidth = CORE_WIDTH; ed.OutputHeight = CORE_HEIGHT; ed.Usage = D3D11_VIDEO_USAGE_OPTIMAL_SPEED;
CComPtr pVideoEnum; CH(pVideoDevice->CreateVideoProcessorEnumerator(&ed, &pVideoEnum));
//Создаем Видео Процессор CH(pVideoDevice->CreateVideoProcessor(pVideoEnum, 0, &pVideoProcessor));
В последствии я планирую вызывать ReadSample, а что делать дальше не знаю, прошу помощи, спасибо. Ради поддержки вышестоящего кода пришлось даже пересесть с Win7 на Win10. Благодарю за любую помощь.
EDIT:
P.S.: Вот код, которым сейчас инициализируется текстура:
D3D11_MAPPED_SUBRESOURCE resource; CH(pD3DDC->Map(pD3DVideoTexture, 0, D3D11_MAP_WRITE_DISCARD, 0, &resource));
memcpy(resource.pData, data, dwSize); pD3DDC->Unmap(pD3DVideoTexture, 0);
SAFE_COM_RELEASE(pD3DVideoTextureRV);
CH(pD3DDevice->CreateShaderResourceView(pD3DVideoTexture, 0, &pD3DVideoTextureRV)); pD3DDC->PSSetShaderResources(0, 1, &pD3DVideoTextureRV);


Ответ

В этой схеме есть сразу несколько потенциальных заторов, один или несколько из которых ограничивают общую работу. Они таковы:
Неизвестна производительность декодера, справляется ли он нормально с задачей или он и тормозит Подключение Sample Grabber таково, что возможно между ним и декодером происходит дополнительное преобразование данных, либо как дополнительный фильтр, либо в рамках LAV декодера - это может сказываться на производительности Sample Grabber выгребает данные в системной памяти независимо от режима аппаратного ускорения Sample Grabber может оперировать ограниченным числом буферов, что в свою очередь может ограничивать возможность декодера своевременно пополнять очередь; само присутствие Sample Grabber как правило является свидетельством неспособности потреблять данные более эффективным способом Не указан тип текстуры заполнение staging текстур в реальном времени имеет слабую производительность
Так, в общем, хорошо бы декодировать аппаратно прямиком в текстуру и дальше уже данными в ресурсе графического адаптера манипулировать рисуя сцену. Такой подход, к сожалению, нереализуем с Sample Grabber. Media Foundation API, к примеру, такие фокусы позволяет.

понедельник, 15 октября 2018 г.

Важна ли видеокарта для сервера?

Есть обычный компьютер (года 2008 выпуска) в качестве сервера на базе Windows 7 x64. Воткнут в розетку и с интернетом. Все подключения к нему осуществляются через RDP если необходимо что-то запустить/настроить. В остальное время молотит без участия пользователя. Хочется максимально снизить шум и тепловыделение. Встроенного видео нет. Другие матери, из имеющихся, оборудованы меньшим количеством разъёмов под оперативку.
Отсюда встал вопрос:
Какие минимальные требования к видеокарте для RDP подключений (максимум 2 подключения)?
Что имею:
PCI-E GeForce 7600GT (стоит в данный момент и шумит), какая-то PCI плата с 1-2МБ памяти из конца 90-х (используется для восстановления BIOS видеокарт). Что-то типа такого S3 Trio


Ответ

RDP не использует ресурсы видео карты, подключается через собственный виртуальный драйвер. Но для запуска самого сервера видео карта нужна. (При определенных условиях компьютер/сервер можно запустить и без видео-карты).
... На сервере RDP использует собственный видеодрайвер для визуализации вывода, создавая информацию рендеринга в сетевых пакетах с использованием протокола RDP и отправки их по сети клиенту. На клиенте RDP получает данные рендеринга и интерпретирует пакеты в соответствии с вызовами API интерфейса графического устройства Microsoft Windows (GDI). Для входного пути события клиентской мыши и клавиатуры перенаправляются от клиента к серверу. На сервере RDP использует свой собственный драйвер клавиатуры и мыши для приема этих событий клавиатуры и мыши. ...
о протоколе RDP