mirror of
https://github.com/Vita3K/Vita3K-Android.git
synced 2024-12-02 18:36:34 +00:00
display, renderer: Sync guest and host fps
This commit is contained in:
parent
021e62a073
commit
0bc9ef9ee7
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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++;
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user