mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-07-07 07:21:25 +12:00
rsx: Move render flip from rsx queue command to flip command
This commit is contained in:
parent
35139ebf5d
commit
699eadc84f
9 changed files with 243 additions and 130 deletions
|
@ -273,20 +273,50 @@ s32 sys_rsx_context_attribute(s32 context_id, u32 package_id, u64 a3, u64 a4, u6
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x102: // Display flip
|
case 0x102: // Display flip
|
||||||
driverInfo.head[a3].flipFlags |= 0x80000000;
|
{
|
||||||
driverInfo.head[a3].lastFlipTime = rsxTimeStamp(); // should rsxthread set this?
|
u32 flip_idx = -1u;
|
||||||
// lets give this a shot for giving bufferid back to gcm
|
|
||||||
driverInfo.head[a3].flipBufferId = driverInfo.head[a3].queuedBufferId;
|
// high bit signifys grabbing a queued buffer
|
||||||
// seems gcmSysWaitLabel uses this offset, so lets set it to 0 every flip
|
// otherwise it contains a display buffer offset
|
||||||
vm::_ref<u32>(render->label_addr + 0x10) = 0;
|
if ((a4 & 0x80000000) != 0)
|
||||||
if (a3 == 0)
|
{
|
||||||
sys_event_port_send(m_sysrsx->rsx_event_port, 0, (1 << 3), 0);
|
// last half byte gives buffer, 0xf seems to trigger just last queued
|
||||||
if (a3 == 1)
|
u8 idx_check = a4 & 0xf;
|
||||||
sys_event_port_send(m_sysrsx->rsx_event_port, 0, (1 << 4), 0);
|
if (idx_check > 7)
|
||||||
break;
|
flip_idx = driverInfo.head[a3].lastQueuedBufferId;
|
||||||
|
else
|
||||||
|
flip_idx = idx_check;
|
||||||
|
|
||||||
|
// fyi -- u32 hardware_channel = (a4 >> 8) & 0xFF;
|
||||||
|
|
||||||
|
// sanity check, the head should have a 'queued' buffer on it, and it should have been previously 'queued'
|
||||||
|
u32 sanity_check = 0x40000000 & (1 << flip_idx);
|
||||||
|
if ((driverInfo.head[a3].flipFlags & sanity_check) != sanity_check)
|
||||||
|
LOG_ERROR(RSX, "Display Flip Queued: Flipping non previously queued buffer 0x%x", a4);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (u32 i = 0; i < render->display_buffers_count; ++i)
|
||||||
|
{
|
||||||
|
if (render->display_buffers[i].offset == a4)
|
||||||
|
{
|
||||||
|
flip_idx = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (flip_idx == -1u)
|
||||||
|
{
|
||||||
|
LOG_ERROR(RSX, "Display Flip: Couldn't find display buffer offset, flipping 0. Offset: 0x%x", a4);
|
||||||
|
flip_idx = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render->request_emu_flip(flip_idx);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case 0x103: // Display Queue
|
case 0x103: // Display Queue
|
||||||
driverInfo.head[a3].queuedBufferId = a4;
|
driverInfo.head[a3].lastQueuedBufferId = a4;
|
||||||
driverInfo.head[a3].flipFlags |= 0x40000000 | (1 << a4);
|
driverInfo.head[a3].flipFlags |= 0x40000000 | (1 << a4);
|
||||||
if (a3 == 0)
|
if (a3 == 0)
|
||||||
sys_event_port_send(m_sysrsx->rsx_event_port, 0, (1 << 5), 0);
|
sys_event_port_send(m_sysrsx->rsx_event_port, 0, (1 << 5), 0);
|
||||||
|
@ -475,4 +505,28 @@ s32 sys_rsx_attribute(u32 packageId, u32 a2, u32 a3, u32 a4, u32 a5)
|
||||||
sys_rsx.warning("sys_rsx_attribute(packageId=0x%x, a2=0x%x, a3=0x%x, a4=0x%x, a5=0x%x)", packageId, a2, a3, a4, a5);
|
sys_rsx.warning("sys_rsx_attribute(packageId=0x%x, a2=0x%x, a3=0x%x, a4=0x%x, a5=0x%x)", packageId, a2, a3, a4, a5);
|
||||||
|
|
||||||
return CELL_OK;
|
return CELL_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sys_rsx_flip_event(u32 buffer)
|
||||||
|
{
|
||||||
|
auto m_sysrsx = fxm::get<SysRsxConfig>();
|
||||||
|
if (!m_sysrsx)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto &driverInfo = vm::_ref<RsxDriverInfo>(m_sysrsx->driverInfo);
|
||||||
|
const auto render = rsx::get_current_renderer();
|
||||||
|
|
||||||
|
// we only ever use head 1 for now
|
||||||
|
|
||||||
|
driverInfo.head[1].flipFlags |= 0x80000000;
|
||||||
|
driverInfo.head[1].lastFlipTime = rsxTimeStamp(); // should rsxthread set this?
|
||||||
|
driverInfo.head[1].flipBufferId = buffer;
|
||||||
|
|
||||||
|
// seems gcmSysWaitLabel uses this offset, so lets set it to 0 every flip
|
||||||
|
vm::_ref<u32>(render->label_addr + 0x10) = 0;
|
||||||
|
|
||||||
|
//if (a3 == 0)
|
||||||
|
// sys_event_port_send(m_sysrsx->rsx_event_port, 0, (1 << 3), 0);
|
||||||
|
//if (a3 == 1)
|
||||||
|
sys_event_port_send(m_sysrsx->rsx_event_port, 0, (1 << 4), 0);
|
||||||
|
}
|
||||||
|
|
|
@ -21,9 +21,9 @@ struct RsxDriverInfo
|
||||||
{
|
{
|
||||||
be_t<u64> lastFlipTime; // 0x0 last flip time
|
be_t<u64> lastFlipTime; // 0x0 last flip time
|
||||||
be_t<u32> flipFlags; // 0x8 flags to handle flip/queue
|
be_t<u32> flipFlags; // 0x8 flags to handle flip/queue
|
||||||
be_t<u32> unk1; // 0xC
|
be_t<u32> offset; // 0xC
|
||||||
be_t<u32> flipBufferId; // 0x10
|
be_t<u32> flipBufferId; // 0x10
|
||||||
be_t<u32> queuedBufferId; // 0x14 todo: this is definately not this variable but its 'unused' so im using it for queueId to pass to flip handler
|
be_t<u32> lastQueuedBufferId; // 0x14 todo: this is definately not this variable but its 'unused' so im using it for queueId to pass to flip handler
|
||||||
be_t<u32> unk3; // 0x18
|
be_t<u32> unk3; // 0x18
|
||||||
be_t<u32> unk6; // 0x18 possible low bits of time stamp? used in getlastVBlankTime
|
be_t<u32> unk6; // 0x18 possible low bits of time stamp? used in getlastVBlankTime
|
||||||
be_t<u64> lastSecondVTime; // 0x20 last time for second vhandler freq
|
be_t<u64> lastSecondVTime; // 0x20 last time for second vhandler freq
|
||||||
|
@ -101,7 +101,7 @@ struct RsxDisplayInfo
|
||||||
|
|
||||||
struct SysRsxConfig
|
struct SysRsxConfig
|
||||||
{
|
{
|
||||||
be_t<u32> rsx_event_port{ 0 };
|
u32 rsx_event_port{ 0 };
|
||||||
u32 driverInfo{ 0 };
|
u32 driverInfo{ 0 };
|
||||||
u32 rsx_context_addr{ 0 };
|
u32 rsx_context_addr{ 0 };
|
||||||
};
|
};
|
||||||
|
@ -118,4 +118,7 @@ s32 sys_rsx_context_iounmap(u32 context_id, u32 io, u32 size);
|
||||||
s32 sys_rsx_context_attribute(s32 context_id, u32 package_id, u64 a3, u64 a4, u64 a5, u64 a6);
|
s32 sys_rsx_context_attribute(s32 context_id, u32 package_id, u64 a3, u64 a4, u64 a5, u64 a6);
|
||||||
s32 sys_rsx_device_map(vm::ptr<u64> dev_addr, vm::ptr<u64> a2, u32 dev_id);
|
s32 sys_rsx_device_map(vm::ptr<u64> dev_addr, vm::ptr<u64> a2, u32 dev_id);
|
||||||
s32 sys_rsx_device_unmap(u32 dev_id);
|
s32 sys_rsx_device_unmap(u32 dev_id);
|
||||||
s32 sys_rsx_attribute(u32 a1, u32 a2, u32 a3, u32 a4, u32 a5);
|
s32 sys_rsx_attribute(u32 a1, u32 a2, u32 a3, u32 a4, u32 a5);
|
||||||
|
|
||||||
|
// Custom 'RSX Events', not lv2 syscalls
|
||||||
|
void sys_rsx_flip_event(u32 buffer);
|
||||||
|
|
|
@ -1722,9 +1722,8 @@ void GLGSRender::do_local_task(rsx::FIFO_state state)
|
||||||
|
|
||||||
if (m_overlay_manager)
|
if (m_overlay_manager)
|
||||||
{
|
{
|
||||||
if (!in_begin_end && native_ui_flip_request.load())
|
if (!in_begin_end && async_flip_requested.test_and_reset(flip_request::native_ui))
|
||||||
{
|
{
|
||||||
native_ui_flip_request.store(false);
|
|
||||||
flip((s32)current_display_buffer);
|
flip((s32)current_display_buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ namespace rsx
|
||||||
const auto now = get_system_time() - 1000000;
|
const auto now = get_system_time() - 1000000;
|
||||||
if ((now - rsxthr->last_flip_time) > min_refresh_duration_us)
|
if ((now - rsxthr->last_flip_time) > min_refresh_duration_us)
|
||||||
{
|
{
|
||||||
rsxthr->native_ui_flip_request.store(true);
|
rsxthr->async_flip_requested.test_and_set(rsx::thread::flip_request::native_ui);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,9 @@
|
||||||
#include "Utilities/GSL.h"
|
#include "Utilities/GSL.h"
|
||||||
#include "Utilities/StrUtil.h"
|
#include "Utilities/StrUtil.h"
|
||||||
|
|
||||||
|
#include <cereal/archives/binary.hpp>
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <fenv.h>
|
#include <fenv.h>
|
||||||
|
@ -364,6 +367,11 @@ namespace rsx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void thread::on_spawn()
|
||||||
|
{
|
||||||
|
m_rsx_thread = std::this_thread::get_id();
|
||||||
|
}
|
||||||
|
|
||||||
void thread::on_task()
|
void thread::on_task()
|
||||||
{
|
{
|
||||||
if (supports_native_ui)
|
if (supports_native_ui)
|
||||||
|
@ -1348,6 +1356,12 @@ namespace rsx
|
||||||
|
|
||||||
void thread::do_local_task(FIFO_state state)
|
void thread::do_local_task(FIFO_state state)
|
||||||
{
|
{
|
||||||
|
if (test(async_flip_requested, flip_request::emu_requested))
|
||||||
|
{
|
||||||
|
handle_emu_flip(async_flip_buffer);
|
||||||
|
async_flip_requested.test_and_reset(flip_request::emu_requested);
|
||||||
|
}
|
||||||
|
|
||||||
if (!in_begin_end && state != FIFO_state::lock_wait)
|
if (!in_begin_end && state != FIFO_state::lock_wait)
|
||||||
{
|
{
|
||||||
reader_lock lock(m_mtx_task);
|
reader_lock lock(m_mtx_task);
|
||||||
|
@ -2790,6 +2804,139 @@ namespace rsx
|
||||||
return performance_counters.approximate_load;
|
return performance_counters.approximate_load;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void thread::request_emu_flip(u32 buffer)
|
||||||
|
{
|
||||||
|
const bool is_rsxthr = std::this_thread::get_id() == m_rsx_thread;
|
||||||
|
|
||||||
|
// requested through command buffer
|
||||||
|
if (is_rsxthr)
|
||||||
|
{
|
||||||
|
// async flip hasnt been handled before next requested...?
|
||||||
|
if (test(async_flip_requested, flip_request::emu_requested))
|
||||||
|
{
|
||||||
|
handle_emu_flip(async_flip_buffer);
|
||||||
|
async_flip_requested.test_and_reset(flip_request::emu_requested);
|
||||||
|
}
|
||||||
|
handle_emu_flip(buffer);
|
||||||
|
}
|
||||||
|
else // requested 'manually' through ppu syscall
|
||||||
|
{
|
||||||
|
// ignore multiple requests until previous happens
|
||||||
|
if (test(async_flip_requested, flip_request::emu_requested))
|
||||||
|
return;
|
||||||
|
|
||||||
|
async_flip_buffer = buffer;
|
||||||
|
async_flip_requested.test_and_set(flip_request::emu_requested);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void thread::handle_emu_flip(u32 buffer)
|
||||||
|
{
|
||||||
|
if (user_asked_for_frame_capture && !g_cfg.video.strict_rendering_mode)
|
||||||
|
{
|
||||||
|
// not dealing with non-strict rendering capture for now
|
||||||
|
user_asked_for_frame_capture = false;
|
||||||
|
LOG_FATAL(RSX, "RSX Capture: Capture only supported when ran with strict rendering mode.");
|
||||||
|
}
|
||||||
|
else if (user_asked_for_frame_capture && !capture_current_frame)
|
||||||
|
{
|
||||||
|
capture_current_frame = true;
|
||||||
|
user_asked_for_frame_capture = false;
|
||||||
|
frame_debug.reset();
|
||||||
|
frame_capture.reset();
|
||||||
|
|
||||||
|
// random number just to jumpstart the size
|
||||||
|
frame_capture.replay_commands.reserve(8000);
|
||||||
|
|
||||||
|
// capture first tile state with nop cmd
|
||||||
|
rsx::frame_capture_data::replay_command replay_cmd;
|
||||||
|
replay_cmd.rsx_command = std::make_pair(NV4097_NO_OPERATION, 0);
|
||||||
|
frame_capture.replay_commands.push_back(replay_cmd);
|
||||||
|
capture::capture_display_tile_state(this, frame_capture.replay_commands.back());
|
||||||
|
}
|
||||||
|
else if (capture_current_frame)
|
||||||
|
{
|
||||||
|
capture_current_frame = false;
|
||||||
|
std::stringstream os;
|
||||||
|
cereal::BinaryOutputArchive archive(os);
|
||||||
|
const std::string& filePath = fs::get_config_dir() + "capture.rrc";
|
||||||
|
archive(frame_capture);
|
||||||
|
{
|
||||||
|
// todo: 'dynamicly' create capture filename, also may want to compress this data?
|
||||||
|
fs::file f(filePath, fs::rewrite);
|
||||||
|
f.write(os.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_SUCCESS(RSX, "capture successful: %s", filePath.c_str());
|
||||||
|
|
||||||
|
frame_capture.reset();
|
||||||
|
Emu.Pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
double limit = 0.;
|
||||||
|
switch (g_cfg.video.frame_limit)
|
||||||
|
{
|
||||||
|
case frame_limit_type::none: limit = 0.; break;
|
||||||
|
case frame_limit_type::_59_94: limit = 59.94; break;
|
||||||
|
case frame_limit_type::_50: limit = 50.; break;
|
||||||
|
case frame_limit_type::_60: limit = 60.; break;
|
||||||
|
case frame_limit_type::_30: limit = 30.; break;
|
||||||
|
case frame_limit_type::_auto: limit = fps_limit; break; // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
if (limit)
|
||||||
|
{
|
||||||
|
const u64 time = get_system_time() - Emu.GetPauseTime() - start_rsx_time;
|
||||||
|
|
||||||
|
if (int_flip_index == 0)
|
||||||
|
{
|
||||||
|
start_rsx_time = time;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Convert limit to expected time value
|
||||||
|
double expected = int_flip_index * 1000000. / limit;
|
||||||
|
|
||||||
|
while (time >= expected + 1000000. / limit)
|
||||||
|
{
|
||||||
|
expected = int_flip_index++ * 1000000. / limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expected > time + 1000)
|
||||||
|
{
|
||||||
|
const auto delay_us = static_cast<s64>(expected - time);
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds{ delay_us / 1000 });
|
||||||
|
performance_counters.idle_time += delay_us;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int_flip_index++;
|
||||||
|
current_display_buffer = buffer;
|
||||||
|
flip(buffer);
|
||||||
|
// After each flip PS3 system is executing a routine that changes registers value to some default.
|
||||||
|
// Some game use this default state (SH3).
|
||||||
|
if (isHLE)
|
||||||
|
reset();
|
||||||
|
|
||||||
|
last_flip_time = get_system_time() - 1000000;
|
||||||
|
flip_status = CELL_GCM_DISPLAY_FLIP_STATUS_DONE;
|
||||||
|
|
||||||
|
if (flip_handler)
|
||||||
|
{
|
||||||
|
intr_thread->cmd_list
|
||||||
|
({
|
||||||
|
{ ppu_cmd::set_args, 1 }, u64{ 1 },
|
||||||
|
{ ppu_cmd::lle_call, flip_handler },
|
||||||
|
{ ppu_cmd::sleep, 0 }
|
||||||
|
});
|
||||||
|
|
||||||
|
intr_thread->notify();
|
||||||
|
}
|
||||||
|
sys_rsx_flip_event(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
namespace reports
|
namespace reports
|
||||||
{
|
{
|
||||||
void ZCULL_control::set_enabled(class ::rsx::thread* ptimer, bool state)
|
void ZCULL_control::set_enabled(class ::rsx::thread* ptimer, bool state)
|
||||||
|
|
|
@ -368,8 +368,16 @@ namespace rsx
|
||||||
}
|
}
|
||||||
performance_counters;
|
performance_counters;
|
||||||
|
|
||||||
// Native UI interrupts
|
enum class flip_request : u32
|
||||||
atomic_t<bool> native_ui_flip_request{ false };
|
{
|
||||||
|
emu_requested,
|
||||||
|
native_ui,
|
||||||
|
|
||||||
|
__bitset_enum_max
|
||||||
|
};
|
||||||
|
|
||||||
|
atomic_t<bs_t<flip_request>> async_flip_requested{};
|
||||||
|
u8 async_flip_buffer{ 0 };
|
||||||
|
|
||||||
GcmTileInfo tiles[limits::tiles_count];
|
GcmTileInfo tiles[limits::tiles_count];
|
||||||
GcmZcullInfo zculls[limits::zculls_count];
|
GcmZcullInfo zculls[limits::zculls_count];
|
||||||
|
@ -468,8 +476,7 @@ namespace rsx
|
||||||
thread();
|
thread();
|
||||||
virtual ~thread();
|
virtual ~thread();
|
||||||
|
|
||||||
void handle_invalidated_memory_range();
|
virtual void on_spawn() override;
|
||||||
|
|
||||||
virtual void on_task() override;
|
virtual void on_task() override;
|
||||||
virtual void on_exit() override;
|
virtual void on_exit() override;
|
||||||
|
|
||||||
|
@ -565,6 +572,8 @@ namespace rsx
|
||||||
|
|
||||||
std::deque<internal_task_entry> m_internal_tasks;
|
std::deque<internal_task_entry> m_internal_tasks;
|
||||||
void do_internal_task();
|
void do_internal_task();
|
||||||
|
void handle_emu_flip(u32 buffer);
|
||||||
|
void handle_invalidated_memory_range();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
//std::future<void> add_internal_task(std::function<bool()> callback);
|
//std::future<void> add_internal_task(std::function<bool()> callback);
|
||||||
|
@ -646,6 +655,9 @@ namespace rsx
|
||||||
tiled_region get_tiled_address(u32 offset, u32 location);
|
tiled_region get_tiled_address(u32 offset, u32 location);
|
||||||
GcmTileInfo *find_tile(u32 offset, u32 location);
|
GcmTileInfo *find_tile(u32 offset, u32 location);
|
||||||
|
|
||||||
|
// Emu App/Game flip, only immediately flips when called from rsxthread
|
||||||
|
void request_emu_flip(u32 buffer);
|
||||||
|
|
||||||
u32 ReadIO32(u32 addr);
|
u32 ReadIO32(u32 addr);
|
||||||
void WriteIO32(u32 addr, u32 value);
|
void WriteIO32(u32 addr, u32 value);
|
||||||
|
|
||||||
|
|
|
@ -827,7 +827,7 @@ bool VKGSRender::on_access_violation(u32 address, bool is_writing)
|
||||||
|
|
||||||
if (result.num_flushable > 0)
|
if (result.num_flushable > 0)
|
||||||
{
|
{
|
||||||
const bool is_rsxthr = std::this_thread::get_id() == rsx_thread;
|
const bool is_rsxthr = std::this_thread::get_id() == m_rsx_thread;
|
||||||
bool has_queue_ref = false;
|
bool has_queue_ref = false;
|
||||||
|
|
||||||
u64 sync_timestamp = 0ull;
|
u64 sync_timestamp = 0ull;
|
||||||
|
@ -1594,7 +1594,6 @@ void VKGSRender::on_init_thread()
|
||||||
}
|
}
|
||||||
|
|
||||||
GSRender::on_init_thread();
|
GSRender::on_init_thread();
|
||||||
rsx_thread = std::this_thread::get_id();
|
|
||||||
zcull_ctrl.reset(static_cast<::rsx::reports::ZCULL_control*>(this));
|
zcull_ctrl.reset(static_cast<::rsx::reports::ZCULL_control*>(this));
|
||||||
|
|
||||||
if (!supports_native_ui)
|
if (!supports_native_ui)
|
||||||
|
@ -2193,9 +2192,8 @@ void VKGSRender::do_local_task(rsx::FIFO_state state)
|
||||||
|
|
||||||
if (m_overlay_manager)
|
if (m_overlay_manager)
|
||||||
{
|
{
|
||||||
if (!in_begin_end && native_ui_flip_request.load())
|
if (!in_begin_end && async_flip_requested.test_and_reset(flip_request::native_ui))
|
||||||
{
|
{
|
||||||
native_ui_flip_request.store(false);
|
|
||||||
flush_command_queue(true);
|
flush_command_queue(true);
|
||||||
flip((s32)current_display_buffer);
|
flip((s32)current_display_buffer);
|
||||||
}
|
}
|
||||||
|
|
|
@ -364,7 +364,6 @@ private:
|
||||||
shared_mutex m_flush_queue_mutex;
|
shared_mutex m_flush_queue_mutex;
|
||||||
flush_request_task m_flush_requests;
|
flush_request_task m_flush_requests;
|
||||||
|
|
||||||
std::thread::id rsx_thread;
|
|
||||||
std::atomic<u64> m_last_sync_event = { 0 };
|
std::atomic<u64> m_last_sync_event = { 0 };
|
||||||
|
|
||||||
bool render_pass_open = false;
|
bool render_pass_open = false;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#include "stdafx.h"
|
#include "stdafx.h"
|
||||||
#include "rsx_methods.h"
|
#include "rsx_methods.h"
|
||||||
#include "RSXThread.h"
|
#include "RSXThread.h"
|
||||||
#include "Emu/Memory/vm.h"
|
#include "Emu/Memory/vm.h"
|
||||||
|
@ -9,9 +9,6 @@
|
||||||
#include "Emu/Cell/lv2/sys_rsx.h"
|
#include "Emu/Cell/lv2/sys_rsx.h"
|
||||||
#include "Capture/rsx_capture.h"
|
#include "Capture/rsx_capture.h"
|
||||||
|
|
||||||
#include <sstream>
|
|
||||||
#include <cereal/archives/binary.hpp>
|
|
||||||
|
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
|
@ -1119,100 +1116,7 @@ namespace rsx
|
||||||
|
|
||||||
void flip_command(thread* rsx, u32, u32 arg)
|
void flip_command(thread* rsx, u32, u32 arg)
|
||||||
{
|
{
|
||||||
if (user_asked_for_frame_capture && !rsx->capture_current_frame)
|
rsx->request_emu_flip(arg);
|
||||||
{
|
|
||||||
rsx->capture_current_frame = true;
|
|
||||||
user_asked_for_frame_capture = false;
|
|
||||||
frame_debug.reset();
|
|
||||||
frame_capture.reset();
|
|
||||||
|
|
||||||
// random number just to jumpstart the size
|
|
||||||
frame_capture.replay_commands.reserve(8000);
|
|
||||||
|
|
||||||
// capture first tile state with nop cmd
|
|
||||||
rsx::frame_capture_data::replay_command replay_cmd;
|
|
||||||
replay_cmd.rsx_command = std::make_pair(NV4097_NO_OPERATION, 0);
|
|
||||||
frame_capture.replay_commands.push_back(replay_cmd);
|
|
||||||
capture::capture_display_tile_state(rsx, frame_capture.replay_commands.back());
|
|
||||||
}
|
|
||||||
else if (rsx->capture_current_frame)
|
|
||||||
{
|
|
||||||
rsx->capture_current_frame = false;
|
|
||||||
std::stringstream os;
|
|
||||||
cereal::BinaryOutputArchive archive(os);
|
|
||||||
const std::string& filePath = fs::get_config_dir() + "captures/" + Emu.GetTitleID() + "_" + date_time::current_time_narrow() +"_capture.rrc";
|
|
||||||
archive(frame_capture);
|
|
||||||
{
|
|
||||||
// todo: 'dynamicly' create capture filename, also may want to compress this data?
|
|
||||||
fs::file f(filePath, fs::rewrite);
|
|
||||||
f.write(os.str());
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG_SUCCESS(RSX, "capture successful: %s", filePath.c_str());
|
|
||||||
|
|
||||||
frame_capture.reset();
|
|
||||||
Emu.Pause();
|
|
||||||
}
|
|
||||||
|
|
||||||
double limit = 0.;
|
|
||||||
switch (g_cfg.video.frame_limit)
|
|
||||||
{
|
|
||||||
case frame_limit_type::none: limit = 0.; break;
|
|
||||||
case frame_limit_type::_59_94: limit = 59.94; break;
|
|
||||||
case frame_limit_type::_50: limit = 50.; break;
|
|
||||||
case frame_limit_type::_60: limit = 60.; break;
|
|
||||||
case frame_limit_type::_30: limit = 30.; break;
|
|
||||||
case frame_limit_type::_auto: limit = rsx->fps_limit; break; // TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
if (limit)
|
|
||||||
{
|
|
||||||
const u64 time = get_system_time() - Emu.GetPauseTime() - rsx->start_rsx_time;
|
|
||||||
|
|
||||||
if (rsx->int_flip_index == 0)
|
|
||||||
{
|
|
||||||
rsx->start_rsx_time = time;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Convert limit to expected time value
|
|
||||||
double expected = rsx->int_flip_index * 1000000. / limit;
|
|
||||||
|
|
||||||
while (time >= expected + 1000000. / limit)
|
|
||||||
{
|
|
||||||
expected = rsx->int_flip_index++ * 1000000. / limit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (expected > time + 1000)
|
|
||||||
{
|
|
||||||
const auto delay_us = static_cast<s64>(expected - time);
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds{delay_us / 1000});
|
|
||||||
rsx->performance_counters.idle_time += delay_us;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rsx->int_flip_index++;
|
|
||||||
rsx->current_display_buffer = arg;
|
|
||||||
rsx->flip(arg);
|
|
||||||
// After each flip cellGcmSys is executing a routine that changes registers value to some default.
|
|
||||||
// Some game use this default state (SH3).
|
|
||||||
if (rsx->isHLE) rsx->reset();
|
|
||||||
|
|
||||||
rsx->last_flip_time = get_system_time() - 1000000;
|
|
||||||
rsx->flip_status = CELL_GCM_DISPLAY_FLIP_STATUS_DONE;
|
|
||||||
|
|
||||||
if (rsx->flip_handler)
|
|
||||||
{
|
|
||||||
rsx->intr_thread->cmd_list
|
|
||||||
({
|
|
||||||
{ ppu_cmd::set_args, 1 }, u64{1},
|
|
||||||
{ ppu_cmd::lle_call, rsx->flip_handler },
|
|
||||||
{ ppu_cmd::sleep, 0 }
|
|
||||||
});
|
|
||||||
|
|
||||||
rsx->intr_thread->notify();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void user_command(thread* rsx, u32, u32 arg)
|
void user_command(thread* rsx, u32, u32 arg)
|
||||||
|
@ -1233,8 +1137,6 @@ namespace rsx
|
||||||
|
|
||||||
namespace gcm
|
namespace gcm
|
||||||
{
|
{
|
||||||
// not entirely sure which one should actually do the flip, or if these should be handled separately,
|
|
||||||
// so for now lets flip in queue and just let the driver deal with it
|
|
||||||
template<u32 index>
|
template<u32 index>
|
||||||
struct driver_flip
|
struct driver_flip
|
||||||
{
|
{
|
||||||
|
@ -1249,7 +1151,6 @@ namespace rsx
|
||||||
{
|
{
|
||||||
static void impl(thread* rsx, u32 _reg, u32 arg)
|
static void impl(thread* rsx, u32 _reg, u32 arg)
|
||||||
{
|
{
|
||||||
flip_command(rsx, _reg, arg);
|
|
||||||
sys_rsx_context_attribute(0x55555555, 0x103, index, arg, 0, 0);
|
sys_rsx_context_attribute(0x55555555, 0x103, index, arg, 0, 0);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue