From 12a3818fcf7e954ed29685a168c66998f1a55f78 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Mon, 15 Dec 2025 07:56:30 +0100 Subject: [PATCH 1/2] Fix logging of gem configs --- rpcs3/Emu/Cell/Modules/cellGem.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellGem.cpp b/rpcs3/Emu/Cell/Modules/cellGem.cpp index df46a92a05..bed35276cf 100644 --- a/rpcs3/Emu/Cell/Modules/cellGem.cpp +++ b/rpcs3/Emu/Cell/Modules/cellGem.cpp @@ -626,9 +626,9 @@ public: cellGem.notice("Could not load mouse gem config. Using defaults."); } - cellGem.notice("Real gem config=\n", g_cfg_gem_real.to_string()); - cellGem.notice("Fake gem config=\n", g_cfg_gem_fake.to_string()); - cellGem.notice("Mouse gem config=\n", g_cfg_gem_mouse.to_string()); + cellGem.notice("Real gem config=%s", g_cfg_gem_real.to_string()); + cellGem.notice("Fake gem config=%s", g_cfg_gem_fake.to_string()); + cellGem.notice("Mouse gem config=%s", g_cfg_gem_mouse.to_string()); } }; From cf87f24587754cd3869430d0df6d396985db3165 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Mon, 15 Dec 2025 13:34:06 +0100 Subject: [PATCH 2/2] cellGem: improve bayer demosaicing --- rpcs3/Emu/Cell/Modules/cellGem.cpp | 129 ++++++++++++++++++++----- rpcs3/rpcs3qt/qt_camera_video_sink.cpp | 63 +++++++----- 2 files changed, 142 insertions(+), 50 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellGem.cpp b/rpcs3/Emu/Cell/Modules/cellGem.cpp index bed35276cf..2de2d2cd3c 100644 --- a/rpcs3/Emu/Cell/Modules/cellGem.cpp +++ b/rpcs3/Emu/Cell/Modules/cellGem.cpp @@ -719,43 +719,120 @@ namespace gem constexpr u32 in_pitch = 640; constexpr u32 out_pitch = 640 * 4; - for (u32 y = 0; y < 480 - 1; y += 2) + // Hamilton–Adams demosaicing + for (s32 y = 0; y < 480; y++) { - const u8* src0 = src + y * in_pitch; - const u8* src1 = src0 + in_pitch; + const bool is_even_y = (y % 2) == 0; + const u8* srcc = src + y * in_pitch; + const u8* srcu = src + std::max(0, y - 1) * in_pitch; + const u8* srcd = src + std::min(480 - 1, y + 1) * in_pitch; u8* dst0 = dst + y * out_pitch; - u8* dst1 = dst0 + out_pitch; - for (u32 x = 0; x < 640 - 1; x += 2, src0 += 2, src1 += 2, dst0 += 8, dst1 += 8) + // Split loops (roughly twice the performance by removing one condition) + if (is_even_y) { - u8 b = src0[0]; - u8 g0 = src0[1]; - u8 g1 = src1[0]; - u8 r = src1[1]; - - if constexpr (use_gain) + for (s32 x = 0; x < 640; x++, dst0 += 4) { - b = static_cast(std::clamp(b * gain_b, 0.0f, 255.0f)); - g0 = static_cast(std::clamp(g0 * gain_g, 0.0f, 255.0f)); - g1 = static_cast(std::clamp(g1 * gain_g, 0.0f, 255.0f)); - r = static_cast(std::clamp(r * gain_r, 0.0f, 255.0f)); + const bool is_even_x = (x % 2) == 0; + const int xl = std::max(0, x - 1); + const int xr = std::min(640 - 1, x + 1); + + u8 r, b, g; + + if (is_even_x) + { + // Blue pixel + const u8 up = srcu[x]; + const u8 down = srcd[x]; + const u8 left = srcc[xl]; + const u8 right = srcc[xr]; + const int dh = std::abs(int(left) - int(right)); + const int dv = std::abs(int(up) - int(down)); + + r = (srcu[xl] + srcu[xr] + srcd[xl] + srcd[xr]) / 4; + if (dh < dv) + g = (left + right) / 2; + else if (dv < dh) + g = (up + down) / 2; + else + g = (up + down + left + right) / 4; + b = srcc[x]; + } + else + { + // Green (on blue row) + r = (srcu[x] + srcd[x]) / 2; + g = srcc[x]; + b = (srcc[xl] + srcc[xr]) / 2; + } + + if constexpr (use_gain) + { + dst0[0] = static_cast(std::clamp(r * gain_r, 0.0f, 255.0f)); + dst0[1] = static_cast(std::clamp(b * gain_b, 0.0f, 255.0f)); + dst0[2] = static_cast(std::clamp(g * gain_g, 0.0f, 255.0f)); + } + else + { + dst0[0] = r; + dst0[1] = g; + dst0[2] = b; + } + dst0[3] = alpha; } + } + else + { + for (s32 x = 0; x < 640; x++, dst0 += 4) + { + const bool is_even_x = (x % 2) == 0; + const int xl = std::max(0, x - 1); + const int xr = std::min(640 - 1, x + 1); - const u8 top[4] = { r, g0, b, alpha }; - const u8 bottom[4] = { r, g1, b, alpha }; + u8 r, b, g; - // Top-Left - std::memcpy(dst0, top, 4); + if (is_even_x) + { + // Green (on red row) + r = (srcc[xl] + srcc[xr]) / 2; + g = srcc[x]; + b = (srcu[x] + srcd[x]) / 2; + } + else + { + // Red pixel + const u8 up = srcu[x]; + const u8 down = srcd[x]; + const u8 left = srcc[xl]; + const u8 right = srcc[xr]; + const int dh = std::abs(int(left) - int(right)); + const int dv = std::abs(int(up) - int(down)); - // Top-Right Pixel - std::memcpy(dst0 + 4, top, 4); + r = srcc[x]; + if (dh < dv) + g = (left + right) / 2; + else if (dv < dh) + g = (up + down) / 2; + else + g = (up + down + left + right) / 4; + b = (srcu[xl] + srcu[xr] + srcd[xl] + srcd[xr]) / 4; + } - // Bottom-Left Pixel - std::memcpy(dst1, bottom, 4); - - // Bottom-Right Pixel - std::memcpy(dst1 + 4, bottom, 4); + if constexpr (use_gain) + { + dst0[0] = static_cast(std::clamp(r * gain_r, 0.0f, 255.0f)); + dst0[1] = static_cast(std::clamp(b * gain_b, 0.0f, 255.0f)); + dst0[2] = static_cast(std::clamp(g * gain_g, 0.0f, 255.0f)); + } + else + { + dst0[0] = r; + dst0[1] = g; + dst0[2] = b; + } + dst0[3] = alpha; + } } } } diff --git a/rpcs3/rpcs3qt/qt_camera_video_sink.cpp b/rpcs3/rpcs3qt/qt_camera_video_sink.cpp index de63d80b5d..4e1c8b3ccf 100644 --- a/rpcs3/rpcs3qt/qt_camera_video_sink.cpp +++ b/rpcs3/rpcs3qt/qt_camera_video_sink.cpp @@ -114,45 +114,60 @@ bool qt_camera_video_sink::present(const QVideoFrame& frame) case CELL_CAMERA_RAW8: // The game seems to expect BGGR { // Let's use a very simple algorithm to convert the image to raw BGGR - const auto convert_to_bggr = [&image_buffer, &image, width, height](u32 y_begin, u32 y_end) + const auto convert_to_bggr = [this, &image_buffer, &image, width, height](u32 y_begin, u32 y_end) { u8* dst = &image_buffer.data[image_buffer.width * y_begin]; for (u32 y = y_begin; y < height && y < y_end; y++) { const u8* src = image.constScanLine(y); + const u8* srcu = image.constScanLine(std::max(0, y - 1)); + const u8* srcd = image.constScanLine(std::min(height - 1, y + 1)); const bool is_top_pixel = (y % 2) == 0; + // We apply gaussian blur to get better demosaicing results later when debayering again + const auto blurred = [&](s32 x, s32 c) + { + const s32 i = x * 4 + c; + const s32 il = std::max(0, x - 1) * 4 + c; + const s32 ir = std::min(width - 1, x + 1) * 4 + c; + const s32 sum = + srcu[i] + + src[il] + 4 * src[i] + src[ir] + + srcd[i]; + return static_cast(std::clamp((sum + 4) / 8, 0, 255)); + }; + // Split loops (roughly twice the performance by removing one condition) if (is_top_pixel) { - for (u32 x = 0; x < width; x++, dst++, src += 4) + for (u32 x = 0; x < width; x++, dst++) { const bool is_left_pixel = (x % 2) == 0; if (is_left_pixel) { - *dst = src[2]; // Blue + *dst = blurred(x, 2); // Blue } else { - *dst = src[1]; // Green + *dst = blurred(x, 1); // Green } } } else { - for (u32 x = 0; x < width; x++, dst++, src += 4) + for (u32 x = 0; x < width; x++, dst++) { const bool is_left_pixel = (x % 2) == 0; if (is_left_pixel) { - *dst = src[1]; // Green + *dst = blurred(x, 1); // Green } else { - *dst = src[0]; // Red + *dst = blurred(x, 0); // Red } } } @@ -182,13 +197,13 @@ bool qt_camera_video_sink::present(const QVideoFrame& frame) // Simple RGB to Y0_U_Y1_V conversion from stackoverflow. const auto convert_to_yuv422 = [&image_buffer, &image, width, height, format = m_format](u32 y_begin, u32 y_end) { - constexpr int yuv_bytes_per_pixel = 2; - const int yuv_pitch = image_buffer.width * yuv_bytes_per_pixel; + constexpr s32 yuv_bytes_per_pixel = 2; + const s32 yuv_pitch = image_buffer.width * yuv_bytes_per_pixel; - const int y0_offset = (format == CELL_CAMERA_Y0_U_Y1_V) ? 0 : 3; - const int u_offset = (format == CELL_CAMERA_Y0_U_Y1_V) ? 1 : 2; - const int y1_offset = (format == CELL_CAMERA_Y0_U_Y1_V) ? 2 : 1; - const int v_offset = (format == CELL_CAMERA_Y0_U_Y1_V) ? 3 : 0; + const s32 y0_offset = (format == CELL_CAMERA_Y0_U_Y1_V) ? 0 : 3; + const s32 u_offset = (format == CELL_CAMERA_Y0_U_Y1_V) ? 1 : 2; + const s32 y1_offset = (format == CELL_CAMERA_Y0_U_Y1_V) ? 2 : 1; + const s32 v_offset = (format == CELL_CAMERA_Y0_U_Y1_V) ? 3 : 0; for (u32 y = y_begin; y < height && y < y_end; y++) { @@ -197,19 +212,19 @@ bool qt_camera_video_sink::present(const QVideoFrame& frame) for (u32 x = 0; x < width - 1; x += 2, src += 8) { - const float r1 = src[0]; - const float g1 = src[1]; - const float b1 = src[2]; - const float r2 = src[4]; - const float g2 = src[5]; - const float b2 = src[6]; + const f32 r1 = src[0]; + const f32 g1 = src[1]; + const f32 b1 = src[2]; + const f32 r2 = src[4]; + const f32 g2 = src[5]; + const f32 b2 = src[6]; - const int y0 = (0.257f * r1) + (0.504f * g1) + (0.098f * b1) + 16.0f; - const int u = -(0.148f * r1) - (0.291f * g1) + (0.439f * b1) + 128.0f; - const int v = (0.439f * r1) - (0.368f * g1) - (0.071f * b1) + 128.0f; - const int y1 = (0.257f * r2) + (0.504f * g2) + (0.098f * b2) + 16.0f; + const s32 y0 = (0.257f * r1) + (0.504f * g1) + (0.098f * b1) + 16.0f; + const s32 u = -(0.148f * r1) - (0.291f * g1) + (0.439f * b1) + 128.0f; + const s32 v = (0.439f * r1) - (0.368f * g1) - (0.071f * b1) + 128.0f; + const s32 y1 = (0.257f * r2) + (0.504f * g2) + (0.098f * b2) + 16.0f; - const int yuv_index = x * yuv_bytes_per_pixel; + const s32 yuv_index = x * yuv_bytes_per_pixel; yuv_row_ptr[yuv_index + y0_offset] = static_cast(std::clamp(y0, 0, 255)); yuv_row_ptr[yuv_index + u_offset] = static_cast(std::clamp( u, 0, 255)); yuv_row_ptr[yuv_index + y1_offset] = static_cast(std::clamp(y1, 0, 255));