display, renderer: Sync guest and host fps

This commit is contained in:
Macdu 2022-09-04 22:58:33 +02:00 committed by Zangetsu
parent 021e62a073
commit 0bc9ef9ee7
10 changed files with 62 additions and 10 deletions

View File

@ -43,9 +43,10 @@ static void vblank_sync_thread(EmuEnvState &emuenv) {
display.vblank_count++;
// register framebuf change made by _sceDisplaySetFrameBuf
if (display.has_next_frame) {
display.frame = display.next_frame;
display.has_next_frame = false;
emuenv.renderer->should_display = true;
// do not set it here as it as already been set in _sceDisplaySetFrameBuf
// display.frame = display.next_frame;
// emuenv.renderer->should_display = true;
}
}

View File

@ -22,6 +22,7 @@
#include <io/state.h>
#include <kernel/state.h>
#include <packages/functions.h>
#include <renderer/state.h>
#include <util/safe_time.h>
@ -1035,6 +1036,8 @@ void draw_live_area_screen(GuiState &gui, EmuEnvState &emuenv) {
update_time_app_used(gui, emuenv, app_path);
emuenv.kernel.exit_delete_all_threads();
emuenv.load_exec = true;
// make sure we are not stuck waiting for a gpu command
emuenv.renderer->should_display = true;
} else {
gui.apps_list_opened.erase(get_app_open_list_index(gui, app_path));
if (gui.current_app_selected == 0) {

View File

@ -20,6 +20,7 @@
#include <io/state.h>
#include <kernel/state.h>
#include <packages/sfo.h>
#include <renderer/state.h>
#include <util/tracy.h>
#ifdef TRACY_ENABLE
@ -422,6 +423,8 @@ EXPORT(SceInt32, _sceAppMgrLoadExec, const char *appPath, Ptr<char> const argv[]
emuenv.load_app_path = emuenv.io.app_path;
emuenv.load_exec_path = exec_path;
emuenv.load_exec = true;
// make sure we are not stuck waiting for a gpu command
emuenv.renderer->should_display = true;
return SCE_KERNEL_OK;
}

View File

@ -25,6 +25,7 @@
#include <display/state.h>
#include <kernel/state.h>
#include <packages/functions.h>
#include <renderer/state.h>
#include <util/lock_and_find.h>
#include <util/types.h>
@ -128,6 +129,14 @@ EXPORT(SceInt32, _sceDisplaySetFrameBuf, const SceDisplayFrameBuf *pFrameBuf, Sc
info.image_size.x = pFrameBuf->width;
info.image_size.y = pFrameBuf->height;
emuenv.display.last_setframe_vblank_count = emuenv.display.vblank_count.load();
// hack (kind of)
// we can assume the framebuffer is already fully rendered
// (always the case when using gxm, and should also be the case when it is not used)
// so set this buffer as ready to be displayed
// this should decrease the latency
emuenv.display.frame = emuenv.display.next_frame;
emuenv.renderer->should_display = true;
}
emuenv.frame_count++;

View File

@ -41,8 +41,10 @@ void finish(State &state, Context *context);
/**
* \brief Wait for all subjects to be done with the given sync object.
*
* Return true if the wait didn't timeout
*/
void wishlist(SceGxmSyncObject *sync_object, const uint32_t timestamp);
bool wishlist(SceGxmSyncObject *sync_object, const uint32_t timestamp, const int32_t timeout_micros = -1);
/**
* \brief Set list of subject with sync object to done.

View File

@ -59,7 +59,7 @@ struct State {
uint32_t shaders_count_compiled = 0;
uint32_t programs_count_pre_compiled = 0;
std::atomic<bool> should_display;
bool should_display;
virtual bool init(const char *base_path, const bool hashless_texture_cache) = 0;
virtual void render_frame(const SceFVector2 &viewport_pos, const SceFVector2 &viewport_size, const DisplayState &display,

View File

@ -23,6 +23,7 @@
#include <renderer/vulkan/types.h>
#include <config/state.h>
#include <functional>
#include <util/log.h>
#include <util/string_utils.h>
@ -55,6 +56,16 @@ bool is_cmd_ready(MemState &mem, CommandList &command_list) {
return sync->timestamp_current >= timestamp;
}
bool wait_cmd(MemState &mem, CommandList &command_list) {
// we assume here that the cmd starts with a WaitSyncObject
SceGxmSyncObject *sync = reinterpret_cast<Ptr<SceGxmSyncObject> *>(&command_list.first->data[0])->get(mem);
const uint32_t timestamp = *reinterpret_cast<uint32_t *>(&command_list.first->data[sizeof(uint32_t) + 2 * sizeof(void *)]);
// wait 500 micro secibds and then return in case should_display is set to true
return renderer::wishlist(sync, timestamp, 500);
}
void process_batch(renderer::State &state, const FeatureState &features, MemState &mem, Config &config, CommandList &command_list) {
using CommandHandlerFunc = std::function<void(renderer::State &, MemState &, Config &,
CommandHelper &, const FeatureState &, Context *, const char *, const char *, const char *)>;
@ -106,13 +117,22 @@ void process_batch(renderer::State &state, const FeatureState &features, MemStat
}
void process_batches(renderer::State &state, const FeatureState &features, MemState &mem, Config &config) {
while (!state.should_display.exchange(false)) {
while (!state.should_display) {
// Try to wait for a batch (about 2 or 3ms, game should be fast for this)
auto cmd_list = state.command_buffer_queue.top(3);
if (!cmd_list || !is_cmd_ready(mem, *cmd_list)) {
// Try to wait for a batch (about 2 or 3ms, game should be fast for this)
state.should_display = false;
return;
// beginning of the game or homebrew not using gxm
if (state.context == nullptr)
return;
// keep the old behavior for opengl with vsync as it looks like the new one causes some issues
if (state.current_backend == Backend::OpenGL && config.current_config.v_sync)
return;
if (!cmd_list || !wait_cmd(mem, *cmd_list))
// this mean the command is still not ready, check if we can display it again
continue;
}
state.command_buffer_queue.pop();

View File

@ -667,6 +667,8 @@ void get_surface_data(GLState &renderer, GLContext &context, uint32_t *pixels, S
void GLState::render_frame(const SceFVector2 &viewport_pos, const SceFVector2 &viewport_size, const DisplayState &display,
const GxmState &gxm, MemState &mem) {
should_display = false;
if (!display.frame.base)
return;

View File

@ -102,10 +102,19 @@ int wait_for_status(State &state, int *status, int signal, bool wake_on_equal) {
return *status;
}
void wishlist(SceGxmSyncObject *sync_object, const uint32_t timestamp) {
bool wishlist(SceGxmSyncObject *sync_object, const uint32_t timestamp, const int32_t timeout_micros) {
std::unique_lock<std::mutex> lock(sync_object->lock);
if (sync_object->timestamp_current < timestamp) {
sync_object->cond.wait(lock, [&]() { return sync_object->timestamp_current >= timestamp; });
const auto &pred = [&]() { return sync_object->timestamp_current >= timestamp; };
if (timeout_micros == -1) {
sync_object->cond.wait(lock, pred);
return true;
} else {
return sync_object->cond.wait_for(lock, std::chrono::microseconds(timeout_micros), pred);
}
} else {
return true;
}
}

View File

@ -388,6 +388,9 @@ void VKState::cleanup() {
void VKState::render_frame(const SceFVector2 &viewport_pos, const SceFVector2 &viewport_size, const DisplayState &display,
const GxmState &gxm, MemState &mem) {
// we are displaying this frame, wait for a new one
should_display = false;
if (!display.frame.base)
return;