mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-07-27 04:25:12 +00:00
video_out: Move presentation to separate thread
This commit is contained in:
parent
bc28ed66e8
commit
cc81ba6793
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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{};
|
||||||
};
|
};
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user