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()) { if (!liverpool->IsGpuIdle()) {
submission_lock = true; submission_lock = true;
} }
liverpool->SubmitDone();
send_init_packet = true; send_init_packet = true;
++frames_submitted; ++frames_submitted;
return ORBIS_OK; return ORBIS_OK;

View File

@ -3,6 +3,7 @@
#include <pthread.h> #include <pthread.h>
#include "common/assert.h" #include "common/assert.h"
#include "common/debug.h"
#include "core/libraries/error_codes.h" #include "core/libraries/error_codes.h"
#include "core/libraries/kernel/time_management.h" #include "core/libraries/kernel/time_management.h"
#include "core/libraries/videoout/driver.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.fullHeight = height;
main_port.resolution.paneWidth = width; main_port.resolution.paneWidth = width;
main_port.resolution.paneHeight = height; main_port.resolution.paneHeight = height;
present_thread = std::jthread([&](std::stop_token token) { PresentThread(token); });
} }
VideoOutDriver::~VideoOutDriver() = default; VideoOutDriver::~VideoOutDriver() = default;
@ -158,27 +160,7 @@ int VideoOutDriver::UnregisterBuffers(VideoOutPort* port, s32 attributeIndex) {
return ORBIS_OK; return ORBIS_OK;
} }
void VideoOutDriver::Flip(std::chrono::microseconds timeout) { void VideoOutDriver::Flip(const Request& req) {
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);
}
std::scoped_lock lock{mutex}; std::scoped_lock lock{mutex};
// Update flip status. // 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 } // namespace Libraries::VideoOut

View File

@ -6,6 +6,7 @@
#include <condition_variable> #include <condition_variable>
#include <mutex> #include <mutex>
#include <queue> #include <queue>
#include "common/polyfill_thread.h"
#include "core/libraries/videoout/video_out.h" #include "core/libraries/videoout/video_out.h"
namespace Vulkan { namespace Vulkan {
@ -63,7 +64,6 @@ public:
const BufferAttribute* attribute); const BufferAttribute* attribute);
int UnregisterBuffers(VideoOutPort* port, s32 attributeIndex); 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); bool SubmitFlip(VideoOutPort* port, s32 index, s64 flip_arg, bool is_eop = false);
void Vblank(); void Vblank();
@ -78,10 +78,14 @@ private:
bool eop; bool eop;
}; };
void Flip(const Request& req);
void PresentThread(std::stop_token token);
std::mutex mutex; std::mutex mutex;
VideoOutPort main_port{}; VideoOutPort main_port{};
std::condition_variable_any submit_cond; std::condition_variable_any submit_cond;
std::condition_variable done_cond; std::condition_variable done_cond;
std::jthread present_thread;
std::queue<Request> requests; std::queue<Request> requests;
bool is_neo{}; 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, LOG_INFO(Lib_VideoOut, "bufferIndex = {}, flipMode = {}, flipArg = {}", bufferIndex, flipMode,
flipArg); flipArg);
if (!driver->SubmitFlip(port, bufferIndex, flipArg)) { // Next time the gpu enters idle state, submit the flip
LOG_ERROR(Lib_VideoOut, "Flip queue is full"); Platform::IrqC::Instance()->RegisterOnce(
return ORBIS_VIDEO_OUT_ERROR_FLIP_QUEUE_FULL; Platform::InterruptId::GpuIdle, [=](Platform::InterruptId irq) {
} const auto result = driver->SubmitFlip(port, bufferIndex, flipArg);
ASSERT_MSG(result, "Flip submission failed");
});
return ORBIS_OK; return ORBIS_OK;
} }
@ -229,14 +231,6 @@ s32 PS4_SYSV_ABI sceVideoOutUnregisterBuffers(s32 handle, s32 attributeIndex) {
return driver->UnregisterBuffers(port, 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) { void sceVideoOutGetBufferLabelAddress(s32 handle, uintptr_t* label_addr) {
auto* port = driver->GetPort(handle); auto* port = driver->GetPort(handle);
ASSERT(port); ASSERT(port);

View File

@ -104,9 +104,6 @@ s32 PS4_SYSV_ABI sceVideoOutOpen(SceUserServiceUserId userId, s32 busType, s32 i
const void* param); const void* param);
s32 PS4_SYSV_ABI sceVideoOutClose(s32 handle); s32 PS4_SYSV_ABI sceVideoOutClose(s32 handle);
void Flip(std::chrono::microseconds micros);
void Vblank();
// Internal system functions // Internal system functions
void sceVideoOutGetBufferLabelAddress(s32 handle, uintptr_t* label_addr); void sceVideoOutGetBufferLabelAddress(s32 handle, uintptr_t* label_addr);
s32 sceVideoOutSubmitEopFlip(s32 handle, u32 buf_id, u32 mode, u32 arg, void** unk); 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 mainthread =
std::jthread([this](std::stop_token stop_token) { linker->Execute(); }); 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()) { while (window->isOpen()) {
window->waitEvent(); window->waitEvent();
Libraries::VideoOut::Flip(FlipPeriod);
Libraries::VideoOut::Vblank();
FRAME_END;
} }
std::exit(0); std::exit(0);

View File

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

View File

@ -32,7 +32,7 @@ void Liverpool::Process(std::stop_token stoken) {
while (!stoken.stop_requested()) { while (!stoken.stop_requested()) {
{ {
std::unique_lock lk{submit_mutex}; 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()) { if (stoken.stop_requested()) {
break; 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); 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 SubmitGfx(std::span<const u32> dcb, std::span<const u32> ccb);
void SubmitAsc(u32 vqid, std::span<const u32> acb); 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 { bool IsGpuIdle() const {
return num_submits == 0; return num_submits == 0;
} }
@ -1061,6 +1067,7 @@ private:
Vulkan::Rasterizer* rasterizer{}; Vulkan::Rasterizer* rasterizer{};
std::jthread process_thread{}; std::jthread process_thread{};
std::atomic<u32> num_submits{}; std::atomic<u32> num_submits{};
std::atomic<bool> submit_done{};
std::mutex submit_mutex; std::mutex submit_mutex;
std::condition_variable_any submit_cv; 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); cmdbuf.dispatch(cs_program.dim_x, cs_program.dim_y, cs_program.dim_z);
} }
void Rasterizer::Flush() {
scheduler.Flush();
}
void Rasterizer::BeginRendering() { void Rasterizer::BeginRendering() {
const auto& regs = liverpool->regs; const auto& regs = liverpool->regs;
RenderState state; RenderState state;

View File

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