improved refresh rate code

This commit is contained in:
thrust26 2020-05-18 21:13:18 +02:00
parent 77f24947f0
commit 987bfaab1d
7 changed files with 115 additions and 80 deletions

View File

@ -218,6 +218,10 @@ bool FrameBufferSDL2::setVideoMode(const string& title, const VideoMode& mode)
if(SDL_WasInit(SDL_INIT_VIDEO) == 0) if(SDL_WasInit(SDL_INIT_VIDEO) == 0)
return false; return false;
const bool fullScreen = mode.fsIndex != -1;
const bool shouldAdapt = fullScreen && myOSystem.settings().getBool("tia.fs_refresh")
&& refreshRate() != gameRefreshRate();
// TODO: On multiple displays, switching from centered mode, does not respect // TODO: On multiple displays, switching from centered mode, does not respect
// current window's display (which many not be centered anymore) // current window's display (which many not be centered anymore)
@ -261,8 +265,11 @@ bool FrameBufferSDL2::setVideoMode(const string& title, const VideoMode& mode)
posX = BSPF::clamp(posX, x0 - Int32(mode.screen.w) + 50, x1 - 50); posX = BSPF::clamp(posX, x0 - Int32(mode.screen.w) + 50, x1 - 50);
posY = BSPF::clamp(posY, y0 + 50, y1 - 50); posY = BSPF::clamp(posY, y0 + 50, y1 - 50);
} }
uInt32 flags = mode.fsIndex != -1 ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0;
flags |= SDL_WINDOW_ALLOW_HIGHDPI; SDL_DisplayMode adaptedSdlMode;
const bool adaptRefresh = shouldAdapt && adaptRefreshRate(displayIndex, adaptedSdlMode);
const uInt32 flags = SDL_WINDOW_ALLOW_HIGHDPI
| (fullScreen ? adaptRefresh ? SDL_WINDOW_FULLSCREEN : SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
// macOS seems to have issues with destroying the window, and wants to // macOS seems to have issues with destroying the window, and wants to
// keep the same handle // keep the same handle
@ -278,12 +285,14 @@ bool FrameBufferSDL2::setVideoMode(const string& title, const VideoMode& mode)
int w, h; int w, h;
SDL_GetWindowSize(myWindow, &w, &h); SDL_GetWindowSize(myWindow, &w, &h);
if(d != displayIndex || uInt32(w) != mode.screen.w || uInt32(h) != mode.screen.h) if(d != displayIndex || uInt32(w) != mode.screen.w || uInt32(h) != mode.screen.h
|| shouldAdapt)
{ {
SDL_DestroyWindow(myWindow); SDL_DestroyWindow(myWindow);
myWindow = nullptr; myWindow = nullptr;
} }
} }
if(myWindow) if(myWindow)
{ {
// Even though window size stayed the same, the title may have changed // Even though window size stayed the same, the title may have changed
@ -312,8 +321,24 @@ bool FrameBufferSDL2::setVideoMode(const string& title, const VideoMode& mode)
Logger::error(msg); Logger::error(msg);
return false; return false;
} }
setWindowIcon(); setWindowIcon();
} }
if(adaptRefresh)
{
// Switch to mode for adapted refresh rate
if(SDL_SetWindowDisplayMode(myWindow, &adaptedSdlMode) != 0)
{
Logger::error("Display refresh rate change failed");
}
else
{
ostringstream msg;
msg << "Display refresh rate changed to " << adaptedSdlMode.refresh_rate << "Hz";
Logger::info(msg.str());
}
}
uInt32 renderFlags = SDL_RENDERER_ACCELERATED; uInt32 renderFlags = SDL_RENDERER_ACCELERATED;
if(myOSystem.settings().getBool("vsync") if(myOSystem.settings().getBool("vsync")
@ -340,70 +365,35 @@ bool FrameBufferSDL2::setVideoMode(const string& title, const VideoMode& mode)
if(SDL_GetRendererInfo(myRenderer, &renderinfo) >= 0) if(SDL_GetRendererInfo(myRenderer, &renderinfo) >= 0)
myOSystem.settings().setValue("video", renderinfo.name); myOSystem.settings().setValue("video", renderinfo.name);
adaptRefreshRate();
return true; return true;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FrameBufferSDL2::adaptRefreshRate() bool FrameBufferSDL2::adaptRefreshRate(Int32 displayIndex, SDL_DisplayMode& closestSdlMode)
{ {
const bool adapt = myOSystem.settings().getBool("tia.refresh"); SDL_DisplayMode sdlMode;
// adapt only in emulation (and debugger?) and fullscreen mode if(SDL_GetCurrentDisplayMode(displayIndex, &sdlMode) != 0)
// TODO: adapt while creating new window
if(adapt && fullScreen()
&& (myBufferType == BufferType::Emulator/* || myBufferType == BufferType::Debugger*/))
{ {
SDL_DisplayMode sdlMode; Logger::error("Display mode could not be retrieved");
return false;
}
if(SDL_GetWindowDisplayMode(myWindow, &sdlMode) != 0) const int currentRefreshRate = sdlMode.refresh_rate;
sdlMode.refresh_rate = gameRefreshRate();
if(currentRefreshRate != sdlMode.refresh_rate)
{
// Note: Modes are scanned with size being first priority,
// therefore the size will never change.
if(SDL_GetClosestDisplayMode(displayIndex, &sdlMode, &closestSdlMode) == NULL)
{ {
Logger::error("Display mode could not be retrieved"); Logger::error("Closest display mode could not be retrieved");
return false; return false;
} }
// Only change if the display supports a better refresh rate
const int currentRefreshRate = sdlMode.refresh_rate; return currentRefreshRate != closestSdlMode.refresh_rate;
const string format = myOSystem.console().getFormatString();
const bool isNtsc = format == "NTSC" || format == "PAL60" || format == "SECAM60";
sdlMode.refresh_rate = isNtsc ? 60 : 50; // TODO: check for multiples e.g. 120/100 too
if(currentRefreshRate != sdlMode.refresh_rate)
{
const int display = SDL_GetWindowDisplayIndex(myWindow);
SDL_DisplayMode closestSdlMode;
if(SDL_GetClosestDisplayMode(display, &sdlMode, &closestSdlMode) == NULL)
{
Logger::error("Closest display mode could not be retrieved");
return false;
}
// Note: Modes are scanned with size being first priority,
// therefore the size will never change.
// Only change if the display supports a better refresh rate
if(currentRefreshRate != closestSdlMode.refresh_rate)
{
// Switch to new mode
if(SDL_SetWindowDisplayMode(myWindow, &closestSdlMode) != 0)
{
Logger::error("Display refresh rate change failed");
return false;
}
// Any change only works in real fullscreen mode!
if(SDL_SetWindowFullscreen(myWindow, SDL_WINDOW_FULLSCREEN) != 0)
{
Logger::error("Display fullscreen change failed");
return false;
}
ostringstream msg;
msg << "Display refresh rate changed from " << currentRefreshRate << "Hz to "
<< closestSdlMode.refresh_rate << "Hz";
Logger::info(msg.str());
return true;
}
}
} }
return false; return false;
} }
@ -469,6 +459,34 @@ bool FrameBufferSDL2::fullScreen() const
#endif #endif
} }
int FrameBufferSDL2::refreshRate() const
{
ASSERT_MAIN_THREAD;
const uInt32 displayIndex = SDL_GetWindowDisplayIndex(myWindow);
SDL_DisplayMode sdlMode;
if(SDL_GetCurrentDisplayMode(displayIndex, &sdlMode) == 0)
return sdlMode.refresh_rate;
if (myWindow != NULL)
Logger::error("Could not retrieve current display mode");
return 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int FrameBufferSDL2::gameRefreshRate() const
{
if(myOSystem.hasConsole())
{
const string format = myOSystem.console().getFormatString();
const bool isNtsc = format == "NTSC" || format == "PAL60" || format == "SECAM60";
return isNtsc ? 60 : 50; // TODO: check for multiples e.g. 120/100 too
}
return 60;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferSDL2::renderToScreen() void FrameBufferSDL2::renderToScreen()
{ {

View File

@ -183,11 +183,11 @@ class FrameBufferSDL2 : public FrameBuffer
/** /**
Adapt display refresh rate to game refresh rate in (real) fullscreen mode Checks if the display refresh rate should be adapted to game refresh rate in (real) fullscreen mode
@return True if the refresh rate was changed @return True if the refresh rate should be changed
*/ */
bool adaptRefreshRate(); bool adaptRefreshRate(Int32 displayIndex, SDL_DisplayMode& closestSdlMode);
/** /**
This method is called to create a surface with the given attributes. This method is called to create a surface with the given attributes.
@ -241,6 +241,16 @@ class FrameBufferSDL2 : public FrameBuffer
*/ */
void determineDimensions(); void determineDimensions();
/**
Retrieve the current display's refresh rate, or 0 if no window
*/
int refreshRate() const override;
/**
Retrieve the current game's refresh rate, or 60 if no game
*/
int gameRefreshRate() const;
private: private:
// The SDL video buffer // The SDL video buffer
SDL_Window* myWindow{nullptr}; SDL_Window* myWindow{nullptr};

View File

@ -993,7 +993,15 @@ void FrameBuffer::toggleFullscreen(bool toggle)
setFullscreen(isFullscreen); setFullscreen(isFullscreen);
showMessage(string("Fullscreen ") + (isFullscreen ? "enabled" : "disabled")); ostringstream msg;
msg << "Fullscreen ";
if(isFullscreen)
msg << "enabled (" << refreshRate() << " Hz)";
else
msg << "disabled";
showMessage(msg.str());
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -518,6 +518,11 @@ class FrameBuffer
*/ */
virtual string about() const = 0; virtual string about() const = 0;
/**
Retrieve the current display's refresh rate
*/
virtual int refreshRate() const { return 0; }
protected: protected:
// The parent system for the framebuffer // The parent system for the framebuffer
OSystem& myOSystem; OSystem& myOSystem;

View File

@ -52,9 +52,9 @@ Settings::Settings()
setPermanent("tia.zoom", "3"); setPermanent("tia.zoom", "3");
setPermanent("fullscreen", "false"); setPermanent("fullscreen", "false");
setPermanent("tia.fs_stretch", "false"); setPermanent("tia.fs_stretch", "false");
setPermanent("tia.fs_refresh", "false");
setPermanent("tia.fs_overscan", "0"); setPermanent("tia.fs_overscan", "0");
setPermanent("tia.vsizeadjust", 0); setPermanent("tia.vsizeadjust", 0);
setPermanent("tia.refresh", "false");
setPermanent("tia.dbgcolors", "roygpb"); setPermanent("tia.dbgcolors", "roygpb");
// Palette options // Palette options
setPermanent("palette", PaletteHandler::SETTING_STANDARD); setPermanent("palette", PaletteHandler::SETTING_STANDARD);
@ -442,6 +442,7 @@ void Settings::usage() const
<< " -tia.inter <1|0> Enable interpolated (smooth) scaling for TIA\n" << " -tia.inter <1|0> Enable interpolated (smooth) scaling for TIA\n"
<< " image\n" << " image\n"
<< " -tia.fs_stretch <1|0> Stretch TIA image to fill fullscreen mode\n" << " -tia.fs_stretch <1|0> Stretch TIA image to fill fullscreen mode\n"
<< " -tia.fs_refresh <1|0> Try to adapt display refresh rate to game\n"
<< " -tia.fs_overscan <0-10> Add overscan to TIA image in fullscreen mode\n" << " -tia.fs_overscan <0-10> Add overscan to TIA image in fullscreen mode\n"
<< " -tia.dbgcolors <string> Debug colors to use for each object (see manual\n" << " -tia.dbgcolors <string> Debug colors to use for each object (see manual\n"
<< " for description)\n" << " for description)\n"

View File

@ -149,29 +149,23 @@ void VideoAudioDialog::addDisplayTab()
wid.push_back(myFullscreen); wid.push_back(myFullscreen);
ypos += lineHeight + VGAP; ypos += lineHeight + VGAP;
/*pwidth = font.getStringWidth("0: 3840x2860@120Hz");
myFullScreenMode = new PopUpWidget(myTab, font, xpos + INDENT + 2, ypos, pwidth, lineHeight,
instance().frameBuffer().supportedScreenModes(), "Mode ");
wid.push_back(myFullScreenMode);
ypos += lineHeight + VGAP;*/
// FS stretch // FS stretch
myUseStretch = new CheckboxWidget(myTab, _font, xpos + INDENT, ypos + 1, "Stretch"); myUseStretch = new CheckboxWidget(myTab, _font, xpos + INDENT, ypos + 1, "Stretch");
wid.push_back(myUseStretch); wid.push_back(myUseStretch);
// Adapt refresh rate
ypos += lineHeight + VGAP; ypos += lineHeight + VGAP;
myRefreshAdapt = new CheckboxWidget(myTab, _font, xpos + INDENT, ypos + 1, "Adapt display refresh rate");
wid.push_back(myRefreshAdapt);
// FS overscan // FS overscan
ypos += lineHeight + VGAP;
myTVOverscan = new SliderWidget(myTab, _font, xpos + INDENT, ypos - 1, swidth, lineHeight, myTVOverscan = new SliderWidget(myTab, _font, xpos + INDENT, ypos - 1, swidth, lineHeight,
"Overscan", lwidth - INDENT, kOverscanChanged, fontWidth * 3, "%"); "Overscan", lwidth - INDENT, kOverscanChanged, fontWidth * 3, "%");
myTVOverscan->setMinValue(0); myTVOverscan->setMaxValue(10); myTVOverscan->setMinValue(0); myTVOverscan->setMaxValue(10);
myTVOverscan->setTickmarkIntervals(2); myTVOverscan->setTickmarkIntervals(2);
wid.push_back(myTVOverscan); wid.push_back(myTVOverscan);
// Adapt refresh rate
ypos += lineHeight + VGAP;
myRefreshAdjust = new CheckboxWidget(myTab, _font, xpos + INDENT, ypos + 1, "Adapt refresh rate");
wid.push_back(myRefreshAdjust);
// Vertical size // Vertical size
ypos += lineHeight + VGAP; ypos += lineHeight + VGAP;
myVSizeAdjust = myVSizeAdjust =
@ -490,10 +484,10 @@ void VideoAudioDialog::loadConfig()
myFullScreenMode->setSelected(mode);*/ myFullScreenMode->setSelected(mode);*/
// Fullscreen stretch setting // Fullscreen stretch setting
myUseStretch->setState(instance().settings().getBool("tia.fs_stretch")); myUseStretch->setState(instance().settings().getBool("tia.fs_stretch"));
// Adapt refresh rate
myRefreshAdapt->setState(instance().settings().getBool("tia.fs_refresh"));
// Fullscreen overscan setting // Fullscreen overscan setting
myTVOverscan->setValue(instance().settings().getInt("tia.fs_overscan")); myTVOverscan->setValue(instance().settings().getInt("tia.fs_overscan"));
// Adapt refresh rate
myRefreshAdjust->setState(instance().settings().getBool("tia.refresh"));
handleFullScreenChange(); handleFullScreenChange();
// Aspect ratio setting (NTSC and PAL) // Aspect ratio setting (NTSC and PAL)
@ -603,10 +597,10 @@ void VideoAudioDialog::saveConfig()
instance().settings().setValue("fullscreen", myFullscreen->getState()); instance().settings().setValue("fullscreen", myFullscreen->getState());
// Fullscreen stretch setting // Fullscreen stretch setting
instance().settings().setValue("tia.fs_stretch", myUseStretch->getState()); instance().settings().setValue("tia.fs_stretch", myUseStretch->getState());
// Adapt refresh rate
instance().settings().setValue("tia.fs_refresh", myRefreshAdapt->getState());
// Fullscreen overscan // Fullscreen overscan
instance().settings().setValue("tia.fs_overscan", myTVOverscan->getValueLabel()); instance().settings().setValue("tia.fs_overscan", myTVOverscan->getValueLabel());
// Adapt refresh rate
instance().settings().setValue("tia.refresh", myRefreshAdjust->getState());
// TIA zoom levels // TIA zoom levels
instance().settings().setValue("tia.zoom", myTIAZoom->getValue() / 100.0); instance().settings().setValue("tia.zoom", myTIAZoom->getValue() / 100.0);
@ -718,8 +712,8 @@ void VideoAudioDialog::setDefaults()
myFullscreen->setState(false); myFullscreen->setState(false);
//myFullScreenMode->setSelectedIndex(0); //myFullScreenMode->setSelectedIndex(0);
myUseStretch->setState(false); myUseStretch->setState(false);
myRefreshAdapt->setState(false);
myTVOverscan->setValue(0); myTVOverscan->setValue(0);
myRefreshAdjust->setState(false);
myTIAZoom->setValue(300); myTIAZoom->setValue(300);
myVSizeAdjust->setValue(0); myVSizeAdjust->setValue(0);
@ -844,8 +838,8 @@ void VideoAudioDialog::handleFullScreenChange()
{ {
bool enable = myFullscreen->getState(); bool enable = myFullscreen->getState();
myUseStretch->setEnabled(enable); myUseStretch->setEnabled(enable);
myRefreshAdapt->setEnabled(enable);
myTVOverscan->setEnabled(enable); myTVOverscan->setEnabled(enable);
myRefreshAdjust->setEnabled(enable);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -71,10 +71,9 @@ class VideoAudioDialog : public Dialog
PopUpWidget* myRenderer{nullptr}; PopUpWidget* myRenderer{nullptr};
CheckboxWidget* myTIAInterpolate{nullptr}; CheckboxWidget* myTIAInterpolate{nullptr};
CheckboxWidget* myFullscreen{nullptr}; CheckboxWidget* myFullscreen{nullptr};
//PopUpWidget* myFullScreenMode;
CheckboxWidget* myUseStretch{nullptr}; CheckboxWidget* myUseStretch{nullptr};
SliderWidget* myTVOverscan{nullptr}; SliderWidget* myTVOverscan{nullptr};
CheckboxWidget* myRefreshAdjust{nullptr}; CheckboxWidget* myRefreshAdapt{nullptr};
SliderWidget* myTIAZoom{nullptr}; SliderWidget* myTIAZoom{nullptr};
SliderWidget* myVSizeAdjust{nullptr}; SliderWidget* myVSizeAdjust{nullptr};