mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-07-27 12:34:37 +00:00
videoout: Proper flip rate and vblank management
This commit is contained in:
parent
502dc1cbf3
commit
fb325e33bd
@ -296,7 +296,7 @@ static void ResetSubmissionLock(Platform::InterruptId irq) {
|
|||||||
cv_lock.notify_all();
|
cv_lock.notify_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WaitGpuIdle() {
|
static void WaitGpuIdle() {
|
||||||
HLE_TRACE;
|
HLE_TRACE;
|
||||||
std::unique_lock lock{m_submission};
|
std::unique_lock lock{m_submission};
|
||||||
cv_lock.wait(lock, [] { return submission_lock == 0; });
|
cv_lock.wait(lock, [] { return submission_lock == 0; });
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/debug.h"
|
#include "common/debug.h"
|
||||||
|
#include "common/thread.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"
|
||||||
@ -12,10 +13,6 @@
|
|||||||
extern std::unique_ptr<Vulkan::RendererVulkan> renderer;
|
extern std::unique_ptr<Vulkan::RendererVulkan> renderer;
|
||||||
extern std::unique_ptr<AmdGpu::Liverpool> liverpool;
|
extern std::unique_ptr<AmdGpu::Liverpool> liverpool;
|
||||||
|
|
||||||
namespace Libraries::GnmDriver {
|
|
||||||
void WaitGpuIdle();
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Libraries::VideoOut {
|
namespace Libraries::VideoOut {
|
||||||
|
|
||||||
constexpr static bool Is32BppPixelFormat(PixelFormat format) {
|
constexpr static bool Is32BppPixelFormat(PixelFormat format) {
|
||||||
@ -160,8 +157,18 @@ int VideoOutDriver::UnregisterBuffers(VideoOutPort* port, s32 attributeIndex) {
|
|||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoOutDriver::Flip(const Request& req) {
|
std::chrono::microseconds VideoOutDriver::Flip(const Request& req) {
|
||||||
std::scoped_lock lock{mutex};
|
if (!req) {
|
||||||
|
return std::chrono::microseconds{0};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto start = std::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
|
// Whatever the game is rendering show splash if it is active
|
||||||
|
if (!renderer->ShowSplash(req.frame)) {
|
||||||
|
// Present the frame.
|
||||||
|
renderer->Present(req.frame);
|
||||||
|
}
|
||||||
|
|
||||||
// Update flip status.
|
// Update flip status.
|
||||||
auto* port = req.port;
|
auto* port = req.port;
|
||||||
@ -187,6 +194,9 @@ void VideoOutDriver::Flip(const Request& req) {
|
|||||||
port->buffer_labels[req.index] = 0;
|
port->buffer_labels[req.index] = 0;
|
||||||
port->SignalVoLabel();
|
port->SignalVoLabel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto end = std::chrono::high_resolution_clock::now();
|
||||||
|
return std::chrono::duration_cast<std::chrono::microseconds>(end - start);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VideoOutDriver::SubmitFlip(VideoOutPort* port, s32 index, s64 flip_arg,
|
bool VideoOutDriver::SubmitFlip(VideoOutPort* port, s32 index, s64 flip_arg,
|
||||||
@ -195,7 +205,7 @@ bool VideoOutDriver::SubmitFlip(VideoOutPort* port, s32 index, s64 flip_arg,
|
|||||||
|
|
||||||
if (!is_eop) {
|
if (!is_eop) {
|
||||||
liverpool->SubmitDone();
|
liverpool->SubmitDone();
|
||||||
GnmDriver::WaitGpuIdle();
|
liverpool->WaitGpuIdle();
|
||||||
}
|
}
|
||||||
|
|
||||||
Vulkan::Frame* frame;
|
Vulkan::Frame* frame;
|
||||||
@ -246,31 +256,42 @@ void VideoOutDriver::Vblank() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VideoOutDriver::PresentThread(std::stop_token token) {
|
void VideoOutDriver::PresentThread(std::stop_token token) {
|
||||||
static constexpr std::chrono::milliseconds FlipPeriod{16};
|
static constexpr std::chrono::microseconds VblankPeriod{16683};
|
||||||
while (!token.stop_requested()) {
|
Common::SetCurrentThreadName("VblankThread");
|
||||||
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.
|
const auto receive_request = [this] -> Request {
|
||||||
req = requests.front();
|
std::scoped_lock lk{mutex};
|
||||||
|
if (!requests.empty()) {
|
||||||
|
const auto request = requests.front();
|
||||||
requests.pop();
|
requests.pop();
|
||||||
|
return request;
|
||||||
}
|
}
|
||||||
|
return {};
|
||||||
|
};
|
||||||
|
|
||||||
// Whatever the game is rendering show splash if it is active
|
auto delay = std::chrono::microseconds{0};
|
||||||
if (!renderer->ShowSplash(req.frame)) {
|
while (!token.stop_requested()) {
|
||||||
// Present the frame.
|
// Sleep for most of the vblank duration.
|
||||||
renderer->Present(req.frame);
|
std::this_thread::sleep_for(VblankPeriod - delay);
|
||||||
|
|
||||||
|
// Check if it's time to take a request.
|
||||||
|
auto& vblank_status = main_port.vblank_status;
|
||||||
|
if (vblank_status.count % (main_port.flip_rate + 1) == 0) {
|
||||||
|
const auto request = receive_request();
|
||||||
|
delay = Flip(request);
|
||||||
|
FRAME_END;
|
||||||
}
|
}
|
||||||
|
vblank_status.count++;
|
||||||
|
vblank_status.processTime = Libraries::Kernel::sceKernelGetProcessTime();
|
||||||
|
vblank_status.tsc = Libraries::Kernel::sceKernelReadTsc();
|
||||||
|
|
||||||
Flip(req);
|
// Trigger flip events for the port.
|
||||||
Vblank();
|
for (auto& event : main_port.vblank_events) {
|
||||||
FRAME_END;
|
if (event != nullptr) {
|
||||||
|
event->TriggerEvent(SCE_VIDEO_OUT_EVENT_VBLANK,
|
||||||
|
Kernel::SceKernelEvent::Filter::VideoOut, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,9 +94,13 @@ private:
|
|||||||
s64 flip_arg;
|
s64 flip_arg;
|
||||||
u64 submit_tsc;
|
u64 submit_tsc;
|
||||||
bool eop;
|
bool eop;
|
||||||
|
|
||||||
|
operator bool() const noexcept {
|
||||||
|
return frame != nullptr;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void Flip(const Request& req);
|
std::chrono::microseconds Flip(const Request& req);
|
||||||
void PresentThread(std::stop_token token);
|
void PresentThread(std::stop_token token);
|
||||||
|
|
||||||
std::mutex mutex;
|
std::mutex mutex;
|
||||||
|
@ -65,6 +65,8 @@ void Liverpool::Process(std::stop_token stoken) {
|
|||||||
queue.submits.pop();
|
queue.submits.pop();
|
||||||
|
|
||||||
--num_submits;
|
--num_submits;
|
||||||
|
std::scoped_lock lock2{submit_mutex};
|
||||||
|
submit_cv.notify_all();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1002,6 +1002,11 @@ public:
|
|||||||
submit_cv.notify_one();
|
submit_cv.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WaitGpuIdle() noexcept {
|
||||||
|
std::unique_lock lk{submit_mutex};
|
||||||
|
submit_cv.wait(lk, [this] { return num_submits == 0; });
|
||||||
|
}
|
||||||
|
|
||||||
bool IsGpuIdle() const {
|
bool IsGpuIdle() const {
|
||||||
return num_submits == 0;
|
return num_submits == 0;
|
||||||
}
|
}
|
||||||
|
@ -182,10 +182,11 @@ Frame* RendererVulkan::PrepareFrameInternal(VideoCore::Image& image, bool is_eop
|
|||||||
// Request a free presentation frame.
|
// Request a free presentation frame.
|
||||||
Frame* frame = GetRenderFrame();
|
Frame* frame = GetRenderFrame();
|
||||||
|
|
||||||
// EOP flips are triggered from GPU thread to use the drawing scheduler to record
|
// EOP flips are triggered from GPU thread so use the drawing scheduler to record
|
||||||
// commands. Otherwise we are dealing with a CPU flip which could have arrived
|
// commands. Otherwise we are dealing with a CPU flip which could have arrived
|
||||||
// from any guest thread. Use a separate scheduler for that.
|
// from any guest thread. Use a separate scheduler for that.
|
||||||
auto& scheduler = is_eop ? draw_scheduler : flip_scheduler;
|
auto& scheduler = is_eop ? draw_scheduler : flip_scheduler;
|
||||||
|
scheduler.EndRendering();
|
||||||
const auto cmdbuf = scheduler.CommandBuffer();
|
const auto cmdbuf = scheduler.CommandBuffer();
|
||||||
|
|
||||||
image.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits::eTransferRead,
|
image.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits::eTransferRead,
|
||||||
|
@ -55,7 +55,7 @@ void Swapchain::Create(u32 width_, u32 height_, vk::SurfaceKHR surface_) {
|
|||||||
.pQueueFamilyIndices = queue_family_indices.data(),
|
.pQueueFamilyIndices = queue_family_indices.data(),
|
||||||
.preTransform = transform,
|
.preTransform = transform,
|
||||||
.compositeAlpha = composite_alpha,
|
.compositeAlpha = composite_alpha,
|
||||||
.presentMode = vk::PresentModeKHR::eFifo,
|
.presentMode = vk::PresentModeKHR::eMailbox,
|
||||||
.clipped = true,
|
.clipped = true,
|
||||||
.oldSwapchain = nullptr,
|
.oldSwapchain = nullptr,
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user