From 304964ba71f88347909facce810ab3e9fa9576ac Mon Sep 17 00:00:00 2001 From: Megamouse Date: Thu, 19 Dec 2024 22:30:59 +0100 Subject: [PATCH] cellGem: add debug setting for painting spheres into the live image --- rpcs3/Emu/Cell/Modules/cellGem.cpp | 135 +++++++++++++++++++++++++++++ rpcs3/Emu/system_config.h | 1 + 2 files changed, 136 insertions(+) diff --git a/rpcs3/Emu/Cell/Modules/cellGem.cpp b/rpcs3/Emu/Cell/Modules/cellGem.cpp index 2f4325d30c..b7982a4677 100644 --- a/rpcs3/Emu/Cell/Modules/cellGem.cpp +++ b/rpcs3/Emu/Cell/Modules/cellGem.cpp @@ -389,6 +389,8 @@ public: } } + void paint_spheres(CellGemVideoConvertFormatEnum output_format, u32 width, u32 height, u8* video_data_out, u32 video_data_out_size); + gem_config_data() { if (!g_cfg_gem_real.load()) @@ -474,6 +476,29 @@ static inline s32 cellGemGetVideoConvertSize(s32 output_format) namespace gem { + struct gem_position + { + public: + void set_position(f32 x, f32 y) + { + std::lock_guard lock(m_mutex); + m_x = x; + m_y = y; + } + void get_position(f32& x, f32& y) + { + std::lock_guard lock(m_mutex); + x = m_x; + y = m_y; + } + private: + std::mutex m_mutex; + f32 m_x = 0.0f; + f32 m_y = 0.0f; + }; + + std::array positions {}; + bool convert_image_format(CellCameraFormat input_format, CellGemVideoConvertFormatEnum output_format, const std::vector& video_data_in, u32 width, u32 height, u8* video_data_out, u32 video_data_out_size) @@ -602,6 +627,101 @@ namespace gem } } +void gem_config_data::paint_spheres(CellGemVideoConvertFormatEnum output_format, u32 width, u32 height, u8* video_data_out, u32 video_data_out_size) +{ + if (!width || !height || !video_data_out || !video_data_out_size) + { + return; + } + + struct sphere_information + { + f32 radius = 0.0f; + s16 x = 0; + s16 y = 0; + u8 r = 0; + u8 g = 0; + u8 b = 0; + }; + + std::vector sphere_info; + { + reader_lock lock(mtx); + + for (u32 gem_num = 0; gem_num < CELL_GEM_MAX_NUM; gem_num++) + { + const gem_config_data::gem_controller& controller = controllers[gem_num]; + if (!controller.radius_valid || controller.radius <= 0.0f) continue; + + f32 x, y; + ::at32(gem::positions, gem_num).get_position(x, y); + + const u8 r = static_cast(std::clamp(controller.sphere_rgb.r * 255.0f, 0.0f, 255.0f)); + const u8 g = static_cast(std::clamp(controller.sphere_rgb.g * 255.0f, 0.0f, 255.0f)); + const u8 b = static_cast(std::clamp(controller.sphere_rgb.b * 255.0f, 0.0f, 255.0f)); + + sphere_info.push_back({ controller.radius, static_cast(x), static_cast(y), r, g, b }); + } + } + + switch (output_format) + { + case CELL_GEM_RGBA_640x480: // RGBA output; 640*480*4-byte output buffer required + { + cellGem.trace("Painting spheres for CELL_GEM_RGBA_640x480"); + + const u32 out_pitch = width * 4; + + for (const sphere_information& info : sphere_info) + { + const s32 x_begin = std::max(0, static_cast(std::floor(info.x - info.radius))); + const s32 x_end = std::min(width, static_cast(std::ceil(info.x + info.radius))); + const s32 y_begin = std::max(0, static_cast(std::floor(info.y - info.radius))); + const s32 y_end = std::min(height, static_cast(std::ceil(info.y + info.radius))); + + for (s32 y = y_begin; y < y_end; y++) + { + u8* dst = video_data_out + y * out_pitch + x_begin * 4; + + for (s32 x = x_begin; x < x_end; x++, dst += 4) + { + const f32 distance = static_cast(std::sqrt(std::pow(info.x - x, 2) + std::pow(info.y - y, 2))); + if (distance > info.radius) continue; + + dst[0] = info.r; + dst[1] = info.g; + dst[2] = info.b; + dst[3] = 255; + } + } + } + + break; + } + case CELL_GEM_BAYER_RESTORED: // Bayer pattern output, 640x480, gamma and white balance applied, output buffer required + case CELL_GEM_RGBA_320x240: // RGBA output; 320*240*4-byte output buffer required + case CELL_GEM_YUV_640x480: // YUV output; 640*480+640*480+640*480-byte output buffer required (contiguous) + case CELL_GEM_YUV422_640x480: // YUV output; 640*480+320*480+320*480-byte output buffer required (contiguous) + case CELL_GEM_YUV411_640x480: // YUV411 output; 640*480+320*240+320*240-byte output buffer required (contiguous) + case CELL_GEM_BAYER_RESTORED_RGGB: // Restored Bayer output, 2x2 pixels rearranged into 320x240 RG1G2B + case CELL_GEM_BAYER_RESTORED_RASTERIZED: // Restored Bayer output, R,G1,G2,B rearranged into 4 contiguous 320x240 1-channel rasters + { + cellGem.trace("Unimplemented: painting spheres for %s", output_format); + break; + } + case CELL_GEM_NO_VIDEO_OUTPUT: // Disable video output + { + cellGem.trace("Ignoring painting spheres for CELL_GEM_NO_VIDEO_OUTPUT"); + break; + } + default: + { + cellGem.trace("Ignoring painting spheres for %d", static_cast(output_format)); + break; + } + } +} + void gem_config_data::operator()() { cellGem.notice("Starting thread"); @@ -635,6 +755,11 @@ void gem_config_data::operator()() if (gem::convert_image_format(shared_data.format, vc.output_format, video_data_in, shared_data.width, shared_data.height, vc_attribute.video_data_out ? vc_attribute.video_data_out.get_ptr() : nullptr, video_data_out_size)) { cellGem.trace("Converted video frame of format %s to %s", shared_data.format.load(), vc.output_format.get()); + + if (g_cfg.io.paint_move_spheres) + { + paint_spheres(vc.output_format, shared_data.width, shared_data.height, vc_attribute.video_data_out ? vc_attribute.video_data_out.get_ptr() : nullptr, video_data_out_size); + } } video_conversion_in_progress = false; @@ -902,6 +1027,11 @@ static inline void pos_to_gem_image_state(u32 gem_num, const gem_config::gem_con { draw_overlay_cursor(gem_num, controller, x_pos, y_pos, x_max, y_max); } + + if (g_cfg.io.paint_move_spheres) + { + ::at32(gem::positions, gem_num).set_position(image_x, image_y); + } } static inline void pos_to_gem_state(u32 gem_num, gem_config::gem_controller& controller, vm::ptr& gem_state, s32 x_pos, s32 y_pos, s32 x_max, s32 y_max, const ps_move_data& move_data) @@ -983,6 +1113,11 @@ static inline void pos_to_gem_state(u32 gem_num, gem_config::gem_controller& con { draw_overlay_cursor(gem_num, controller, x_pos, y_pos, x_max, y_max); } + + if (g_cfg.io.paint_move_spheres) + { + ::at32(gem::positions, gem_num).set_position(image_x, image_y); + } } extern bool is_input_allowed(); diff --git a/rpcs3/Emu/system_config.h b/rpcs3/Emu/system_config.h index b476d62941..289f4c2ae3 100644 --- a/rpcs3/Emu/system_config.h +++ b/rpcs3/Emu/system_config.h @@ -279,6 +279,7 @@ struct cfg_root : cfg::node cfg::uint<0, 100'000> pad_sleep{this, "Pad handler sleep (microseconds)", 1'000, true}; cfg::_bool background_input_enabled{this, "Background input enabled", true, true}; cfg::_bool show_move_cursor{this, "Show move cursor", false, true}; + cfg::_bool paint_move_spheres{this, "Paint move spheres", false, true}; cfg::_bool lock_overlay_input_to_player_one{this, "Lock overlay input to player one", false, true}; cfg::string midi_devices{this, "Emulated Midi devices", "ßßß@@@ßßß@@@ßßß@@@"}; cfg::_bool load_sdl_mappings{ this, "Load SDL GameController Mappings", true };