2 * Copyright (C) 2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
24 #include "DisplaySettings.h"
25 #include "dialogs/GUIDialogYesNo.h"
26 #include "guilib/GraphicContext.h"
27 #include "guilib/gui3d.h"
28 #include "guilib/LocalizeStrings.h"
29 #include "guilib/StereoscopicsManager.h"
30 #include "settings/AdvancedSettings.h"
31 #include "settings/lib/Setting.h"
32 #include "settings/Settings.h"
33 #include "threads/SingleLock.h"
34 #include "utils/log.h"
35 #include "utils/StringUtils.h"
36 #include "utils/XMLUtils.h"
37 #include "windowing/WindowingFactory.h"
39 // 0.1 second increments
40 #define MAX_REFRESH_CHANGE_DELAY 200
44 static RESOLUTION_INFO EmptyResolution;
45 static RESOLUTION_INFO EmptyModifiableResolution;
47 float square_error(float x, float y)
49 float yonx = (x > 0) ? y / x : 0;
50 float xony = (y > 0) ? x / y : 0;
51 return std::max(yonx, xony);
54 static CStdString ModeFlagsToString(unsigned int flags, bool identifier)
57 if(flags & D3DPRESENTFLAG_INTERLACED)
65 if(flags & D3DPRESENTFLAG_MODE3DSBS)
67 else if(flags & D3DPRESENTFLAG_MODE3DTB)
74 CDisplaySettings::CDisplaySettings()
76 m_resolutions.insert(m_resolutions.begin(), RES_CUSTOM, RESOLUTION_INFO());
80 m_verticalShift = 0.0f;
81 m_nonLinearStretched = false;
82 m_resolutionChangeAborted = false;
85 CDisplaySettings::~CDisplaySettings()
88 CDisplaySettings& CDisplaySettings::Get()
90 static CDisplaySettings sDisplaySettings;
91 return sDisplaySettings;
94 bool CDisplaySettings::Load(const TiXmlNode *settings)
96 CSingleLock lock(m_critical);
97 m_calibrations.clear();
102 const TiXmlElement *pElement = settings->FirstChildElement("resolutions");
105 CLog::Log(LOGERROR, "CDisplaySettings: settings file doesn't contain <resolutions>");
109 const TiXmlElement *pResolution = pElement->FirstChildElement("resolution");
112 // get the data for this calibration
115 XMLUtils::GetString(pResolution, "description", cal.strMode);
116 XMLUtils::GetInt(pResolution, "subtitles", cal.iSubtitles);
117 XMLUtils::GetFloat(pResolution, "pixelratio", cal.fPixelRatio);
119 XMLUtils::GetFloat(pResolution, "refreshrate", cal.fRefreshRate);
120 XMLUtils::GetString(pResolution, "output", cal.strOutput);
121 XMLUtils::GetString(pResolution, "xrandrid", cal.strId);
124 const TiXmlElement *pOverscan = pResolution->FirstChildElement("overscan");
127 XMLUtils::GetInt(pOverscan, "left", cal.Overscan.left);
128 XMLUtils::GetInt(pOverscan, "top", cal.Overscan.top);
129 XMLUtils::GetInt(pOverscan, "right", cal.Overscan.right);
130 XMLUtils::GetInt(pOverscan, "bottom", cal.Overscan.bottom);
133 // mark calibration as not updated
134 // we must not delete those, resolution just might not be available
135 cal.iWidth = cal.iHeight = 0;
137 // store calibration, avoid adding duplicates
139 for (ResolutionInfos::const_iterator it = m_calibrations.begin(); it != m_calibrations.end(); ++it)
141 if (it->strMode.Equals(cal.strMode))
148 m_calibrations.push_back(cal);
151 pResolution = pResolution->NextSiblingElement("resolution");
158 bool CDisplaySettings::Save(TiXmlNode *settings) const
160 if (settings == NULL)
163 CSingleLock lock(m_critical);
164 TiXmlElement xmlRootElement("resolutions");
165 TiXmlNode *pRoot = settings->InsertEndChild(xmlRootElement);
170 for (ResolutionInfos::const_iterator it = m_calibrations.begin(); it != m_calibrations.end(); ++it)
172 // Write the resolution tag
173 TiXmlElement resElement("resolution");
174 TiXmlNode *pNode = pRoot->InsertEndChild(resElement);
178 // Now write each of the pieces of information we need...
179 XMLUtils::SetString(pNode, "description", it->strMode);
180 XMLUtils::SetInt(pNode, "subtitles", it->iSubtitles);
181 XMLUtils::SetFloat(pNode, "pixelratio", it->fPixelRatio);
183 XMLUtils::SetFloat(pNode, "refreshrate", it->fRefreshRate);
184 XMLUtils::SetString(pNode, "output", it->strOutput);
185 XMLUtils::SetString(pNode, "xrandrid", it->strId);
188 // create the overscan child
189 TiXmlElement overscanElement("overscan");
190 TiXmlNode *pOverscanNode = pNode->InsertEndChild(overscanElement);
191 if (pOverscanNode == NULL)
194 XMLUtils::SetInt(pOverscanNode, "left", it->Overscan.left);
195 XMLUtils::SetInt(pOverscanNode, "top", it->Overscan.top);
196 XMLUtils::SetInt(pOverscanNode, "right", it->Overscan.right);
197 XMLUtils::SetInt(pOverscanNode, "bottom", it->Overscan.bottom);
203 void CDisplaySettings::Clear()
205 CSingleLock lock(m_critical);
206 m_calibrations.clear();
207 m_resolutions.clear();
211 m_verticalShift = 0.0f;
212 m_nonLinearStretched = false;
215 bool CDisplaySettings::OnSettingChanging(const CSetting *setting)
220 const std::string &settingId = setting->GetId();
221 if (settingId == "videoscreen.resolution" ||
222 settingId == "videoscreen.screen")
224 RESOLUTION newRes = RES_DESKTOP;
225 if (settingId == "videoscreen.resolution")
226 newRes = (RESOLUTION)((CSettingInt*)setting)->GetValue();
227 else if (settingId == "videoscreen.screen")
228 newRes = GetResolutionForScreen();
230 string screenmode = GetStringFromResolution(newRes);
231 CSettings::Get().SetString("videoscreen.screenmode", screenmode);
233 if (settingId == "videoscreen.screenmode")
235 RESOLUTION oldRes = GetCurrentResolution();
236 RESOLUTION newRes = GetResolutionFromString(((CSettingString*)setting)->GetValue());
238 SetCurrentResolution(newRes, false);
239 g_graphicsContext.SetVideoResolution(newRes);
241 // check if the old or the new resolution was/is windowed
242 // in which case we don't show any prompt to the user
243 if (oldRes != RES_WINDOW && newRes != RES_WINDOW && oldRes != newRes)
245 if (!m_resolutionChangeAborted)
247 bool cancelled = false;
248 if (!CGUIDialogYesNo::ShowAndGetInput(13110, 13111, 20022, 20022, -1, -1, cancelled, 10000))
250 m_resolutionChangeAborted = true;
255 m_resolutionChangeAborted = false;
262 bool CDisplaySettings::OnSettingUpdate(CSetting* &setting, const char *oldSettingId, const TiXmlNode *oldSettingNode)
267 const std::string &settingId = setting->GetId();
268 if (settingId == "videoscreen.screenmode")
270 CSettingString *screenmodeSetting = (CSettingString*)setting;
271 std::string screenmode = screenmodeSetting->GetValue();
272 // in Eden there was no character ("i" or "p") indicating interlaced/progressive
273 // at the end so we just add a "p" and assume progressive
274 // no 3d mode existed before, so just assume std modes
275 if (screenmode.size() == 20)
276 return screenmodeSetting->SetValue(screenmode + "pstd");
277 if (screenmode.size() == 21)
278 return screenmodeSetting->SetValue(screenmode + "std");
280 else if (settingId == "videoscreen.vsync")
282 // This ifdef is intended to catch everything except Linux and FreeBSD
283 #if !defined(TARGET_LINUX) || defined(TARGET_DARWIN) || defined(TARGET_ANDROID) || defined(TARGET_RASPBERRY_PI)
284 // in the Gotham alphas through beta3 the default value for darwin and android was set incorrectly.
285 CSettingInt *vsyncSetting = (CSettingInt*)setting;
286 if (vsyncSetting->GetValue() == VSYNC_DRIVER)
287 return vsyncSetting->SetValue(VSYNC_ALWAYS);
294 void CDisplaySettings::SetCurrentResolution(RESOLUTION resolution, bool save /* = false */)
298 string mode = GetStringFromResolution(resolution);
299 CSettings::Get().SetString("videoscreen.screenmode", mode.c_str());
302 m_currentResolution = resolution;
307 RESOLUTION CDisplaySettings::GetDisplayResolution() const
309 return GetResolutionFromString(CSettings::Get().GetString("videoscreen.screenmode"));
312 const RESOLUTION_INFO& CDisplaySettings::GetResolutionInfo(size_t index) const
314 CSingleLock lock(m_critical);
315 if (index >= m_resolutions.size())
316 return EmptyResolution;
318 return m_resolutions[index];
321 const RESOLUTION_INFO& CDisplaySettings::GetResolutionInfo(RESOLUTION resolution) const
323 if (resolution <= RES_INVALID)
324 return EmptyResolution;
326 return GetResolutionInfo((size_t)resolution);
329 RESOLUTION_INFO& CDisplaySettings::GetResolutionInfo(size_t index)
331 CSingleLock lock(m_critical);
332 if (index >= m_resolutions.size())
334 EmptyModifiableResolution = RESOLUTION_INFO();
335 return EmptyModifiableResolution;
338 return m_resolutions[index];
341 RESOLUTION_INFO& CDisplaySettings::GetResolutionInfo(RESOLUTION resolution)
343 if (resolution <= RES_INVALID)
345 EmptyModifiableResolution = RESOLUTION_INFO();
346 return EmptyModifiableResolution;
349 return GetResolutionInfo((size_t)resolution);
352 void CDisplaySettings::AddResolutionInfo(const RESOLUTION_INFO &resolution)
354 CSingleLock lock(m_critical);
355 RESOLUTION_INFO res(resolution);
357 if((res.dwFlags & D3DPRESENTFLAG_MODE3DTB) == 0)
359 /* add corrections for some special case modes frame packing modes */
361 if(res.iScreenWidth == 1920
362 && res.iScreenHeight == 2205)
365 res.dwFlags |= D3DPRESENTFLAG_MODE3DTB;
368 if(res.iScreenWidth == 1280
369 && res.iScreenHeight == 1470)
372 res.dwFlags |= D3DPRESENTFLAG_MODE3DTB;
375 m_resolutions.push_back(res);
378 void CDisplaySettings::ApplyCalibrations()
380 CSingleLock lock(m_critical);
381 // apply all calibrations to the resolutions
382 for (ResolutionInfos::const_iterator itCal = m_calibrations.begin(); itCal != m_calibrations.end(); ++itCal)
385 for (size_t res = 0; res < m_resolutions.size(); ++res)
387 if (res == RES_WINDOW)
389 if (itCal->strMode.Equals(m_resolutions[res].strMode))
392 m_resolutions[res].Overscan.left = itCal->Overscan.left;
393 if (m_resolutions[res].Overscan.left < -m_resolutions[res].iWidth/4)
394 m_resolutions[res].Overscan.left = -m_resolutions[res].iWidth/4;
395 if (m_resolutions[res].Overscan.left > m_resolutions[res].iWidth/4)
396 m_resolutions[res].Overscan.left = m_resolutions[res].iWidth/4;
398 m_resolutions[res].Overscan.top = itCal->Overscan.top;
399 if (m_resolutions[res].Overscan.top < -m_resolutions[res].iHeight/4)
400 m_resolutions[res].Overscan.top = -m_resolutions[res].iHeight/4;
401 if (m_resolutions[res].Overscan.top > m_resolutions[res].iHeight/4)
402 m_resolutions[res].Overscan.top = m_resolutions[res].iHeight/4;
404 m_resolutions[res].Overscan.right = itCal->Overscan.right;
405 if (m_resolutions[res].Overscan.right < m_resolutions[res].iWidth / 2)
406 m_resolutions[res].Overscan.right = m_resolutions[res].iWidth / 2;
407 if (m_resolutions[res].Overscan.right > m_resolutions[res].iWidth * 3/2)
408 m_resolutions[res].Overscan.right = m_resolutions[res].iWidth *3/2;
410 m_resolutions[res].Overscan.bottom = itCal->Overscan.bottom;
411 if (m_resolutions[res].Overscan.bottom < m_resolutions[res].iHeight / 2)
412 m_resolutions[res].Overscan.bottom = m_resolutions[res].iHeight / 2;
413 if (m_resolutions[res].Overscan.bottom > m_resolutions[res].iHeight * 3/2)
414 m_resolutions[res].Overscan.bottom = m_resolutions[res].iHeight * 3/2;
416 m_resolutions[res].iSubtitles = itCal->iSubtitles;
417 if (m_resolutions[res].iSubtitles < m_resolutions[res].iHeight / 2)
418 m_resolutions[res].iSubtitles = m_resolutions[res].iHeight / 2;
419 if (m_resolutions[res].iSubtitles > m_resolutions[res].iHeight* 5/4)
420 m_resolutions[res].iSubtitles = m_resolutions[res].iHeight* 5/4;
422 m_resolutions[res].fPixelRatio = itCal->fPixelRatio;
423 if (m_resolutions[res].fPixelRatio < 0.5f)
424 m_resolutions[res].fPixelRatio = 0.5f;
425 if (m_resolutions[res].fPixelRatio > 2.0f)
426 m_resolutions[res].fPixelRatio = 2.0f;
433 void CDisplaySettings::UpdateCalibrations()
435 CSingleLock lock(m_critical);
436 for (size_t res = RES_DESKTOP; res < m_resolutions.size(); ++res)
440 for (ResolutionInfos::iterator itCal = m_calibrations.begin(); itCal != m_calibrations.end(); ++itCal)
442 if (itCal->strMode.Equals(m_resolutions[res].strMode))
444 // TODO: erase calibrations with default values
445 *itCal = m_resolutions[res];
452 m_calibrations.push_back(m_resolutions[res]);
456 DisplayMode CDisplaySettings::GetCurrentDisplayMode() const
458 if (GetCurrentResolution() == RES_WINDOW)
461 return GetCurrentResolutionInfo().iScreen;
464 RESOLUTION CDisplaySettings::FindBestMatchingResolution(const std::map<RESOLUTION, RESOLUTION_INFO> &resolutionInfos, int screen, int width, int height, float refreshrate, unsigned flags)
466 // find the closest match to these in our res vector. If we have the screen, we score the res
467 RESOLUTION bestRes = RES_DESKTOP;
468 float bestScore = FLT_MAX;
469 flags &= D3DPRESENTFLAG_MODEMASK;
471 for (std::map<RESOLUTION, RESOLUTION_INFO>::const_iterator it = resolutionInfos.begin(); it != resolutionInfos.end(); ++it)
473 const RESOLUTION_INFO &info = it->second;
475 if ( info.iScreen != screen
476 || (info.dwFlags & D3DPRESENTFLAG_MODEMASK) != flags)
479 float score = 10 * (square_error((float)info.iScreenWidth, (float)width) +
480 square_error((float)info.iScreenHeight, (float)height) +
481 square_error(info.fRefreshRate, refreshrate));
482 if (score < bestScore)
492 RESOLUTION CDisplaySettings::GetResolutionFromString(const std::string &strResolution)
494 if (strResolution == "DESKTOP")
496 else if (strResolution == "WINDOW")
498 else if (strResolution.size() >= 21)
500 // format: SWWWWWHHHHHRRR.RRRRRP333, where S = screen, W = width, H = height, R = refresh, P = interlace, 3 = stereo mode
501 int screen = strtol(StringUtils::Mid(strResolution, 0,1).c_str(), NULL, 10);
502 int width = strtol(StringUtils::Mid(strResolution, 1,5).c_str(), NULL, 10);
503 int height = strtol(StringUtils::Mid(strResolution, 6,5).c_str(), NULL, 10);
504 float refresh = (float)strtod(StringUtils::Mid(strResolution, 11,9).c_str(), NULL);
507 // look for 'i' and treat everything else as progressive,
508 if(StringUtils::Mid(strResolution, 20,1) == "i")
509 flags |= D3DPRESENTFLAG_INTERLACED;
511 if(StringUtils::Mid(strResolution, 21,3) == "sbs")
512 flags |= D3DPRESENTFLAG_MODE3DSBS;
513 else if(StringUtils::Mid(strResolution, 21,3) == "tab")
514 flags |= D3DPRESENTFLAG_MODE3DTB;
516 std::map<RESOLUTION, RESOLUTION_INFO> resolutionInfos;
517 for (size_t resolution = RES_DESKTOP; resolution < CDisplaySettings::Get().ResolutionInfoSize(); resolution++)
518 resolutionInfos.insert(make_pair((RESOLUTION)resolution, CDisplaySettings::Get().GetResolutionInfo(resolution)));
520 return FindBestMatchingResolution(resolutionInfos, screen, width, height, refresh, flags);
526 std::string CDisplaySettings::GetStringFromResolution(RESOLUTION resolution, float refreshrate /* = 0.0f */)
528 if (resolution == RES_WINDOW)
531 if (resolution >= RES_DESKTOP && resolution < (RESOLUTION)CDisplaySettings::Get().ResolutionInfoSize())
533 const RESOLUTION_INFO &info = CDisplaySettings::Get().GetResolutionInfo(resolution);
534 // also handle RES_DESKTOP resolutions with non-default refresh rates
535 if (resolution != RES_DESKTOP || (refreshrate > 0.0f && refreshrate != info.fRefreshRate))
537 return StringUtils::Format("%1i%05i%05i%09.5f%s", info.iScreen,
538 info.iScreenWidth, info.iScreenHeight,
539 refreshrate > 0.0f ? refreshrate : info.fRefreshRate, ModeFlagsToString(info.dwFlags, true).c_str());
546 RESOLUTION CDisplaySettings::GetResolutionForScreen()
548 DisplayMode mode = CSettings::Get().GetInt("videoscreen.screen");
549 if (mode == DM_WINDOWED)
552 for (int idx=0; idx < g_Windowing.GetNumScreens(); idx++)
554 if (CDisplaySettings::Get().GetResolutionInfo(RES_DESKTOP + idx).iScreen == mode)
555 return (RESOLUTION)(RES_DESKTOP + idx);
561 void CDisplaySettings::SettingOptionsRefreshChangeDelaysFiller(const CSetting *setting, std::vector< std::pair<std::string, int> > &list, int ¤t)
563 list.push_back(make_pair(g_localizeStrings.Get(13551), 0));
565 for (int i = 1; i <= MAX_REFRESH_CHANGE_DELAY; i++)
566 list.push_back(make_pair(StringUtils::Format(g_localizeStrings.Get(13553).c_str(), (double)i / 10.0), i));
569 void CDisplaySettings::SettingOptionsRefreshRatesFiller(const CSetting *setting, std::vector< std::pair<std::string, std::string> > &list, std::string ¤t)
571 // get the proper resolution
572 RESOLUTION res = CDisplaySettings::Get().GetDisplayResolution();
573 if (res < RES_WINDOW)
576 // only add "Windowed" if in windowed mode
577 if (res == RES_WINDOW)
580 list.push_back(make_pair(g_localizeStrings.Get(242), current));
584 RESOLUTION_INFO resInfo = CDisplaySettings::Get().GetResolutionInfo(res);
585 // The only meaningful parts of res here are iScreen, iScreenWidth, iScreenHeight
586 vector<REFRESHRATE> refreshrates = g_Windowing.RefreshRates(resInfo.iScreen, resInfo.iScreenWidth, resInfo.iScreenHeight, resInfo.dwFlags);
589 for (vector<REFRESHRATE>::const_iterator refreshrate = refreshrates.begin(); refreshrate != refreshrates.end(); ++refreshrate)
591 std::string screenmode = GetStringFromResolution((RESOLUTION)refreshrate->ResInfo_Index, refreshrate->RefreshRate);
592 if (!match && StringUtils::EqualsNoCase(((CSettingString*)setting)->GetValue(), screenmode))
594 list.push_back(make_pair(StringUtils::Format("%.02f", refreshrate->RefreshRate), screenmode));
598 current = GetStringFromResolution(res, g_Windowing.DefaultRefreshRate(resInfo.iScreen, refreshrates).RefreshRate);
601 void CDisplaySettings::SettingOptionsResolutionsFiller(const CSetting *setting, std::vector< std::pair<std::string, int> > &list, int ¤t)
603 RESOLUTION res = CDisplaySettings::Get().GetDisplayResolution();
604 RESOLUTION_INFO info = CDisplaySettings::Get().GetResolutionInfo(res);
605 if (res == RES_WINDOW)
608 list.push_back(make_pair(g_localizeStrings.Get(242), res));
612 std::map<RESOLUTION, RESOLUTION_INFO> resolutionInfos;
613 vector<RESOLUTION_WHR> resolutions = g_Windowing.ScreenResolutions(info.iScreen, info.fRefreshRate);
614 for (vector<RESOLUTION_WHR>::const_iterator resolution = resolutions.begin(); resolution != resolutions.end(); ++resolution)
616 list.push_back(make_pair(
617 StringUtils::Format("%dx%d%s", resolution->width, resolution->height,
618 ModeFlagsToString(resolution->flags, false).c_str()),
619 resolution->ResInfo_Index));
621 resolutionInfos.insert(make_pair((RESOLUTION)resolution->ResInfo_Index, CDisplaySettings::Get().GetResolutionInfo(resolution->ResInfo_Index)));
624 current = FindBestMatchingResolution(resolutionInfos, info.iScreen,
625 info.iScreenWidth, info.iScreenHeight,
626 info.fRefreshRate, info.dwFlags);
630 void CDisplaySettings::SettingOptionsScreensFiller(const CSetting *setting, std::vector< std::pair<std::string, int> > &list, int ¤t)
632 if (g_advancedSettings.m_canWindowed)
633 list.push_back(make_pair(g_localizeStrings.Get(242), DM_WINDOWED));
635 for (int idx = 0; idx < g_Windowing.GetNumScreens(); idx++)
637 int screen = CDisplaySettings::Get().GetResolutionInfo(RES_DESKTOP + idx).iScreen;
638 list.push_back(make_pair(StringUtils::Format(g_localizeStrings.Get(241), screen + 1), screen));
641 RESOLUTION res = CDisplaySettings::Get().GetDisplayResolution();
642 if (res == RES_WINDOW)
643 current = DM_WINDOWED;
646 RESOLUTION_INFO resInfo = CDisplaySettings::Get().GetResolutionInfo(res);
647 current = resInfo.iScreen;
651 void CDisplaySettings::SettingOptionsVerticalSyncsFiller(const CSetting *setting, std::vector< std::pair<std::string, int> > &list, int ¤t)
653 // This ifdef is intended to catch everything except Linux and FreeBSD
654 #if defined(TARGET_LINUX) && !defined(TARGET_DARWIN) && !defined(TARGET_ANDROID) && !defined(TARGET_RASPBERRY_PI)
655 list.push_back(make_pair(g_localizeStrings.Get(13101), VSYNC_DRIVER));
657 list.push_back(make_pair(g_localizeStrings.Get(13106), VSYNC_DISABLED));
658 list.push_back(make_pair(g_localizeStrings.Get(13107), VSYNC_VIDEO));
659 list.push_back(make_pair(g_localizeStrings.Get(13108), VSYNC_ALWAYS));
662 void CDisplaySettings::SettingOptionsStereoscopicModesFiller(const CSetting *setting, std::vector< std::pair<std::string, int> > &list, int ¤t)
664 for (int i = RENDER_STEREO_MODE_OFF; i < RENDER_STEREO_MODE_COUNT; i++)
666 RENDER_STEREO_MODE mode = (RENDER_STEREO_MODE) i;
667 if (g_Windowing.SupportsStereo(mode))
668 list.push_back(make_pair(CStereoscopicsManager::Get().GetLabelForStereoMode(mode), mode));
672 void CDisplaySettings::SettingOptionsPreferredStereoscopicViewModesFiller(const CSetting *setting, std::vector< std::pair<std::string, int> > &list, int ¤t)
674 SettingOptionsStereoscopicModesFiller(setting, list, current);
675 list.push_back(make_pair(g_localizeStrings.Get(36525), RENDER_STEREO_MODE_AUTO)); // option for autodetect