cellCamera: improve image conversion speed

This commit is contained in:
Megamouse 2021-10-19 02:59:03 +02:00
parent 263b7854c1
commit d1ac92fd99

View file

@ -4,6 +4,8 @@
#include "Emu/Cell/Modules/cellCamera.h" #include "Emu/Cell/Modules/cellCamera.h"
#include "Emu/system_config.h" #include "Emu/system_config.h"
#include <QtConcurrent>
LOG_CHANNEL(camera_log, "Camera"); LOG_CHANNEL(camera_log, "Camera");
qt_camera_video_surface::qt_camera_video_surface(bool front_facing, QObject *parent) qt_camera_video_surface::qt_camera_video_surface(bool front_facing, QObject *parent)
@ -42,18 +44,27 @@ bool qt_camera_video_surface::present(const QVideoFrame& frame)
{ {
if (!frame.isValid()) if (!frame.isValid())
{ {
camera_log.error("Received invalid video frame");
return false; return false;
} }
// Get video image // Get video image. Map frame for faster read operations.
QImage image = frame.image(); QVideoFrame tmp(frame);
if (!tmp.map(QAbstractVideoBuffer::ReadOnly))
{
camera_log.error("Failed to map video frame");
return false;
}
// Create shallow copy
QImage image(tmp.bits(), tmp.width(), tmp.height(), tmp.bytesPerLine(), QVideoFrame::imageFormatFromPixelFormat(tmp.pixelFormat()));
if (!image.isNull()) if (!image.isNull())
{ {
// Scale image if necessary // Scale image if necessary
if (m_width > 0 && m_height > 0 && m_width != image.width() && m_height != image.height()) if (m_width > 0 && m_height > 0 && m_width != image.width() && m_height != image.height())
{ {
image = image.scaled(m_width, m_height, Qt::AspectRatioMode::KeepAspectRatio, Qt::SmoothTransformation); image = image.scaled(m_width, m_height, Qt::AspectRatioMode::IgnoreAspectRatio, Qt::SmoothTransformation);
} }
// Determine image flip // Determine image flip
@ -120,7 +131,9 @@ bool qt_camera_video_surface::present(const QVideoFrame& frame)
case CELL_CAMERA_RAW8: // The game seems to expect BGGR case CELL_CAMERA_RAW8: // The game seems to expect BGGR
{ {
// Let's use a very simple algorithm to convert the image to raw BGGR // Let's use a very simple algorithm to convert the image to raw BGGR
for (int y = 0; y < std::min<int>(image_buffer.height, image.height()); y++) const auto convert_to_bggr = [&image_buffer, &image](int y_begin, int y_end)
{
for (int y = y_begin; y < std::min<int>(image_buffer.height, image.height()) && y < y_end; y++)
{ {
for (int x = 0; x < std::min<int>(image_buffer.width, image.width()); x++) for (int x = 0; x < std::min<int>(image_buffer.width, image.width()); x++)
{ {
@ -142,6 +155,22 @@ bool qt_camera_video_surface::present(const QVideoFrame& frame)
} }
} }
} }
};
// Use a multithreaded workload. The faster we get this done, the better.
constexpr u32 thread_count = 4;
const int lines_per_thread = std::ceil(image_buffer.height / static_cast<double>(thread_count));
int y_begin = 0;
int y_end = lines_per_thread;
QFutureSynchronizer<void> synchronizer;
for (u32 i = 0; i < thread_count; i++)
{
synchronizer.addFuture(QtConcurrent::run(convert_to_bggr, y_begin, y_end));
y_begin = y_end;
y_end += lines_per_thread;
}
synchronizer.waitForFinished();
break; break;
} }
//case CELL_CAMERA_Y0_U_Y1_V: //case CELL_CAMERA_Y0_U_Y1_V:
@ -152,12 +181,12 @@ bool qt_camera_video_surface::present(const QVideoFrame& frame)
const int yuv_bytes_per_pixel = 2; const int yuv_bytes_per_pixel = 2;
const int yuv_pitch = image_buffer.width * yuv_bytes_per_pixel; const int yuv_pitch = image_buffer.width * yuv_bytes_per_pixel;
for (int y = 0; y < image_buffer.height; y++) for (u32 y = 0; y < image_buffer.height; y++)
{ {
const uint8_t* rgb_row_ptr = image.constScanLine(y); const uint8_t* rgb_row_ptr = image.constScanLine(y);
uint8_t* yuv_row_ptr = &image_buffer.data[y * yuv_pitch]; uint8_t* yuv_row_ptr = &image_buffer.data[y * yuv_pitch];
for (int x = 0; x < image_buffer.width - 1; x += 2) for (u32 x = 0; x < image_buffer.width - 1; x += 2)
{ {
const int rgb_index = x * rgb_bytes_per_pixel; const int rgb_index = x * rgb_bytes_per_pixel;
const int yuv_index = x * yuv_bytes_per_pixel; const int yuv_index = x * yuv_bytes_per_pixel;
@ -194,8 +223,11 @@ bool qt_camera_video_surface::present(const QVideoFrame& frame)
} }
} }
camera_log.trace("Wrote image to video surface. index=%d, m_frame_number=%d, width=%d, height=%d, bytesPerLine=%d", // Unmap frame memory
m_write_index, m_frame_number.load(), image.width(), image.height(), image.bytesPerLine()); tmp.unmap();
camera_log.trace("Wrote image to video surface. index=%d, m_frame_number=%d, width=%d, height=%d, bytes_per_pixel=%d",
m_write_index, m_frame_number.load(), m_width, m_height, m_bytes_per_pixel);
// Toggle write/read index // Toggle write/read index
std::lock_guard lock(m_mutex); std::lock_guard lock(m_mutex);