pvr: auto frame skip to replace current and previous synchronous rendering

Restore previous synchronous rendering behaviour (normal auto frame skip)
No frame skipping when disabled
better cpu speed measure over 4 vblanks
This commit is contained in:
Flyinghead 2020-12-25 12:08:44 +01:00
parent b9d7c11936
commit a00aad5fa7
7 changed files with 128 additions and 76 deletions

View File

@ -1,3 +1,4 @@
#include <array>
#include "spg.h"
#include "hw/holly/holly_intc.h"
#include "hw/holly/sb.h"
@ -23,6 +24,11 @@ static u32 Frame_Cycles;
int render_end_schid;
int vblank_schid;
static std::array<double, 4> real_times;
static std::array<u64, 4> cpu_cycles;
static u32 cpu_time_idx;
bool SH4FastEnough;
void CalculateSync()
{
u32 pixel_clock = PIXEL_CLOCK / (FB_R_CTRL.vclk_div ? 1 : 2);
@ -127,13 +133,28 @@ int spg_line_sched(int tag, int cycl, int jit)
else
SPG_STATUS.fieldnum=0;
vblk_cnt++;
rend_vblank();
double now = os_GetSeconds() * 1000000.0;
cpu_time_idx = (cpu_time_idx + 1) % cpu_cycles.size();
if (cpu_cycles[cpu_time_idx] != 0)
{
u32 cycle_span = (u32)(sh4_sched_now64() - cpu_cycles[cpu_time_idx]);
double time_span = now - real_times[cpu_time_idx];
double cpu_speed = ((double)cycle_span / time_span) / (SH4_MAIN_CLOCK / 100000000);
SH4FastEnough = cpu_speed >= 85.0;
}
else
SH4FastEnough = false;
cpu_cycles[cpu_time_idx] = sh4_sched_now64();
real_times[cpu_time_idx] = now;
#ifdef TEST_AUTOMATION
replay_input();
#endif
#if !defined(NDEBUG) || defined(DEBUGFAST)
vblk_cnt++;
if ((os_GetSeconds()-last_fps)>2)
{
static int Last_FC;
@ -190,6 +211,7 @@ int spg_line_sched(int tag, int cycl, int jit)
fskip=0;
last_fps=os_GetSeconds();
}
#endif
}
if (lightgun_line != 0xffff && lightgun_line == prv_cur_scanline)
{
@ -273,6 +295,11 @@ void spg_Term()
void spg_Reset(bool hard)
{
CalculateSync();
SH4FastEnough = false;
cpu_time_idx = 0;
cpu_cycles.fill(0);
real_times.fill(0.0);
}
void SetREP(TA_context* cntx)

View File

@ -1,6 +1,8 @@
#pragma once
#include "ta_ctx.h"
extern bool SH4FastEnough;
bool spg_Init();
void spg_Term();
void spg_Reset(bool Manual);

View File

@ -1,8 +1,10 @@
#include "ta_ctx.h"
#include "spg.h"
#include "oslib/oslib.h"
extern u32 fskip;
extern u32 FrameCount;
static int RenderCount;
TA_context* ta_ctx;
tad_context ta_tad;
@ -73,13 +75,18 @@ bool QueueRender(TA_context* ctx)
{
verify(ctx != 0);
if (rqueue && settings.pvr.SynchronousRender)
//wait for a frame if
// we have another one queue'd
// and SynchronousRender is enabled
bool skipFrame = false;
RenderCount++;
if (RenderCount % (settings.pvr.ta_skip + 1) != 0)
skipFrame = true;
else if (rqueue && (settings.pvr.AutoSkipFrame == 0
|| (settings.pvr.AutoSkipFrame == 1 && SH4FastEnough)))
// The previous render hasn't completed yet so we wait.
// If autoskipframe is enabled (normal level), we only do so if the CPU is running
// fast enough over the last frames
frame_finished.Wait();
if (rqueue)
if (skipFrame || rqueue)
{
tactx_Recycle(ctx);
fskip++;

View File

@ -1432,8 +1432,6 @@ static void getRegionTileClipping(u32& xmin, u32& xmax, u32& ymin, u32& ymax);
FifoSplitter<0> TAFifo0;
int ta_parse_cnt = 0;
//
// Check if a vertex has huge x,y,z values or negative z
//
@ -1581,69 +1579,66 @@ bool ta_parse_vdrc(TA_context* ctx)
vd_ctx = ctx;
vd_rc = vd_ctx->rend;
ta_parse_cnt++;
if (ctx->rend.isRTT || 0 == (ta_parse_cnt % ( settings.pvr.ta_skip + 1)))
TAFifo0.vdec_init();
bool empty_context = true;
int op_poly_count = 0;
int pt_poly_count = 0;
int tr_poly_count = 0;
PolyParam *bgpp = vd_rc.global_param_op.head();
if (bgpp->pcw.Texture)
{
TAFifo0.vdec_init();
bool empty_context = true;
int op_poly_count = 0;
int pt_poly_count = 0;
int tr_poly_count = 0;
PolyParam *bgpp = vd_rc.global_param_op.head();
if (bgpp->pcw.Texture)
{
bgpp->texid = renderer->GetTexture(bgpp->tsp, bgpp->tcw);
empty_context = false;
}
for (u32 pass = 0; pass <= ctx->tad.render_pass_count; pass++)
{
ctx->MarkRend(pass);
vd_rc.proc_start = ctx->rend.proc_start;
vd_rc.proc_end = ctx->rend.proc_end;
Ta_Dma* ta_data=(Ta_Dma*)vd_rc.proc_start;
Ta_Dma* ta_data_end=((Ta_Dma*)vd_rc.proc_end)-1;
do
{
ta_data =TaCmd(ta_data,ta_data_end);
}
while(ta_data<=ta_data_end);
if (ctx->rend.Overrun)
break;
bool empty_pass = vd_rc.global_param_op.used() == (pass == 0 ? 0 : (int)vd_rc.render_passes.LastPtr()->op_count)
&& vd_rc.global_param_pt.used() == (pass == 0 ? 0 : (int)vd_rc.render_passes.LastPtr()->pt_count)
&& vd_rc.global_param_tr.used() == (pass == 0 ? 0 : (int)vd_rc.render_passes.LastPtr()->tr_count);
empty_context = empty_context && empty_pass;
if (pass == 0 || !empty_pass)
{
RenderPass *render_pass = vd_rc.render_passes.Append();
render_pass->op_count = vd_rc.global_param_op.used();
make_index(&vd_rc.global_param_op, op_poly_count,
render_pass->op_count, true, &vd_rc);
op_poly_count = render_pass->op_count;
render_pass->mvo_count = vd_rc.global_param_mvo.used();
render_pass->pt_count = vd_rc.global_param_pt.used();
make_index(&vd_rc.global_param_pt, pt_poly_count,
render_pass->pt_count, true, &vd_rc);
pt_poly_count = render_pass->pt_count;
render_pass->tr_count = vd_rc.global_param_tr.used();
make_index(&vd_rc.global_param_tr, tr_poly_count,
render_pass->tr_count, false, &vd_rc);
tr_poly_count = render_pass->tr_count;
render_pass->mvo_tr_count = vd_rc.global_param_mvo_tr.used();
render_pass->autosort = UsingAutoSort(pass);
render_pass->z_clear = ClearZBeforePass(pass);
}
}
rv = !empty_context;
bgpp->texid = renderer->GetTexture(bgpp->tsp, bgpp->tcw);
empty_context = false;
}
for (u32 pass = 0; pass <= ctx->tad.render_pass_count; pass++)
{
ctx->MarkRend(pass);
vd_rc.proc_start = ctx->rend.proc_start;
vd_rc.proc_end = ctx->rend.proc_end;
Ta_Dma* ta_data=(Ta_Dma*)vd_rc.proc_start;
Ta_Dma* ta_data_end=((Ta_Dma*)vd_rc.proc_end)-1;
do
{
ta_data =TaCmd(ta_data,ta_data_end);
}
while(ta_data<=ta_data_end);
if (ctx->rend.Overrun)
break;
bool empty_pass = vd_rc.global_param_op.used() == (pass == 0 ? 0 : (int)vd_rc.render_passes.LastPtr()->op_count)
&& vd_rc.global_param_pt.used() == (pass == 0 ? 0 : (int)vd_rc.render_passes.LastPtr()->pt_count)
&& vd_rc.global_param_tr.used() == (pass == 0 ? 0 : (int)vd_rc.render_passes.LastPtr()->tr_count);
empty_context = empty_context && empty_pass;
if (pass == 0 || !empty_pass)
{
RenderPass *render_pass = vd_rc.render_passes.Append();
render_pass->op_count = vd_rc.global_param_op.used();
make_index(&vd_rc.global_param_op, op_poly_count,
render_pass->op_count, true, &vd_rc);
op_poly_count = render_pass->op_count;
render_pass->mvo_count = vd_rc.global_param_mvo.used();
render_pass->pt_count = vd_rc.global_param_pt.used();
make_index(&vd_rc.global_param_pt, pt_poly_count,
render_pass->pt_count, true, &vd_rc);
pt_poly_count = render_pass->pt_count;
render_pass->tr_count = vd_rc.global_param_tr.used();
make_index(&vd_rc.global_param_tr, tr_poly_count,
render_pass->tr_count, false, &vd_rc);
tr_poly_count = render_pass->tr_count;
render_pass->mvo_tr_count = vd_rc.global_param_mvo_tr.used();
render_pass->autosort = UsingAutoSort(pass);
render_pass->z_clear = ClearZBeforePass(pass);
}
}
rv = !empty_context;
bool overrun = ctx->rend.Overrun;
if (overrun)
WARN_LOG(PVR, "ERROR: TA context overrun");

15
core/nullDC.cpp Executable file → Normal file
View File

@ -760,7 +760,7 @@ void InitSettings()
settings.pvr.ta_skip = 0;
settings.pvr.MaxThreads = 3;
settings.pvr.SynchronousRender = true;
settings.pvr.AutoSkipFrame = 0;
settings.debug.SerialConsole = false;
settings.debug.SerialPTY = false;
@ -860,7 +860,16 @@ void LoadSettings(bool game_specific)
settings.pvr.ta_skip = cfgLoadInt(config_section, "ta.skip", settings.pvr.ta_skip);
settings.pvr.MaxThreads = cfgLoadInt(config_section, "pvr.MaxThreads", settings.pvr.MaxThreads);
settings.pvr.SynchronousRender = cfgLoadBool(config_section, "pvr.SynchronousRendering", settings.pvr.SynchronousRender);
if (game_specific)
settings.pvr.AutoSkipFrame = cfgLoadInt(config_section, "pvr.AutoSkipFrame", settings.pvr.AutoSkipFrame);
else
{
// compatibility with previous SynchronousRendering option
int autoskip = cfgLoadInt(config_section, "pvr.AutoSkipFrame", 99);
if (autoskip == 99)
autoskip = cfgLoadBool(config_section, "pvr.SynchronousRendering", true) ? 1 : 2;
settings.pvr.AutoSkipFrame = autoskip;
}
settings.debug.SerialConsole = cfgLoadBool(config_section, "Debug.SerialConsoleEnabled", settings.debug.SerialConsole);
settings.debug.SerialPTY = cfgLoadBool(config_section, "Debug.SerialPTY", settings.debug.SerialPTY);
@ -1017,7 +1026,7 @@ void SaveSettings()
cfgSaveBool("config", "rend.WidescreenGameHacks", settings.rend.WidescreenGameHacks);
cfgSaveInt("config", "pvr.MaxThreads", settings.pvr.MaxThreads);
cfgSaveBool("config", "pvr.SynchronousRendering", settings.pvr.SynchronousRender);
cfgSaveInt("config", "pvr.AutoSkipFrame", settings.pvr.AutoSkipFrame);
cfgSaveBool("config", "Debug.SerialConsoleEnabled", settings.debug.SerialConsole);
cfgSaveBool("config", "Debug.SerialPTY", settings.debug.SerialPTY);

View File

@ -1151,9 +1151,21 @@ static void gui_display_settings()
}
if (ImGui::CollapsingHeader("Rendering Options", ImGuiTreeNodeFlags_DefaultOpen))
{
ImGui::Checkbox("Synchronous Rendering", &settings.pvr.SynchronousRender);
ImGui::Text("Automatic Frame Skipping:");
ImGui::Columns(3, "autoskip", false);
ImGui::RadioButton("Disabled", &settings.pvr.AutoSkipFrame, 0);
ImGui::SameLine();
ShowHelpMarker("Reduce frame skipping by pausing the CPU when possible. Recommended for most platforms");
ShowHelpMarker("No frame skipping");
ImGui::NextColumn();
ImGui::RadioButton("Normal", &settings.pvr.AutoSkipFrame, 1);
ImGui::SameLine();
ShowHelpMarker("Skip a frame when the GPU and CPU are both running slow");
ImGui::NextColumn();
ImGui::RadioButton("Maximum", &settings.pvr.AutoSkipFrame, 2);
ImGui::SameLine();
ShowHelpMarker("Skip a frame when the GPU is running slow");
ImGui::Columns(1, nullptr, false);
ImGui::Checkbox("Clipping", &settings.rend.Clipping);
ImGui::SameLine();
ShowHelpMarker("Enable clipping. May produce graphical errors when disabled");

View File

@ -475,7 +475,7 @@ struct settings_t
RenderType rend;
u32 MaxThreads;
bool SynchronousRender;
int AutoSkipFrame; // 0: none, 1: some, 2: more
bool IsOpenGL() { return rend == RenderType::OpenGL || rend == RenderType::OpenGL_OIT; }
} pvr;