screen/video: Add refresh rate hint mechanism (#1447)

This constitutes one puzzle piece that will help improve ares's frame
pacing, particularly for variable refresh rate displays.

While continued work on ares's audio drivers is likely to smooth the
pace at which ares generates video frames, transient changes in
application and system load inevitably means that frames will be
delivered earlier or later than expected. Additionally, relying on video
frames to be generated precisely when needed and presenting them
immediately is fairly demanding. If we can pace frames some distance in
advance, it can significantly lessen the burden on the CPU and GPU.

This PR is one tool to help the video backend deal with these issues.
Cores can now inform the screen of their expected rate of frame
generation at any given time, and the screen will inform the video
backend. The backend can then employ various strategies to perform
optimal frame pacing and drop frames when necessary.

The function is emphasized as a 'hint' to indicate that the rate is only
that which is expected, rather than certain. Unpredictable changes in
system conditions mean that we can never be entirely certain when a
frame will be generated or presented.

> [!NOTE]
> No concrete `refreshRateHint` implementations are added in this PR.
Given the variety of core refresh rates across ares, as well as the
consideration that for some cores refresh rates can change during
runtime, that work is left for separate PRs.

Co-authored-by: jcm <[email protected]>
This commit is contained in:
jcmandjcm authored and GitHub committed 2024-04-09 09:34:57 +01:00
1 parent e24e89b272
commit a3c57b4fbd
8 files changed
+21 -2

No files matched your search

+5
View File
@@ -72,6 +72,11 @@ auto Screen::setRefresh(function<void ()> refresh) -> void {
_refresh = refresh;
}
auto Screen::refreshRateHint(double refreshRate) -> void {
lock_guard<recursive_mutex> lock(_mutex);
platform->refreshRateHint(refreshRate);
}
auto Screen::setViewport(u32 x, u32 y, u32 width, u32 height) -> void {
lock_guard<recursive_mutex> lock(_mutex);
_viewportX = x;
+1
View File
@@ -34,6 +34,7 @@ struct Screen : Video {
auto setRefresh(function<void ()> refresh) -> void;
auto setViewport(u32 x, u32 y, u32 width, u32 height) -> void;
auto refreshRateHint(double refreshRate) -> void;
auto setSize(u32 width, u32 height) -> void;
auto setScale(f64 scaleX, f64 scaleY) -> void;
+1
View File
@@ -18,6 +18,7 @@ struct Platform {
virtual auto log(Node::Debugger::Tracer::Tracer, string_view message) -> void {}
virtual auto status(string_view message) -> void {}
virtual auto video(Node::Video::Screen, const u32* data, u32 pitch, u32 width, u32 height) -> void {}
virtual auto refreshRateHint(double refreshRate) -> void {}
virtual auto audio(Node::Audio::Stream) -> void {}
virtual auto input(Node::Input::Input) -> void {}
virtual auto cheat(u32 addr) -> maybe<u32> { return nothing; }
+5 -1
View File
@@ -132,6 +132,10 @@ auto Program::video(ares::Node::Video::Screen node, const u32* data, u32 pitch,
}
}
auto Program::refreshRateHint(double refreshRate) -> void {
ruby::video.refreshRateHint(refreshRate);
}
auto Program::audio(ares::Node::Audio::Stream node) -> void {
if(!streams) return;
@@ -187,4 +191,4 @@ auto Program::input(ares::Node::Input::Input node) -> void {
auto Program::cheat(u32 address) -> maybe<u32> {
return cheatEditor.find(address);
}
}
+1
View File
@@ -11,6 +11,7 @@ struct Program : ares::Platform {
auto log(ares::Node::Debugger::Tracer::Tracer tracer, string_view message) -> void override;
auto status(string_view message) -> void override;
auto video(ares::Node::Video::Screen, const u32* data, u32 pitch, u32 width, u32 height) -> void override;
auto refreshRateHint(double refreshRate) -> void override;
auto audio(ares::Node::Audio::Stream) -> void override;
auto input(ares::Node::Input::Input) -> void override;
auto cheat(u32 address) -> maybe<u32> override;
+1 -1
View File
@@ -79,7 +79,7 @@ struct VideoMetal : VideoDriver, Metal {
return true;
}
auto hintRefreshRate(double refreshRate) -> void {
auto refreshRateHint(double refreshRate) -> void {
_refreshRateHint = refreshRate;
updatePresentInterval();
}
+5
View File
@@ -92,6 +92,11 @@ auto Video::setShader(string shader) -> bool {
return true;
}
auto Video::refreshRateHint(double refreshRate) -> void {
lock_guard<recursive_mutex> lock(mutex);
instance->refreshRateHint(refreshRate);
}
//
auto Video::focused() -> bool {
+2
View File
@@ -29,6 +29,7 @@ struct VideoDriver {
virtual auto setFlush(bool flush) -> bool { return true; }
virtual auto setFormat(string format) -> bool { return true; }
virtual auto setShader(string shader) -> bool { return true; }
virtual auto refreshRateHint(double refreshRate) -> void {}
virtual auto focused() -> bool { return true; }
virtual auto clear() -> void {}
@@ -114,6 +115,7 @@ struct Video {
auto setFlush(bool flush) -> bool;
auto setFormat(string format) -> bool;
auto setShader(string shader) -> bool;
auto refreshRateHint(double refreshRate) -> void;
auto focused() -> bool;
auto clear() -> void;