rsx/lv2: Implement hardware bitmap cursor

This commit is contained in:
kd-11
2026-10-02 19:56:35 +03:00
committed by kd-11
parent dd12be585e
commit 5d8efbfeb9
4 changed files with 171 additions and 0 deletions
+78
View File
@@ -9,6 +9,8 @@
#include "Emu/Memory/vm_locking.h"
#include "Emu/RSX/Core/RSXEngLock.hpp"
#include "Emu/RSX/Core/RSXReservationLock.hpp"
#include "Emu/RSX/Overlays/overlay_cursor.h"
#include "Emu/RSX/Overlays/overlay_manager.h"
#include "Emu/RSX/RSXThread.h"
#include "util/asm.hpp"
#include "sys_event.h"
@@ -36,6 +38,62 @@ void fmt_class_string<sys_rsx_error>::format(std::string& out, u64 arg)
});
}
// ---- Hardware cursor ----
// The hardware cursor is part of the display circuitry. It is not rendered directly by PGRAPH or any of the other 2D objects.
// Emulation is implemented using the overlay system. It has a fixed 64x64 bitmap size.
rsx::overlays::bitmap_cursor* _sys_rsx_get_cursor()
{
if (auto manager = g_fxo->try_get<rsx::overlays::display_manager>())
{
return manager->get<rsx::overlays::bitmap_cursor>().get();
}
return nullptr;
}
void _sys_rsx_init_cursor_overlay()
{
if (auto manager = g_fxo->try_get<rsx::overlays::display_manager>())
{
manager->create<rsx::overlays::bitmap_cursor>();
}
}
void _sys_rsx_enable_cursor_overlay()
{
if (auto cursor = _sys_rsx_get_cursor())
{
cursor->enable();
}
}
void _sys_rsx_disable_cursor_overlay()
{
if (auto cursor = _sys_rsx_get_cursor())
{
cursor->disable();
}
}
void _sys_rsx_move_cursor_overlay(s32 x, s32 y)
{
if (auto cursor = _sys_rsx_get_cursor())
{
cursor->set_pos(x, y);
}
}
void _sys_rsx_update_cursor_image(u32 cursor_offset)
{
if (auto cursor = _sys_rsx_get_cursor())
{
cursor->set_bitmap(cursor_offset);
}
}
// ---- Util ----
static u64 rsx_timeStamp()
{
return get_timebased_time();
@@ -142,6 +200,8 @@ bool rsx::thread::send_event(u64 data1, u64 event_flags, u64 data3)
return true;
}
// ---- Event Queue ----
void _sys_rsx_drain_event_queue(rsx::thread* rsxthr, u64 event_flags = umax, u64 wait_timeout_ms = 1000ull)
{
const auto& driverInfo = *vm::_ptr<RsxDriverInfo>(rsxthr->driver_info);
@@ -204,6 +264,8 @@ void _sys_rsx_drain_event_queue(rsx::thread* rsxthr, u64 event_flags = umax, u64
}
}
// ---- LV2 ----
error_code sys_rsx_device_open(cpu_thread& cpu)
{
cpu.state += cpu_flag::wait;
@@ -780,11 +842,27 @@ error_code sys_rsx_context_attribute(u32 context_id, u32 package_id, u64 a3, u64
case 0x10b:
// when a4=3, cellGcmSetCursorPosition(a5=xpos, a6=ypos)
// when a4=2, cellGcmSetCursorImageOffset(a5=offset)
if (a4 == 3)
_sys_rsx_move_cursor_overlay(static_cast<s32>(a5), static_cast<s32>(a6));
else if (a4 == 2)
_sys_rsx_update_cursor_image(static_cast<u32>(a5));
else
sys_rsx.error("Unknown subfunction 0x%llx (package=0x10b)", a4);
break;
case 0x10c:
// when a4=1, cellGcmSetCursorEnable()
// when a4=2, cellGcmSetCursorDisable()
if (a4 == 1)
_sys_rsx_enable_cursor_overlay();
else if (a4 == 2)
_sys_rsx_disable_cursor_overlay();
else
sys_rsx.error("Unknown subfunction 0x%llx (package=0x10c)", a4);
break;
case 0x10d:
// cellGcmInitCursor(a3=1, a4=1, a5=0, a6=0)
ensure(a4 == 1);
_sys_rsx_init_cursor_overlay();
break;
case 0x300: // Tiles
+20
View File
@@ -72,6 +72,26 @@ namespace rsx
static std::unique_ptr<image_info> load_icon(const std::string& icon_path, const std::string& archive_path);
};
struct memory_image_info : public image_info_base
{
public:
memory_image_info() = default;
memory_image_info(u16 w, u16 h, u8 bpp, const u8* data)
{
this->w = w;
this->h = h;
this->bpp = bpp;
this->m_data_ptr = data;
}
const u8* get_data() const override { return m_data_ptr; }
usz size_bytes() const override { return static_cast<usz>(w * h * bpp); }
private:
const u8* m_data_ptr = nullptr;
};
struct resource_config
{
enum standard_image_resource : u8
+55
View File
@@ -2,9 +2,13 @@
#include "overlay_cursor.h"
#include "overlay_manager.h"
#include "Emu/Cell/timers.hpp"
#include "Emu/Memory/vm.h"
#include "Emu/RSX/gcm_enums.h"
namespace rsx
{
extern u32 get_address(u32 offset, u32 location, u32 size_to_check = 0, std::source_location src_loc = std::source_location::current());
namespace overlays
{
cursor_item::cursor_item()
@@ -151,5 +155,56 @@ namespace rsx
}
}
void bitmap_cursor::enable()
{
m_visible = true;
}
void bitmap_cursor::disable()
{
m_visible = false;
}
void bitmap_cursor::set_pos(s32 x, s32 y)
{
std::lock_guard lock(m_mutex);
m_position.x = ::narrow<s16>(x);
m_position.y = ::narrow<s16>(y);
if (m_bitmap)
{
m_bitmap->set_pos(m_position.x, m_position.y);
}
}
void bitmap_cursor::set_bitmap(u32 address)
{
std::lock_guard lock(m_mutex);
if (!m_bitmap)
{
m_bitmap = std::make_unique<image_view>();
m_bitmap->set_size(64, 64);
m_bitmap->set_pos(m_position.x, m_position.y);
}
auto vm_address = rsx::get_address(address, CELL_GCM_CONTEXT_DMA_MEMORY_FRAME_BUFFER);
auto data = vm::get_super_ptr<const u8>(vm_address);
m_image_storage = { 64, 64, 4, data };
m_bitmap->set_raw_image(&m_image_storage);
}
compiled_resource bitmap_cursor::get_compiled()
{
std::lock_guard lock(m_mutex);
if (!m_visible || !m_bitmap)
{
return {};
}
return m_bitmap->get_compiled();
}
} // namespace overlays
} // namespace rsx
+18
View File
@@ -50,6 +50,24 @@ namespace rsx
std::map<u32, cursor_item> m_cursors;
};
class bitmap_cursor final : public overlay
{
public:
void enable();
void disable();
void set_pos(s32 x, s32 y);
void set_bitmap(u32 address);
compiled_resource get_compiled() override;
private:
bool m_visible = false;
position2_base<s16> m_position = {};
std::unique_ptr<overlays::image_view> m_bitmap;
overlays::memory_image_info m_image_storage;
shared_mutex m_mutex;
};
void set_cursor(u32 id, s16 x, s16 y, const color4f& color, u64 duration_us, bool force_update);
} // namespace overlays