Кодирование + отображение без отслеживания (NVEnc, OpenGL, CUDA)
Я пишу приложение для Windows, которое обрабатывает, кодирует в H.264 и отображает живые изображения, полученные с камеры с частотой 60 Гц. Я использую Quadro P400. Мне нужно, чтобы задержка от приобретения до отображения была минимальной.
Изображения, загружаемые в графический процессор, имеют формат 1920x1080, байерский формат. Я использую примитивы Npp для де-Байера и масштабирования изображений до 4K (часть моего требования). Я использую OpenGL для отображения.
Функционально приложение корректно (изображения отображаются и кодируются правильно), но период кадра составляет около 22 мс, что означает, что оно сбрасывает кадры.
Я ищу несколько советов о том, как реструктурировать приложение для повышения производительности.
Я профилировал приложение, используя профилировщик NSight в Visual Studio, и я вижу, что ядра загружены на 100%. Текстура memcpy to OpenGL (cudaMemcpyToArray()) существенно медленнее, чем простые вызовы cudaMemcpy(). Можно ли это улучшить? Есть несколько простоев для GPU, которые я не понимаю. Я не могу прикрепить трассировку профилировщика, но могу предоставить ее по запросу.
Я заметил, что комментирование на дисплее приводит к тому, что время простоя графического процессора исчезает. Вызывает ли зависание взаимодействия с OpenGL графический процессор по какой-то причине? Весь код выполняется в одном потоке, и есть только один поток CUDA. Поможет ли использование большего количества потоков и / или потоков?
Псевдокод для основного цикла приложения показан ниже.
NvQueryPerformanceCounter(&lStart);
for (int i = 0; i < N; ++i) {
// Copy Lena to GPU
__cuda(cudaMemcpy(d_lena, h_lena, lena_size, cudaMemcpyHostToDevice));
// Debayer using NPP
__npp(nppiCFAToRGBA_8u_C1AC4R(d_lena, nSrcStep, oSrcSize, oSrcROI, d_rgba, nDstStep, eGrid, eInterpolation, nAlpha));
// Upscale to 4K using NPP
__npp(nppiResize_8u_C4R(d_rgba, nSrcStep, oSrcSize, oSrcROI, d_upscaled_rgba, nDstStep, oDstSize, oDstROI, eInterpolation));
// Encode
nv_enc.Encode(d_upscaled_rgba);
// Display
gl_window.SetImageFromCUDA(d_upscaled_rgba);
gl_window.Render()
++numFramesEncoded;
}
NvQueryPerformanceCounter(&lEnd);
NvQueryPerformanceFrequency(&lFreq);
double elapsedTime = (double)(lEnd - lStart);
print("Average Processing Time : %6.2fms\n", ((elapsedTime*1000.0) / numFramesEncoded) / lFreq);
- Хост-буфер закреплен в памяти.
- nv_enc.Encode () берет копию входного изображения RGBA и затем вызывает NVENC::nvEncEncodePicture(). NVENC API внутренне преобразует изображение из RGBA в YUV перед отправкой в кодировщик.
- gl_window.SetImageFromCUDA копирует изображение в текстурную память с помощью cudaMemcpyToArray(), отображает его в квад и вызывает SwapBuffers(). Указатель cudaArray был получен с помощью функции cuGraphicsMapResources(), как показано в этом примере https://github.com/nvpro-samples/gl_cuda_interop_pingpong_st.
- Дисплей синхронизируется с VSync.
РЕДАКТИРОВАТЬ: добавил код отображения ниже
int CGLWindow::SetImageFromCUDA(void *frame)
{
// m_pImageData is a cudaArray * that is mapped once at initialisation
int size_tex_data = m_nWidth * m_nHeight * sizeof(uint32_t);
cudaMemcpyToArray(m_pImageData, 0, 0, frame, size_tex_data, cudaMemcpyDeviceToDevice);
}
int CGLWindow::Render()
{
glActiveTexture(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
glDisable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glBindTexture(GL_TEXTURE_2D, m_textureID);
glUseProgram(m_programID);
glUniform1i(m_loc_tex, 0);
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, -1.0f, 0.0f);
glTexCoord2f(0.0f, 0.0f); glVertex3f(+1.0f, -1.0f, 0.0f);
glTexCoord2f(1.0f, 0.0f); glVertex3f(+1.0f, +1.0f, 0.0f);
glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, +1.0f, 0.0f);
glEnd();
glDisable(GL_TEXTURE_2D);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
SwapBuffers(m_hDC);
}