Merge pull request #416 from oneandonlydean/fix/videoout-flip-release-vblank

fix(videoout): pace the next flip from the vblank a flip was released at
This commit is contained in:
Alexey 2026-10-03 09:34:19 +03:00 committed by GitHub
commit aa23d52878
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 27 additions and 1 deletions

View File

@ -394,6 +394,7 @@ void VideoOutDriver::processFlip(FlipRequest& req) {
req.cfg->vblankCond.wait(lock, req.cfg->shutdownToken, [&] { return req.cfg->vblankStatus.count >= target || req.cfg->failure || req.cfg->closing; });
timing.Mark("vblank_wait");
checkConfig(*req.cfg);
req.cfg->lastFlipVblank = req.cfg->vblankStatus.count;
}
require(req.width != 0 && req.height != 0 && req.width <= static_cast<uint32_t>(std::numeric_limits<int>::max()) && req.height <= static_cast<uint32_t>(std::numeric_limits<int>::max()), "invalid window dimensions");
window.Ensure(req.width, req.height);
@ -444,7 +445,6 @@ void VideoOutDriver::processFlip(FlipRequest& req) {
require(req.cfg->flipStatus.count != std::numeric_limits<uint64_t>::max(), "flip counter overflow");
triggerEvents(*req.cfg, VIDEO_OUT_EVENT_FLIP, reinterpret_cast<void*>(req.flipArg));
++req.cfg->flipStatus.count;
req.cfg->lastFlipVblank = req.cfg->vblankStatus.count;
req.cfg->flipStatus.processTime = sceKernelGetProcessTime();
req.cfg->flipStatus.processTimeCounter = sceKernelGetProcessTimeCounter();
req.cfg->flipStatus.flipArg = req.flipArg;

View File

@ -273,6 +273,31 @@ void testBackToBack() {
LibcRunShutdown_nid_postfix();
}
void testReleaseVblank() {
const int handle = sceVideoOutOpen(255, 0, 0, nullptr);
auto cfg = VideoOutDriver::Get().GetConfig(handle);
std::vector<std::byte> allocation(6 * 65536 + 65535);
const auto storage = alignedBuffer(allocation);
fillBuffer(storage, 259, 137);
VideoOutBuffers buffer{storage.data(), nullptr, {nullptr, nullptr}};
VideoOutBufferAttribute2 attribute{};
sceVideoOutSetBufferAttribute2(&attribute, 0x8000000000000000ull, 0, 259, 137, 0, 0, 0);
sceVideoOutRegisterBuffers2(handle, 0, 0, &buffer, 1, &attribute, 0, nullptr);
check(sceVideoOutSetFlipRate(handle, 2) == 0, "flip rate was not applied");
sceVideoOutSubmitFlip(handle, 0, 1, 1);
{
std::unique_lock lock(cfg->mutex);
const bool done = cfg->vblankCond.wait_for(lock, std::chrono::seconds(15), [&] { return cfg->failure || cfg->flipStatus.count == 1; });
if (cfg->failure) std::rethrow_exception(cfg->failure);
check(done, "the flip did not complete");
check(cfg->vblankStatus.count > 3, "the first presentation did not outlast the vblank it was released at");
check(cfg->lastFlipVblank == 3, "the next flip is not paced from the vblank this flip was released at");
}
sceVideoOutUnregisterBuffers(handle, 0);
sceVideoOutClose(handle);
LibcRunShutdown_nid_postfix();
}
}
int main(int argc, char** argv) {
@ -281,6 +306,7 @@ int main(int argc, char** argv) {
else if (argc == 2 && std::string(argv[1]) == "controls") testControls();
else if (argc == 2 && std::string(argv[1]) == "present") testPresentation(false);
else if (argc == 2 && std::string(argv[1]) == "backtoback") testBackToBack();
else if (argc == 2 && std::string(argv[1]) == "pacing") testReleaseVblank();
else if (argc == 2 && std::string(argv[1]) == "unavailable") testPresentation(true);
else testLifetime(argc == 2 && std::string(argv[1]) == "reopen");
std::puts("VideoOut flip tests passed");