video_out: Move presentation to separate thread

This commit is contained in:
IndecisiveTurtle 2024-07-13 00:17:57 +03:00
parent bc28ed66e8
commit cc81ba6793
11 changed files with 66 additions and 45 deletions

View file

@ -2131,6 +2131,7 @@ int PS4_SYSV_ABI sceGnmSubmitDone() {
if (!liverpool->IsGpuIdle()) {
submission_lock = true;
}
liverpool->SubmitDone();
send_init_packet = true;
++frames_submitted;
return ORBIS_OK;

View file

@ -3,6 +3,7 @@
#include <pthread.h>
#include "common/assert.h"
#include "common/debug.h"
#include "core/libraries/error_codes.h"
#include "core/libraries/kernel/time_management.h"
#include "core/libraries/videoout/driver.h"
@ -41,6 +42,7 @@ VideoOutDriver::VideoOutDriver(u32 width, u32 height) {
main_port.resolution.fullHeight = height;
main_port.resolution.paneWidth = width;
main_port.resolution.paneHeight = height;
present_thread = std::jthread([&](std::stop_token token) { PresentThread(token); });
}
VideoOutDriver::~VideoOutDriver() = default;
@ -158,27 +160,7 @@ int VideoOutDriver::UnregisterBuffers(VideoOutPort* port, s32 attributeIndex) {
return ORBIS_OK;
}
void VideoOutDriver::Flip(std::chrono::microseconds timeout) {
Request req;
{
std::unique_lock lock{mutex};
submit_cond.wait_for(lock, timeout, [&] { return !requests.empty(); });
if (requests.empty()) {
renderer->ShowSplash();
return;
}
// Retrieve the request.
req = requests.front();
requests.pop();
}
// Whatever the game is rendering show splash if it is active
if (!renderer->ShowSplash(req.frame)) {
// Present the frame.
renderer->Present(req.frame);
}
void VideoOutDriver::Flip(const Request& req) {
std::scoped_lock lock{mutex};
// Update flip status.
@ -256,4 +238,33 @@ void VideoOutDriver::Vblank() {
}
}
void VideoOutDriver::PresentThread(std::stop_token token) {
static constexpr std::chrono::milliseconds FlipPeriod{16};
while (!token.stop_requested()) {
Request req;
{
std::unique_lock lock{mutex};
submit_cond.wait_for(lock, FlipPeriod, [&] { return !requests.empty(); });
if (requests.empty()) {
renderer->ShowSplash();
continue;
}
// Retrieve the request.
req = requests.front();
requests.pop();
}
// Whatever the game is rendering show splash if it is active
if (!renderer->ShowSplash(req.frame)) {
// Present the frame.
renderer->Present(req.frame);
}
Flip(req);
Vblank();
FRAME_END;
}
}
} // namespace Libraries::VideoOut

View file

@ -6,6 +6,7 @@
#include <condition_variable>
#include <mutex>
#include <queue>
#include "common/polyfill_thread.h"
#include "core/libraries/videoout/video_out.h"
namespace Vulkan {
@ -63,7 +64,6 @@ public:
const BufferAttribute* attribute);
int UnregisterBuffers(VideoOutPort* port, s32 attributeIndex);
void Flip(std::chrono::microseconds timeout);
bool SubmitFlip(VideoOutPort* port, s32 index, s64 flip_arg, bool is_eop = false);
void Vblank();
@ -78,10 +78,14 @@ private:
bool eop;
};
void Flip(const Request& req);
void PresentThread(std::stop_token token);
std::mutex mutex;
VideoOutPort main_port{};
std::condition_variable_any submit_cond;
std::condition_variable done_cond;
std::jthread present_thread;
std::queue<Request> requests;
bool is_neo{};
};

View file

@ -141,10 +141,12 @@ s32 PS4_SYSV_ABI sceVideoOutSubmitFlip(s32 handle, s32 bufferIndex, s32 flipMode
LOG_INFO(Lib_VideoOut, "bufferIndex = {}, flipMode = {}, flipArg = {}", bufferIndex, flipMode,
flipArg);
if (!driver->SubmitFlip(port, bufferIndex, flipArg)) {
LOG_ERROR(Lib_VideoOut, "Flip queue is full");
return ORBIS_VIDEO_OUT_ERROR_FLIP_QUEUE_FULL;
}
// Next time the gpu enters idle state, submit the flip
Platform::IrqC::Instance()->RegisterOnce(
Platform::InterruptId::GpuIdle, [=](Platform::InterruptId irq) {
const auto result = driver->SubmitFlip(port, bufferIndex, flipArg);
ASSERT_MSG(result, "Flip submission failed");
});
return ORBIS_OK;
}
@ -229,14 +231,6 @@ s32 PS4_SYSV_ABI sceVideoOutUnregisterBuffers(s32 handle, s32 attributeIndex) {
return driver->UnregisterBuffers(port, attributeIndex);
}
void Flip(std::chrono::microseconds micros) {
return driver->Flip(micros);
}
void Vblank() {
return driver->Vblank();
}
void sceVideoOutGetBufferLabelAddress(s32 handle, uintptr_t* label_addr) {
auto* port = driver->GetPort(handle);
ASSERT(port);

View file

@ -104,9 +104,6 @@ s32 PS4_SYSV_ABI sceVideoOutOpen(SceUserServiceUserId userId, s32 busType, s32 i
const void* param);
s32 PS4_SYSV_ABI sceVideoOutClose(s32 handle);
void Flip(std::chrono::microseconds micros);
void Vblank();
// Internal system functions
void sceVideoOutGetBufferLabelAddress(s32 handle, uintptr_t* label_addr);
s32 sceVideoOutSubmitEopFlip(s32 handle, u32 buf_id, u32 mode, u32 arg, void** unk);

View file

@ -136,14 +136,8 @@ void Emulator::Run(const std::filesystem::path& file) {
std::jthread mainthread =
std::jthread([this](std::stop_token stop_token) { linker->Execute(); });
// Begin main window loop until the application exits
static constexpr std::chrono::milliseconds FlipPeriod{16};
while (window->isOpen()) {
window->waitEvent();
Libraries::VideoOut::Flip(FlipPeriod);
Libraries::VideoOut::Vblank();
FRAME_END;
}
std::exit(0);

View file

@ -71,7 +71,7 @@ void WindowSDL::waitEvent() {
// Called on main thread
SDL_Event event;
if (!SDL_PollEvent(&event)) {
if (!SDL_WaitEvent(&event)) {
return;
}

View file

@ -32,7 +32,7 @@ void Liverpool::Process(std::stop_token stoken) {
while (!stoken.stop_requested()) {
{
std::unique_lock lk{submit_mutex};
Common::CondvarWait(submit_cv, lk, stoken, [this] { return num_submits != 0; });
Common::CondvarWait(submit_cv, lk, stoken, [this] { return num_submits != 0 || submit_done; });
}
if (stoken.stop_requested()) {
break;
@ -67,6 +67,13 @@ void Liverpool::Process(std::stop_token stoken) {
}
}
if (submit_done) {
if (rasterizer) {
rasterizer->Flush();
}
submit_done = false;
}
Platform::IrqC::Instance()->Signal(Platform::InterruptId::GpuIdle);
}
}

View file

@ -991,6 +991,12 @@ public:
void SubmitGfx(std::span<const u32> dcb, std::span<const u32> ccb);
void SubmitAsc(u32 vqid, std::span<const u32> acb);
void SubmitDone() noexcept {
std::scoped_lock lk{submit_mutex};
submit_done = true;
submit_cv.notify_one();
}
bool IsGpuIdle() const {
return num_submits == 0;
}
@ -1061,6 +1067,7 @@ private:
Vulkan::Rasterizer* rasterizer{};
std::jthread process_thread{};
std::atomic<u32> num_submits{};
std::atomic<bool> submit_done{};
std::mutex submit_mutex;
std::condition_variable_any submit_cv;
};

View file

@ -96,6 +96,10 @@ void Rasterizer::DispatchDirect() {
cmdbuf.dispatch(cs_program.dim_x, cs_program.dim_y, cs_program.dim_z);
}
void Rasterizer::Flush() {
scheduler.Flush();
}
void Rasterizer::BeginRendering() {
const auto& regs = liverpool->regs;
RenderState state;

View file

@ -35,6 +35,8 @@ public:
void ScopeMarkerBegin(const std::string& str);
void ScopeMarkerEnd();
void Flush();
private:
u32 SetupIndexBuffer(bool& is_indexed, u32 index_offset);