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:
parent
b9d7c11936
commit
a00aad5fa7
|
@ -1,3 +1,4 @@
|
||||||
|
#include <array>
|
||||||
#include "spg.h"
|
#include "spg.h"
|
||||||
#include "hw/holly/holly_intc.h"
|
#include "hw/holly/holly_intc.h"
|
||||||
#include "hw/holly/sb.h"
|
#include "hw/holly/sb.h"
|
||||||
|
@ -23,6 +24,11 @@ static u32 Frame_Cycles;
|
||||||
int render_end_schid;
|
int render_end_schid;
|
||||||
int vblank_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()
|
void CalculateSync()
|
||||||
{
|
{
|
||||||
u32 pixel_clock = PIXEL_CLOCK / (FB_R_CTRL.vclk_div ? 1 : 2);
|
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
|
else
|
||||||
SPG_STATUS.fieldnum=0;
|
SPG_STATUS.fieldnum=0;
|
||||||
|
|
||||||
vblk_cnt++;
|
|
||||||
rend_vblank();
|
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
|
#ifdef TEST_AUTOMATION
|
||||||
replay_input();
|
replay_input();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if !defined(NDEBUG) || defined(DEBUGFAST)
|
||||||
|
vblk_cnt++;
|
||||||
if ((os_GetSeconds()-last_fps)>2)
|
if ((os_GetSeconds()-last_fps)>2)
|
||||||
{
|
{
|
||||||
static int Last_FC;
|
static int Last_FC;
|
||||||
|
@ -190,6 +211,7 @@ int spg_line_sched(int tag, int cycl, int jit)
|
||||||
fskip=0;
|
fskip=0;
|
||||||
last_fps=os_GetSeconds();
|
last_fps=os_GetSeconds();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
if (lightgun_line != 0xffff && lightgun_line == prv_cur_scanline)
|
if (lightgun_line != 0xffff && lightgun_line == prv_cur_scanline)
|
||||||
{
|
{
|
||||||
|
@ -273,6 +295,11 @@ void spg_Term()
|
||||||
void spg_Reset(bool hard)
|
void spg_Reset(bool hard)
|
||||||
{
|
{
|
||||||
CalculateSync();
|
CalculateSync();
|
||||||
|
|
||||||
|
SH4FastEnough = false;
|
||||||
|
cpu_time_idx = 0;
|
||||||
|
cpu_cycles.fill(0);
|
||||||
|
real_times.fill(0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetREP(TA_context* cntx)
|
void SetREP(TA_context* cntx)
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "ta_ctx.h"
|
#include "ta_ctx.h"
|
||||||
|
|
||||||
|
extern bool SH4FastEnough;
|
||||||
|
|
||||||
bool spg_Init();
|
bool spg_Init();
|
||||||
void spg_Term();
|
void spg_Term();
|
||||||
void spg_Reset(bool Manual);
|
void spg_Reset(bool Manual);
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
#include "ta_ctx.h"
|
#include "ta_ctx.h"
|
||||||
|
#include "spg.h"
|
||||||
#include "oslib/oslib.h"
|
#include "oslib/oslib.h"
|
||||||
|
|
||||||
extern u32 fskip;
|
extern u32 fskip;
|
||||||
extern u32 FrameCount;
|
extern u32 FrameCount;
|
||||||
|
static int RenderCount;
|
||||||
|
|
||||||
TA_context* ta_ctx;
|
TA_context* ta_ctx;
|
||||||
tad_context ta_tad;
|
tad_context ta_tad;
|
||||||
|
@ -73,13 +75,18 @@ bool QueueRender(TA_context* ctx)
|
||||||
{
|
{
|
||||||
verify(ctx != 0);
|
verify(ctx != 0);
|
||||||
|
|
||||||
if (rqueue && settings.pvr.SynchronousRender)
|
bool skipFrame = false;
|
||||||
//wait for a frame if
|
RenderCount++;
|
||||||
// we have another one queue'd
|
if (RenderCount % (settings.pvr.ta_skip + 1) != 0)
|
||||||
// and SynchronousRender is enabled
|
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();
|
frame_finished.Wait();
|
||||||
|
|
||||||
if (rqueue)
|
if (skipFrame || rqueue)
|
||||||
{
|
{
|
||||||
tactx_Recycle(ctx);
|
tactx_Recycle(ctx);
|
||||||
fskip++;
|
fskip++;
|
||||||
|
|
|
@ -1432,8 +1432,6 @@ static void getRegionTileClipping(u32& xmin, u32& xmax, u32& ymin, u32& ymax);
|
||||||
|
|
||||||
FifoSplitter<0> TAFifo0;
|
FifoSplitter<0> TAFifo0;
|
||||||
|
|
||||||
int ta_parse_cnt = 0;
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Check if a vertex has huge x,y,z values or negative z
|
// 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_ctx = ctx;
|
||||||
vd_rc = vd_ctx->rend;
|
vd_rc = vd_ctx->rend;
|
||||||
|
|
||||||
ta_parse_cnt++;
|
TAFifo0.vdec_init();
|
||||||
if (ctx->rend.isRTT || 0 == (ta_parse_cnt % ( settings.pvr.ta_skip + 1)))
|
|
||||||
|
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();
|
bgpp->texid = renderer->GetTexture(bgpp->tsp, bgpp->tcw);
|
||||||
|
empty_context = false;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
bool overrun = ctx->rend.Overrun;
|
||||||
if (overrun)
|
if (overrun)
|
||||||
WARN_LOG(PVR, "ERROR: TA context overrun");
|
WARN_LOG(PVR, "ERROR: TA context overrun");
|
||||||
|
|
|
@ -760,7 +760,7 @@ void InitSettings()
|
||||||
settings.pvr.ta_skip = 0;
|
settings.pvr.ta_skip = 0;
|
||||||
|
|
||||||
settings.pvr.MaxThreads = 3;
|
settings.pvr.MaxThreads = 3;
|
||||||
settings.pvr.SynchronousRender = true;
|
settings.pvr.AutoSkipFrame = 0;
|
||||||
|
|
||||||
settings.debug.SerialConsole = false;
|
settings.debug.SerialConsole = false;
|
||||||
settings.debug.SerialPTY = 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.ta_skip = cfgLoadInt(config_section, "ta.skip", settings.pvr.ta_skip);
|
||||||
|
|
||||||
settings.pvr.MaxThreads = cfgLoadInt(config_section, "pvr.MaxThreads", settings.pvr.MaxThreads);
|
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.SerialConsole = cfgLoadBool(config_section, "Debug.SerialConsoleEnabled", settings.debug.SerialConsole);
|
||||||
settings.debug.SerialPTY = cfgLoadBool(config_section, "Debug.SerialPTY", settings.debug.SerialPTY);
|
settings.debug.SerialPTY = cfgLoadBool(config_section, "Debug.SerialPTY", settings.debug.SerialPTY);
|
||||||
|
@ -1017,7 +1026,7 @@ void SaveSettings()
|
||||||
cfgSaveBool("config", "rend.WidescreenGameHacks", settings.rend.WidescreenGameHacks);
|
cfgSaveBool("config", "rend.WidescreenGameHacks", settings.rend.WidescreenGameHacks);
|
||||||
|
|
||||||
cfgSaveInt("config", "pvr.MaxThreads", settings.pvr.MaxThreads);
|
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.SerialConsoleEnabled", settings.debug.SerialConsole);
|
||||||
cfgSaveBool("config", "Debug.SerialPTY", settings.debug.SerialPTY);
|
cfgSaveBool("config", "Debug.SerialPTY", settings.debug.SerialPTY);
|
||||||
|
|
|
@ -1151,9 +1151,21 @@ static void gui_display_settings()
|
||||||
}
|
}
|
||||||
if (ImGui::CollapsingHeader("Rendering Options", ImGuiTreeNodeFlags_DefaultOpen))
|
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();
|
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::Checkbox("Clipping", &settings.rend.Clipping);
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ShowHelpMarker("Enable clipping. May produce graphical errors when disabled");
|
ShowHelpMarker("Enable clipping. May produce graphical errors when disabled");
|
||||||
|
|
|
@ -475,7 +475,7 @@ struct settings_t
|
||||||
RenderType rend;
|
RenderType rend;
|
||||||
|
|
||||||
u32 MaxThreads;
|
u32 MaxThreads;
|
||||||
bool SynchronousRender;
|
int AutoSkipFrame; // 0: none, 1: some, 2: more
|
||||||
|
|
||||||
bool IsOpenGL() { return rend == RenderType::OpenGL || rend == RenderType::OpenGL_OIT; }
|
bool IsOpenGL() { return rend == RenderType::OpenGL || rend == RenderType::OpenGL_OIT; }
|
||||||
} pvr;
|
} pvr;
|
||||||
|
|
Loading…
Reference in New Issue